about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--src/tools/run-make-support/CHANGELOG.md67
-rw-r--r--src/tools/run-make-support/Cargo.toml2
-rw-r--r--src/tools/run-make-support/src/cc.rs7
-rw-r--r--src/tools/run-make-support/src/clang.rs2
-rw-r--r--src/tools/run-make-support/src/command.rs115
-rw-r--r--src/tools/run-make-support/src/diff/mod.rs10
-rw-r--r--src/tools/run-make-support/src/drop_bomb/mod.rs50
-rw-r--r--src/tools/run-make-support/src/drop_bomb/tests.rs15
-rw-r--r--src/tools/run-make-support/src/lib.rs32
-rw-r--r--src/tools/run-make-support/src/llvm_readobj.rs2
-rw-r--r--src/tools/run-make-support/src/run.rs30
-rw-r--r--src/tools/run-make-support/src/rustc.rs5
-rw-r--r--src/tools/run-make-support/src/rustdoc.rs5
-rw-r--r--tests/run-make/compiler-builtins/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-map-file/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-scrape-examples-macros/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-scrape-examples-remap/scrape.rs2
-rw-r--r--tests/run-make/rustdoc-themes/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-with-out-dir-option/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-with-output-option/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs2
22 files changed, 280 insertions, 80 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ba8b2c27005..e5ea9fee8a6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3452,7 +3452,7 @@ dependencies = [
 
 [[package]]
 name = "run_make_support"
-version = "0.0.0"
+version = "0.1.0"
 dependencies = [
  "gimli 0.28.1",
  "object 0.34.0",
diff --git a/src/tools/run-make-support/CHANGELOG.md b/src/tools/run-make-support/CHANGELOG.md
new file mode 100644
index 00000000000..a37de9fda80
--- /dev/null
+++ b/src/tools/run-make-support/CHANGELOG.md
@@ -0,0 +1,67 @@
+# Changelog
+
+All notable changes to the `run_make_support` library should be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the support
+library should adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) even if it's
+not intended for public consumption (it's moreso to help internally, to help test writers track
+changes to the support library).
+
+This support library will probably never reach 1.0. Please bump the minor version in `Cargo.toml` if
+you make any breaking changes or other significant changes, or bump the patch version for bug fixes.
+
+## [0.1.0] - 2024-06-09
+
+### Changed
+
+- Use *drop bombs* to enforce that commands are executed; a command invocation will panic if it is
+  constructed but never executed. Execution methods `Command::{run, run_fail}` will defuse the drop
+  bomb.
+- Added `Command` helpers that forward to `std::process::Command` counterparts.
+
+### Removed
+
+- The `env_var` method which was incorrectly named and is `env_clear` underneath and is a footgun
+  from `impl_common_helpers`. For example, removing `TMPDIR` on Unix and `TMP`/`TEMP` breaks
+  `std::env::temp_dir` and wrecks anything using that, such as rustc's codgen.
+- Removed `Deref`/`DerefMut` for `run_make_support::Command` -> `std::process::Command` because it
+  causes a method chain like `htmldocck().arg().run()` to fail, because `arg()` resolves to
+  `std::process::Command` which also returns a `&mut std::process::Command`, causing the `run()` to
+  be not found.
+
+## [0.0.0] - 2024-06-09
+
+Consider this version to contain all changes made to the support library before we started to track
+changes in this changelog.
+
+### Added
+
+- Custom command wrappers around `std::process::Command` (`run_make_support::Command`) and custom
+  wrapper around `std::process::Output` (`CompletedProcess`) to make it more convenient to work with
+  commands and their output, and help avoid forgetting to check for exit status.
+    - `Command`: `set_stdin`, `run`, `run_fail`.
+    - `CompletedProcess`: `std{err,out}_utf8`, `status`, `assert_std{err,out}_{equals, contains,
+      not_contains}`, `assert_exit_code`.
+- `impl_common_helpers` macro to avoid repeating adding common convenience methods, including:
+    - Environment manipulation methods: `env`, `env_remove`
+    - Command argument providers: `arg`, `args`
+    - Common invocation inspection (of the command invocation up until `inspect` is called):
+      `inspect`
+    - Execution methods: `run` (for commands expected to succeed execution, exit status `0`) and
+      `run_fail` (for commands expected to fail execution, exit status non-zero).
+- Command wrappers around: `rustc`, `clang`, `cc`, `rustc`, `rustdoc`, `llvm-readobj`.
+- Thin helpers to construct `python` and `htmldocck` commands.
+- `run` and `run_fail` (like `Command::{run, run_fail}`) for running binaries, which sets suitable
+  env vars (like `LD_LIB_PATH` or equivalent, `TARGET_RPATH_ENV`, `PATH` on Windows).
+- Pseudo command `diff` which has similar functionality as the cli util but not the same API.
+- Convenience panic-on-fail helpers `env_var`, `env_var_os`, `cwd` for their `std::env` conterparts.
+- Convenience panic-on-fail helpers for reading respective env vars: `target`, `source_root`.
+- Platform check helpers: `is_windows`, `is_msvc`, `cygpath_windows`, `uname`.
+- fs helpers: `copy_dir_all`.
+- `recursive_diff` helper.
+- Generic `assert_not_contains` helper.
+- Scoped run-with-teardown helper `run_in_tmpdir` which is designed to run commands in a temporary
+  directory that is cleared when closure returns.
+- Helpers for constructing the name of binaries and libraries: `rust_lib_name`, `static_lib_name`,
+  `bin_name`, `dynamic_lib_name`.
+- Re-export libraries: `gimli`, `object`, `regex`, `wasmparsmer`.
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index cf4ae4b16cd..5450620e7f0 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "run_make_support"
-version = "0.0.0"
+version = "0.1.0"
 edition = "2021"
 
 [dependencies]
diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs
index 5c0158d7547..8694740cfc3 100644
--- a/src/tools/run-make-support/src/cc.rs
+++ b/src/tools/run-make-support/src/cc.rs
@@ -7,6 +7,7 @@ use crate::{bin_name, cygpath_windows, env_var, is_msvc, is_windows, uname};
 ///
 /// WARNING: This means that what flags are accepted by the underlying C compiler is
 /// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
+#[track_caller]
 pub fn cc() -> Cc {
     Cc::new()
 }
@@ -25,6 +26,7 @@ impl Cc {
     ///
     /// WARNING: This means that what flags are accepted by the underlying C compile is
     /// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
+    #[track_caller]
     pub fn new() -> Self {
         let compiler = env_var("CC");
 
@@ -84,11 +86,6 @@ impl Cc {
         self.cmd.arg(path.as_ref());
         self
     }
-
-    /// Get the [`Output`][::std::process::Output] of the finished process.
-    pub fn command_output(&mut self) -> ::std::process::Output {
-        self.cmd.output().expect("failed to get output of finished process")
-    }
 }
 
 /// `EXTRACFLAGS`
diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs
index d2ebed7ab06..a31004659c1 100644
--- a/src/tools/run-make-support/src/clang.rs
+++ b/src/tools/run-make-support/src/clang.rs
@@ -4,6 +4,7 @@ use crate::command::Command;
 use crate::{bin_name, env_var};
 
 /// Construct a new `clang` invocation. `clang` is not always available for all targets.
+#[track_caller]
 pub fn clang() -> Clang {
     Clang::new()
 }
@@ -18,6 +19,7 @@ crate::impl_common_helpers!(Clang);
 
 impl Clang {
     /// Construct a new `clang` invocation. `clang` is not always available for all targets.
+    #[track_caller]
     pub fn new() -> Self {
         let clang = env_var("CLANG");
         let cmd = Command::new(clang);
diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs
index cc395e491ee..d689dc2f979 100644
--- a/src/tools/run-make-support/src/command.rs
+++ b/src/tools/run-make-support/src/command.rs
@@ -1,36 +1,107 @@
-use crate::{assert_not_contains, handle_failed_output};
+use std::ffi;
 use std::ffi::OsStr;
 use std::io::Write;
-use std::ops::{Deref, DerefMut};
+use std::panic;
+use std::path::Path;
 use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
 
-/// 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.
+use crate::drop_bomb::DropBomb;
+use crate::{assert_not_contains, handle_failed_output};
+
+/// 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.
+///
+/// # A [`Command`] must be executed
+///
+/// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If
+/// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test
+/// to panic. Execution methods [`run`] and [`run_fail`] will defuse the drop bomb. A test
+/// containing constructed but never executed commands is dangerous because it can give a false
+/// sense of confidence.
+///
+/// [`run`]: Self::run
+/// [`run_fail`]: Self::run_fail
 #[derive(Debug)]
 pub struct Command {
     cmd: StdCommand,
     stdin: Option<Box<[u8]>>,
+    drop_bomb: DropBomb,
 }
 
 impl Command {
-    pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
-        Self { cmd: StdCommand::new(program), stdin: None }
+    #[track_caller]
+    pub fn new<P: AsRef<OsStr>>(program: P) -> Self {
+        let program = program.as_ref();
+        Self { cmd: StdCommand::new(program), stdin: None, drop_bomb: DropBomb::arm(program) }
     }
 
     pub fn set_stdin(&mut self, stdin: Box<[u8]>) {
         self.stdin = Some(stdin);
     }
 
+    /// Specify an environment variable.
+    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
+    where
+        K: AsRef<ffi::OsStr>,
+        V: AsRef<ffi::OsStr>,
+    {
+        self.cmd.env(key, value);
+        self
+    }
+
+    /// Remove an environmental variable.
+    pub fn env_remove<K>(&mut self, key: K) -> &mut Self
+    where
+        K: AsRef<ffi::OsStr>,
+    {
+        self.cmd.env_remove(key);
+        self
+    }
+
+    /// Generic command argument provider. Prefer specific helper methods if possible.
+    /// Note that for some executables, arguments might be platform specific. For C/C++
+    /// compilers, arguments might be platform *and* compiler specific.
+    pub fn arg<S>(&mut self, arg: S) -> &mut Self
+    where
+        S: AsRef<ffi::OsStr>,
+    {
+        self.cmd.arg(arg);
+        self
+    }
+
+    /// Generic command arguments provider. Prefer specific helper methods if possible.
+    /// Note that for some executables, arguments might be platform specific. For C/C++
+    /// compilers, arguments might be platform *and* compiler specific.
+    pub fn args<S>(&mut self, args: &[S]) -> &mut Self
+    where
+        S: AsRef<ffi::OsStr>,
+    {
+        self.cmd.args(args);
+        self
+    }
+
+    /// Inspect what the underlying [`std::process::Command`] is up to the
+    /// current construction.
+    pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
+    where
+        I: FnOnce(&StdCommand),
+    {
+        inspector(&self.cmd);
+        self
+    }
+
+    /// Set the path where the command will be run.
+    pub fn current_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.current_dir(path);
+        self
+    }
+
     /// Run the constructed command and assert that it is successfully run.
     #[track_caller]
     pub fn run(&mut self) -> CompletedProcess {
-        let caller_location = std::panic::Location::caller();
-        let caller_line_number = caller_location.line();
-
         let output = self.command_output();
         if !output.status().success() {
-            handle_failed_output(&self, output, caller_line_number);
+            handle_failed_output(&self, output, panic::Location::caller().line());
         }
         output
     }
@@ -38,18 +109,16 @@ impl Command {
     /// Run the constructed command and assert that it does not successfully run.
     #[track_caller]
     pub fn run_fail(&mut self) -> CompletedProcess {
-        let caller_location = std::panic::Location::caller();
-        let caller_line_number = caller_location.line();
-
         let output = self.command_output();
         if output.status().success() {
-            handle_failed_output(&self, output, caller_line_number);
+            handle_failed_output(&self, output, panic::Location::caller().line());
         }
         output
     }
 
     #[track_caller]
-    pub(crate) fn command_output(&mut self) -> CompletedProcess {
+    fn command_output(&mut self) -> CompletedProcess {
+        self.drop_bomb.defuse();
         // let's make sure we piped all the input and outputs
         self.cmd.stdin(Stdio::piped());
         self.cmd.stdout(Stdio::piped());
@@ -71,20 +140,6 @@ impl Command {
     }
 }
 
-impl Deref for Command {
-    type Target = StdCommand;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cmd
-    }
-}
-
-impl DerefMut for Command {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cmd
-    }
-}
-
 /// Represents the result of an executed process.
 /// The various `assert_` helper methods should preferably be used for
 /// checking the contents of stdout/stderr.
diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs
index d864ddf4eb1..0fb6fec9d58 100644
--- a/src/tools/run-make-support/src/diff/mod.rs
+++ b/src/tools/run-make-support/src/diff/mod.rs
@@ -2,9 +2,12 @@ use regex::Regex;
 use similar::TextDiff;
 use std::path::Path;
 
+use crate::drop_bomb::DropBomb;
+
 #[cfg(test)]
 mod tests;
 
+#[track_caller]
 pub fn diff() -> Diff {
     Diff::new()
 }
@@ -16,10 +19,12 @@ pub struct Diff {
     actual: Option<String>,
     actual_name: Option<String>,
     normalizers: Vec<(String, String)>,
+    drop_bomb: DropBomb,
 }
 
 impl Diff {
     /// Construct a bare `diff` invocation.
+    #[track_caller]
     pub fn new() -> Self {
         Self {
             expected: None,
@@ -27,6 +32,7 @@ impl Diff {
             actual: None,
             actual_name: None,
             normalizers: Vec::new(),
+            drop_bomb: DropBomb::arm("diff"),
         }
     }
 
@@ -79,9 +85,9 @@ impl Diff {
         self
     }
 
-    /// Executes the diff process, prints any differences to the standard error.
     #[track_caller]
-    pub fn run(&self) {
+    pub fn run(&mut self) {
+        self.drop_bomb.defuse();
         let expected = self.expected.as_ref().expect("expected text not set");
         let mut actual = self.actual.as_ref().expect("actual text not set").to_string();
         let expected_name = self.expected_name.as_ref().unwrap();
diff --git a/src/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/run-make-support/src/drop_bomb/mod.rs
new file mode 100644
index 00000000000..2fc84892c1b
--- /dev/null
+++ b/src/tools/run-make-support/src/drop_bomb/mod.rs
@@ -0,0 +1,50 @@
+//! This module implements "drop bombs" intended for use by command wrappers to ensure that the
+//! constructed commands are *eventually* executed. This is exactly like `rustc_errors::Diag` where
+//! we force every `Diag` to be consumed or we emit a bug, but we panic instead.
+//!
+//! This is adapted from <https://docs.rs/drop_bomb/latest/drop_bomb/> and simplified for our
+//! purposes.
+
+use std::ffi::{OsStr, OsString};
+use std::panic;
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Debug)]
+pub(crate) struct DropBomb {
+    command: OsString,
+    defused: bool,
+    armed_line: u32,
+}
+
+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.
+    #[track_caller]
+    pub(crate) fn arm<S: AsRef<OsStr>>(command: S) -> DropBomb {
+        DropBomb {
+            command: command.as_ref().into(),
+            defused: false,
+            armed_line: panic::Location::caller().line(),
+        }
+    }
+
+    /// Defuse the [`DropBomb`]. This will prevent the drop bomb from panicking when dropped.
+    pub(crate) fn defuse(&mut self) {
+        self.defused = true;
+    }
+}
+
+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,
+                self.command.to_string_lossy()
+            )
+        }
+    }
+}
diff --git a/src/tools/run-make-support/src/drop_bomb/tests.rs b/src/tools/run-make-support/src/drop_bomb/tests.rs
new file mode 100644
index 00000000000..4a488c0f670
--- /dev/null
+++ b/src/tools/run-make-support/src/drop_bomb/tests.rs
@@ -0,0 +1,15 @@
+use super::DropBomb;
+
+#[test]
+#[should_panic]
+fn test_arm() {
+    let bomb = DropBomb::arm("hi :3");
+    drop(bomb); // <- armed bomb should explode when not defused
+}
+
+#[test]
+fn test_defuse() {
+    let mut bomb = DropBomb::arm("hi :3");
+    bomb.defuse();
+    drop(bomb); // <- defused bomb should not explode
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index cea1313e29d..bf74d94f911 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -7,6 +7,7 @@ pub mod cc;
 pub mod clang;
 mod command;
 pub mod diff;
+mod drop_bomb;
 pub mod llvm_readobj;
 pub mod run;
 pub mod rustc;
@@ -16,6 +17,7 @@ use std::env;
 use std::ffi::OsString;
 use std::fs;
 use std::io;
+use std::panic;
 use std::path::{Path, PathBuf};
 
 pub use gimli;
@@ -31,6 +33,7 @@ pub use run::{cmd, run, run_fail};
 pub use rustc::{aux_build, rustc, Rustc};
 pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
 
+#[track_caller]
 pub fn env_var(name: &str) -> String {
     match env::var(name) {
         Ok(v) => v,
@@ -38,6 +41,7 @@ pub fn env_var(name: &str) -> String {
     }
 }
 
+#[track_caller]
 pub fn env_var_os(name: &str) -> OsString {
     match env::var_os(name) {
         Some(v) => v,
@@ -65,11 +69,13 @@ pub fn is_darwin() -> bool {
     target().contains("darwin")
 }
 
+#[track_caller]
 pub fn python_command() -> Command {
     let python_path = env_var("PYTHON");
     Command::new(python_path)
 }
 
+#[track_caller]
 pub fn htmldocck() -> Command {
     let mut python = python_command();
     python.arg(source_root().join("src/etc/htmldocck.py"));
@@ -161,15 +167,13 @@ pub fn cwd() -> PathBuf {
 /// available on the platform!
 #[track_caller]
 pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
+    let caller = panic::Location::caller();
     let mut cygpath = Command::new("cygpath");
     cygpath.arg("-w");
     cygpath.arg(path.as_ref());
-    let output = cygpath.command_output();
+    let output = cygpath.run();
     if !output.status().success() {
-        handle_failed_output(&cygpath, output, caller_line_number);
+        handle_failed_output(&cygpath, output, caller.line());
     }
     // cygpath -w can attach a newline
     output.stdout_utf8().trim().to_string()
@@ -178,13 +182,11 @@ pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String {
 /// Run `uname`. This assumes that `uname` is available on the platform!
 #[track_caller]
 pub fn uname() -> String {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
+    let caller = panic::Location::caller();
     let mut uname = Command::new("uname");
-    let output = uname.command_output();
+    let output = uname.run();
     if !output.status().success() {
-        handle_failed_output(&uname, output, caller_line_number);
+        handle_failed_output(&uname, output, caller.line());
     }
     output.stdout_utf8()
 }
@@ -364,12 +366,6 @@ macro_rules! impl_common_helpers {
                 self
             }
 
-            /// Clear all environmental variables.
-            pub fn env_var(&mut self) -> &mut Self {
-                self.cmd.env_clear();
-                self
-            }
-
             /// Generic command argument provider. Prefer specific helper methods if possible.
             /// Note that for some executables, arguments might be platform specific. For C/C++
             /// compilers, arguments might be platform *and* compiler specific.
@@ -398,7 +394,7 @@ macro_rules! impl_common_helpers {
             where
                 I: FnOnce(&::std::process::Command),
             {
-                inspector(&self.cmd);
+                self.cmd.inspect(inspector);
                 self
             }
 
@@ -415,7 +411,7 @@ macro_rules! impl_common_helpers {
             }
 
             /// Set the path where the command will be run.
-            pub fn current_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+            pub fn current_dir<P: AsRef<::std::path::Path>>(&mut self, path: P) -> &mut Self {
                 self.cmd.current_dir(path);
                 self
             }
diff --git a/src/tools/run-make-support/src/llvm_readobj.rs b/src/tools/run-make-support/src/llvm_readobj.rs
index db2f9db6e41..57ddfc205e6 100644
--- a/src/tools/run-make-support/src/llvm_readobj.rs
+++ b/src/tools/run-make-support/src/llvm_readobj.rs
@@ -5,6 +5,7 @@ use crate::env_var;
 
 /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
 /// at `$LLVM_BIN_DIR/llvm-readobj`.
+#[track_caller]
 pub fn llvm_readobj() -> LlvmReadobj {
     LlvmReadobj::new()
 }
@@ -20,6 +21,7 @@ crate::impl_common_helpers!(LlvmReadobj);
 impl LlvmReadobj {
     /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
     /// at `$LLVM_BIN_DIR/llvm-readobj`.
+    #[track_caller]
     pub fn new() -> Self {
         let llvm_bin_dir = env_var("LLVM_BIN_DIR");
         let llvm_bin_dir = PathBuf::from(llvm_bin_dir);
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
index 4a6fd7c432e..6fa1a75363c 100644
--- a/src/tools/run-make-support/src/run.rs
+++ b/src/tools/run-make-support/src/run.rs
@@ -1,5 +1,6 @@
 use std::env;
 use std::ffi::OsStr;
+use std::panic;
 use std::path::{Path, PathBuf};
 
 use crate::command::{Command, CompletedProcess};
@@ -7,7 +8,8 @@ use crate::{cwd, env_var, is_windows, set_host_rpath};
 
 use super::handle_failed_output;
 
-fn run_common(name: &str) -> (Command, CompletedProcess) {
+#[track_caller]
+fn run_common(name: &str) -> Command {
     let mut bin_path = PathBuf::new();
     bin_path.push(cwd());
     bin_path.push(name);
@@ -34,19 +36,17 @@ fn run_common(name: &str) -> (Command, CompletedProcess) {
         cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
     }
 
-    let output = cmd.command_output();
-    (cmd, output)
+    cmd
 }
 
 /// Run a built binary and make sure it succeeds.
 #[track_caller]
 pub fn run(name: &str) -> CompletedProcess {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
-    let (cmd, output) = run_common(name);
+    let caller = panic::Location::caller();
+    let mut cmd = run_common(name);
+    let output = cmd.run();
     if !output.status().success() {
-        handle_failed_output(&cmd, output, caller_line_number);
+        handle_failed_output(&cmd, output, caller.line());
     }
     output
 }
@@ -54,18 +54,18 @@ pub fn run(name: &str) -> CompletedProcess {
 /// Run a built binary and make sure it fails.
 #[track_caller]
 pub fn run_fail(name: &str) -> CompletedProcess {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
-    let (cmd, output) = run_common(name);
+    let caller = panic::Location::caller();
+    let mut cmd = run_common(name);
+    let output = cmd.run_fail();
     if output.status().success() {
-        handle_failed_output(&cmd, output, caller_line_number);
+        handle_failed_output(&cmd, output, caller.line());
     }
     output
 }
 
-/// Create a new custom Command.
-/// This should be preferred to creating `std::process::Command` directly.
+/// Create a new custom [`Command`]. This should be preferred to creating [`std::process::Command`]
+/// directly.
+#[track_caller]
 pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command {
     let mut command = Command::new(program);
     set_host_rpath(&mut command);
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index 5ea231442bc..32fa5018d80 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -5,11 +5,13 @@ use std::path::Path;
 use crate::{command, cwd, env_var, set_host_rpath};
 
 /// Construct a new `rustc` invocation.
+#[track_caller]
 pub fn rustc() -> Rustc {
     Rustc::new()
 }
 
 /// Construct a new `rustc` aux-build invocation.
+#[track_caller]
 pub fn aux_build() -> Rustc {
     Rustc::new_aux_build()
 }
@@ -22,6 +24,7 @@ pub struct Rustc {
 
 crate::impl_common_helpers!(Rustc);
 
+#[track_caller]
 fn setup_common() -> Command {
     let rustc = env_var("RUSTC");
     let mut cmd = Command::new(rustc);
@@ -34,12 +37,14 @@ impl Rustc {
     // `rustc` invocation constructor methods
 
     /// Construct a new `rustc` invocation.
+    #[track_caller]
     pub fn new() -> Self {
         let cmd = setup_common();
         Self { cmd }
     }
 
     /// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`).
+    #[track_caller]
     pub fn new_aux_build() -> Self {
         let mut cmd = setup_common();
         cmd.arg("--crate-type=lib");
diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs
index 39a698a47b4..332906f739a 100644
--- a/src/tools/run-make-support/src/rustdoc.rs
+++ b/src/tools/run-make-support/src/rustdoc.rs
@@ -5,11 +5,13 @@ use crate::command::Command;
 use crate::{env_var, env_var_os, set_host_rpath};
 
 /// Construct a plain `rustdoc` invocation with no flags set.
+#[track_caller]
 pub fn bare_rustdoc() -> Rustdoc {
     Rustdoc::bare()
 }
 
 /// Construct a new `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set.
+#[track_caller]
 pub fn rustdoc() -> Rustdoc {
     Rustdoc::new()
 }
@@ -21,6 +23,7 @@ pub struct Rustdoc {
 
 crate::impl_common_helpers!(Rustdoc);
 
+#[track_caller]
 fn setup_common() -> Command {
     let rustdoc = env_var("RUSTDOC");
     let mut cmd = Command::new(rustdoc);
@@ -30,12 +33,14 @@ fn setup_common() -> Command {
 
 impl Rustdoc {
     /// Construct a bare `rustdoc` invocation.
+    #[track_caller]
     pub fn bare() -> Self {
         let cmd = setup_common();
         Self { cmd }
     }
 
     /// Construct a `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set.
+    #[track_caller]
     pub fn new() -> Self {
         let mut cmd = setup_common();
         let target_rpath_dir = env_var_os("TARGET_RPATH_DIR");
diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs
index a2c0ad5f6de..a102e5b3390 100644
--- a/tests/run-make/compiler-builtins/rmake.rs
+++ b/tests/run-make/compiler-builtins/rmake.rs
@@ -35,7 +35,7 @@ fn main() {
     let rustc = env_var("RUSTC");
     let bootstrap_cargo = env_var("BOOTSTRAP_CARGO");
     let mut cmd = cmd(bootstrap_cargo);
-    cmd.args([
+    cmd.args(&[
         "build",
         "--manifest-path",
         manifest_path.to_str().unwrap(),
diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs
index de75561c9fb..08f9595ef9f 100644
--- a/tests/run-make/rustdoc-map-file/rmake.rs
+++ b/tests/run-make/rustdoc-map-file/rmake.rs
@@ -9,5 +9,5 @@ fn main() {
         .output(&out_dir)
         .run();
     // FIXME (GuillaumeGomez): Port the python script to Rust as well.
-    assert!(python_command().arg("validate_json.py").arg(&out_dir).status().unwrap().success());
+    python_command().arg("validate_json.py").arg(&out_dir).run();
 }
diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
index c9650def347..bfe4a1df456 100644
--- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
+++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
@@ -56,5 +56,5 @@ fn main() {
         .arg(&ex_dir)
         .run();
 
-    assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }
diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
index 41efb837458..6bf48a94a49 100644
--- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
+++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
@@ -45,5 +45,5 @@ pub fn scrape(extra_args: &[&str]) {
     }
     rustdoc.run();
 
-    assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }
diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs
index c28821b7628..bfda2d97888 100644
--- a/tests/run-make/rustdoc-themes/rmake.rs
+++ b/tests/run-make/rustdoc-themes/rmake.rs
@@ -28,5 +28,5 @@ fn main() {
     std::fs::write(&test_css, test_content).unwrap();
 
     rustdoc().output(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run();
-    assert!(htmldocck().arg(out_dir).arg("foo.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("foo.rs").run();
 }
diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
index 405da8412ae..ded89c9ae79 100644
--- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
@@ -3,5 +3,5 @@ use run_make_support::{htmldocck, rustdoc};
 fn main() {
     let out_dir = "rustdoc";
     rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run();
-    assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }
diff --git a/tests/run-make/rustdoc-with-output-option/rmake.rs b/tests/run-make/rustdoc-with-output-option/rmake.rs
index a3b1c8ca0dd..f7fbbec6986 100644
--- a/tests/run-make/rustdoc-with-output-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-output-option/rmake.rs
@@ -12,5 +12,5 @@ fn main() {
         .arg(&out_dir)
         .run();
 
-    assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }
diff --git a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
index b536fbe2303..4a8896cc975 100644
--- a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
@@ -12,5 +12,5 @@ fn main() {
         .arg(&out_dir)
         .run();
 
-    assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
+    htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }