Skip to content

Commit 2a9bcbb

Browse files
committed
Consolidate semantics of failure behaviors and output capturing in BootstrapCommand
1 parent fe8058e commit 2a9bcbb

File tree

4 files changed

+69
-68
lines changed

4 files changed

+69
-68
lines changed

src/bootstrap/src/core/build_steps/format.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,11 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
160160
override_builder.add(&format!("!{ignore}")).expect(&ignore);
161161
}
162162
}
163-
let git_available =
164-
build.run(helpers::git(None).print_on_failure().arg("--version")).is_success();
163+
let git_available = build.run(helpers::git(None).capture().arg("--version")).is_success();
165164

166165
let mut adjective = None;
167166
if git_available {
168-
let in_working_tree =
169-
build.run(helpers::git(Some(&build.src)).print_on_failure()).is_success();
167+
let in_working_tree = build.run(helpers::git(Some(&build.src)).capture()).is_success();
170168
if in_working_tree {
171169
let untracked_paths_output = output(
172170
&mut helpers::git(Some(&build.src))

src/bootstrap/src/core/build_steps/test.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
2626
use crate::core::config::flags::get_completion;
2727
use crate::core::config::flags::Subcommand;
2828
use crate::core::config::TargetSelection;
29-
use crate::utils::exec::{BootstrapCommand, OutputMode};
29+
use crate::utils::exec::BootstrapCommand;
3030
use crate::utils::helpers::{
3131
self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
3232
linker_args, linker_flags, output, t, target_supports_cranelift_backend, up_to_date,
@@ -2309,7 +2309,7 @@ impl Step for ErrorIndex {
23092309
let guard =
23102310
builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
23112311
let _time = helpers::timeit(builder);
2312-
builder.run(tool.output_mode(OutputMode::OnlyOnFailure));
2312+
builder.run(tool.capture());
23132313
drop(guard);
23142314
// The tests themselves need to link to std, so make sure it is
23152315
// available.
@@ -2340,7 +2340,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
23402340

23412341
cmd = cmd.delay_failure();
23422342
if !builder.config.verbose_tests {
2343-
cmd = cmd.print_on_failure();
2343+
cmd = cmd.capture();
23442344
}
23452345
builder.run(cmd).is_success()
23462346
}

src/bootstrap/src/lib.rs

+37-35
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ impl Build {
607607
let output = self
608608
.run(
609609
helpers::git(Some(&self.src))
610-
.quiet()
610+
.capture()
611611
.args(["config", "--file"])
612612
.arg(self.config.src.join(".gitmodules"))
613613
.args(["--get-regexp", "path"]),
@@ -971,60 +971,62 @@ impl Build {
971971

972972
self.verbose(|| println!("running: {command:?}"));
973973

974-
let output_mode = command.output_mode.unwrap_or_else(|| match self.is_verbose() {
975-
true => OutputMode::All,
976-
false => OutputMode::OnlyOutput,
977-
});
978-
let (output, print_error): (io::Result<CommandOutput>, bool) = match output_mode {
979-
mode @ (OutputMode::All | OutputMode::OnlyOutput) => (
980-
command.command.status().map(|status| status.into()),
981-
matches!(mode, OutputMode::All),
982-
),
983-
mode @ (OutputMode::OnlyOnFailure | OutputMode::Quiet) => (
984-
command.command.output().map(|o| o.into()),
985-
matches!(mode, OutputMode::OnlyOnFailure),
986-
),
974+
let output: io::Result<CommandOutput> = match command.output_mode {
975+
OutputMode::Print => command.command.status().map(|status| status.into()),
976+
OutputMode::CaptureAll => command.command.output().map(|o| o.into()),
977+
OutputMode::CaptureStdout => {
978+
command.command.stderr(Stdio::inherit());
979+
command.command.output().map(|o| o.into())
980+
}
987981
};
988982

989983
let output = match output {
990984
Ok(output) => output,
991-
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", command, e)),
985+
Err(e) => fail(&format!("failed to execute command: {command:?}\nerror: {e}")),
992986
};
993987
if !output.is_success() {
994-
if print_error {
995-
println!(
996-
"\n\nCommand did not execute successfully.\
997-
\nExpected success, got: {}",
998-
output.status(),
999-
);
988+
use std::fmt::Write;
989+
990+
// Here we build an error message, and below we decide if it should be printed or not.
991+
let mut message = String::new();
992+
writeln!(
993+
message,
994+
"\n\nCommand {command:?} did not execute successfully.\
995+
\nExpected success, got: {}",
996+
output.status(),
997+
)
998+
.unwrap();
1000999

1001-
if !self.is_verbose() {
1002-
println!("Add `-v` to see more details.\n");
1003-
}
1000+
// If the output mode is OutputMode::Print, the output has already been printed to
1001+
// stdout/stderr, and we thus don't have anything captured to print anyway.
1002+
if matches!(command.output_mode, OutputMode::CaptureAll | OutputMode::CaptureStdout) {
1003+
writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
10041004

1005-
self.verbose(|| {
1006-
println!(
1007-
"\nSTDOUT ----\n{}\n\
1008-
STDERR ----\n{}\n",
1009-
output.stdout(),
1010-
output.stderr(),
1011-
)
1012-
});
1005+
// Stderr is added to the message only if it was captured
1006+
if matches!(command.output_mode, OutputMode::CaptureAll) {
1007+
writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
1008+
}
10131009
}
10141010

10151011
match command.failure_behavior {
10161012
BehaviorOnFailure::DelayFail => {
10171013
if self.fail_fast {
1014+
println!("{message}");
10181015
exit!(1);
10191016
}
10201017

10211018
let mut failures = self.delayed_failures.borrow_mut();
1022-
failures.push(format!("{command:?}"));
1019+
failures.push(message);
10231020
}
10241021
BehaviorOnFailure::Exit => {
1022+
println!("{message}");
10251023
exit!(1);
10261024
}
1027-
BehaviorOnFailure::Ignore => {}
1025+
BehaviorOnFailure::Ignore => {
1026+
// If failures are allowed, either the error has been printed already
1027+
// (OutputMode::Print) or the user used a capture output mode and wants to
1028+
// handle the error output on their own.
1029+
}
10281030
}
10291031
}
10301032
output
@@ -1515,7 +1517,7 @@ impl Build {
15151517
// (Note that we use a `..` range, not the `...` symmetric difference.)
15161518
self.run(
15171519
helpers::git(Some(&self.src))
1518-
.quiet()
1520+
.capture()
15191521
.arg("rev-list")
15201522
.arg("--count")
15211523
.arg("--merges")

src/bootstrap/src/utils/exec.rs

+27-26
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,19 @@ pub enum BehaviorOnFailure {
1313
Ignore,
1414
}
1515

16-
/// How should the output of the command be handled.
16+
/// How should the output of the command be handled (whether it should be captured or printed).
1717
#[derive(Debug, Copy, Clone)]
1818
pub enum OutputMode {
19-
/// Print both the output (by inheriting stdout/stderr) and also the command itself, if it
20-
/// fails.
21-
All,
22-
/// Print the output (by inheriting stdout/stderr).
23-
OnlyOutput,
24-
/// Suppress the output if the command succeeds, otherwise print the output.
25-
OnlyOnFailure,
26-
/// Suppress the output of the command.
27-
Quiet,
19+
/// Prints the stdout/stderr of the command to stdout/stderr of bootstrap (by inheriting these
20+
/// streams).
21+
/// Corresponds to calling `cmd.status()`.
22+
Print,
23+
/// Captures the stdout and stderr of the command into memory.
24+
/// Corresponds to calling `cmd.output()`.
25+
CaptureAll,
26+
/// Captures the stdout of the command into memory, inherits stderr.
27+
/// Corresponds to calling `cmd.output()`.
28+
CaptureStdout,
2829
}
2930

3031
/// Wrapper around `std::process::Command`.
@@ -34,26 +35,26 @@ pub enum OutputMode {
3435
/// If you want to delay failures until the end of bootstrap, use [delay_failure].
3536
///
3637
/// By default, the command will print its stdout/stderr to stdout/stderr of bootstrap
37-
/// ([OutputMode::OnlyOutput]). If bootstrap uses verbose mode, then it will also print the
38-
/// command itself in case of failure ([OutputMode::All]).
39-
/// If you want to handle the output programmatically, use `output_mode(OutputMode::OnlyOnFailure)`,
40-
/// which will print the output only if the command fails.
38+
/// ([OutputMode::Print]).
39+
/// If you want to handle the output programmatically, use [BootstrapCommand::capture].
40+
///
41+
/// Bootstrap will print a debug log to stdout if the command fails and failure is not allowed.
4142
///
4243
/// [allow_failure]: BootstrapCommand::allow_failure
4344
/// [delay_failure]: BootstrapCommand::delay_failure
4445
#[derive(Debug)]
4546
pub struct BootstrapCommand {
4647
pub command: Command,
4748
pub failure_behavior: BehaviorOnFailure,
48-
pub output_mode: Option<OutputMode>,
49+
pub output_mode: OutputMode,
4950
}
5051

5152
impl BootstrapCommand {
5253
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
5354
Self {
5455
command: Command::new(program),
5556
failure_behavior: BehaviorOnFailure::Exit,
56-
output_mode: None,
57+
output_mode: OutputMode::Print,
5758
}
5859
}
5960

@@ -110,18 +111,14 @@ impl BootstrapCommand {
110111
Self { failure_behavior: BehaviorOnFailure::Ignore, ..self }
111112
}
112113

113-
/// Do not print the output of the command, unless it fails.
114-
pub fn print_on_failure(self) -> Self {
115-
self.output_mode(OutputMode::OnlyOnFailure)
116-
}
117-
118-
/// Do not print the output of the command.
119-
pub fn quiet(self) -> Self {
120-
self.output_mode(OutputMode::Quiet)
114+
/// Capture the output of the command, do not print it.
115+
pub fn capture(self) -> Self {
116+
Self { output_mode: OutputMode::CaptureAll, ..self }
121117
}
122118

123-
pub fn output_mode(self, output_mode: OutputMode) -> Self {
124-
Self { output_mode: Some(output_mode), ..self }
119+
/// Capture stdout of the command, do not print it.
120+
pub fn capture_stdout(self) -> Self {
121+
Self { output_mode: OutputMode::CaptureStdout, ..self }
125122
}
126123
}
127124

@@ -154,6 +151,10 @@ impl CommandOutput {
154151
String::from_utf8(self.0.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
155152
}
156153

154+
pub fn stdout_if_ok(&self) -> Option<String> {
155+
if self.is_success() { Some(self.stdout()) } else { None }
156+
}
157+
157158
pub fn stderr(&self) -> String {
158159
String::from_utf8(self.0.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
159160
}

0 commit comments

Comments
 (0)