diff --git a/Cargo.lock b/Cargo.lock index eba4eed3686..cafc623c185 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3420,6 +3420,7 @@ version = "0.2.0" dependencies = [ "ar", "bstr", + "build_helper", "gimli 0.28.1", "object 0.34.0", "regex", diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 714a1004281..ef2af9c2873 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2080,7 +2080,8 @@ pub fn stream_cargo( tail_args: Vec, cb: &mut dyn FnMut(CargoMessage<'_>), ) -> bool { - let mut cargo = cargo.into_cmd().command; + let mut cmd = cargo.into_cmd(); + let cargo = cmd.as_command_mut(); // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. let mut message_format = if builder.config.json_output { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7bc5405e92f..1e9d2025bc7 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1060,11 +1060,7 @@ impl Step for PlainSourceTarball { cmd.arg("--sync").arg(manifest_path); } - let config = if !builder.config.dry_run() { - cmd.capture().run(builder).stdout() - } else { - String::new() - }; + let config = cmd.capture().run(builder).stdout(); let cargo_config_dir = plain_dst_src.join(".cargo"); builder.create_dir(&cargo_config_dir); @@ -2072,11 +2068,7 @@ fn maybe_install_llvm( let mut cmd = command(llvm_config); cmd.arg("--libfiles"); builder.verbose(|| println!("running {cmd:?}")); - let files = if builder.config.dry_run() { - "".into() - } else { - cmd.capture_stdout().run(builder).stdout() - }; + let files = cmd.capture_stdout().run(builder).stdout(); let build_llvm_out = &builder.llvm_out(builder.config.build); let target_llvm_out = &builder.llvm_out(target); for file in files.trim_end().split(' ') { diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 4b35d6c5d4c..dc46af6cf48 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -146,7 +146,6 @@ impl Step for RustbookSrc

{ let out = out.join(&name); let index = out.join("index.html"); let rustbook = builder.tool_exe(Tool::Rustbook); - let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); if !builder.config.dry_run() && (!up_to_date(&src, &index) || !up_to_date(&rustbook, &index)) @@ -154,7 +153,13 @@ impl Step for RustbookSrc

{ builder.info(&format!("Rustbook ({target}) - {name}")); let _ = fs::remove_dir_all(&out); - rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder); + builder + .tool_cmd(Tool::Rustbook) + .arg("build") + .arg(&src) + .arg("-d") + .arg(&out) + .run(builder); for lang in &self.languages { let out = out.join(lang); @@ -253,10 +258,6 @@ impl Step for TheBook { // build the version info page and CSS let shared_assets = builder.ensure(SharedAssets { target }); - // build the command first so we don't nest GHA groups - // FIXME: this doesn't do anything! - builder.rustdoc_cmd(compiler); - // build the redirect pages let _guard = builder.msg_doc(compiler, "book redirect pages", target); for file in t!(fs::read_dir(redirect_path)) { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 872823506f8..41dff2123f1 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -172,7 +172,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` config.src.join("src/version"), ]); - output(&mut rev_list.command).trim().to_owned() + output(rev_list.as_command_mut()).trim().to_owned() } else if let Some(info) = channel::read_commit_info_file(&config.src) { info.sha.trim().to_owned() } else { @@ -254,7 +254,7 @@ pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { // `true` here. let llvm_sha = detect_llvm_sha(config, true); let head_sha = - output(&mut helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").command); + output(helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").as_command_mut()); let head_sha = head_sha.trim(); llvm_sha == head_sha } diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index e6a09e8cb8e..29cc5e00637 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -484,7 +484,7 @@ impl Step for Hook { fn install_git_hook_maybe(config: &Config) -> io::Result<()> { let git = helpers::git(Some(&config.src)) .args(["rev-parse", "--git-common-dir"]) - .command + .as_command_mut() .output() .map(|output| { assert!(output.status.success(), "failed to run `git`"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0b60587bb79..9b4c7c91349 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -471,16 +471,12 @@ impl Miri { // We re-use the `cargo` from above. cargo.arg("--print-sysroot"); - if builder.config.dry_run() { - String::new() - } else { - builder.verbose(|| println!("running: {cargo:?}")); - let stdout = cargo.capture_stdout().run(builder).stdout(); - // Output is "\n". - let sysroot = stdout.trim_end(); - builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}")); - sysroot.to_owned() - } + builder.verbose(|| println!("running: {cargo:?}")); + let stdout = cargo.capture_stdout().run(builder).stdout(); + // Output is "\n". + let sysroot = stdout.trim_end(); + builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}")); + sysroot.to_owned() } } @@ -1352,6 +1348,52 @@ impl Step for CrateRunMakeSupport { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CrateBuildHelper { + host: TargetSelection, +} + +impl Step for CrateBuildHelper { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/build_helper") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CrateBuildHelper { host: run.target }); + } + + /// Runs `cargo test` for build_helper. + fn run(self, builder: &Builder<'_>) { + let host = self.host; + let compiler = builder.compiler(0, host); + + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolBootstrap, + host, + "test", + "src/tools/build_helper", + SourceType::InTree, + &[], + ); + cargo.allow_features("test"); + run_cargo_test( + cargo, + &[], + &[], + "build_helper", + "build_helper self test", + compiler, + host, + builder, + ); + } +} + default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" }); default_test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes" }); @@ -2058,7 +2100,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--nightly-branch").arg(git_config.nightly_branch); // FIXME: Move CiEnv back to bootstrap, it is only used here anyway - builder.ci_env.force_coloring_in_ci(&mut cmd.command); + builder.ci_env.force_coloring_in_ci(cmd.as_command_mut()); #[cfg(feature = "build-metrics")] builder.metrics.begin_test_suite( diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index e3e7931a5a2..9ddd68da59d 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -106,7 +106,7 @@ fn check_changed_files(toolstates: &HashMap, ToolState>) { .arg("--name-status") .arg("HEAD") .arg("HEAD^") - .command + .as_command_mut() .output(); let output = match output { Ok(o) => o, @@ -329,7 +329,7 @@ fn checkout_toolstate_repo() { .arg("--depth=1") .arg(toolstate_repo()) .arg(TOOLSTATE_DIR) - .command + .as_command_mut() .status(); let success = match status { Ok(s) => s.success(), @@ -343,8 +343,13 @@ fn checkout_toolstate_repo() { /// Sets up config and authentication for modifying the toolstate repo. fn prepare_toolstate_config(token: &str) { fn git_config(key: &str, value: &str) { - let status = - helpers::git(None).arg("config").arg("--global").arg(key).arg(value).command.status(); + let status = helpers::git(None) + .arg("config") + .arg("--global") + .arg(key) + .arg(value) + .as_command_mut() + .status(); let success = match status { Ok(s) => s.success(), Err(_) => false, @@ -413,7 +418,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { .arg("-a") .arg("-m") .arg(&message) - .command + .as_command_mut() .status()); if !status.success() { success = true; @@ -424,7 +429,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { .arg("push") .arg("origin") .arg("master") - .command + .as_command_mut() .status()); // If we successfully push, exit. if status.success() { @@ -437,14 +442,14 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { .arg("fetch") .arg("origin") .arg("master") - .command + .as_command_mut() .status()); assert!(status.success()); let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR))) .arg("reset") .arg("--hard") .arg("origin/master") - .command + .as_command_mut() .status()); assert!(status.success()); } @@ -460,7 +465,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { /// `publish_toolstate.py` script if the PR passes all tests and is merged to /// master. fn publish_test_results(current_toolstate: &ToolstateData) { - let commit = t!(helpers::git(None).arg("rev-parse").arg("HEAD").command.output()); + let commit = t!(helpers::git(None).arg("rev-parse").arg("HEAD").as_command_mut().output()); let commit = t!(String::from_utf8(commit.stdout)); let toolstate_serialized = t!(serde_json::to_string(¤t_toolstate)); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 65cc1fa7478..b14a0c5f072 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -860,6 +860,7 @@ impl<'a> Builder<'a> { test::Clippy, test::CompiletestTest, test::CrateRunMakeSupport, + test::CrateBuildHelper, test::RustdocJSStd, test::RustdocJSNotStd, test::RustdocGUI, @@ -2104,7 +2105,7 @@ impl<'a> Builder<'a> { // Try to use a sysroot-relative bindir, in case it was configured absolutely. cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); - self.ci_env.force_coloring_in_ci(&mut cargo.command); + self.ci_env.force_coloring_in_ci(cargo.as_command_mut()); // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 3327df972bf..11207cf8935 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1259,7 +1259,7 @@ impl Config { cmd.arg("rev-parse").arg("--show-cdup"); // Discard stderr because we expect this to fail when building from a tarball. let output = cmd - .command + .as_command_mut() .stderr(std::process::Stdio::null()) .output() .ok() @@ -2163,7 +2163,7 @@ impl Config { let mut git = helpers::git(Some(&self.src)); git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); - output(&mut git.command) + output(git.as_command_mut()) } /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. @@ -2469,11 +2469,11 @@ impl Config { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let merge_base = output( - &mut helpers::git(Some(&self.src)) + helpers::git(Some(&self.src)) .arg("rev-list") .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) .args(["-n1", "--first-parent", "HEAD"]) - .command, + .as_command_mut(), ); let commit = merge_base.trim_end(); if commit.is_empty() { @@ -2489,7 +2489,7 @@ impl Config { .args(["diff-index", "--quiet", commit]) .arg("--") .args([self.src.join("compiler"), self.src.join("library")]) - .command + .as_command_mut() .status()) .success(); if has_changes { @@ -2563,11 +2563,11 @@ impl Config { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let merge_base = output( - &mut helpers::git(Some(&self.src)) + helpers::git(Some(&self.src)) .arg("rev-list") .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) .args(["-n1", "--first-parent", "HEAD"]) - .command, + .as_command_mut(), ); let commit = merge_base.trim_end(); if commit.is_empty() { @@ -2589,7 +2589,7 @@ impl Config { git.arg(top_level.join(path)); } - let has_changes = !t!(git.command.status()).success(); + let has_changes = !t!(git.as_command_mut().status()).success(); if has_changes { if if_unchanged { if self.verbose > 0 { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 9995da3a1e5..4bdc8ac0795 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -209,7 +209,7 @@ than building it. #[cfg(not(feature = "bootstrap-self-test"))] let stage0_supported_target_list: HashSet = crate::utils::helpers::output( - &mut command(&build.config.initial_rustc).args(["--print", "target-list"]).command, + command(&build.config.initial_rustc).args(["--print", "target-list"]).as_command_mut(), ) .lines() .map(|s| s.to_string()) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index f16afb29796..10ec7d135f0 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -493,11 +493,14 @@ impl Build { let submodule_git = || helpers::git(Some(&absolute_path)); // Determine commit checked out in submodule. - let checked_out_hash = output(&mut submodule_git().args(["rev-parse", "HEAD"]).command); + let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"]).as_command_mut()); let checked_out_hash = checked_out_hash.trim_end(); // Determine commit that the submodule *should* have. let recorded = output( - &mut helpers::git(Some(&self.src)).args(["ls-tree", "HEAD"]).arg(relative_path).command, + helpers::git(Some(&self.src)) + .args(["ls-tree", "HEAD"]) + .arg(relative_path) + .as_command_mut(), ); let actual_hash = recorded .split_whitespace() @@ -522,7 +525,7 @@ impl Build { let current_branch = { let output = helpers::git(Some(&self.src)) .args(["symbolic-ref", "--short", "HEAD"]) - .command + .as_command_mut() .stderr(Stdio::inherit()) .output(); let output = t!(output); @@ -548,7 +551,7 @@ impl Build { git }; // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails. - if !update(true).command.status().map_or(false, |status| status.success()) { + if !update(true).as_command_mut().status().map_or(false, |status| status.success()) { update(false).run(self); } @@ -934,17 +937,26 @@ impl Build { /// Execute a command and return its output. /// This method should be used for all command executions in bootstrap. + #[track_caller] fn run(&self, command: &mut BootstrapCommand) -> CommandOutput { + command.mark_as_executed(); if self.config.dry_run() && !command.run_always { return CommandOutput::default(); } - self.verbose(|| println!("running: {command:?}")); + let created_at = command.get_created_location(); + let executed_at = std::panic::Location::caller(); - command.command.stdout(command.stdout.stdio()); - command.command.stderr(command.stderr.stdio()); + self.verbose(|| { + println!("running: {command:?} (created at {created_at}, executed at {executed_at})") + }); - let output = command.command.output(); + let stdout = command.stdout.stdio(); + command.as_command_mut().stdout(stdout); + let stderr = command.stderr.stdio(); + command.as_command_mut().stderr(stderr); + + let output = command.as_command_mut().output(); use std::fmt::Write; @@ -956,8 +968,11 @@ impl Build { Ok(output) => { writeln!( message, - "\n\nCommand {command:?} did not execute successfully.\ - \nExpected success, got: {}", + r#" +Command {command:?} did not execute successfully. +Expected success, got {} +Created at: {created_at} +Executed at: {executed_at}"#, output.status, ) .unwrap(); @@ -1931,7 +1946,7 @@ fn envify(s: &str) -> String { pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String { let diff = helpers::git(Some(dir)) .arg("diff") - .command + .as_command_mut() .output() .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) .unwrap_or_default(); @@ -1941,7 +1956,7 @@ pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String { .arg("--porcelain") .arg("-z") .arg("--untracked-files=normal") - .command + .as_command_mut() .output() .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) .unwrap_or_default(); diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index 2ca86bdb0ed..f8bcb584991 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -45,7 +45,7 @@ impl GitInfo { } // Make sure git commands work - match helpers::git(Some(dir)).arg("rev-parse").command.output() { + match helpers::git(Some(dir)).arg("rev-parse").as_command_mut().output() { Ok(ref out) if out.status.success() => {} _ => return GitInfo::Absent, } @@ -58,16 +58,17 @@ impl GitInfo { // Ok, let's scrape some info let ver_date = output( - &mut helpers::git(Some(dir)) + helpers::git(Some(dir)) .arg("log") .arg("-1") .arg("--date=short") .arg("--pretty=format:%cd") - .command, + .as_command_mut(), ); - let ver_hash = output(&mut helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").command); + let ver_hash = + output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut()); let short_ver_hash = output( - &mut helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").command, + helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").as_command_mut(), ); GitInfo::Present(Some(Info { commit_date: ver_date.trim().to_string(), diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index ba963f52dc2..b0530164997 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,5 +1,7 @@ use crate::Build; +use build_helper::drop_bomb::DropBomb; use std::ffi::OsStr; +use std::fmt::{Debug, Formatter}; use std::path::Path; use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio}; @@ -53,17 +55,20 @@ impl OutputMode { /// /// [allow_failure]: BootstrapCommand::allow_failure /// [delay_failure]: BootstrapCommand::delay_failure -#[derive(Debug)] pub struct BootstrapCommand { - pub command: Command, + command: Command, pub failure_behavior: BehaviorOnFailure, pub stdout: OutputMode, 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() } @@ -142,19 +147,55 @@ impl BootstrapCommand { } /// Run the command, returning its output. + #[track_caller] pub fn run(&mut self, builder: &Build) -> CommandOutput { builder.run(self) } + + /// Provides access to the stdlib Command inside. + /// FIXME: 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 executed, 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(); + } + + /// Returns the source code location where this command was created. + pub fn get_created_location(&self) -> std::panic::Location<'static> { + self.drop_bomb.get_created_location() + } +} + +impl Debug for BootstrapCommand { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.command)?; + write!( + f, + " (failure_mode={:?}, stdout_mode={:?}, stderr_mode={:?})", + self.failure_behavior, self.stdout, self.stderr + ) + } } 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), } } } @@ -169,6 +210,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 5dd3ba96786..f695b3229fe 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -244,7 +244,7 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef>( // FIXME: get rid of this function pub fn check_run(cmd: &mut BootstrapCommand, print_cmd_on_fail: bool) -> bool { - let status = match cmd.command.status() { + let status = match cmd.as_command_mut().status() { Ok(status) => status, Err(e) => { println!("failed to execute command: {cmd:?}\nERROR: {e}"); @@ -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"); diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 2e99bc68a8b..a3d0d36e754 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -33,6 +33,7 @@ pub(crate) fn try_run_tests( stream: bool, ) -> bool { if builder.config.dry_run() { + cmd.mark_as_executed(); return true; } @@ -50,7 +51,7 @@ pub(crate) fn try_run_tests( } fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool { - let cmd = &mut cmd.command; + let cmd = cmd.as_command_mut(); cmd.stdout(Stdio::piped()); builder.verbose(|| println!("running: {cmd:?}")); diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 4f0104e4bba..f5fc94273c9 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -370,11 +370,11 @@ impl<'a> Tarball<'a> { if self.builder.rust_info().is_managed_git_subrepository() { // %ct means committer date let timestamp = helpers::output( - &mut helpers::git(Some(&self.builder.src)) + helpers::git(Some(&self.builder.src)) .arg("log") .arg("-1") .arg("--format=%ct") - .command, + .as_command_mut(), ); cmd.args(["--override-file-mtime", timestamp.trim()]); } diff --git a/src/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs similarity index 71% rename from src/tools/run-make-support/src/drop_bomb/mod.rs rename to src/tools/build_helper/src/drop_bomb/mod.rs index 2fc84892c1b..0a5bb04b55b 100644 --- a/src/tools/run-make-support/src/drop_bomb/mod.rs +++ b/src/tools/build_helper/src/drop_bomb/mod.rs @@ -12,27 +12,31 @@ use std::panic; mod tests; #[derive(Debug)] -pub(crate) struct DropBomb { +pub struct DropBomb { command: OsString, defused: bool, - armed_line: u32, + armed_location: panic::Location<'static>, } impl DropBomb { /// Arm a [`DropBomb`]. If the value is dropped without being [`defused`][Self::defused], then /// it will panic. It is expected that the command wrapper uses `#[track_caller]` to help - /// propagate the caller info from rmake.rs. + /// propagate the caller location. #[track_caller] - pub(crate) fn arm>(command: S) -> DropBomb { + pub fn arm>(command: S) -> DropBomb { DropBomb { command: command.as_ref().into(), defused: false, - armed_line: panic::Location::caller().line(), + armed_location: *panic::Location::caller(), } } + pub fn get_created_location(&self) -> panic::Location<'static> { + self.armed_location + } + /// Defuse the [`DropBomb`]. This will prevent the drop bomb from panicking when dropped. - pub(crate) fn defuse(&mut self) { + pub fn defuse(&mut self) { self.defused = true; } } @@ -41,8 +45,8 @@ impl Drop for DropBomb { fn drop(&mut self) { if !self.defused && !std::thread::panicking() { panic!( - "command constructed but not executed at line {}: `{}`", - self.armed_line, + "command constructed at `{}` was dropped without being executed: `{}`", + self.armed_location, self.command.to_string_lossy() ) } diff --git a/src/tools/run-make-support/src/drop_bomb/tests.rs b/src/tools/build_helper/src/drop_bomb/tests.rs similarity index 100% rename from src/tools/run-make-support/src/drop_bomb/tests.rs rename to src/tools/build_helper/src/drop_bomb/tests.rs diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs index 15807d1c0d8..4a4f0ca2a9d 100644 --- a/src/tools/build_helper/src/lib.rs +++ b/src/tools/build_helper/src/lib.rs @@ -1,6 +1,7 @@ //! Types and functions shared across tools in this workspace. pub mod ci; +pub mod drop_bomb; pub mod git; pub mod metrics; pub mod stage0_parser; diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index ec3b8a96ef3..969552dec84 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -11,3 +11,5 @@ wasmparser = "0.118.2" regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace gimli = "0.28.1" ar = "0.9.0" + +build_helper = { path = "../build_helper" } diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index c506c3d6b61..5017a4b88da 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -5,8 +5,8 @@ use std::panic; use std::path::Path; use std::process::{Command as StdCommand, ExitStatus, Output, Stdio}; -use crate::drop_bomb::DropBomb; use crate::{assert_contains, assert_equals, assert_not_contains, handle_failed_output}; +use build_helper::drop_bomb::DropBomb; /// This is a custom command wrapper that simplifies working with commands and makes it easier to /// ensure that we check the exit status of executed processes. diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs index 24fa88af82e..ad989b74e4d 100644 --- a/src/tools/run-make-support/src/diff/mod.rs +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -2,8 +2,8 @@ use regex::Regex; use similar::TextDiff; use std::path::{Path, PathBuf}; -use crate::drop_bomb::DropBomb; use crate::fs_wrapper; +use build_helper::drop_bomb::DropBomb; #[cfg(test)] mod tests; diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 04b6fd2d6c1..e5f1ce1bf34 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -7,7 +7,6 @@ pub mod cc; pub mod clang; mod command; pub mod diff; -mod drop_bomb; pub mod fs_wrapper; pub mod llvm; pub mod run;