Skip to content

Commit e9c91e3

Browse files
committed
Modify run-make-support to support CC invocations
1 parent 0157da4 commit e9c91e3

File tree

4 files changed

+293
-19
lines changed

4 files changed

+293
-19
lines changed

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

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use std::env;
2+
use std::path::Path;
3+
use std::process::{Command, Output};
4+
5+
use crate::{bin_name, cygpath_windows, handle_failed_output, is_msvc, is_windows, tmp_dir, uname};
6+
7+
/// Construct a new platform-specific C/C++ compiler invocation.
8+
///
9+
/// WARNING: This means that what flags are accepted by the underlying C/C++ compile is
10+
/// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
11+
pub fn cc() -> Cc {
12+
Cc::new()
13+
}
14+
15+
/// A platform-specific C/C++ compiler invocation builder. The specific C/C++ compiler used is
16+
/// passed down from compiletest.
17+
#[derive(Debug)]
18+
pub struct Cc {
19+
cmd: Command,
20+
}
21+
22+
impl Cc {
23+
/// Construct a new platform-specific C/C++ compiler invocation.
24+
///
25+
/// WARNING: This means that what flags are accepted by the underlying C/C++ compile is
26+
/// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`.
27+
pub fn new() -> Self {
28+
let var = env::var("CC").unwrap();
29+
// FIXME(jieyouxu): wouldn't this be less hacky if we passed the compiler and preset
30+
// flags separately?
31+
let (compiler, flags) = var
32+
.split_once(" ")
33+
.expect("expected CC to be a compiler followed by flags like `cc -funsafe-math`");
34+
let mut cmd = Command::new(compiler);
35+
for flag in flags.split(char::is_whitespace) {
36+
cmd.arg(flag);
37+
}
38+
Self { cmd }
39+
}
40+
41+
/// Specify path of the input file.
42+
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
43+
self.cmd.arg(path.as_ref());
44+
self
45+
}
46+
47+
/// Add a *platform-and-compiler-specific* argument. Please consult the docs for the various
48+
/// possible C/C++ compilers on the various platforms to check which arguments are legal for
49+
/// which compiler.
50+
pub fn arg(&mut self, flag: &str) -> &mut Self {
51+
self.cmd.arg(flag);
52+
self
53+
}
54+
55+
/// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable
56+
/// is under `$TMPDIR`.
57+
pub fn out_exe(&mut self, name: &str) -> &mut Self {
58+
// Ref: tools.mk (irrelevant lines omitted):
59+
//
60+
// ```makefile
61+
// ifdef IS_MSVC
62+
// OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \
63+
// -Fo:`cygpath -w $(TMPDIR)/$(1).obj`
64+
// else
65+
// OUT_EXE=-o $(TMPDIR)/$(1)
66+
// endif
67+
// ```
68+
69+
if is_msvc() {
70+
let fe_path = cygpath_windows(tmp_dir().join(bin_name(name)));
71+
let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj")));
72+
self.cmd.arg(format!("-Fe:{fe_path}"));
73+
self.cmd.arg(format!("-Fo:{fo_path}"));
74+
} else {
75+
self.cmd.arg("-o");
76+
self.cmd.arg(tmp_dir().join(name));
77+
}
78+
79+
self
80+
}
81+
82+
/// Run the constructed C/C++ invocation command and assert that it is successfully run.
83+
#[track_caller]
84+
pub fn run(&mut self) -> Output {
85+
let caller_location = std::panic::Location::caller();
86+
let caller_line_number = caller_location.line();
87+
88+
let output = self.cmd.output().unwrap();
89+
if !output.status.success() {
90+
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
91+
}
92+
output
93+
}
94+
95+
/// Inspect what the underlying [`Command`] is up to the current construction.
96+
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
97+
f(&self.cmd);
98+
self
99+
}
100+
}
101+
102+
/// `EXTRACFLAGS`
103+
pub fn extra_c_flags() -> String {
104+
// Adapted from tools.mk (trimmed):
105+
//
106+
// ```makefile
107+
// ifdef IS_WINDOWS
108+
// ifdef IS_MSVC
109+
// EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
110+
// else
111+
// EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization
112+
// endif
113+
// else
114+
// ifeq ($(UNAME),Darwin)
115+
// EXTRACFLAGS := -lresolv
116+
// else
117+
// ifeq ($(UNAME),FreeBSD)
118+
// EXTRACFLAGS := -lm -lpthread -lgcc_s
119+
// else
120+
// ifeq ($(UNAME),SunOS)
121+
// EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv
122+
// else
123+
// ifeq ($(UNAME),OpenBSD)
124+
// EXTRACFLAGS := -lm -lpthread -lc++abi
125+
// else
126+
// EXTRACFLAGS := -lm -lrt -ldl -lpthread
127+
// endif
128+
// endif
129+
// endif
130+
// endif
131+
// endif
132+
// ```
133+
134+
if is_windows() {
135+
if is_msvc() {
136+
"ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib"
137+
.to_string()
138+
} else {
139+
"-lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization".to_string()
140+
}
141+
} else {
142+
match uname() {
143+
n if n.contains("Darwin") => "-lresolv".to_string(),
144+
n if n.contains("FreeBSD") => "-lm -lpthread -lgcc_s".to_string(),
145+
n if n.contains("SunOS") => "-lm -lpthread -lposix4 -lsocket -lresolv".to_string(),
146+
n if n.contains("OpenBSD") => "-lm -lpthread -lc++abi".to_string(),
147+
_ => "-lm -lrt -ldl -lpthread".to_string(),
148+
}
149+
}
150+
}
151+
152+
/// `EXTRACXXFLAGS`
153+
pub fn extra_cxx_flags() -> String {
154+
// Adapted from tools.mk (trimmed):
155+
//
156+
// ```makefile
157+
// ifdef IS_WINDOWS
158+
// ifdef IS_MSVC
159+
// else
160+
// EXTRACXXFLAGS := -lstdc++
161+
// endif
162+
// else
163+
// ifeq ($(UNAME),Darwin)
164+
// EXTRACXXFLAGS := -lc++
165+
// else
166+
// ifeq ($(UNAME),FreeBSD)
167+
// else
168+
// ifeq ($(UNAME),SunOS)
169+
// else
170+
// ifeq ($(UNAME),OpenBSD)
171+
// else
172+
// EXTRACXXFLAGS := -lstdc++
173+
// endif
174+
// endif
175+
// endif
176+
// endif
177+
// endif
178+
// ```
179+
if is_windows() {
180+
if is_msvc() { String::new() } else { "-lstdc++".to_string() }
181+
} else {
182+
match uname() {
183+
n if n.contains("Darwin") => "-lc++".to_string(),
184+
_ => "-lstdc++".to_string(),
185+
}
186+
}
187+
}

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

+85-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
pub mod cc;
12
pub mod run;
23
pub mod rustc;
34
pub mod rustdoc;
45

56
use std::env;
6-
use std::path::PathBuf;
7-
use std::process::Output;
7+
use std::path::{Path, PathBuf};
8+
use std::process::{Command, Output};
89

910
pub use object;
1011
pub use wasmparser;
1112

13+
pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
1214
pub use run::{run, run_fail};
1315
pub use rustc::{aux_build, rustc, Rustc};
1416
pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
@@ -18,6 +20,87 @@ pub fn tmp_dir() -> PathBuf {
1820
env::var_os("TMPDIR").unwrap().into()
1921
}
2022

23+
/// `TARGET`
24+
pub fn target() -> String {
25+
env::var("TARGET").unwrap()
26+
}
27+
28+
/// Check if target is windows-like.
29+
pub fn is_windows() -> bool {
30+
env::var_os("IS_WINDOWS").is_some()
31+
}
32+
33+
/// Check if target uses msvc.
34+
pub fn is_msvc() -> bool {
35+
env::var_os("IS_MSVC").is_some()
36+
}
37+
38+
/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a
39+
/// path with `$TMPDIR` joined with platform-and-compiler-specific library name.
40+
pub fn static_lib(name: &str) -> PathBuf {
41+
tmp_dir().join(static_lib_name(name))
42+
}
43+
44+
/// Construct the static library name based on the platform.
45+
pub fn static_lib_name(name: &str) -> String {
46+
// See tools.mk (irrelevant lines omitted):
47+
//
48+
// ```makefile
49+
// ifeq ($(UNAME),Darwin)
50+
// STATICLIB = $(TMPDIR)/lib$(1).a
51+
// else
52+
// ifdef IS_WINDOWS
53+
// ifdef IS_MSVC
54+
// STATICLIB = $(TMPDIR)/$(1).lib
55+
// else
56+
// STATICLIB = $(TMPDIR)/lib$(1).a
57+
// endif
58+
// else
59+
// STATICLIB = $(TMPDIR)/lib$(1).a
60+
// endif
61+
// endif
62+
// ```
63+
assert!(!name.contains(char::is_whitespace), "name cannot contain whitespace");
64+
65+
if target().contains("msvc") { format!("{name}.lib") } else { format!("lib{name}.a") }
66+
}
67+
68+
/// Construct the binary name based on platform.
69+
pub fn bin_name(name: &str) -> String {
70+
if is_windows() { format!("{name}.exe") } else { name.to_string() }
71+
}
72+
73+
/// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is
74+
/// available on the platform!
75+
#[track_caller]
76+
pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String {
77+
let caller_location = std::panic::Location::caller();
78+
let caller_line_number = caller_location.line();
79+
80+
let mut cygpath = Command::new("cygpath");
81+
cygpath.arg("-w");
82+
cygpath.arg(path.as_ref());
83+
let output = cygpath.output().unwrap();
84+
if !output.status.success() {
85+
handle_failed_output(&format!("{:#?}", cygpath), output, caller_line_number);
86+
}
87+
String::from_utf8(output.stdout).unwrap()
88+
}
89+
90+
/// Run `uname`. This assumes that `uname` is available on the platform!
91+
#[track_caller]
92+
pub fn uname() -> String {
93+
let caller_location = std::panic::Location::caller();
94+
let caller_line_number = caller_location.line();
95+
96+
let mut uname = Command::new("uname");
97+
let output = uname.output().unwrap();
98+
if !output.status.success() {
99+
handle_failed_output(&format!("{:#?}", uname), output, caller_line_number);
100+
}
101+
String::from_utf8(output.stdout).unwrap()
102+
}
103+
21104
fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
22105
eprintln!("command failed at line {caller_line_number}");
23106
eprintln!("{cmd}");

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

+9-12
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@ use std::env;
22
use std::path::{Path, PathBuf};
33
use std::process::{Command, Output};
44

5-
use super::handle_failed_output;
5+
use crate::is_windows;
66

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() };
7+
use super::{bin_name, handle_failed_output};
128

9+
fn run_common(name: &str) -> (Command, Output) {
1310
let mut bin_path = PathBuf::new();
1411
bin_path.push(env::var("TMPDIR").unwrap());
15-
bin_path.push(&bin_name);
12+
bin_path.push(&bin_name(name));
1613
let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
1714
let mut cmd = Command::new(bin_path);
1815
cmd.env(&ld_lib_path_envvar, {
@@ -27,7 +24,7 @@ fn run_common(bin_name: &str) -> (Command, Output) {
2724
env::join_paths(paths.iter()).unwrap()
2825
});
2926

30-
if target.contains("windows") {
27+
if is_windows() {
3128
let mut paths = vec![];
3229
for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
3330
paths.push(p.to_path_buf());
@@ -42,11 +39,11 @@ fn run_common(bin_name: &str) -> (Command, Output) {
4239

4340
/// Run a built binary and make sure it succeeds.
4441
#[track_caller]
45-
pub fn run(bin_name: &str) -> Output {
42+
pub fn run(name: &str) -> Output {
4643
let caller_location = std::panic::Location::caller();
4744
let caller_line_number = caller_location.line();
4845

49-
let (cmd, output) = run_common(bin_name);
46+
let (cmd, output) = run_common(name);
5047
if !output.status.success() {
5148
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
5249
}
@@ -55,11 +52,11 @@ pub fn run(bin_name: &str) -> Output {
5552

5653
/// Run a built binary and make sure it fails.
5754
#[track_caller]
58-
pub fn run_fail(bin_name: &str) -> Output {
55+
pub fn run_fail(name: &str) -> Output {
5956
let caller_location = std::panic::Location::caller();
6057
let caller_line_number = caller_location.line();
6158

62-
let (cmd, output) = run_common(bin_name);
59+
let (cmd, output) = run_common(name);
6360
if output.status.success() {
6461
handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
6562
}

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

+12-5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ impl Rustc {
9292
self
9393
}
9494

95+
/// Specify `--crate-type`.
96+
pub fn crate_type(&mut self, crate_type: &str) -> &mut Self {
97+
assert!(!crate_type.contains(char::is_whitespace), "crate_type cannot contain whitespace");
98+
self.cmd.arg(format!("--crate-type={crate_type}"));
99+
self
100+
}
101+
95102
/// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
96103
/// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z <name>` or `-C <name>`
97104
/// is passed (note the space).
@@ -121,11 +128,6 @@ impl Rustc {
121128

122129
// Command inspection, output and running helper methods
123130

124-
/// Get the [`Output`][std::process::Output] of the finished `rustc` process.
125-
pub fn output(&mut self) -> Output {
126-
self.cmd.output().unwrap()
127-
}
128-
129131
/// Run the constructed `rustc` command and assert that it is successfully run.
130132
#[track_caller]
131133
pub fn run(&mut self) -> Output {
@@ -139,6 +141,11 @@ impl Rustc {
139141
output
140142
}
141143

144+
/// Get the [`Output`][std::process::Output] of the finished `rustc` process.
145+
pub fn output(&mut self) -> Output {
146+
self.cmd.output().unwrap()
147+
}
148+
142149
/// Inspect what the underlying [`Command`] is up to the current construction.
143150
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
144151
f(&self.cmd);

0 commit comments

Comments
 (0)