Skip to content

Commit 2b6e996

Browse files
committed
Auto merge of #5862 - kennytm:capture-rustc-output, r=alexcrichton
Fully capture rustc and rustdoc output when -Zcompile-progress is passed Fixes #5764 and #5695. On Windows, we will parse the ANSI escape code into console commands via my `fwdansi` package, based on @ishitatsuyuki's idea in #5695 (comment). Outside of Windows the content is forwarded as-is.
2 parents 524a578 + 641f7ff commit 2b6e996

File tree

6 files changed

+111
-55
lines changed

6 files changed

+111
-55
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ core-foundation = { version = "0.6.0", features = ["mac_os_10_7_support"] }
6767

6868
[target.'cfg(windows)'.dependencies]
6969
miow = "0.3.1"
70+
fwdansi = "1"
7071

7172
[target.'cfg(windows)'.dependencies.winapi]
7273
version = "0.3"

src/cargo/core/compiler/custom_build.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
254254
let build_scripts = super::load_build_deps(cx, unit);
255255
let kind = unit.kind;
256256
let json_messages = bcx.build_config.json_messages();
257+
let extra_verbose = bcx.config.extra_verbose();
257258

258259
// Check to see if the build script has already run, and if it has keep
259260
// track of whether it has told us about some explicit dependencies
@@ -320,17 +321,12 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
320321
state.build_plan(invocation_name, cmd.clone(), Arc::new(Vec::new()));
321322
} else {
322323
state.running(&cmd);
323-
let output = cmd.exec_with_streaming(
324-
&mut |out_line| {
325-
state.stdout(out_line);
326-
Ok(())
327-
},
328-
&mut |err_line| {
329-
state.stderr(err_line);
330-
Ok(())
331-
},
332-
true,
333-
).map_err(|e| {
324+
let output = if extra_verbose {
325+
state.capture_output(cmd, true)
326+
} else {
327+
cmd.exec_with_output()
328+
};
329+
let output = output.map_err(|e| {
334330
format_err!(
335331
"failed to run custom build command for `{}`\n{}",
336332
pkg_name,

src/cargo/core/compiler/job_queue.rs

+27-28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::io;
55
use std::mem;
66
use std::sync::mpsc::{channel, Receiver, Sender};
77
use std::sync::Arc;
8+
use std::process::Output;
89

910
use crossbeam_utils;
1011
use crossbeam_utils::thread::Scope;
@@ -107,12 +108,22 @@ impl<'a> JobState<'a> {
107108
.send(Message::BuildPlanMsg(module_name, cmd, filenames));
108109
}
109110

110-
pub fn stdout(&self, out: &str) {
111-
let _ = self.tx.send(Message::Stdout(out.to_string()));
112-
}
113-
114-
pub fn stderr(&self, err: &str) {
115-
let _ = self.tx.send(Message::Stderr(err.to_string()));
111+
pub fn capture_output(
112+
&self,
113+
cmd: ProcessBuilder,
114+
print_output: bool,
115+
) -> CargoResult<Output> {
116+
cmd.exec_with_streaming(
117+
&mut |out| {
118+
let _ = self.tx.send(Message::Stdout(out.to_string()));
119+
Ok(())
120+
},
121+
&mut |err| {
122+
let _ = self.tx.send(Message::Stderr(err.to_string()));
123+
Ok(())
124+
},
125+
print_output,
126+
)
116127
}
117128
}
118129

@@ -226,7 +237,6 @@ impl<'a> JobQueue<'a> {
226237
// currently a pretty big task. This is issue #5695.
227238
let mut error = None;
228239
let mut progress = Progress::with_style("Building", ProgressStyle::Ratio, cx.bcx.config);
229-
let mut progress_maybe_changed = true; // avoid flickering due to build script
230240
if !cx.bcx.config.cli_unstable().compile_progress {
231241
progress.disable();
232242
}
@@ -274,22 +284,13 @@ impl<'a> JobQueue<'a> {
274284
// to the jobserver itself.
275285
tokens.truncate(self.active.len() - 1);
276286

277-
if progress_maybe_changed {
278-
let count = total - self.queue.len();
279-
let active_names = self.active.iter()
280-
.map(Key::name_for_progress)
281-
.collect::<Vec<_>>();
282-
drop(progress.tick_now(count, total, &format!(": {}", active_names.join(", "))));
283-
}
287+
let count = total - self.queue.len();
288+
let active_names = self.active.iter()
289+
.map(Key::name_for_progress)
290+
.collect::<Vec<_>>();
291+
drop(progress.tick_now(count, total, &format!(": {}", active_names.join(", "))));
284292
let event = self.rx.recv().unwrap();
285-
286-
progress_maybe_changed = match event {
287-
Message::Stdout(_) | Message::Stderr(_) => cx.bcx.config.extra_verbose(),
288-
_ => true,
289-
};
290-
if progress_maybe_changed {
291-
progress.clear();
292-
}
293+
progress.clear();
293294

294295
match event {
295296
Message::Run(cmd) => {
@@ -302,14 +303,12 @@ impl<'a> JobQueue<'a> {
302303
plan.update(&module_name, &cmd, &filenames)?;
303304
}
304305
Message::Stdout(out) => {
305-
if cx.bcx.config.extra_verbose() {
306-
println!("{}", out);
307-
}
306+
println!("{}", out);
308307
}
309308
Message::Stderr(err) => {
310-
if cx.bcx.config.extra_verbose() {
311-
writeln!(cx.bcx.config.shell().err(), "{}", err)?;
312-
}
309+
let mut shell = cx.bcx.config.shell();
310+
shell.print_ansi(err.as_bytes())?;
311+
shell.err().write(b"\n")?;
313312
}
314313
Message::FixDiagnostic(msg) => {
315314
print.print(&msg)?;

src/cargo/core/compiler/mod.rs

+53-16
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ pub trait Executor: Send + Sync + 'static {
7272
Ok(())
7373
}
7474

75+
fn exec_and_capture_output(
76+
&self,
77+
cmd: ProcessBuilder,
78+
id: &PackageId,
79+
target: &Target,
80+
mode: CompileMode,
81+
_state: &job_queue::JobState<'_>,
82+
) -> CargoResult<()> {
83+
// we forward to exec() to keep RLS working.
84+
self.exec(cmd, id, target, mode)
85+
}
86+
7587
fn exec_json(
7688
&self,
7789
cmd: ProcessBuilder,
@@ -97,7 +109,18 @@ pub trait Executor: Send + Sync + 'static {
97109
#[derive(Copy, Clone)]
98110
pub struct DefaultExecutor;
99111

100-
impl Executor for DefaultExecutor {}
112+
impl Executor for DefaultExecutor {
113+
fn exec_and_capture_output(
114+
&self,
115+
cmd: ProcessBuilder,
116+
_id: &PackageId,
117+
_target: &Target,
118+
_mode: CompileMode,
119+
state: &job_queue::JobState<'_>,
120+
) -> CargoResult<()> {
121+
state.capture_output(cmd, false).map(drop)
122+
}
123+
}
101124

102125
fn compile<'a, 'cfg: 'a>(
103126
cx: &mut Context<'a, 'cfg>,
@@ -216,6 +239,8 @@ fn rustc<'a, 'cfg>(
216239
.unwrap_or_else(|| cx.bcx.config.cwd())
217240
.to_path_buf();
218241

242+
let should_capture_output = cx.bcx.config.cli_unstable().compile_progress;
243+
219244
return Ok(Work::new(move |state| {
220245
// Only at runtime have we discovered what the extra -L and -l
221246
// arguments are for native libraries, so we process those here. We
@@ -291,7 +316,12 @@ fn rustc<'a, 'cfg>(
291316
} else if build_plan {
292317
state.build_plan(buildkey, rustc.clone(), outputs.clone());
293318
} else {
294-
exec.exec(rustc, &package_id, &target, mode)
319+
let exec_result = if should_capture_output {
320+
exec.exec_and_capture_output(rustc, &package_id, &target, mode, state)
321+
} else {
322+
exec.exec(rustc, &package_id, &target, mode)
323+
};
324+
exec_result
295325
.map_err(Internal::new)
296326
.chain_err(|| format!("Could not compile `{}`.", name))?;
297327
}
@@ -580,6 +610,7 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
580610
rustdoc.arg("--crate-name").arg(&unit.target.crate_name());
581611
add_path_args(bcx, unit, &mut rustdoc);
582612
add_cap_lints(bcx, unit, &mut rustdoc);
613+
add_color(bcx, &mut rustdoc);
583614

584615
if unit.kind != Kind::Host {
585616
if let Some(ref target) = bcx.build_config.requested_target {
@@ -612,6 +643,8 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
612643
let build_state = cx.build_state.clone();
613644
let key = (unit.pkg.package_id().clone(), unit.kind);
614645

646+
let should_capture_output = cx.bcx.config.cli_unstable().compile_progress;
647+
615648
Ok(Work::new(move |state| {
616649
if let Some(output) = build_state.outputs.lock().unwrap().get(&key) {
617650
for cfg in output.cfgs.iter() {
@@ -622,9 +655,13 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
622655
}
623656
}
624657
state.running(&rustdoc);
625-
rustdoc
626-
.exec()
627-
.chain_err(|| format!("Could not document `{}`.", name))?;
658+
659+
let exec_result = if should_capture_output {
660+
state.capture_output(rustdoc, false).map(drop)
661+
} else {
662+
rustdoc.exec()
663+
};
664+
exec_result.chain_err(|| format!("Could not document `{}`.", name))?;
628665
Ok(())
629666
}))
630667
}
@@ -672,6 +709,15 @@ fn add_cap_lints(bcx: &BuildContext, unit: &Unit, cmd: &mut ProcessBuilder) {
672709
}
673710
}
674711

712+
fn add_color(bcx: &BuildContext, cmd: &mut ProcessBuilder) {
713+
let capture_output = bcx.config.cli_unstable().compile_progress;
714+
let shell = bcx.config.shell();
715+
if capture_output || shell.color_choice() != ColorChoice::CargoAuto {
716+
let color = if shell.supports_color() { "always" } else { "never" };
717+
cmd.args(&["--color", color]);
718+
}
719+
}
720+
675721
fn build_base_args<'a, 'cfg>(
676722
cx: &mut Context<'a, 'cfg>,
677723
cmd: &mut ProcessBuilder,
@@ -696,17 +742,8 @@ fn build_base_args<'a, 'cfg>(
696742

697743
cmd.arg("--crate-name").arg(&unit.target.crate_name());
698744

699-
add_path_args(&cx.bcx, unit, cmd);
700-
701-
match bcx.config.shell().color_choice() {
702-
ColorChoice::Always => {
703-
cmd.arg("--color").arg("always");
704-
}
705-
ColorChoice::Never => {
706-
cmd.arg("--color").arg("never");
707-
}
708-
ColorChoice::CargoAuto => {}
709-
}
745+
add_path_args(bcx, unit, cmd);
746+
add_color(bcx, cmd);
710747

711748
if bcx.build_config.json_messages() {
712749
cmd.arg("--error-format").arg("json");

src/cargo/core/shell.rs

+21
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,27 @@ impl Shell {
231231
ShellOut::Write(_) => ColorChoice::Never,
232232
}
233233
}
234+
235+
/// Whether the shell supports color.
236+
pub fn supports_color(&self) -> bool {
237+
match &self.err {
238+
ShellOut::Write(_) => false,
239+
ShellOut::Stream { stream, .. } => stream.supports_color(),
240+
}
241+
}
242+
243+
/// Prints a message and translates ANSI escape code into console colors.
244+
pub fn print_ansi(&mut self, message: &[u8]) -> CargoResult<()> {
245+
#[cfg(windows)]
246+
{
247+
if let ShellOut::Stream { stream, .. } = &mut self.err {
248+
::fwdansi::write_ansi(stream, message)?;
249+
return Ok(());
250+
}
251+
}
252+
self.err().write_all(message)?;
253+
Ok(())
254+
}
234255
}
235256

236257
impl Default for Shell {

src/cargo/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern crate failure;
2626
extern crate filetime;
2727
extern crate flate2;
2828
extern crate fs2;
29+
#[cfg(windows)]
30+
extern crate fwdansi;
2931
extern crate git2;
3032
extern crate glob;
3133
extern crate hex;

0 commit comments

Comments
 (0)