about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-03-27 17:43:20 +0000
committerbors <bors@rust-lang.org>2024-03-27 17:43:20 +0000
commit0157da41ee9a1f6fb1a20e00ea62ab4122e2feaa (patch)
tree741b5837eef2affdf7001be59d47174ebe1ce1fd /src/tools
parentd5db7fb537c0352eec3855aa0331c271c48ac4f6 (diff)
parent1f2178b9e7b43f4efe549e19889eafeec9061e8c (diff)
downloadrust-0157da41ee9a1f6fb1a20e00ea62ab4122e2feaa.tar.gz
rust-0157da41ee9a1f6fb1a20e00ea62ab4122e2feaa.zip
Auto merge of #122460 - jieyouxu:rmake-example-refactor, r=Nilstrieb
Rework rmake support library API

### Take 1: Strongly-typed API

Context: https://github.com/rust-lang/rust/pull/122448#discussion_r1523774427

> My 2 cents: from my experience with writing similar "test DSLs", I would suggest to create these helpers as soon as possible in the process (basically the first time someone needs them, not only after N similar usages), and basically treat any imperative code in these high-level tests as a maintenance burden, basically making them as declarative as possible. Otherwise it might be a bit annoying to keep refactoring the tests later once such helpers are available.
>
> I would even discourage the arg method and create explicit methods for setting things like unpretty, the output file etc., but this might be more controversial, as it will make the invoked command-line arguments more opaque.

cc `@Kobzol` for the testing DSL suggestion.

Example:

```rs
let output = Rustc::new()
    .input_file("main.rs")
    .emit(&[EmitKind::Metadata])
    .extern_("stable", &stable_path)
    .output();
```

### Take 2: xshell-based macro API

Example:

```rs
let sh = Shell::new()?;
let stable_path = stable_path.to_string_lossy();
let output = cmd!(sh, "rustc main.rs --emit=metadata --extern stable={stable_path}").output()?;
```

### Take 3: Weakly-typed API with a few helper methods

```rs
let output = Rustc::new()
    .input("main.rs")
    .emit("metadata")
    .extern_("stable", &stable_path)
    .output();
```
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/run-make-support/src/lib.rs189
-rw-r--r--src/tools/run-make-support/src/run.rs67
-rw-r--r--src/tools/run-make-support/src/rustc.rs147
-rw-r--r--src/tools/run-make-support/src/rustdoc.rs80
4 files changed, 306 insertions, 177 deletions
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 419b04231b5..7975677286d 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -1,19 +1,21 @@
+pub mod run;
+pub mod rustc;
+pub mod rustdoc;
+
 use std::env;
-use std::path::{Path, PathBuf};
-use std::process::{Command, Output};
+use std::path::PathBuf;
+use std::process::Output;
 
 pub use object;
 pub use wasmparser;
 
-pub fn out_dir() -> PathBuf {
-    env::var_os("TMPDIR").unwrap().into()
-}
+pub use run::{run, run_fail};
+pub use rustc::{aux_build, rustc, Rustc};
+pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
 
-fn setup_common_build_cmd(command: &str) -> Command {
-    let rustc = env::var(command).unwrap();
-    let mut cmd = Command::new(rustc);
-    cmd.arg("--out-dir").arg(out_dir()).arg("-L").arg(out_dir());
-    cmd
+/// Path of `TMPDIR` (a temporary build directory, not under `/tmp`).
+pub fn tmp_dir() -> PathBuf {
+    env::var_os("TMPDIR").unwrap().into()
 }
 
 fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
@@ -24,170 +26,3 @@ fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> !
     eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
     std::process::exit(1)
 }
-
-pub fn rustc() -> RustcInvocationBuilder {
-    RustcInvocationBuilder::new()
-}
-
-pub fn aux_build() -> AuxBuildInvocationBuilder {
-    AuxBuildInvocationBuilder::new()
-}
-
-pub fn rustdoc() -> Rustdoc {
-    Rustdoc::new()
-}
-
-#[derive(Debug)]
-pub struct RustcInvocationBuilder {
-    cmd: Command,
-}
-
-impl RustcInvocationBuilder {
-    fn new() -> Self {
-        let cmd = setup_common_build_cmd("RUSTC");
-        Self { cmd }
-    }
-
-    pub fn arg(&mut self, arg: &str) -> &mut RustcInvocationBuilder {
-        self.cmd.arg(arg);
-        self
-    }
-
-    pub fn args(&mut self, args: &[&str]) -> &mut RustcInvocationBuilder {
-        self.cmd.args(args);
-        self
-    }
-
-    #[track_caller]
-    pub fn run(&mut self) -> Output {
-        let caller_location = std::panic::Location::caller();
-        let caller_line_number = caller_location.line();
-
-        let output = self.cmd.output().unwrap();
-        if !output.status.success() {
-            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
-        }
-        output
-    }
-}
-
-#[derive(Debug)]
-pub struct AuxBuildInvocationBuilder {
-    cmd: Command,
-}
-
-impl AuxBuildInvocationBuilder {
-    fn new() -> Self {
-        let mut cmd = setup_common_build_cmd("RUSTC");
-        cmd.arg("--crate-type=lib");
-        Self { cmd }
-    }
-
-    pub fn arg(&mut self, arg: &str) -> &mut AuxBuildInvocationBuilder {
-        self.cmd.arg(arg);
-        self
-    }
-
-    #[track_caller]
-    pub fn run(&mut self) -> Output {
-        let caller_location = std::panic::Location::caller();
-        let caller_line_number = caller_location.line();
-
-        let output = self.cmd.output().unwrap();
-        if !output.status.success() {
-            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
-        }
-        output
-    }
-}
-
-#[derive(Debug)]
-pub struct Rustdoc {
-    cmd: Command,
-}
-
-impl Rustdoc {
-    fn new() -> Self {
-        let cmd = setup_common_build_cmd("RUSTDOC");
-        Self { cmd }
-    }
-
-    pub fn arg(&mut self, arg: &str) -> &mut Self {
-        self.cmd.arg(arg);
-        self
-    }
-
-    #[track_caller]
-    pub fn run(&mut self) -> Output {
-        let caller_location = std::panic::Location::caller();
-        let caller_line_number = caller_location.line();
-
-        let output = self.cmd.output().unwrap();
-        if !output.status.success() {
-            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
-        }
-        output
-    }
-}
-
-fn run_common(bin_name: &str) -> (Command, Output) {
-    let target = env::var("TARGET").unwrap();
-
-    let bin_name =
-        if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
-
-    let mut bin_path = PathBuf::new();
-    bin_path.push(env::var("TMPDIR").unwrap());
-    bin_path.push(&bin_name);
-    let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
-    let mut cmd = Command::new(bin_path);
-    cmd.env(&ld_lib_path_envvar, {
-        let mut paths = vec![];
-        paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
-        for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
-            paths.push(p.to_path_buf());
-        }
-        for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
-            paths.push(p.to_path_buf());
-        }
-        env::join_paths(paths.iter()).unwrap()
-    });
-
-    if target.contains("windows") {
-        let mut paths = vec![];
-        for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
-            paths.push(p.to_path_buf());
-        }
-        paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
-        cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
-    }
-
-    let output = cmd.output().unwrap();
-    (cmd, output)
-}
-
-/// Run a built binary and make sure it succeeds.
-#[track_caller]
-pub fn run(bin_name: &str) -> Output {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
-    let (cmd, output) = run_common(bin_name);
-    if !output.status.success() {
-        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
-    }
-    output
-}
-
-/// Run a built binary and make sure it fails.
-#[track_caller]
-pub fn run_fail(bin_name: &str) -> Output {
-    let caller_location = std::panic::Location::caller();
-    let caller_line_number = caller_location.line();
-
-    let (cmd, output) = run_common(bin_name);
-    if output.status.success() {
-        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
-    }
-    output
-}
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
new file mode 100644
index 00000000000..42dcf54da22
--- /dev/null
+++ b/src/tools/run-make-support/src/run.rs
@@ -0,0 +1,67 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Output};
+
+use super::handle_failed_output;
+
+fn run_common(bin_name: &str) -> (Command, Output) {
+    let target = env::var("TARGET").unwrap();
+
+    let bin_name =
+        if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
+
+    let mut bin_path = PathBuf::new();
+    bin_path.push(env::var("TMPDIR").unwrap());
+    bin_path.push(&bin_name);
+    let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
+    let mut cmd = Command::new(bin_path);
+    cmd.env(&ld_lib_path_envvar, {
+        let mut paths = vec![];
+        paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
+        for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
+            paths.push(p.to_path_buf());
+        }
+        for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
+            paths.push(p.to_path_buf());
+        }
+        env::join_paths(paths.iter()).unwrap()
+    });
+
+    if target.contains("windows") {
+        let mut paths = vec![];
+        for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
+            paths.push(p.to_path_buf());
+        }
+        paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
+        cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
+    }
+
+    let output = cmd.output().unwrap();
+    (cmd, output)
+}
+
+/// Run a built binary and make sure it succeeds.
+#[track_caller]
+pub fn run(bin_name: &str) -> Output {
+    let caller_location = std::panic::Location::caller();
+    let caller_line_number = caller_location.line();
+
+    let (cmd, output) = run_common(bin_name);
+    if !output.status.success() {
+        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
+    }
+    output
+}
+
+/// Run a built binary and make sure it fails.
+#[track_caller]
+pub fn run_fail(bin_name: &str) -> Output {
+    let caller_location = std::panic::Location::caller();
+    let caller_line_number = caller_location.line();
+
+    let (cmd, output) = run_common(bin_name);
+    if output.status.success() {
+        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
+    }
+    output
+}
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
new file mode 100644
index 00000000000..d0ab8df42d2
--- /dev/null
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -0,0 +1,147 @@
+use std::env;
+use std::path::Path;
+use std::process::{Command, Output};
+
+use crate::{handle_failed_output, tmp_dir};
+
+/// Construct a new `rustc` invocation.
+pub fn rustc() -> Rustc {
+    Rustc::new()
+}
+
+/// Construct a new `rustc` aux-build invocation.
+pub fn aux_build() -> Rustc {
+    Rustc::new_aux_build()
+}
+
+/// A `rustc` invocation builder.
+#[derive(Debug)]
+pub struct Rustc {
+    cmd: Command,
+}
+
+fn setup_common() -> Command {
+    let rustc = env::var("RUSTC").unwrap();
+    let mut cmd = Command::new(rustc);
+    cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir());
+    cmd
+}
+
+impl Rustc {
+    // `rustc` invocation constructor methods
+
+    /// Construct a new `rustc` invocation.
+    pub fn new() -> Self {
+        let cmd = setup_common();
+        Self { cmd }
+    }
+
+    /// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`).
+    pub fn new_aux_build() -> Self {
+        let mut cmd = setup_common();
+        cmd.arg("--crate-type=lib");
+        Self { cmd }
+    }
+
+    // Argument provider methods
+
+    /// Configure the compilation environment.
+    pub fn cfg(&mut self, s: &str) -> &mut Self {
+        self.cmd.arg("--cfg");
+        self.cmd.arg(s);
+        self
+    }
+
+    /// Specify default optimization level `-O` (alias for `-C opt-level=2`).
+    pub fn opt(&mut self) -> &mut Self {
+        self.cmd.arg("-O");
+        self
+    }
+
+    /// Specify type(s) of output files to generate.
+    pub fn emit(&mut self, kinds: &str) -> &mut Self {
+        self.cmd.arg(format!("--emit={kinds}"));
+        self
+    }
+
+    /// Specify where an external library is located.
+    pub fn extern_<P: AsRef<Path>>(&mut self, crate_name: &str, path: P) -> &mut Self {
+        assert!(
+            !crate_name.contains(|c: char| c.is_whitespace() || c == '\\' || c == '/'),
+            "crate name cannot contain whitespace or path separators"
+        );
+
+        let path = path.as_ref().to_string_lossy();
+
+        self.cmd.arg("--extern");
+        self.cmd.arg(format!("{crate_name}={path}"));
+
+        self
+    }
+
+    /// Specify path to the input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Specify target triple.
+    pub fn target(&mut self, target: &str) -> &mut Self {
+        assert!(!target.contains(char::is_whitespace), "target triple cannot contain spaces");
+        self.cmd.arg(format!("--target={target}"));
+        self
+    }
+
+    /// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
+    /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>`
+    /// is passed (note the space).
+    pub fn arg(&mut self, arg: &str) -> &mut Self {
+        assert!(
+            !(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")),
+            "use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`"
+        );
+        self.cmd.arg(arg);
+        self
+    }
+
+    /// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
+    /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>`
+    /// is passed (note the space).
+    pub fn args(&mut self, args: &[&str]) -> &mut Self {
+        for arg in args {
+            assert!(
+                !(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")),
+                "use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`"
+            );
+        }
+
+        self.cmd.args(args);
+        self
+    }
+
+    // Command inspection, output and running helper methods
+
+    /// Get the [`Output`][std::process::Output] of the finished `rustc` process.
+    pub fn output(&mut self) -> Output {
+        self.cmd.output().unwrap()
+    }
+
+    /// Run the constructed `rustc` command and assert that it is successfully run.
+    #[track_caller]
+    pub fn run(&mut self) -> Output {
+        let caller_location = std::panic::Location::caller();
+        let caller_line_number = caller_location.line();
+
+        let output = self.cmd.output().unwrap();
+        if !output.status.success() {
+            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
+        }
+        output
+    }
+
+    /// Inspect what the underlying [`Command`] is up to the current construction.
+    pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
+        f(&self.cmd);
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs
new file mode 100644
index 00000000000..9607ff02f96
--- /dev/null
+++ b/src/tools/run-make-support/src/rustdoc.rs
@@ -0,0 +1,80 @@
+use std::env;
+use std::path::Path;
+use std::process::{Command, Output};
+
+use crate::handle_failed_output;
+
+/// Construct a plain `rustdoc` invocation with no flags set.
+pub fn bare_rustdoc() -> Rustdoc {
+    Rustdoc::bare()
+}
+
+/// Construct a new `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set.
+pub fn rustdoc() -> Rustdoc {
+    Rustdoc::new()
+}
+
+#[derive(Debug)]
+pub struct Rustdoc {
+    cmd: Command,
+}
+
+fn setup_common() -> Command {
+    let rustdoc = env::var("RUSTDOC").unwrap();
+    Command::new(rustdoc)
+}
+
+impl Rustdoc {
+    /// Construct a bare `rustdoc` invocation.
+    pub fn bare() -> Self {
+        let cmd = setup_common();
+        Self { cmd }
+    }
+
+    /// Construct a `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set.
+    pub fn new() -> Self {
+        let mut cmd = setup_common();
+        let target_rpath_dir = env::var_os("TARGET_RPATH_DIR").unwrap();
+        cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy()));
+        Self { cmd }
+    }
+
+    /// Specify path to the input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Specify output directory.
+    pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg("--out-dir").arg(path.as_ref());
+        self
+    }
+
+    /// Given a `path`, pass `@{path}` to `rustdoc` as an
+    /// [arg file](https://doc.rust-lang.org/rustdoc/command-line-arguments.html#path-load-command-line-flags-from-a-path).
+    pub fn arg_file<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(format!("@{}", path.as_ref().display()));
+        self
+    }
+
+    /// Fallback argument provider. Consider adding meaningfully named methods instead of using
+    /// this method.
+    pub fn arg(&mut self, arg: &str) -> &mut Self {
+        self.cmd.arg(arg);
+        self
+    }
+
+    /// Run the build `rustdoc` command and assert that the run is successful.
+    #[track_caller]
+    pub fn run(&mut self) -> Output {
+        let caller_location = std::panic::Location::caller();
+        let caller_line_number = caller_location.line();
+
+        let output = self.cmd.output().unwrap();
+        if !output.status.success() {
+            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
+        }
+        output
+    }
+}