Skip to content

Commit

Permalink
Implement new command execution logic
Browse files Browse the repository at this point in the history
This function both handles error printing and early/late failures, but it also always returns the actual output of the command
  • Loading branch information
Kobzol committed Jun 20, 2024
1 parent 894f7a4 commit a12f541
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 2 deletions.
61 changes: 60 additions & 1 deletion src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
45 changes: 44 additions & 1 deletion src/bootstrap/src/utils/exec.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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() }
}
}

0 comments on commit a12f541

Please sign in to comment.