about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakub Beránek <jakub.beranek@vsb.cz>2024-06-29 16:00:23 +0200
committerJakub Beránek <jakub.beranek@vsb.cz>2024-07-03 21:13:55 +0200
commit60c20bfe0c6e34d36d9a40d835ce6946a10f0238 (patch)
tree9493a6f666ad6b6612e72929af72c47c567c2699
parente8c8860142a999d086efe579e8e17df8437a9730 (diff)
downloadrust-60c20bfe0c6e34d36d9a40d835ce6946a10f0238.tar.gz
rust-60c20bfe0c6e34d36d9a40d835ce6946a10f0238.zip
Refactor command outcome handling
To handle the case of failing to start a `BootstrapCommand`.
-rw-r--r--src/bootstrap/src/core/sanity.rs15
-rw-r--r--src/bootstrap/src/lib.rs77
-rw-r--r--src/bootstrap/src/utils/exec.rs52
3 files changed, 93 insertions, 51 deletions
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index d64923e69cb..78862ccb8cd 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -13,7 +13,6 @@ use std::env;
 use std::ffi::{OsStr, OsString};
 use std::fs;
 use std::path::PathBuf;
-use std::process::Command;
 
 #[cfg(not(feature = "bootstrap-self-test"))]
 use crate::builder::Builder;
@@ -25,7 +24,6 @@ use std::collections::HashSet;
 use crate::builder::Kind;
 use crate::core::config::Target;
 use crate::utils::exec::BootstrapCommand;
-use crate::utils::helpers::output;
 use crate::Build;
 
 pub struct Finder {
@@ -210,11 +208,14 @@ than building it.
         .or_else(|| cmd_finder.maybe_have("reuse"));
 
     #[cfg(not(feature = "bootstrap-self-test"))]
-    let stage0_supported_target_list: HashSet<String> =
-        output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"]))
-            .lines()
-            .map(|s| s.to_string())
-            .collect();
+    let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output(
+        &mut BootstrapCommand::new(&build.config.initial_rustc)
+            .args(["--print", "target-list"])
+            .command,
+    )
+    .lines()
+    .map(|s| s.to_string())
+    .collect();
 
     // We're gonna build some custom C code here and there, host triples
     // also build some C++ shims for LLVM so we need a C++ compiler.
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index dfaa1e5eb12..309fec474a1 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -23,14 +23,13 @@ use std::fmt::Display;
 use std::fs::{self, File};
 use std::io;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::{Command, Output, Stdio};
 use std::str;
 use std::sync::OnceLock;
 use std::time::SystemTime;
 
 use build_helper::ci::{gha, CiEnv};
 use build_helper::exit;
-use build_helper::util::fail;
 use filetime::FileTime;
 use sha2::digest::Digest;
 use termcolor::{ColorChoice, StandardStream, WriteColor};
@@ -945,43 +944,61 @@ impl Build {
 
         self.verbose(|| println!("running: {command:?}"));
 
-        let output: io::Result<CommandOutput> = match command.output_mode {
-            OutputMode::Print => command.command.status().map(|status| status.into()),
-            OutputMode::CaptureAll => command.command.output().map(|o| o.into()),
+        let output: io::Result<Output> = match command.output_mode {
+            OutputMode::Print => command.command.status().map(|status| Output {
+                status,
+                stdout: vec![],
+                stderr: vec![],
+            }),
+            OutputMode::CaptureAll => command.command.output(),
             OutputMode::CaptureStdout => {
                 command.command.stderr(Stdio::inherit());
-                command.command.output().map(|o| o.into())
+                command.command.output()
             }
         };
 
-        let output = match output {
-            Ok(output) => output,
-            Err(e) => fail(&format!("failed to execute command: {command:?}\nerror: {e}")),
-        };
-        if !output.is_success() {
-            use std::fmt::Write;
-
-            // Here we build an error message, and below we decide if it should be printed or not.
-            let mut message = String::new();
-            writeln!(
-                message,
-                "\n\nCommand {command:?} did not execute successfully.\
+        use std::fmt::Write;
+
+        let mut message = String::new();
+        let output: CommandOutput = match output {
+            // Command has succeeded
+            Ok(output) if output.status.success() => output.into(),
+            // Command has started, but then it failed
+            Ok(output) => {
+                writeln!(
+                    message,
+                    "\n\nCommand {command:?} did not execute successfully.\
             \nExpected success, got: {}",
-                output.status(),
-            )
-            .unwrap();
-
-            // If the output mode is OutputMode::Print, the output has already been printed to
-            // stdout/stderr, and we thus don't have anything captured to print anyway.
-            if matches!(command.output_mode, OutputMode::CaptureAll | OutputMode::CaptureStdout) {
-                writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
+                    output.status,
+                )
+                .unwrap();
+
+                let output: CommandOutput = output.into();
+                // If the output mode is OutputMode::Print, the output has already been printed to
+                // stdout/stderr, and we thus don't have anything captured to print anyway.
+                if matches!(command.output_mode, OutputMode::CaptureAll | OutputMode::CaptureStdout)
+                {
+                    writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
 
-                // Stderr is added to the message only if it was captured
-                if matches!(command.output_mode, OutputMode::CaptureAll) {
-                    writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
+                    // Stderr is added to the message only if it was captured
+                    if matches!(command.output_mode, OutputMode::CaptureAll) {
+                        writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
+                    }
                 }
+                output
             }
-
+            // The command did not even start
+            Err(e) => {
+                writeln!(
+                    message,
+                    "\n\nCommand {command:?} did not execute successfully.\
+            \nIt was not possible to execute the command: {e:?}"
+                )
+                .unwrap();
+                CommandOutput::did_not_start()
+            }
+        };
+        if !output.is_success() {
             match command.failure_behavior {
                 BehaviorOnFailure::DelayFail => {
                     if self.fail_fast {
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index bda6e0bfbac..78a3b917e45 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -132,25 +132,47 @@ impl From<Command> for BootstrapCommand {
     }
 }
 
+/// Represents the outcome of starting a command.
+enum CommandOutcome {
+    /// The command has started and finished with some status.
+    Finished(ExitStatus),
+    /// It was not even possible to start the command.
+    DidNotStart,
+}
+
 /// Represents the output of an executed process.
 #[allow(unused)]
-pub struct CommandOutput(Output);
+pub struct CommandOutput {
+    outcome: CommandOutcome,
+    stdout: Vec<u8>,
+    stderr: Vec<u8>,
+}
 
 impl CommandOutput {
+    pub fn did_not_start() -> Self {
+        Self { outcome: CommandOutcome::DidNotStart, stdout: vec![], stderr: vec![] }
+    }
+
     pub fn is_success(&self) -> bool {
-        self.0.status.success()
+        match self.outcome {
+            CommandOutcome::Finished(status) => status.success(),
+            CommandOutcome::DidNotStart => false,
+        }
     }
 
     pub fn is_failure(&self) -> bool {
         !self.is_success()
     }
 
-    pub fn status(&self) -> ExitStatus {
-        self.0.status
+    pub fn status(&self) -> Option<ExitStatus> {
+        match self.outcome {
+            CommandOutcome::Finished(status) => Some(status),
+            CommandOutcome::DidNotStart => None,
+        }
     }
 
     pub fn stdout(&self) -> String {
-        String::from_utf8(self.0.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
+        String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
     }
 
     pub fn stdout_if_ok(&self) -> Option<String> {
@@ -158,24 +180,26 @@ impl CommandOutput {
     }
 
     pub fn stderr(&self) -> String {
-        String::from_utf8(self.0.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
+        String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
     }
 }
 
 impl Default for CommandOutput {
     fn default() -> Self {
-        Self(Output { status: Default::default(), stdout: vec![], stderr: vec![] })
+        Self {
+            outcome: CommandOutcome::Finished(ExitStatus::default()),
+            stdout: vec![],
+            stderr: vec![],
+        }
     }
 }
 
 impl From<Output> for CommandOutput {
     fn from(output: Output) -> Self {
-        Self(output)
-    }
-}
-
-impl From<ExitStatus> for CommandOutput {
-    fn from(status: ExitStatus) -> Self {
-        Self(Output { status, stdout: vec![], stderr: vec![] })
+        Self {
+            outcome: CommandOutcome::Finished(output.status),
+            stdout: output.stdout,
+            stderr: output.stderr,
+        }
     }
 }