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