Skip to main content

run_make_support/external_deps/
rustc.rs

1use std::ffi::{OsStr, OsString};
2use std::path::{Path, PathBuf};
3use std::str::FromStr as _;
4
5use crate::command::Command;
6use crate::env::env_var;
7use crate::path_helpers::{cwd, source_root};
8use crate::util::set_host_compiler_dylib_path;
9use crate::{is_aix, is_darwin, is_windows, is_windows_msvc, target, uname};
10
11/// Construct a new `rustc` invocation. This will automatically set the library
12/// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
13#[track_caller]
14pub fn rustc() -> Rustc {
15    Rustc::new()
16}
17
18/// Construct a plain `rustc` invocation with no flags set. Note that [`set_host_compiler_dylib_path`]
19/// still presets the environment variable `HOST_RUSTC_DYLIB_PATH` by default.
20#[track_caller]
21pub fn bare_rustc() -> Rustc {
22    Rustc::bare()
23}
24
25/// Construct a `rustc` invocation for building `minicore`.
26///
27/// This function:
28///
29/// - adds `tests/auxiliary/minicore.rs` as an input
30/// - sets the crate name to `"minicore"`
31/// - sets the crate type to `rlib`
32///
33/// # Example
34///
35/// ```ignore (illustrative)
36/// rustc_minicore().target("wasm32-wasip1").target_cpu("mvp").output("libminicore.rlib").run();
37///
38/// rustc()
39///     .input("foo.rs")
40///     .target("wasm32-wasip1")
41///     .target_cpu("mvp")
42///     .extern_("minicore", path("libminicore.rlib"))
43///     // ...
44///     .run()
45/// ```
46#[track_caller]
47pub fn rustc_minicore() -> Rustc {
48    let mut builder = rustc();
49
50    let minicore_path = source_root().join("tests/auxiliary/minicore.rs");
51    builder.input(minicore_path).crate_name("minicore").crate_type("rlib");
52
53    builder
54}
55
56/// A `rustc` invocation builder.
57#[derive(Debug)]
58#[must_use]
59pub struct Rustc {
60    cmd: Command,
61    target: Option<String>,
62}
63
64// Only fill in the target just before execution, so that it can be overridden.
65crate::macros::impl_common_helpers!(Rustc, |rustc: &mut Rustc| {
66    if let Some(target) = &rustc.target {
67        rustc.cmd.arg(&format!("--target={target}"));
68    }
69});
70
71pub fn rustc_path() -> String {
72    env_var("RUSTC")
73}
74
75#[track_caller]
76fn setup_common() -> Command {
77    let mut cmd = Command::new(rustc_path());
78    set_host_compiler_dylib_path(&mut cmd);
79    cmd
80}
81
82impl Rustc {
83    // `rustc` invocation constructor methods
84
85    /// Construct a new `rustc` invocation. This will automatically set the library
86    /// search path as `-L cwd()`, configure the compilation target and enable
87    /// dynamic linkage by default on musl hosts.
88    /// Use [`bare_rustc`] to avoid this.
89    #[track_caller]
90    pub fn new() -> Self {
91        let mut cmd = setup_common();
92        cmd.arg("-L").arg(cwd());
93
94        // FIXME: On musl hosts, we currently default to static linkage, while
95        // for running run-make tests, we rely on dynamic linkage by default
96        if std::env::var("IS_MUSL_HOST").is_ok_and(|i| i == "1") {
97            cmd.arg("-Ctarget-feature=-crt-static");
98        }
99
100        // Automatically default to cross-compilation
101        Self { cmd, target: Some(target()) }
102    }
103
104    /// Construct a bare `rustc` invocation with no flags set.
105    #[track_caller]
106    pub fn bare() -> Self {
107        let cmd = setup_common();
108        Self { cmd, target: None }
109    }
110
111    // Argument provider methods
112
113    /// Configure the compilation environment.
114    pub fn cfg(&mut self, s: &str) -> &mut Self {
115        self.cmd.arg("--cfg");
116        self.cmd.arg(s);
117        self
118    }
119
120    /// Specify default optimization level `-O` (alias for `-C opt-level=3`).
121    pub fn opt(&mut self) -> &mut Self {
122        self.cmd.arg("-O");
123        self
124    }
125
126    /// Specify a specific optimization level.
127    pub fn opt_level(&mut self, option: &str) -> &mut Self {
128        self.cmd.arg(format!("-Copt-level={option}"));
129        self
130    }
131
132    /// Incorporate a hashed string to mangled symbols.
133    pub fn metadata(&mut self, meta: &str) -> &mut Self {
134        self.cmd.arg(format!("-Cmetadata={meta}"));
135        self
136    }
137
138    /// Add a suffix in each output filename.
139    pub fn extra_filename(&mut self, suffix: &str) -> &mut Self {
140        self.cmd.arg(format!("-Cextra-filename={suffix}"));
141        self
142    }
143
144    /// Specify type(s) of output files to generate.
145    pub fn emit<S: AsRef<str>>(&mut self, kinds: S) -> &mut Self {
146        let kinds = kinds.as_ref();
147        self.cmd.arg(format!("--emit={kinds}"));
148        self
149    }
150
151    /// Specify where an external library is located.
152    pub fn extern_<P: AsRef<Path>>(&mut self, crate_name: &str, path: P) -> &mut Self {
153        assert!(
154            !crate_name.contains(|c: char| c.is_whitespace() || c == '\\' || c == '/'),
155            "crate name cannot contain whitespace or path separators"
156        );
157
158        let path = path.as_ref().to_string_lossy();
159
160        self.cmd.arg("--extern");
161        self.cmd.arg(format!("{crate_name}={path}"));
162
163        self
164    }
165
166    /// Remap source path prefixes in all output.
167    pub fn remap_path_prefix<P: AsRef<Path>, P2: AsRef<Path>>(
168        &mut self,
169        from: P,
170        to: P2,
171    ) -> &mut Self {
172        let from = from.as_ref().to_string_lossy();
173        let to = to.as_ref().to_string_lossy();
174
175        self.cmd.arg("--remap-path-prefix");
176        self.cmd.arg(format!("{from}={to}"));
177
178        self
179    }
180
181    /// Specify path to the input file.
182    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
183        self.cmd.arg(path.as_ref());
184        self
185    }
186
187    //Adjust the backtrace level, displaying more detailed information at higher levels.
188    pub fn set_backtrace_level<R: AsRef<OsStr>>(&mut self, level: R) -> &mut Self {
189        self.cmd.env("RUST_BACKTRACE", level);
190        self
191    }
192
193    /// Specify path to the output file. Equivalent to `-o`` in rustc.
194    pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
195        self.cmd.arg("-o");
196        self.cmd.arg(path.as_ref());
197        self
198    }
199
200    /// Specify path to the output directory. Equivalent to `--out-dir`` in rustc.
201    pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
202        self.cmd.arg("--out-dir");
203        self.cmd.arg(path.as_ref());
204        self
205    }
206
207    /// This flag enables LTO in the specified form.
208    pub fn lto(&mut self, option: &str) -> &mut Self {
209        self.cmd.arg(format!("-Clto={option}"));
210        self
211    }
212
213    /// This flag defers LTO optimizations to the linker.
214    pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self {
215        self.cmd.arg(format!("-Clinker-plugin-lto={option}"));
216        self
217    }
218
219    /// Specify what happens when the code panics.
220    pub fn panic(&mut self, option: &str) -> &mut Self {
221        self.cmd.arg(format!("-Cpanic={option}"));
222        self
223    }
224
225    /// Specify number of codegen units
226    pub fn codegen_units(&mut self, units: usize) -> &mut Self {
227        self.cmd.arg(format!("-Ccodegen-units={units}"));
228        self
229    }
230
231    /// Specify directory path used for incremental cache
232    pub fn incremental<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
233        let mut arg = OsString::new();
234        arg.push("-Cincremental=");
235        arg.push(path.as_ref());
236        self.cmd.arg(&arg);
237        self
238    }
239
240    /// Specify directory path used for profile generation
241    pub fn profile_generate<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
242        let mut arg = OsString::new();
243        arg.push("-Cprofile-generate=");
244        arg.push(path.as_ref());
245        self.cmd.arg(&arg);
246        self
247    }
248
249    /// Specify directory path used for profile usage
250    pub fn profile_use<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
251        let mut arg = OsString::new();
252        arg.push("-Cprofile-use=");
253        arg.push(path.as_ref());
254        self.cmd.arg(&arg);
255        self
256    }
257
258    /// Specify option of `-C symbol-mangling-version`.
259    pub fn symbol_mangling_version(&mut self, option: &str) -> &mut Self {
260        self.cmd.arg(format!("-Csymbol-mangling-version={option}"));
261        self
262    }
263
264    /// Specify `-C prefer-dynamic`.
265    pub fn prefer_dynamic(&mut self) -> &mut Self {
266        self.cmd.arg(format!("-Cprefer-dynamic"));
267        self
268    }
269
270    /// Specify error format to use
271    pub fn error_format(&mut self, format: &str) -> &mut Self {
272        self.cmd.arg(format!("--error-format={format}"));
273        self
274    }
275
276    /// Specify json messages printed by the compiler
277    pub fn json(&mut self, items: &str) -> &mut Self {
278        self.cmd.arg(format!("--json={items}"));
279        self
280    }
281
282    /// Normalize the line number in the stderr output
283    pub fn ui_testing(&mut self) -> &mut Self {
284        self.cmd.arg(format!("-Zui-testing"));
285        self
286    }
287
288    /// Specify the target triple, or a path to a custom target json spec file.
289    pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
290        // We store the target as a separate field, so that it can be specified multiple times.
291        // This is in particular useful to override the default target set in Rustc::new().
292        self.target = Some(target.as_ref().to_string());
293        self
294    }
295
296    /// Specify the target CPU.
297    pub fn target_cpu<S: AsRef<str>>(&mut self, target_cpu: S) -> &mut Self {
298        let target_cpu = target_cpu.as_ref();
299        self.cmd.arg(format!("-Ctarget-cpu={target_cpu}"));
300        self
301    }
302
303    /// Specify the crate type.
304    pub fn crate_type(&mut self, crate_type: &str) -> &mut Self {
305        self.cmd.arg("--crate-type");
306        self.cmd.arg(crate_type);
307        self
308    }
309
310    /// Add a directory to the library search path. Equivalent to `-L` in rustc.
311    pub fn library_search_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
312        self.cmd.arg("-L");
313        self.cmd.arg(path.as_ref());
314        self
315    }
316
317    /// Add a directory to the library search path with a restriction, where `kind` is a dependency
318    /// type. Equivalent to `-L KIND=PATH` in rustc.
319    pub fn specific_library_search_path<P: AsRef<Path>>(
320        &mut self,
321        kind: &str,
322        path: P,
323    ) -> &mut Self {
324        assert!(["dependency", "native", "all", "framework", "crate"].contains(&kind));
325        let path = path.as_ref().to_string_lossy();
326        self.cmd.arg(format!("-L{kind}={path}"));
327        self
328    }
329
330    /// Override the system root. Equivalent to `--sysroot` in rustc.
331    pub fn sysroot<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
332        self.cmd.arg("--sysroot");
333        self.cmd.arg(path.as_ref());
334        self
335    }
336
337    /// Specify the edition year.
338    pub fn edition(&mut self, edition: &str) -> &mut Self {
339        self.cmd.arg("--edition");
340        self.cmd.arg(edition);
341        self
342    }
343
344    /// Specify the print request.
345    pub fn print(&mut self, request: &str) -> &mut Self {
346        self.cmd.arg("--print");
347        self.cmd.arg(request);
348        self
349    }
350
351    /// Add an extra argument to the linker invocation, via `-Clink-arg`.
352    pub fn link_arg(&mut self, link_arg: &str) -> &mut Self {
353        self.cmd.arg(format!("-Clink-arg={link_arg}"));
354        self
355    }
356
357    /// Add multiple extra arguments to the linker invocation, via `-Clink-args`.
358    pub fn link_args(&mut self, link_args: &str) -> &mut Self {
359        self.cmd.arg(format!("-Clink-args={link_args}"));
360        self
361    }
362
363    /// Specify a stdin input buffer.
364    pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
365        self.cmd.stdin_buf(input);
366        self
367    }
368
369    /// Specify the crate name.
370    pub fn crate_name<S: AsRef<OsStr>>(&mut self, name: S) -> &mut Self {
371        self.cmd.arg("--crate-name");
372        self.cmd.arg(name.as_ref());
373        self
374    }
375
376    /// Specify the linker
377    pub fn linker(&mut self, linker: &str) -> &mut Self {
378        self.cmd.arg(format!("-Clinker={linker}"));
379        self
380    }
381
382    /// Specify the linker flavor
383    pub fn linker_flavor(&mut self, linker_flavor: &str) -> &mut Self {
384        self.cmd.arg(format!("-Clinker-flavor={linker_flavor}"));
385        self
386    }
387
388    /// Specify `-C debuginfo=...`.
389    pub fn debuginfo(&mut self, level: &str) -> &mut Self {
390        self.cmd.arg(format!("-Cdebuginfo={level}"));
391        self
392    }
393
394    /// Specify `-C split-debuginfo={packed,unpacked,off}`.
395    pub fn split_debuginfo(&mut self, split_kind: &str) -> &mut Self {
396        self.cmd.arg(format!("-Csplit-debuginfo={split_kind}"));
397        self
398    }
399
400    pub fn split_dwarf_out_dir(&mut self, out_dir: Option<&str>) -> &mut Self {
401        if let Some(out_dir) = out_dir {
402            self.cmd.arg(format!("-Zsplit-dwarf-out-dir={out_dir}"));
403        }
404        self
405    }
406
407    /// Pass the `--verbose` flag.
408    pub fn verbose(&mut self) -> &mut Self {
409        self.cmd.arg("--verbose");
410        self
411    }
412
413    /// `EXTRARSCXXFLAGS`
414    pub fn extra_rs_cxx_flags(&mut self) -> &mut Self {
415        if is_windows() {
416            // So this is a bit hacky: we can't use the DLL version of libstdc++ because
417            // it pulls in the DLL version of libgcc, which means that we end up with 2
418            // instances of the DW2 unwinding implementation. This is a problem on
419            // i686-pc-windows-gnu because each module (DLL/EXE) needs to register its
420            // unwind information with the unwinding implementation, and libstdc++'s
421            // __cxa_throw won't see the unwinding info we registered with our statically
422            // linked libgcc.
423            //
424            // Now, simply statically linking libstdc++ would fix this problem, except
425            // that it is compiled with the expectation that pthreads is dynamically
426            // linked as a DLL and will fail to link with a statically linked libpthread.
427            //
428            // So we end up with the following hack: we link use static:-bundle to only
429            // link the parts of libstdc++ that we actually use, which doesn't include
430            // the dependency on the pthreads DLL.
431            if !is_windows_msvc() {
432                self.cmd.arg("-lstatic:-bundle=stdc++");
433            };
434        } else if is_darwin() {
435            self.cmd.arg("-lc++");
436        } else if is_aix() {
437            self.cmd.arg("-lc++");
438            self.cmd.arg("-lc++abi");
439        } else {
440            if !matches!(&uname()[..], "FreeBSD" | "SunOS" | "OpenBSD") {
441                self.cmd.arg("-lstdc++");
442            };
443        };
444        self
445    }
446
447    /// Make that the generated LLVM IR is in source order.
448    pub fn codegen_source_order(&mut self) -> &mut Self {
449        self.cmd.arg("-Zcodegen-source-order");
450        self
451    }
452}
453
454/// Query the sysroot path corresponding `rustc --print=sysroot`.
455#[track_caller]
456pub fn sysroot() -> PathBuf {
457    let path = rustc().print("sysroot").run().stdout_utf8();
458    PathBuf::from_str(path.trim()).unwrap()
459}