From cefd5b38348d129f657597f741dcb45cf2ca44b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 16:15:59 +0200 Subject: [PATCH] Add `DropBomb` to `BootstrapCommand` This makes it harder to accidentally forget to execute a created command in bootstrap. --- src/bootstrap/src/lib.rs | 1 + src/bootstrap/src/utils/exec.rs | 18 ++++++++++++++++++ src/bootstrap/src/utils/helpers.rs | 1 + 3 files changed, 20 insertions(+) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index b79e1badb2a9..4a69068b51e7 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -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(); } diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 09801a05fa36..a9291d45f216 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -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>(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 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>(program: S) -> BootstrapCommand { BootstrapCommand::new(program) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index fd8aee37d900..f695b3229fe6 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -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");