Skip to content

Commit 4acb17e

Browse files
committed
Rework rmake support library to use a DSL
1 parent 9ce37dc commit 4acb17e

File tree

15 files changed

+375
-174
lines changed

15 files changed

+375
-174
lines changed

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

+190-88
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
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 wasmparser;
68

9+
pub use run::{run, run_fail};
10+
711
pub fn out_dir() -> PathBuf {
812
env::var_os("TMPDIR").unwrap().into()
913
}
@@ -24,65 +28,148 @@ fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> !
2428
std::process::exit(1)
2529
}
2630

27-
pub fn rustc() -> RustcInvocationBuilder {
28-
RustcInvocationBuilder::new()
29-
}
30-
31-
pub fn aux_build() -> AuxBuildInvocationBuilder {
32-
AuxBuildInvocationBuilder::new()
33-
}
34-
31+
/// A `rustc` invocation builder.
3532
#[derive(Debug)]
36-
pub struct RustcInvocationBuilder {
33+
pub struct Rustc {
3734
cmd: Command,
3835
}
3936

40-
impl RustcInvocationBuilder {
41-
fn new() -> Self {
37+
impl Rustc {
38+
// `rustc` invocation constructor methods
39+
40+
/// Construct a new `rustc` invocation.
41+
pub fn new() -> Self {
4242
let cmd = setup_common_build_cmd();
4343
Self { cmd }
4444
}
4545

46-
pub fn arg(&mut self, arg: &str) -> &mut RustcInvocationBuilder {
47-
self.cmd.arg(arg);
46+
/// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`).
47+
pub fn new_aux_build() -> Self {
48+
let mut cmd = setup_common_build_cmd();
49+
cmd.arg("--crate-type=lib");
50+
Self { cmd }
51+
}
52+
53+
// Argument provider methods
54+
55+
/// Configure the compilation environment.
56+
pub fn cfg(&mut self, s: &str) -> &mut Self {
57+
self.cmd.arg("--cfg");
58+
self.cmd.arg(s);
4859
self
4960
}
5061

51-
pub fn args(&mut self, args: &[&str]) -> &mut RustcInvocationBuilder {
52-
self.cmd.args(args);
62+
/// Configure codegen options.
63+
pub fn codegen_opt(&mut self, c: CodegenOpt) -> &mut Self {
64+
self.cmd.arg("-C");
65+
66+
match c {
67+
CodegenOpt::PreferDynamic => self.cmd.arg("prefer-dynamic=true"),
68+
CodegenOpt::SymbolManglingVersion(v) => match v {
69+
SymbolManglingVersion::Legacy => self.cmd.arg("symbol-mangling-version=legacy"),
70+
SymbolManglingVersion::V0 => self.cmd.arg("symbol-mangling-version=v0"),
71+
},
72+
CodegenOpt::Lto(kind) => match kind {
73+
LtoKind::Fat => self.cmd.arg("lto=fat"),
74+
LtoKind::Thin => self.cmd.arg("lto=thin"),
75+
LtoKind::None => self.cmd.arg("lto=false"),
76+
},
77+
CodegenOpt::OptLevel(level) => match level {
78+
OptLevel::O0 => self.cmd.arg("opt-level=0"),
79+
OptLevel::O1 => self.cmd.arg("opt-level=1"),
80+
OptLevel::O2 => self.cmd.arg("opt-level=2"),
81+
OptLevel::O3 => self.cmd.arg("opt-level=3"),
82+
OptLevel::Os => self.cmd.arg("opt-level=s"),
83+
OptLevel::Oz => self.cmd.arg("opt-level=z"),
84+
},
85+
CodegenOpt::OverflowChecks => self.cmd.arg("overflow-checks=true"),
86+
CodegenOpt::Panic(strat) => match strat {
87+
PanicStrategy::Abort => self.cmd.arg("panic=abort"),
88+
PanicStrategy::Unwind => self.cmd.arg("panic=unwind"),
89+
},
90+
};
91+
5392
self
5493
}
5594

56-
#[track_caller]
57-
pub fn run(&mut self) -> Output {
58-
let caller_location = std::panic::Location::caller();
59-
let caller_line_number = caller_location.line();
95+
/// Specify default optimization level `-O` (alias for `-C opt-level=2`).
96+
pub fn default_opt(&mut self) -> &mut Self {
97+
self.cmd.arg("-O");
98+
self
99+
}
60100

61-
let output = self.cmd.output().unwrap();
62-
if !output.status.success() {
63-
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
64-
}
65-
output
101+
/// Specify types of output files to generate. See [`EmitKind`] for kinds.
102+
pub fn emit(&mut self, kinds: &[EmitKind]) -> &mut Self {
103+
let kinds = kinds
104+
.iter()
105+
.map(|kind| match kind {
106+
EmitKind::Metadata => "metadata",
107+
})
108+
.collect::<Vec<_>>();
109+
let kinds_str: String = kinds.join(",");
110+
self.cmd.arg(format!("--emit={kinds_str}"));
111+
self
66112
}
67-
}
68113

69-
#[derive(Debug)]
70-
pub struct AuxBuildInvocationBuilder {
71-
cmd: Command,
72-
}
114+
/// Set `-Z unstable-options`
115+
pub fn enable_unstable_options(&mut self) -> &mut Self {
116+
self.cmd.arg("-Z");
117+
self.cmd.arg("unstable-options");
118+
self
119+
}
73120

74-
impl AuxBuildInvocationBuilder {
75-
fn new() -> Self {
76-
let mut cmd = setup_common_build_cmd();
77-
cmd.arg("--crate-type=lib");
78-
Self { cmd }
121+
/// Specify where an external library is located.
122+
pub fn extern_<P: AsRef<Path>>(&mut self, crate_name: &str, path: P) -> &mut Self {
123+
assert!(
124+
!crate_name.contains(|c: char| c.is_whitespace() || c == '\\' || c == '/'),
125+
"crate name cannot contain whitespace or path separators"
126+
);
127+
128+
let path = path.as_ref().to_string_lossy();
129+
130+
self.cmd.arg("--extern");
131+
self.cmd.arg(format!("{crate_name}={path}"));
132+
133+
self
79134
}
80135

81-
pub fn arg(&mut self, arg: &str) -> &mut AuxBuildInvocationBuilder {
136+
/// Specify path to the input file.
137+
pub fn input_file<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
138+
self.cmd.arg(path.as_ref());
139+
self
140+
}
141+
142+
/// Specify target triple.
143+
pub fn target(&mut self, target: &str) -> &mut Self {
144+
assert!(!target.contains(char::is_whitespace), "target triple cannot contain spaces");
145+
self.cmd.arg(format!("--target={target}"));
146+
self
147+
}
148+
149+
// Last-resort builder methods
150+
151+
/// Fallback command argument provider. Prefer using semantically meaningful builder methods
152+
/// (add them if they don't exist) whenever possible.
153+
pub fn arg(&mut self, arg: &str) -> &mut Self {
82154
self.cmd.arg(arg);
83155
self
84156
}
85157

158+
/// Fallback command arguments provider. Prefer using semantically meaningful builder methods
159+
/// (add them if they don't exist) whenever possible.
160+
pub fn args(&mut self, args: &[&str]) -> &mut Self {
161+
self.cmd.args(args);
162+
self
163+
}
164+
165+
// Command inspection, output and running helper methods
166+
167+
/// Get the [`Output`][std::process::Output] of the finished `rustc` process.
168+
pub fn output(&mut self) -> Output {
169+
self.cmd.output().unwrap()
170+
}
171+
172+
/// Run the constructed `rustc` command and assert that it is successfully run.
86173
#[track_caller]
87174
pub fn run(&mut self) -> Output {
88175
let caller_location = std::panic::Location::caller();
@@ -94,66 +181,81 @@ impl AuxBuildInvocationBuilder {
94181
}
95182
output
96183
}
97-
}
98184

99-
fn run_common(bin_name: &str) -> (Command, Output) {
100-
let target = env::var("TARGET").unwrap();
101-
102-
let bin_name =
103-
if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
104-
105-
let mut bin_path = PathBuf::new();
106-
bin_path.push(env::var("TMPDIR").unwrap());
107-
bin_path.push(&bin_name);
108-
let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
109-
let mut cmd = Command::new(bin_path);
110-
cmd.env(&ld_lib_path_envvar, {
111-
let mut paths = vec![];
112-
paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
113-
for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
114-
paths.push(p.to_path_buf());
115-
}
116-
for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
117-
paths.push(p.to_path_buf());
118-
}
119-
env::join_paths(paths.iter()).unwrap()
120-
});
121-
122-
if target.contains("windows") {
123-
let mut paths = vec![];
124-
for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
125-
paths.push(p.to_path_buf());
126-
}
127-
paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
128-
cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
185+
/// Inspect what the underlying [`Command`] is up to the current construction.
186+
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
187+
f(&self.cmd);
188+
self
129189
}
190+
}
130191

131-
let output = cmd.output().unwrap();
132-
(cmd, output)
192+
/// Specifies the types of output files to generate.
193+
pub enum EmitKind {
194+
/// Generates a file containing metadata about the crate. The default output filename is
195+
/// `libCRATE_NAME.rmeta`.
196+
Metadata,
133197
}
134198

135-
/// Run a built binary and make sure it succeeds.
136-
#[track_caller]
137-
pub fn run(bin_name: &str) -> Output {
138-
let caller_location = std::panic::Location::caller();
139-
let caller_line_number = caller_location.line();
199+
/// Specifies codegen options.
200+
pub enum CodegenOpt {
201+
/// By default, rustc prefers to statically link dependencies. This option will indicate that
202+
/// dynamic linking should be used if possible if both a static and dynamic versions of a
203+
/// library are available.
204+
PreferDynamic,
205+
/// Controls the name mangling format for encoding Rust item names for the purpose of generating
206+
/// object code and linking.
207+
SymbolManglingVersion(SymbolManglingVersion),
208+
/// Controls whether LLVM uses link time optimizations to produce better optimized code, using
209+
/// whole-program analysis, at the cost of longer linking time.
210+
Lto(LtoKind),
211+
///
212+
OptLevel(OptLevel),
213+
/// Control the behavior of runtime integer overflow. When `overflow-checks` are enabled, a
214+
/// panic will occur on overflow.
215+
OverflowChecks,
216+
/// Control what happens when the code panics.
217+
Panic(PanicStrategy),
218+
}
140219

141-
let (cmd, output) = run_common(bin_name);
142-
if !output.status.success() {
143-
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
144-
}
145-
output
220+
/// The name mangling format for encoding Rust item names for the purpose of generating object code
221+
/// and linking.
222+
pub enum SymbolManglingVersion {
223+
Legacy,
224+
V0,
146225
}
147226

148-
/// Run a built binary and make sure it fails.
149-
#[track_caller]
150-
pub fn run_fail(bin_name: &str) -> Output {
151-
let caller_location = std::panic::Location::caller();
152-
let caller_line_number = caller_location.line();
227+
/// Kind of LTO to perform.
228+
pub enum LtoKind {
229+
/// Perform "fat" LTO which attempts to perform optimizations across all crates within the
230+
/// dependency graph.
231+
Fat,
232+
/// Similar to "fat", but takes substantially less time to run while still achieving performance
233+
/// gains similar to "fat".
234+
Thin,
235+
/// Disable LTO.
236+
None,
237+
}
153238

154-
let (cmd, output) = run_common(bin_name);
155-
if output.status.success() {
156-
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
157-
}
158-
output
239+
/// Optimization level.
240+
pub enum OptLevel {
241+
/// No optimizations, also turns on `cfg(debug_assertions)` (the default).
242+
O0,
243+
/// Basic optimizations.
244+
O1,
245+
/// Some optimizations.
246+
O2,
247+
/// All optimizations.
248+
O3,
249+
/// Optimize for binary size.
250+
Os,
251+
/// Optimize for binary size, but also turn off loop vectorization.
252+
Oz,
253+
}
254+
255+
/// What happens when the code panics.
256+
pub enum PanicStrategy {
257+
/// Terminate the process upon panic.
258+
Abort,
259+
/// Unwind the stack upon panic.
260+
Unwind,
159261
}

0 commit comments

Comments
 (0)