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