diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 449d8c128ec63..b53f054c18740 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -41,7 +41,7 @@ use crate::core::builder::Kind; use crate::core::config::{flags, LldMode}; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; -use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, OutputMode}; +use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode}; use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; mod core; @@ -958,6 +958,65 @@ impl Build { }) } + fn run_tracked(&self, command: BootstrapCommand) -> CommandOutput { + if self.config.dry_run() { + return CommandOutput::default(); + } + + self.verbose(|| println!("running: {command:?}")); + + let (output, print_error): (io::Result, bool) = match command.output_mode { + mode @ (OutputMode::PrintAll | OutputMode::PrintOutput) => ( + command.command.status().map(|status| status.into()), + matches!(mode, OutputMode::PrintAll), + ), + OutputMode::SuppressOnSuccess => (command.command.output().map(|o| o.into()), true), + }; + + let output = match output { + Ok(output) => output, + Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", command, e)), + }; + if !output.is_success() { + if print_error { + println!( + "\n\nCommand did not execute successfully.\ + \nExpected success, got: {}", + output.status(), + ); + + if !self.is_verbose() { + println!("Add `-v` to see more details.\n"); + } + + self.verbose(|| { + println!( + "\nSTDOUT ----\n{}\n\ + STDERR ----\n{}\n", + output.stdout(), + output.stderr(), + ) + }); + } + + match command.failure_behavior { + BehaviorOnFailure::DelayFail => { + if self.fail_fast { + exit!(1); + } + + let mut failures = self.delayed_failures.borrow_mut(); + failures.push(format!("{command:?}")); + } + BehaviorOnFailure::Exit => { + exit!(1); + } + BehaviorOnFailure::Ignore => {} + } + } + output + } + /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode( diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 0aede2022badd..e7dedfa99f2c2 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,4 +1,4 @@ -use std::process::Command; +use std::process::{Command, ExitStatus, Output}; /// What should be done when the command fails. #[derive(Debug, Copy, Clone)] @@ -58,3 +58,46 @@ impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { } } } + +/// Represents the output of an executed process. +#[allow(unused)] +#[derive(Default)] +pub struct CommandOutput { + status: ExitStatus, + stdout: Vec, + stderr: Vec, +} + +impl CommandOutput { + pub fn is_success(&self) -> bool { + self.status.success() + } + + pub fn is_failure(&self) -> bool { + !self.is_success() + } + + pub fn status(&self) -> ExitStatus { + self.status + } + + pub fn stdout(&self) -> String { + String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8") + } + + pub fn stderr(&self) -> String { + String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8") + } +} + +impl From for CommandOutput { + fn from(output: Output) -> Self { + Self { status: output.status, stdout: output.stdout, stderr: output.stderr } + } +} + +impl From for CommandOutput { + fn from(status: ExitStatus) -> Self { + Self { status, stdout: Default::default(), stderr: Default::default() } + } +}