1
Fork 0

Add DropBomb to BootstrapCommand

This makes it harder to accidentally forget to execute a created command in bootstrap.
This commit is contained in:
Jakub Beránek 2024-07-04 16:15:59 +02:00 committed by Jakub Beránek
parent a1626d709c
commit cefd5b3834
No known key found for this signature in database
GPG key ID: 909CD0D26483516B
3 changed files with 20 additions and 0 deletions

View file

@ -938,6 +938,7 @@ impl Build {
/// Execute a command and return its output.
/// This method should be used for all command executions in bootstrap.
fn run(&self, command: &mut BootstrapCommand) -> CommandOutput {
command.mark_as_executed();
if self.config.dry_run() && !command.run_always {
return CommandOutput::default();
}

View file

@ -1,4 +1,5 @@
use crate::Build;
use build_helper::drop_bomb::DropBomb;
use std::ffi::OsStr;
use std::path::Path;
use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio};
@ -61,9 +62,13 @@ pub struct BootstrapCommand {
pub stderr: OutputMode,
// Run the command even during dry run
pub run_always: bool,
// This field makes sure that each command is executed (or disarmed) before it is dropped,
// to avoid forgetting to execute a command.
drop_bomb: DropBomb,
}
impl BootstrapCommand {
#[track_caller]
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
Command::new(program).into()
}
@ -149,18 +154,30 @@ impl BootstrapCommand {
/// Provides access to the stdlib Command inside.
/// All usages of this function should be eventually removed from bootstrap.
pub fn as_command_mut(&mut self) -> &mut Command {
// We don't know what will happen with the returned command, so we need to mark this
// command as executed proactively.
self.mark_as_executed();
&mut self.command
}
/// Mark the command as being executd, disarming the drop bomb.
/// If this method is not called before the command is dropped, its drop will panic.
pub fn mark_as_executed(&mut self) {
self.drop_bomb.defuse();
}
}
impl From<Command> for BootstrapCommand {
#[track_caller]
fn from(command: Command) -> Self {
let program = command.get_program().to_owned();
Self {
command,
failure_behavior: BehaviorOnFailure::Exit,
stdout: OutputMode::Print,
stderr: OutputMode::Print,
run_always: false,
drop_bomb: DropBomb::arm(program),
}
}
}
@ -175,6 +192,7 @@ enum CommandStatus {
/// Create a new BootstrapCommand. This is a helper function to make command creation
/// shorter than `BootstrapCommand::new`.
#[track_caller]
#[must_use]
pub fn command<S: AsRef<OsStr>>(program: S) -> BootstrapCommand {
BootstrapCommand::new(program)

View file

@ -501,6 +501,7 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String {
/// bootstrap-specific needs/hacks from a single source, rather than applying them on next to every
/// git command creation, which is painful to ensure that the required change is applied
/// on each one of them correctly.
#[track_caller]
pub fn git(source_dir: Option<&Path>) -> BootstrapCommand {
let mut git = command("git");