Skip to content

Commit 79c39a2

Browse files
Rollup merge of rust-lang#122460 - jieyouxu:rmake-example-refactor, r=Nilstrieb
Rework rmake support library API ### Take 1: Strongly-typed API Context: rust-lang#122448 (comment) > 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(); ```
2 parents 6f2f720 + c828984 commit 79c39a2

File tree

15 files changed

+241
-163
lines changed

15 files changed

+241
-163
lines changed

src/tools/run-make-support/src/lib.rs

+96-91
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
pub mod run;
2+
13
use std::env;
24
use std::path::{Path, PathBuf};
35
use std::process::{Command, Output};
46

57
pub use object;
68
pub use wasmparser;
79

10+
pub use run::{run, run_fail};
11+
812
pub fn out_dir() -> PathBuf {
913
env::var_os("TMPDIR").unwrap().into()
1014
}
@@ -25,65 +29,122 @@ fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> !
2529
std::process::exit(1)
2630
}
2731

28-
pub fn rustc() -> RustcInvocationBuilder {
29-
RustcInvocationBuilder::new()
32+
/// Construct a new `rustc` invocation.
33+
pub fn rustc() -> Rustc {
34+
Rustc::new()
3035
}
3136

32-
pub fn aux_build() -> AuxBuildInvocationBuilder {
33-
AuxBuildInvocationBuilder::new()
37+
/// Construct a new `rustc` aux-build invocation.
38+
pub fn aux_build() -> Rustc {
39+
Rustc::new_aux_build()
3440
}
3541

42+
/// A `rustc` invocation builder.
3643
#[derive(Debug)]
37-
pub struct RustcInvocationBuilder {
44+
pub struct Rustc {
3845
cmd: Command,
3946
}
4047

41-
impl RustcInvocationBuilder {
42-
fn new() -> Self {
48+
impl Rustc {
49+
// `rustc` invocation constructor methods
50+
51+
/// Construct a new `rustc` invocation.
52+
pub fn new() -> Self {
4353
let cmd = setup_common_build_cmd();
4454
Self { cmd }
4555
}
4656

47-
pub fn arg(&mut self, arg: &str) -> &mut RustcInvocationBuilder {
48-
self.cmd.arg(arg);
57+
/// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`).
58+
pub fn new_aux_build() -> Self {
59+
let mut cmd = setup_common_build_cmd();
60+
cmd.arg("--crate-type=lib");
61+
Self { cmd }
62+
}
63+
64+
// Argument provider methods
65+
66+
/// Configure the compilation environment.
67+
pub fn cfg(&mut self, s: &str) -> &mut Self {
68+
self.cmd.arg("--cfg");
69+
self.cmd.arg(s);
4970
self
5071
}
5172

52-
pub fn args(&mut self, args: &[&str]) -> &mut RustcInvocationBuilder {
53-
self.cmd.args(args);
73+
/// Specify default optimization level `-O` (alias for `-C opt-level=2`).
74+
pub fn opt(&mut self) -> &mut Self {
75+
self.cmd.arg("-O");
5476
self
5577
}
5678

57-
#[track_caller]
58-
pub fn run(&mut self) -> Output {
59-
let caller_location = std::panic::Location::caller();
60-
let caller_line_number = caller_location.line();
79+
/// Specify type(s) of output files to generate.
80+
pub fn emit(&mut self, kinds: &str) -> &mut Self {
81+
self.cmd.arg(format!("--emit={kinds}"));
82+
self
83+
}
6184

62-
let output = self.cmd.output().unwrap();
63-
if !output.status.success() {
64-
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
65-
}
66-
output
85+
/// Specify where an external library is located.
86+
pub fn extern_<P: AsRef<Path>>(&mut self, crate_name: &str, path: P) -> &mut Self {
87+
assert!(
88+
!crate_name.contains(|c: char| c.is_whitespace() || c == '\\' || c == '/'),
89+
"crate name cannot contain whitespace or path separators"
90+
);
91+
92+
let path = path.as_ref().to_string_lossy();
93+
94+
self.cmd.arg("--extern");
95+
self.cmd.arg(format!("{crate_name}={path}"));
96+
97+
self
6798
}
68-
}
6999

70-
#[derive(Debug)]
71-
pub struct AuxBuildInvocationBuilder {
72-
cmd: Command,
73-
}
100+
/// Specify path to the input file.
101+
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
102+
self.cmd.arg(path.as_ref());
103+
self
104+
}
74105

75-
impl AuxBuildInvocationBuilder {
76-
fn new() -> Self {
77-
let mut cmd = setup_common_build_cmd();
78-
cmd.arg("--crate-type=lib");
79-
Self { cmd }
106+
/// Specify target triple.
107+
pub fn target(&mut self, target: &str) -> &mut Self {
108+
assert!(!target.contains(char::is_whitespace), "target triple cannot contain spaces");
109+
self.cmd.arg(format!("--target={target}"));
110+
self
80111
}
81112

82-
pub fn arg(&mut self, arg: &str) -> &mut AuxBuildInvocationBuilder {
113+
/// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
114+
/// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>`
115+
/// is passed (note the space).
116+
pub fn arg(&mut self, arg: &str) -> &mut Self {
117+
assert!(
118+
!(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")),
119+
"use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`"
120+
);
83121
self.cmd.arg(arg);
84122
self
85123
}
86124

125+
/// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
126+
/// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>`
127+
/// is passed (note the space).
128+
pub fn args(&mut self, args: &[&str]) -> &mut Self {
129+
for arg in args {
130+
assert!(
131+
!(["-Z", "-C"].contains(&arg) || arg.starts_with("-Z ") || arg.starts_with("-C ")),
132+
"use `-Zarg` or `-Carg` over split `-Z` `arg` or `-C` `arg`"
133+
);
134+
}
135+
136+
self.cmd.args(args);
137+
self
138+
}
139+
140+
// Command inspection, output and running helper methods
141+
142+
/// Get the [`Output`][std::process::Output] of the finished `rustc` process.
143+
pub fn output(&mut self) -> Output {
144+
self.cmd.output().unwrap()
145+
}
146+
147+
/// Run the constructed `rustc` command and assert that it is successfully run.
87148
#[track_caller]
88149
pub fn run(&mut self) -> Output {
89150
let caller_location = std::panic::Location::caller();
@@ -95,66 +156,10 @@ impl AuxBuildInvocationBuilder {
95156
}
96157
output
97158
}
98-
}
99-
100-
fn run_common(bin_name: &str) -> (Command, Output) {
101-
let target = env::var("TARGET").unwrap();
102-
103-
let bin_name =
104-
if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
105-
106-
let mut bin_path = PathBuf::new();
107-
bin_path.push(env::var("TMPDIR").unwrap());
108-
bin_path.push(&bin_name);
109-
let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
110-
let mut cmd = Command::new(bin_path);
111-
cmd.env(&ld_lib_path_envvar, {
112-
let mut paths = vec![];
113-
paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
114-
for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
115-
paths.push(p.to_path_buf());
116-
}
117-
for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
118-
paths.push(p.to_path_buf());
119-
}
120-
env::join_paths(paths.iter()).unwrap()
121-
});
122159

123-
if target.contains("windows") {
124-
let mut paths = vec![];
125-
for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
126-
paths.push(p.to_path_buf());
127-
}
128-
paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
129-
cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
130-
}
131-
132-
let output = cmd.output().unwrap();
133-
(cmd, output)
134-
}
135-
136-
/// Run a built binary and make sure it succeeds.
137-
#[track_caller]
138-
pub fn run(bin_name: &str) -> Output {
139-
let caller_location = std::panic::Location::caller();
140-
let caller_line_number = caller_location.line();
141-
142-
let (cmd, output) = run_common(bin_name);
143-
if !output.status.success() {
144-
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
145-
}
146-
output
147-
}
148-
149-
/// Run a built binary and make sure it fails.
150-
#[track_caller]
151-
pub fn run_fail(bin_name: &str) -> Output {
152-
let caller_location = std::panic::Location::caller();
153-
let caller_line_number = caller_location.line();
154-
155-
let (cmd, output) = run_common(bin_name);
156-
if output.status.success() {
157-
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
160+
/// Inspect what the underlying [`Command`] is up to the current construction.
161+
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
162+
f(&self.cmd);
163+
self
158164
}
159-
output
160165
}

src/tools/run-make-support/src/run.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::env;
2+
use std::path::{Path, PathBuf};
3+
use std::process::{Command, Output};
4+
5+
use super::handle_failed_output;
6+
7+
fn run_common(bin_name: &str) -> (Command, Output) {
8+
let target = env::var("TARGET").unwrap();
9+
10+
let bin_name =
11+
if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
12+
13+
let mut bin_path = PathBuf::new();
14+
bin_path.push(env::var("TMPDIR").unwrap());
15+
bin_path.push(&bin_name);
16+
let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
17+
let mut cmd = Command::new(bin_path);
18+
cmd.env(&ld_lib_path_envvar, {
19+
let mut paths = vec![];
20+
paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
21+
for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
22+
paths.push(p.to_path_buf());
23+
}
24+
for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
25+
paths.push(p.to_path_buf());
26+
}
27+
env::join_paths(paths.iter()).unwrap()
28+
});
29+
30+
if target.contains("windows") {
31+
let mut paths = vec![];
32+
for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
33+
paths.push(p.to_path_buf());
34+
}
35+
paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
36+
cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
37+
}
38+
39+
let output = cmd.output().unwrap();
40+
(cmd, output)
41+
}
42+
43+
/// Run a built binary and make sure it succeeds.
44+
#[track_caller]
45+
pub fn run(bin_name: &str) -> Output {
46+
let caller_location = std::panic::Location::caller();
47+
let caller_line_number = caller_location.line();
48+
49+
let (cmd, output) = run_common(bin_name);
50+
if !output.status.success() {
51+
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
52+
}
53+
output
54+
}
55+
56+
/// Run a built binary and make sure it fails.
57+
#[track_caller]
58+
pub fn run_fail(bin_name: &str) -> Output {
59+
let caller_location = std::panic::Location::caller();
60+
let caller_line_number = caller_location.line();
61+
62+
let (cmd, output) = run_common(bin_name);
63+
if output.status.success() {
64+
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
65+
}
66+
output
67+
}

tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
// ignore-tidy-linelength
22

3+
// Check that the `CURRENT_RUSTC_VERSION` placeholder is correctly replaced by the current
4+
// `rustc` version and the `since` property in feature stability gating is properly respected.
5+
36
extern crate run_make_support;
47

58
use std::path::PathBuf;
69

7-
use run_make_support::{aux_build, rustc};
10+
use run_make_support::{rustc, aux_build};
811

912
fn main() {
10-
aux_build()
11-
.arg("--emit=metadata")
12-
.arg("stable.rs")
13-
.run();
13+
aux_build().input("stable.rs").emit("metadata").run();
14+
1415
let mut stable_path = PathBuf::from(env!("TMPDIR"));
1516
stable_path.push("libstable.rmeta");
17+
1618
let output = rustc()
17-
.arg("--emit=metadata")
18-
.arg("--extern")
19-
.arg(&format!("stable={}", &stable_path.to_string_lossy()))
20-
.arg("main.rs")
21-
.run();
19+
.input("main.rs")
20+
.emit("metadata")
21+
.extern_("stable", &stable_path)
22+
.output();
2223

2324
let stderr = String::from_utf8_lossy(&output.stderr);
2425
let version = include_str!(concat!(env!("S"), "/src/version"));

0 commit comments

Comments
 (0)