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