about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakub Beránek <jakub.beranek@vsb.cz>2024-06-20 11:33:23 +0200
committerJakub Beránek <jakub.beranek@vsb.cz>2024-06-20 11:33:23 +0200
commita12f541a18087550d080225835066cd1ddd60ccf (patch)
tree0d61a9b40aa67a71eef3161e024e1278a80c9ba7
parent894f7a4ba6554d3797404bbf550d9919df060b97 (diff)
downloadrust-a12f541a18087550d080225835066cd1ddd60ccf.tar.gz
rust-a12f541a18087550d080225835066cd1ddd60ccf.zip
Implement new command execution logic
This function both handles error printing and early/late failures, but it also always returns the actual output of the command
-rw-r--r--src/bootstrap/src/lib.rs61
-rw-r--r--src/bootstrap/src/utils/exec.rs45
2 files changed, 104 insertions, 2 deletions
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 449d8c128ec..b53f054c187 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<CommandOutput>, 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 0aede2022ba..e7dedfa99f2 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<u8>,
+    stderr: Vec<u8>,
+}
+
+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<Output> for CommandOutput {
+    fn from(output: Output) -> Self {
+        Self { status: output.status, stdout: output.stdout, stderr: output.stderr }
+    }
+}
+
+impl From<ExitStatus> for CommandOutput {
+    fn from(status: ExitStatus) -> Self {
+        Self { status, stdout: Default::default(), stderr: Default::default() }
+    }
+}