Skip to content

Commit 60c20bf

Browse files
committed
Refactor command outcome handling
To handle the case of failing to start a `BootstrapCommand`.
1 parent e8c8860 commit 60c20bf

File tree

3 files changed

+93
-51
lines changed

3 files changed

+93
-51
lines changed

src/bootstrap/src/core/sanity.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::env;
1313
use std::ffi::{OsStr, OsString};
1414
use std::fs;
1515
use std::path::PathBuf;
16-
use std::process::Command;
1716

1817
#[cfg(not(feature = "bootstrap-self-test"))]
1918
use crate::builder::Builder;
@@ -25,7 +24,6 @@ use std::collections::HashSet;
2524
use crate::builder::Kind;
2625
use crate::core::config::Target;
2726
use crate::utils::exec::BootstrapCommand;
28-
use crate::utils::helpers::output;
2927
use crate::Build;
3028

3129
pub struct Finder {
@@ -210,11 +208,14 @@ than building it.
210208
.or_else(|| cmd_finder.maybe_have("reuse"));
211209

212210
#[cfg(not(feature = "bootstrap-self-test"))]
213-
let stage0_supported_target_list: HashSet<String> =
214-
output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"]))
215-
.lines()
216-
.map(|s| s.to_string())
217-
.collect();
211+
let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output(
212+
&mut BootstrapCommand::new(&build.config.initial_rustc)
213+
.args(["--print", "target-list"])
214+
.command,
215+
)
216+
.lines()
217+
.map(|s| s.to_string())
218+
.collect();
218219

219220
// We're gonna build some custom C code here and there, host triples
220221
// also build some C++ shims for LLVM so we need a C++ compiler.

src/bootstrap/src/lib.rs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ use std::fmt::Display;
2323
use std::fs::{self, File};
2424
use std::io;
2525
use std::path::{Path, PathBuf};
26-
use std::process::{Command, Stdio};
26+
use std::process::{Command, Output, Stdio};
2727
use std::str;
2828
use std::sync::OnceLock;
2929
use std::time::SystemTime;
3030

3131
use build_helper::ci::{gha, CiEnv};
3232
use build_helper::exit;
33-
use build_helper::util::fail;
3433
use filetime::FileTime;
3534
use sha2::digest::Digest;
3635
use termcolor::{ColorChoice, StandardStream, WriteColor};
@@ -945,43 +944,61 @@ impl Build {
945944

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

948-
let output: io::Result<CommandOutput> = match command.output_mode {
949-
OutputMode::Print => command.command.status().map(|status| status.into()),
950-
OutputMode::CaptureAll => command.command.output().map(|o| o.into()),
947+
let output: io::Result<Output> = match command.output_mode {
948+
OutputMode::Print => command.command.status().map(|status| Output {
949+
status,
950+
stdout: vec![],
951+
stderr: vec![],
952+
}),
953+
OutputMode::CaptureAll => command.command.output(),
951954
OutputMode::CaptureStdout => {
952955
command.command.stderr(Stdio::inherit());
953-
command.command.output().map(|o| o.into())
956+
command.command.output()
954957
}
955958
};
956959

957-
let output = match output {
958-
Ok(output) => output,
959-
Err(e) => fail(&format!("failed to execute command: {command:?}\nerror: {e}")),
960-
};
961-
if !output.is_success() {
962-
use std::fmt::Write;
963-
964-
// Here we build an error message, and below we decide if it should be printed or not.
965-
let mut message = String::new();
966-
writeln!(
967-
message,
968-
"\n\nCommand {command:?} did not execute successfully.\
960+
use std::fmt::Write;
961+
962+
let mut message = String::new();
963+
let output: CommandOutput = match output {
964+
// Command has succeeded
965+
Ok(output) if output.status.success() => output.into(),
966+
// Command has started, but then it failed
967+
Ok(output) => {
968+
writeln!(
969+
message,
970+
"\n\nCommand {command:?} did not execute successfully.\
969971
\nExpected success, got: {}",
970-
output.status(),
971-
)
972-
.unwrap();
973-
974-
// If the output mode is OutputMode::Print, the output has already been printed to
975-
// stdout/stderr, and we thus don't have anything captured to print anyway.
976-
if matches!(command.output_mode, OutputMode::CaptureAll | OutputMode::CaptureStdout) {
977-
writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
972+
output.status,
973+
)
974+
.unwrap();
975+
976+
let output: CommandOutput = output.into();
977+
// If the output mode is OutputMode::Print, the output has already been printed to
978+
// stdout/stderr, and we thus don't have anything captured to print anyway.
979+
if matches!(command.output_mode, OutputMode::CaptureAll | OutputMode::CaptureStdout)
980+
{
981+
writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
978982

979-
// Stderr is added to the message only if it was captured
980-
if matches!(command.output_mode, OutputMode::CaptureAll) {
981-
writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
983+
// Stderr is added to the message only if it was captured
984+
if matches!(command.output_mode, OutputMode::CaptureAll) {
985+
writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
986+
}
982987
}
988+
output
983989
}
984-
990+
// The command did not even start
991+
Err(e) => {
992+
writeln!(
993+
message,
994+
"\n\nCommand {command:?} did not execute successfully.\
995+
\nIt was not possible to execute the command: {e:?}"
996+
)
997+
.unwrap();
998+
CommandOutput::did_not_start()
999+
}
1000+
};
1001+
if !output.is_success() {
9851002
match command.failure_behavior {
9861003
BehaviorOnFailure::DelayFail => {
9871004
if self.fail_fast {

src/bootstrap/src/utils/exec.rs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,50 +132,74 @@ impl From<Command> for BootstrapCommand {
132132
}
133133
}
134134

135+
/// Represents the outcome of starting a command.
136+
enum CommandOutcome {
137+
/// The command has started and finished with some status.
138+
Finished(ExitStatus),
139+
/// It was not even possible to start the command.
140+
DidNotStart,
141+
}
142+
135143
/// Represents the output of an executed process.
136144
#[allow(unused)]
137-
pub struct CommandOutput(Output);
145+
pub struct CommandOutput {
146+
outcome: CommandOutcome,
147+
stdout: Vec<u8>,
148+
stderr: Vec<u8>,
149+
}
138150

139151
impl CommandOutput {
152+
pub fn did_not_start() -> Self {
153+
Self { outcome: CommandOutcome::DidNotStart, stdout: vec![], stderr: vec![] }
154+
}
155+
140156
pub fn is_success(&self) -> bool {
141-
self.0.status.success()
157+
match self.outcome {
158+
CommandOutcome::Finished(status) => status.success(),
159+
CommandOutcome::DidNotStart => false,
160+
}
142161
}
143162

144163
pub fn is_failure(&self) -> bool {
145164
!self.is_success()
146165
}
147166

148-
pub fn status(&self) -> ExitStatus {
149-
self.0.status
167+
pub fn status(&self) -> Option<ExitStatus> {
168+
match self.outcome {
169+
CommandOutcome::Finished(status) => Some(status),
170+
CommandOutcome::DidNotStart => None,
171+
}
150172
}
151173

152174
pub fn stdout(&self) -> String {
153-
String::from_utf8(self.0.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
175+
String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
154176
}
155177

156178
pub fn stdout_if_ok(&self) -> Option<String> {
157179
if self.is_success() { Some(self.stdout()) } else { None }
158180
}
159181

160182
pub fn stderr(&self) -> String {
161-
String::from_utf8(self.0.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
183+
String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
162184
}
163185
}
164186

165187
impl Default for CommandOutput {
166188
fn default() -> Self {
167-
Self(Output { status: Default::default(), stdout: vec![], stderr: vec![] })
189+
Self {
190+
outcome: CommandOutcome::Finished(ExitStatus::default()),
191+
stdout: vec![],
192+
stderr: vec![],
193+
}
168194
}
169195
}
170196

171197
impl From<Output> for CommandOutput {
172198
fn from(output: Output) -> Self {
173-
Self(output)
174-
}
175-
}
176-
177-
impl From<ExitStatus> for CommandOutput {
178-
fn from(status: ExitStatus) -> Self {
179-
Self(Output { status, stdout: vec![], stderr: vec![] })
199+
Self {
200+
outcome: CommandOutcome::Finished(output.status),
201+
stdout: output.stdout,
202+
stderr: output.stderr,
203+
}
180204
}
181205
}

0 commit comments

Comments
 (0)