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