From 97990a475928cb7428ed945342cec70a4e19c3b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 15:47:30 +0200 Subject: [PATCH 01/15] Move `DropBomb` from `run-make-support` to `build_helper` So that it can be also used in bootstrap. --- Cargo.lock | 1 + .../src/drop_bomb/mod.rs | 8 ++++---- .../src/drop_bomb/tests.rs | 0 src/tools/build_helper/src/lib.rs | 1 + src/tools/run-make-support/Cargo.toml | 2 ++ src/tools/run-make-support/src/command.rs | 2 +- src/tools/run-make-support/src/diff/mod.rs | 2 +- src/tools/run-make-support/src/lib.rs | 1 - 8 files changed, 10 insertions(+), 7 deletions(-) rename src/tools/{run-make-support => build_helper}/src/drop_bomb/mod.rs (88%) rename src/tools/{run-make-support => build_helper}/src/drop_bomb/tests.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index eba4eed3686ba..cafc623c185a6 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/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs similarity index 88% rename from src/tools/run-make-support/src/drop_bomb/mod.rs rename to src/tools/build_helper/src/drop_bomb/mod.rs index 2fc84892c1bdd..ffb86d1c9eeff 100644 --- a/src/tools/run-make-support/src/drop_bomb/mod.rs +++ b/src/tools/build_helper/src/drop_bomb/mod.rs @@ -12,7 +12,7 @@ use std::panic; mod tests; #[derive(Debug)] -pub(crate) struct DropBomb { +pub struct DropBomb { command: OsString, defused: bool, armed_line: u32, @@ -21,9 +21,9 @@ pub(crate) struct DropBomb { 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, @@ -32,7 +32,7 @@ impl DropBomb { } /// 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; } } 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 15807d1c0d8f8..4a4f0ca2a9d48 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 ec3b8a96ef3b6..969552dec84ad 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 c506c3d6b61a2..5017a4b88dad3 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 24fa88af82ec2..ad989b74e4d9e 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 5655318267a2f..cb61aa7516de6 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; From 49f54b8ee8c8ac04e53d30832d52b0b06e43540b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 15:51:21 +0200 Subject: [PATCH 02/15] Configure test execution for the `build_helper` crate in bootstrap To enable the previously moved `DropBomb` tests. --- src/bootstrap/src/core/build_steps/test.rs | 47 ++++++++++++++++++++++ src/bootstrap/src/core/builder.rs | 1 + 2 files changed, 48 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0b60587bb792b..a27d1a59beb6e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1352,6 +1352,53 @@ 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(builder.top_stage, host); + + builder.ensure(compile::Std::new(compiler, host)); + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolStd, + 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" }); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 65cc1fa74782a..fd5ddc6d2e605 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, From 042473fd13eaa51eb795dfb643b7660fb8d653a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 15:56:16 +0200 Subject: [PATCH 03/15] Store full arm location in `DropBomb` Before, only the line was stored. This was enough for run-make tests, since these mostly only contain a single `rmake.rs` file, but not for bootstrap. --- src/tools/build_helper/src/drop_bomb/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/build_helper/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs index ffb86d1c9eeff..854113a5181af 100644 --- a/src/tools/build_helper/src/drop_bomb/mod.rs +++ b/src/tools/build_helper/src/drop_bomb/mod.rs @@ -15,7 +15,7 @@ mod tests; pub struct DropBomb { command: OsString, defused: bool, - armed_line: u32, + armed_location: panic::Location<'static>, } impl DropBomb { @@ -27,7 +27,7 @@ impl DropBomb { DropBomb { command: command.as_ref().into(), defused: false, - armed_line: panic::Location::caller().line(), + armed_location: *panic::Location::caller(), } } @@ -41,8 +41,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 but not executed at {}: `{}`", + self.armed_location, self.command.to_string_lossy() ) } From a1626d709cba2ff9508fe4fccba04110e5a87c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 16:13:33 +0200 Subject: [PATCH 04/15] Make `command` field of `BootstrapCommand` private to force access to it through the `as_command_mut` method This will be useful for disarming drop bombs when the inner command is accessed. --- src/bootstrap/src/core/build_steps/compile.rs | 3 ++- src/bootstrap/src/core/build_steps/llvm.rs | 4 ++-- src/bootstrap/src/core/build_steps/setup.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 2 +- .../src/core/build_steps/toolstate.rs | 23 +++++++++++-------- src/bootstrap/src/core/builder.rs | 2 +- src/bootstrap/src/core/config/config.rs | 16 ++++++------- src/bootstrap/src/core/sanity.rs | 2 +- src/bootstrap/src/lib.rs | 23 +++++++++++-------- src/bootstrap/src/utils/channel.rs | 9 ++++---- src/bootstrap/src/utils/exec.rs | 8 ++++++- src/bootstrap/src/utils/helpers.rs | 2 +- src/bootstrap/src/utils/render_tests.rs | 2 +- src/bootstrap/src/utils/tarball.rs | 4 ++-- 14 files changed, 60 insertions(+), 42 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 11a7a40453516..188426a4ecfea 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/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 872823506f883..41dff2123f13d 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 e6a09e8cb8e8c..29cc5e00637e4 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 a27d1a59beb6e..7357067587035 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2105,7 +2105,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 e3e7931a5a2ab..9ddd68da59d16 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 fd5ddc6d2e605..b14a0c5f072cd 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2105,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 3327df972bf80..11207cf893542 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 9995da3a1e5b8..4bdc8ac07958f 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 f16afb2979651..b79e1badb2a99 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); } @@ -941,10 +944,12 @@ impl Build { self.verbose(|| println!("running: {command:?}")); - command.command.stdout(command.stdout.stdio()); - command.command.stderr(command.stderr.stdio()); + 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.command.output(); + let output = command.as_command_mut().output(); use std::fmt::Write; @@ -1931,7 +1936,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 +1946,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 2ca86bdb0ed27..8d5328c95eb2f 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, } @@ -63,11 +63,12 @@ impl GitInfo { .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 ba963f52dc2b8..09801a05fa36b 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -55,7 +55,7 @@ impl OutputMode { /// [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, @@ -145,6 +145,12 @@ impl BootstrapCommand { pub fn run(&mut self, builder: &Build) -> CommandOutput { builder.run(self) } + + /// 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 { + &mut self.command + } } impl From for BootstrapCommand { diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 5dd3ba96786ca..fd8aee37d900b 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}"); diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 2e99bc68a8b75..3a5776bdfe0dd 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -50,7 +50,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 4f0104e4bbab6..f5fc94273c9ae 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()]); } 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 05/15] 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 b79e1badb2a99..4a69068b51e76 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 09801a05fa36b..a9291d45f2169 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 fd8aee37d900b..f695b3229fe66 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"); From 542344f5bb32a8593b4d775d351d3f4fc8e55fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 20:24:37 +0200 Subject: [PATCH 06/15] Print command creation and execution location when it fails This should make it quicker to debug command failures. --- src/bootstrap/src/lib.rs | 15 ++++++++++++--- src/bootstrap/src/utils/exec.rs | 8 +++++++- src/tools/build_helper/src/drop_bomb/mod.rs | 4 ++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 4a69068b51e76..10ec7d135f0ca 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -937,13 +937,19 @@ 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(); + + self.verbose(|| { + println!("running: {command:?} (created at {created_at}, executed at {executed_at})") + }); let stdout = command.stdout.stdio(); command.as_command_mut().stdout(stdout); @@ -962,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(); diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index a9291d45f2169..38fc0cf70b5ca 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -160,17 +160,23 @@ impl BootstrapCommand { &mut self.command } - /// Mark the command as being executd, disarming the drop bomb. + /// 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 From for BootstrapCommand { #[track_caller] fn from(command: Command) -> Self { let program = command.get_program().to_owned(); + Self { command, failure_behavior: BehaviorOnFailure::Exit, diff --git a/src/tools/build_helper/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs index 854113a5181af..2dd4a6bee26b3 100644 --- a/src/tools/build_helper/src/drop_bomb/mod.rs +++ b/src/tools/build_helper/src/drop_bomb/mod.rs @@ -31,6 +31,10 @@ impl DropBomb { } } + 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 fn defuse(&mut self) { self.defused = true; From fdf1477221ae7a504d6616b001f3edb6c4c50c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 4 Jul 2024 20:32:03 +0200 Subject: [PATCH 07/15] Improve the `Debug` representation of `BootstrapCommand` Avoid printing useless information in the `Debug` output. --- src/bootstrap/src/utils/exec.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 38fc0cf70b5ca..c8457f2d0c906 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,6 +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}; @@ -54,7 +55,6 @@ impl OutputMode { /// /// [allow_failure]: BootstrapCommand::allow_failure /// [delay_failure]: BootstrapCommand::delay_failure -#[derive(Debug)] pub struct BootstrapCommand { command: Command, pub failure_behavior: BehaviorOnFailure, @@ -147,6 +147,7 @@ impl BootstrapCommand { } /// Run the command, returning its output. + #[track_caller] pub fn run(&mut self, builder: &Build) -> CommandOutput { builder.run(self) } @@ -172,6 +173,17 @@ impl BootstrapCommand { } } +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 { From 9634633889a48d1d565ce22cc0b577addd2ab32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 7 Jul 2024 11:57:45 +0200 Subject: [PATCH 08/15] Improve error message of drop bombs --- src/tools/build_helper/src/drop_bomb/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/build_helper/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs index 2dd4a6bee26b3..0a5bb04b55b33 100644 --- a/src/tools/build_helper/src/drop_bomb/mod.rs +++ b/src/tools/build_helper/src/drop_bomb/mod.rs @@ -45,7 +45,7 @@ impl Drop for DropBomb { fn drop(&mut self) { if !self.defused && !std::thread::panicking() { panic!( - "command constructed but not executed at {}: `{}`", + "command constructed at `{}` was dropped without being executed: `{}`", self.armed_location, self.command.to_string_lossy() ) From 8ee18d600f962df67e831da4d07cc556cff39526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 7 Jul 2024 13:28:14 +0200 Subject: [PATCH 09/15] Fix a case where a RustBook command is potentially not executed We can move the command creation to a block where it is clear that the command will be executed. --- src/bootstrap/src/core/build_steps/doc.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 4b35d6c5d4c95..d1080775efe59 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); From ebb30890388d3a569de823d1278a0aa467847d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 7 Jul 2024 13:42:33 +0200 Subject: [PATCH 10/15] Remove unused rustdoc command --- src/bootstrap/src/core/build_steps/doc.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index d1080775efe59..dc46af6cf4868 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -258,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)) { From 0cab9626f416f0f6bcf65221a3ed8ee38e69e97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 7 Jul 2024 16:59:14 +0200 Subject: [PATCH 11/15] Disarm drop bombs for unexecuted test Cargo commands The code for running tests uses a custom command machinery because it streams the output of the command. We thus need to mark the command as executed in a dry run, to avoid a drop bomb panic. --- src/bootstrap/src/utils/channel.rs | 2 +- src/bootstrap/src/utils/render_tests.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index 8d5328c95eb2f..f8bcb584991a1 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -58,7 +58,7 @@ 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") diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 3a5776bdfe0dd..a3d0d36e7547d 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; } From 1089de43394aace6ea7e88bd2bb3b7546a7883fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 7 Jul 2024 18:26:15 +0200 Subject: [PATCH 12/15] Simplify command executions for plain source tarballs If we're in dry run mode, the command will return an empty string, so we can just execute it. --- src/bootstrap/src/core/build_steps/dist.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7bc5405e92f93..aebec60112fb8 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); From 823ea0e9879c234ba9a9a50082180cec8d0634df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 12 Jul 2024 17:06:33 +0200 Subject: [PATCH 13/15] Modify FIXME comment --- src/bootstrap/src/utils/exec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index c8457f2d0c906..b053016499730 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -153,7 +153,7 @@ impl BootstrapCommand { } /// Provides access to the stdlib Command inside. - /// All usages of this function should be eventually removed from bootstrap. + /// 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. From 72c354094d993ae65b9f292d3cb30c98f277e441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 12 Jul 2024 17:49:01 +0200 Subject: [PATCH 14/15] Test `build_helper` with the stage 0 compiler There is no need to build a stage N toolchain for testing it. --- src/bootstrap/src/core/build_steps/test.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 7357067587035..e49c2cbe17b14 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1372,13 +1372,12 @@ impl Step for CrateBuildHelper { /// Runs `cargo test` for build_helper. fn run(self, builder: &Builder<'_>) { let host = self.host; - let compiler = builder.compiler(builder.top_stage, host); + let compiler = builder.compiler(0, host); - builder.ensure(compile::Std::new(compiler, host)); let mut cargo = tool::prepare_tool_cargo( builder, compiler, - Mode::ToolStd, + Mode::ToolBootstrap, host, "test", "src/tools/build_helper", From 0cce0bb204dd6e4ff06a282b605c9ac416a8dbd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 13 Jul 2024 11:41:29 +0200 Subject: [PATCH 15/15] Avoid more instances of unused command execution in dry run --- src/bootstrap/src/core/build_steps/dist.rs | 6 +----- src/bootstrap/src/core/build_steps/test.rs | 16 ++++++---------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index aebec60112fb8..1e9d2025bc78d 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2068,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/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e49c2cbe17b14..9b4c7c9134976 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() } }