1 //! Implementation of rustbuild, the Rust build system.
2 //!
3 //! This module, and its descendants, are the implementation of the Rust build
4 //! system. Most of this build system is backed by Cargo but the outer layer
5 //! here serves as the ability to orchestrate calling Cargo, sequencing Cargo
6 //! builds, building artifacts like LLVM, etc. The goals of rustbuild are:
7 //!
8 //! * To be an easily understandable, easily extensible, and maintainable build
9 //! system.
10 //! * Leverage standard tools in the Rust ecosystem to build the compiler, aka
11 //! crates.io and Cargo.
12 //! * A standard interface to build across all platforms, including MSVC
13 //!
14 //! ## Further information
15 //!
16 //! More documentation can be found in each respective module below, and you can
17 //! also check out the `src/bootstrap/README.md` file for more information.
18
19 use std::cell::{Cell, RefCell};
20 use std::collections::{HashMap, HashSet};
21 use std::env;
22 use std::fmt::Display;
23 use std::fs::{self, File};
24 use std::io;
25 use std::path::{Path, PathBuf};
26 use std::process::{Command, Stdio};
27 use std::str;
28
29 use build_helper::ci::{gha, CiEnv};
30 use build_helper::detail_exit_macro;
31 use channel::GitInfo;
32 use config::{DryRun, Target};
33 use filetime::FileTime;
34 use once_cell::sync::OnceCell;
35
36 use crate::builder::Kind;
37 use crate::config::{LlvmLibunwind, TargetSelection};
38 use crate::util::{
39 exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed,
40 };
41
42 mod bolt;
43 mod builder;
44 mod cache;
45 mod cc_detect;
46 mod channel;
47 mod check;
48 mod clean;
49 mod compile;
50 mod config;
51 mod dist;
52 mod doc;
53 mod download;
54 mod flags;
55 mod format;
56 mod install;
57 mod llvm;
58 mod metadata;
59 mod render_tests;
60 mod run;
61 mod sanity;
62 mod setup;
63 mod suggest;
64 mod synthetic_targets;
65 mod tarball;
66 mod test;
67 mod tool;
68 mod toolstate;
69 pub mod util;
70
71 #[cfg(feature = "build-metrics")]
72 mod metrics;
73
74 #[cfg(windows)]
75 mod job;
76
77 #[cfg(all(unix, not(target_os = "haiku")))]
78 mod job {
setup(build: &mut crate::Build)79 pub unsafe fn setup(build: &mut crate::Build) {
80 if build.config.low_priority {
81 libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
82 }
83 }
84 }
85
86 #[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))]
87 mod job {
setup(_build: &mut crate::Build)88 pub unsafe fn setup(_build: &mut crate::Build) {}
89 }
90
91 pub use crate::builder::PathSet;
92 use crate::cache::{Interned, INTERNER};
93 pub use crate::config::Config;
94 pub use crate::flags::Subcommand;
95 use termcolor::{ColorChoice, StandardStream, WriteColor};
96
97 const LLVM_TOOLS: &[&str] = &[
98 "llvm-cov", // used to generate coverage report
99 "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility
100 "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume
101 "llvm-objdump", // used to disassemble programs
102 "llvm-profdata", // used to inspect and merge files generated by profiles
103 "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide
104 "llvm-size", // used to prints the size of the linker sections of a program
105 "llvm-strip", // used to discard symbols from binary files to reduce their size
106 "llvm-ar", // used for creating and modifying archive files
107 "llvm-as", // used to convert LLVM assembly to LLVM bitcode
108 "llvm-dis", // used to disassemble LLVM bitcode
109 "llc", // used to compile LLVM bytecode
110 "opt", // used to optimize LLVM bytecode
111 ];
112
113 /// LLD file names for all flavors.
114 const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
115
116 pub const VERSION: usize = 2;
117
118 /// Extra --check-cfg to add when building
119 /// (Mode restriction, config name, config values (if any))
120 const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)] = &[
121 (None, "bootstrap", None),
122 (Some(Mode::Rustc), "parallel_compiler", None),
123 (Some(Mode::ToolRustc), "parallel_compiler", None),
124 (Some(Mode::Codegen), "parallel_compiler", None),
125 (Some(Mode::Std), "stdarch_intel_sde", None),
126 (Some(Mode::Std), "no_fp_fmt_parse", None),
127 (Some(Mode::Std), "no_global_oom_handling", None),
128 (Some(Mode::Std), "no_rc", None),
129 (Some(Mode::Std), "no_sync", None),
130 (Some(Mode::Std), "freebsd12", None),
131 (Some(Mode::Std), "freebsd13", None),
132 (Some(Mode::Std), "backtrace_in_libstd", None),
133 /* Extra values not defined in the built-in targets yet, but used in std */
134 (Some(Mode::Std), "target_env", Some(&["libnx"])),
135 // (Some(Mode::Std), "target_os", Some(&[])),
136 (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])),
137 /* Extra names used by dependencies */
138 // FIXME: Used by serde_json, but we should not be triggering on external dependencies.
139 (Some(Mode::Rustc), "no_btreemap_remove_entry", None),
140 (Some(Mode::ToolRustc), "no_btreemap_remove_entry", None),
141 // FIXME: Used by crossbeam-utils, but we should not be triggering on external dependencies.
142 (Some(Mode::Rustc), "crossbeam_loom", None),
143 (Some(Mode::ToolRustc), "crossbeam_loom", None),
144 // FIXME: Used by proc-macro2, but we should not be triggering on external dependencies.
145 (Some(Mode::Rustc), "span_locations", None),
146 (Some(Mode::ToolRustc), "span_locations", None),
147 // FIXME: Used by rustix, but we should not be triggering on external dependencies.
148 (Some(Mode::Rustc), "rustix_use_libc", None),
149 (Some(Mode::ToolRustc), "rustix_use_libc", None),
150 // FIXME: Used by filetime, but we should not be triggering on external dependencies.
151 (Some(Mode::Rustc), "emulate_second_only_system", None),
152 (Some(Mode::ToolRustc), "emulate_second_only_system", None),
153 // Needed to avoid the need to copy windows.lib into the sysroot.
154 (Some(Mode::Rustc), "windows_raw_dylib", None),
155 (Some(Mode::ToolRustc), "windows_raw_dylib", None),
156 ];
157
158 /// A structure representing a Rust compiler.
159 ///
160 /// Each compiler has a `stage` that it is associated with and a `host` that
161 /// corresponds to the platform the compiler runs on. This structure is used as
162 /// a parameter to many methods below.
163 #[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)]
164 pub struct Compiler {
165 stage: u32,
166 host: TargetSelection,
167 }
168
169 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
170 pub enum DocTests {
171 /// Run normal tests and doc tests (default).
172 Yes,
173 /// Do not run any doc tests.
174 No,
175 /// Only run doc tests.
176 Only,
177 }
178
179 pub enum GitRepo {
180 Rustc,
181 Llvm,
182 }
183
184 /// Global configuration for the build system.
185 ///
186 /// This structure transitively contains all configuration for the build system.
187 /// All filesystem-encoded configuration is in `config`, all flags are in
188 /// `flags`, and then parsed or probed information is listed in the keys below.
189 ///
190 /// This structure is a parameter of almost all methods in the build system,
191 /// although most functions are implemented as free functions rather than
192 /// methods specifically on this structure itself (to make it easier to
193 /// organize).
194 #[cfg_attr(not(feature = "build-metrics"), derive(Clone))]
195 pub struct Build {
196 /// User-specified configuration from `config.toml`.
197 config: Config,
198
199 // Version information
200 version: String,
201
202 // Properties derived from the above configuration
203 src: PathBuf,
204 out: PathBuf,
205 bootstrap_out: PathBuf,
206 cargo_info: channel::GitInfo,
207 rust_analyzer_info: channel::GitInfo,
208 clippy_info: channel::GitInfo,
209 miri_info: channel::GitInfo,
210 rustfmt_info: channel::GitInfo,
211 in_tree_llvm_info: channel::GitInfo,
212 local_rebuild: bool,
213 fail_fast: bool,
214 doc_tests: DocTests,
215 verbosity: usize,
216
217 // Targets for which to build
218 build: TargetSelection,
219 hosts: Vec<TargetSelection>,
220 targets: Vec<TargetSelection>,
221
222 initial_rustc: PathBuf,
223 initial_cargo: PathBuf,
224 initial_lld: PathBuf,
225 initial_libdir: PathBuf,
226 initial_sysroot: PathBuf,
227
228 // Runtime state filled in later on
229 // C/C++ compilers and archiver for all targets
230 cc: RefCell<HashMap<TargetSelection, cc::Tool>>,
231 cxx: RefCell<HashMap<TargetSelection, cc::Tool>>,
232 ar: RefCell<HashMap<TargetSelection, PathBuf>>,
233 ranlib: RefCell<HashMap<TargetSelection, PathBuf>>,
234 // Miscellaneous
235 // allow bidirectional lookups: both name -> path and path -> name
236 crates: HashMap<Interned<String>, Crate>,
237 crate_paths: HashMap<PathBuf, Interned<String>>,
238 is_sudo: bool,
239 ci_env: CiEnv,
240 delayed_failures: RefCell<Vec<String>>,
241 prerelease_version: Cell<Option<u32>>,
242
243 #[cfg(feature = "build-metrics")]
244 metrics: metrics::BuildMetrics,
245 }
246
247 #[derive(Debug, Clone)]
248 struct Crate {
249 name: Interned<String>,
250 deps: HashSet<Interned<String>>,
251 path: PathBuf,
252 has_lib: bool,
253 }
254
255 impl Crate {
local_path(&self, build: &Build) -> PathBuf256 fn local_path(&self, build: &Build) -> PathBuf {
257 self.path.strip_prefix(&build.config.src).unwrap().into()
258 }
259 }
260
261 /// When building Rust various objects are handled differently.
262 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
263 pub enum DependencyType {
264 /// Libraries originating from proc-macros.
265 Host,
266 /// Typical Rust libraries.
267 Target,
268 /// Non Rust libraries and objects shipped to ease usage of certain targets.
269 TargetSelfContained,
270 }
271
272 /// The various "modes" of invoking Cargo.
273 ///
274 /// These entries currently correspond to the various output directories of the
275 /// build system, with each mod generating output in a different directory.
276 #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
277 pub enum Mode {
278 /// Build the standard library, placing output in the "stageN-std" directory.
279 Std,
280
281 /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.
282 Rustc,
283
284 /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory.
285 Codegen,
286
287 /// Build a tool, placing output in the "stage0-bootstrap-tools"
288 /// directory. This is for miscellaneous sets of tools that are built
289 /// using the bootstrap stage0 compiler in its entirety (target libraries
290 /// and all). Typically these tools compile with stable Rust.
291 ToolBootstrap,
292
293 /// Build a tool which uses the locally built std, placing output in the
294 /// "stageN-tools" directory. Its usage is quite rare, mainly used by
295 /// compiletest which needs libtest.
296 ToolStd,
297
298 /// Build a tool which uses the locally built rustc and the target std,
299 /// placing the output in the "stageN-tools" directory. This is used for
300 /// anything that needs a fully functional rustc, such as rustdoc, clippy,
301 /// cargo, rls, rustfmt, miri, etc.
302 ToolRustc,
303 }
304
305 impl Mode {
is_tool(&self) -> bool306 pub fn is_tool(&self) -> bool {
307 matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd)
308 }
309
must_support_dlopen(&self) -> bool310 pub fn must_support_dlopen(&self) -> bool {
311 matches!(self, Mode::Std | Mode::Codegen)
312 }
313 }
314
315 pub enum CLang {
316 C,
317 Cxx,
318 }
319
320 macro_rules! forward {
321 ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
322 impl Build {
323 $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {
324 self.config.$fn( $($param),* )
325 } )+
326 }
327 }
328 }
329
330 forward! {
331 verbose(msg: &str),
332 is_verbose() -> bool,
333 create(path: &Path, s: &str),
334 remove(f: &Path),
335 tempdir() -> PathBuf,
336 try_run(cmd: &mut Command) -> Result<(), ()>,
337 llvm_link_shared() -> bool,
338 download_rustc() -> bool,
339 initial_rustfmt() -> Option<PathBuf>,
340 }
341
342 impl Build {
343 /// Creates a new set of build configuration from the `flags` on the command
344 /// line and the filesystem `config`.
345 ///
346 /// By default all build output will be placed in the current directory.
new(mut config: Config) -> Build347 pub fn new(mut config: Config) -> Build {
348 let src = config.src.clone();
349 let out = config.out.clone();
350
351 #[cfg(unix)]
352 // keep this consistent with the equivalent check in x.py:
353 // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797
354 let is_sudo = match env::var_os("SUDO_USER") {
355 Some(_sudo_user) => {
356 let uid = unsafe { libc::getuid() };
357 uid == 0
358 }
359 None => false,
360 };
361 #[cfg(not(unix))]
362 let is_sudo = false;
363
364 let omit_git_hash = config.omit_git_hash;
365 let rust_info = channel::GitInfo::new(omit_git_hash, &src);
366 let cargo_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/cargo"));
367 let rust_analyzer_info =
368 channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rust-analyzer"));
369 let clippy_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/clippy"));
370 let miri_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/miri"));
371 let rustfmt_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rustfmt"));
372
373 // we always try to use git for LLVM builds
374 let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project"));
375
376 let initial_target_libdir_str = if config.dry_run() {
377 "/dummy/lib/path/to/lib/".to_string()
378 } else {
379 output(
380 Command::new(&config.initial_rustc)
381 .arg("--target")
382 .arg(config.build.rustc_target_arg())
383 .arg("--print")
384 .arg("target-libdir"),
385 )
386 };
387 let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap();
388 let initial_lld = initial_target_dir.join("bin").join("rust-lld");
389
390 let initial_sysroot = if config.dry_run() {
391 "/dummy".to_string()
392 } else {
393 output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot"))
394 }
395 .trim()
396 .to_string();
397
398 let initial_libdir = initial_target_dir
399 .parent()
400 .unwrap()
401 .parent()
402 .unwrap()
403 .strip_prefix(&initial_sysroot)
404 .unwrap()
405 .to_path_buf();
406
407 let version = std::fs::read_to_string(src.join("src").join("version"))
408 .expect("failed to read src/version");
409 let version = version.trim();
410
411 let bootstrap_out = std::env::current_exe()
412 .expect("could not determine path to running process")
413 .parent()
414 .unwrap()
415 .to_path_buf();
416 if !bootstrap_out.join(exe("rustc", config.build)).exists() && !cfg!(test) {
417 // this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented
418 panic!(
419 "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",
420 bootstrap_out.display()
421 )
422 }
423
424 if rust_info.is_from_tarball() && config.description.is_none() {
425 config.description = Some("built from a source tarball".to_owned());
426 }
427
428 let mut build = Build {
429 initial_rustc: config.initial_rustc.clone(),
430 initial_cargo: config.initial_cargo.clone(),
431 initial_lld,
432 initial_libdir,
433 initial_sysroot: initial_sysroot.into(),
434 local_rebuild: config.local_rebuild,
435 fail_fast: config.cmd.fail_fast(),
436 doc_tests: config.cmd.doc_tests(),
437 verbosity: config.verbose,
438
439 build: config.build,
440 hosts: config.hosts.clone(),
441 targets: config.targets.clone(),
442
443 config,
444 version: version.to_string(),
445 src,
446 out,
447 bootstrap_out,
448
449 cargo_info,
450 rust_analyzer_info,
451 clippy_info,
452 miri_info,
453 rustfmt_info,
454 in_tree_llvm_info,
455 cc: RefCell::new(HashMap::new()),
456 cxx: RefCell::new(HashMap::new()),
457 ar: RefCell::new(HashMap::new()),
458 ranlib: RefCell::new(HashMap::new()),
459 crates: HashMap::new(),
460 crate_paths: HashMap::new(),
461 is_sudo,
462 ci_env: CiEnv::current(),
463 delayed_failures: RefCell::new(Vec::new()),
464 prerelease_version: Cell::new(None),
465
466 #[cfg(feature = "build-metrics")]
467 metrics: metrics::BuildMetrics::init(),
468 };
469
470 // If local-rust is the same major.minor as the current version, then force a
471 // local-rebuild
472 let local_version_verbose =
473 output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
474 let local_release = local_version_verbose
475 .lines()
476 .filter_map(|x| x.strip_prefix("release:"))
477 .next()
478 .unwrap()
479 .trim();
480 if local_release.split('.').take(2).eq(version.split('.').take(2)) {
481 build.verbose(&format!("auto-detected local-rebuild {}", local_release));
482 build.local_rebuild = true;
483 }
484
485 build.verbose("finding compilers");
486 cc_detect::find(&build);
487 // When running `setup`, the profile is about to change, so any requirements we have now may
488 // be different on the next invocation. Don't check for them until the next time x.py is
489 // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
490 //
491 // Similarly, for `setup` we don't actually need submodules or cargo metadata.
492 if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
493 build.verbose("running sanity check");
494 sanity::check(&mut build);
495
496 // Make sure we update these before gathering metadata so we don't get an error about missing
497 // Cargo.toml files.
498 let rust_submodules = ["src/tools/cargo", "library/backtrace", "library/stdarch"];
499 for s in rust_submodules {
500 build.update_submodule(Path::new(s));
501 }
502 // Now, update all existing submodules.
503 build.update_existing_submodules();
504
505 build.verbose("learning about cargo");
506 metadata::build(&mut build);
507 }
508
509 // Make a symbolic link so we can use a consistent directory in the documentation.
510 let build_triple = build.out.join(&build.build.triple);
511 t!(fs::create_dir_all(&build_triple));
512 let host = build.out.join("host");
513 if host.is_symlink() {
514 // Left over from a previous build; overwrite it.
515 // This matters if `build.build` has changed between invocations.
516 #[cfg(windows)]
517 t!(fs::remove_dir(&host));
518 #[cfg(not(windows))]
519 t!(fs::remove_file(&host));
520 }
521 t!(
522 symlink_dir(&build.config, &build_triple, &host),
523 format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())
524 );
525
526 build
527 }
528
529 // modified from `check_submodule` and `update_submodule` in bootstrap.py
530 /// Given a path to the directory of a submodule, update it.
531 ///
532 /// `relative_path` should be relative to the root of the git repository, not an absolute path.
update_submodule(&self, relative_path: &Path)533 pub(crate) fn update_submodule(&self, relative_path: &Path) {
534 fn dir_is_empty(dir: &Path) -> bool {
535 t!(std::fs::read_dir(dir)).next().is_none()
536 }
537
538 if !self.config.submodules(&self.rust_info()) {
539 return;
540 }
541
542 let absolute_path = self.config.src.join(relative_path);
543
544 // NOTE: The check for the empty directory is here because when running x.py the first time,
545 // the submodule won't be checked out. Check it out now so we can build it.
546 if !channel::GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
547 && !dir_is_empty(&absolute_path)
548 {
549 return;
550 }
551
552 // check_submodule
553 let checked_out_hash =
554 output(Command::new("git").args(&["rev-parse", "HEAD"]).current_dir(&absolute_path));
555 // update_submodules
556 let recorded = output(
557 Command::new("git")
558 .args(&["ls-tree", "HEAD"])
559 .arg(relative_path)
560 .current_dir(&self.config.src),
561 );
562 let actual_hash = recorded
563 .split_whitespace()
564 .nth(2)
565 .unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
566
567 // update_submodule
568 if actual_hash == checked_out_hash.trim_end() {
569 // already checked out
570 return;
571 }
572
573 println!("Updating submodule {}", relative_path.display());
574 self.run(
575 Command::new("git")
576 .args(&["submodule", "-q", "sync"])
577 .arg(relative_path)
578 .current_dir(&self.config.src),
579 );
580
581 // Try passing `--progress` to start, then run git again without if that fails.
582 let update = |progress: bool| {
583 // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
584 // even though that has no relation to the upstream for the submodule.
585 let current_branch = {
586 let output = self
587 .config
588 .git()
589 .args(["symbolic-ref", "--short", "HEAD"])
590 .stderr(Stdio::inherit())
591 .output();
592 let output = t!(output);
593 if output.status.success() {
594 Some(String::from_utf8(output.stdout).unwrap().trim().to_owned())
595 } else {
596 None
597 }
598 };
599
600 let mut git = self.config.git();
601 if let Some(branch) = current_branch {
602 // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
603 // This syntax isn't accepted by `branch.{branch}`. Strip it.
604 let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
605 git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
606 }
607 git.args(&["submodule", "update", "--init", "--recursive", "--depth=1"]);
608 if progress {
609 git.arg("--progress");
610 }
611 git.arg(relative_path);
612 git
613 };
614 // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails.
615 if !update(true).status().map_or(false, |status| status.success()) {
616 self.run(&mut update(false));
617 }
618
619 // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
620 let has_local_modifications = self
621 .try_run(
622 Command::new("git")
623 .args(&["diff-index", "--quiet", "HEAD"])
624 .current_dir(&absolute_path),
625 )
626 .is_err();
627 if has_local_modifications {
628 self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path));
629 }
630
631 self.run(Command::new("git").args(&["reset", "-q", "--hard"]).current_dir(&absolute_path));
632 self.run(Command::new("git").args(&["clean", "-qdfx"]).current_dir(&absolute_path));
633
634 if has_local_modifications {
635 self.run(Command::new("git").args(&["stash", "pop"]).current_dir(absolute_path));
636 }
637 }
638
639 /// If any submodule has been initialized already, sync it unconditionally.
640 /// This avoids contributors checking in a submodule change by accident.
update_existing_submodules(&self)641 pub fn update_existing_submodules(&self) {
642 // Avoid running git when there isn't a git checkout.
643 if !self.config.submodules(&self.rust_info()) {
644 return;
645 }
646 let output = output(
647 self.config
648 .git()
649 .args(&["config", "--file"])
650 .arg(&self.config.src.join(".gitmodules"))
651 .args(&["--get-regexp", "path"]),
652 );
653 for line in output.lines() {
654 // Look for `submodule.$name.path = $path`
655 // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
656 let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap());
657 // Don't update the submodule unless it's already been cloned.
658 if channel::GitInfo::new(false, submodule).is_managed_git_subrepository() {
659 self.update_submodule(submodule);
660 }
661 }
662 }
663
664 /// Executes the entire build, as configured by the flags and configuration.
build(&mut self)665 pub fn build(&mut self) {
666 unsafe {
667 job::setup(self);
668 }
669
670 // Download rustfmt early so that it can be used in rust-analyzer configs.
671 let _ = &builder::Builder::new(&self).initial_rustfmt();
672
673 // hardcoded subcommands
674 match &self.config.cmd {
675 Subcommand::Format { check } => {
676 return format::format(&builder::Builder::new(&self), *check, &self.config.paths);
677 }
678 Subcommand::Suggest { run } => {
679 return suggest::suggest(&builder::Builder::new(&self), *run);
680 }
681 _ => (),
682 }
683
684 {
685 let builder = builder::Builder::new(&self);
686 if let Some(path) = builder.paths.get(0) {
687 if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
688 return;
689 }
690 }
691 }
692
693 if !self.config.dry_run() {
694 {
695 self.config.dry_run = DryRun::SelfCheck;
696 let builder = builder::Builder::new(&self);
697 builder.execute_cli();
698 }
699 self.config.dry_run = DryRun::Disabled;
700 let builder = builder::Builder::new(&self);
701 builder.execute_cli();
702 } else {
703 let builder = builder::Builder::new(&self);
704 builder.execute_cli();
705 }
706
707 // Check for postponed failures from `test --no-fail-fast`.
708 let failures = self.delayed_failures.borrow();
709 if failures.len() > 0 {
710 eprintln!("\n{} command(s) did not execute successfully:\n", failures.len());
711 for failure in failures.iter() {
712 eprintln!(" - {}\n", failure);
713 }
714 detail_exit_macro!(1);
715 }
716
717 #[cfg(feature = "build-metrics")]
718 self.metrics.persist(self);
719 }
720
721 /// Clear out `dir` if `input` is newer.
722 ///
723 /// After this executes, it will also ensure that `dir` exists.
clear_if_dirty(&self, dir: &Path, input: &Path) -> bool724 fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool {
725 let stamp = dir.join(".stamp");
726 let mut cleared = false;
727 if mtime(&stamp) < mtime(input) {
728 self.verbose(&format!("Dirty - {}", dir.display()));
729 let _ = fs::remove_dir_all(dir);
730 cleared = true;
731 } else if stamp.exists() {
732 return cleared;
733 }
734 t!(fs::create_dir_all(dir));
735 t!(File::create(stamp));
736 cleared
737 }
738
rust_info(&self) -> &GitInfo739 fn rust_info(&self) -> &GitInfo {
740 &self.config.rust_info
741 }
742
743 /// Gets the space-separated set of activated features for the standard
744 /// library.
std_features(&self, target: TargetSelection) -> String745 fn std_features(&self, target: TargetSelection) -> String {
746 let mut features = " panic-unwind".to_string();
747
748 match self.config.llvm_libunwind(target) {
749 LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"),
750 LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"),
751 LlvmLibunwind::No => {}
752 }
753 if self.config.backtrace {
754 features.push_str(" backtrace");
755 }
756 if self.config.profiler_enabled(target) {
757 features.push_str(" profiler");
758 }
759 features
760 }
761
762 /// Gets the space-separated set of activated features for the compiler.
rustc_features(&self, kind: Kind) -> String763 fn rustc_features(&self, kind: Kind) -> String {
764 let mut features = vec![];
765 if self.config.jemalloc {
766 features.push("jemalloc");
767 }
768 if self.config.llvm_enabled() || kind == Kind::Check {
769 features.push("llvm");
770 }
771 // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
772 if self.config.rustc_parallel {
773 features.push("rustc_use_parallel_compiler");
774 }
775
776 // If debug logging is on, then we want the default for tracing:
777 // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26
778 // which is everything (including debug/trace/etc.)
779 // if its unset, if debug_assertions is on, then debug_logging will also be on
780 // as well as tracing *ignoring* this feature when debug_assertions is on
781 if !self.config.rust_debug_logging {
782 features.push("max_level_info");
783 }
784
785 features.join(" ")
786 }
787
788 /// Component directory that Cargo will produce output into (e.g.
789 /// release/debug)
cargo_dir(&self) -> &'static str790 fn cargo_dir(&self) -> &'static str {
791 if self.config.rust_optimize.is_release() { "release" } else { "debug" }
792 }
793
tools_dir(&self, compiler: Compiler) -> PathBuf794 fn tools_dir(&self, compiler: Compiler) -> PathBuf {
795 let out = self
796 .out
797 .join(&*compiler.host.triple)
798 .join(format!("stage{}-tools-bin", compiler.stage));
799 t!(fs::create_dir_all(&out));
800 out
801 }
802
803 /// Returns the root directory for all output generated in a particular
804 /// stage when running with a particular host compiler.
805 ///
806 /// The mode indicates what the root directory is for.
stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf807 fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
808 let suffix = match mode {
809 Mode::Std => "-std",
810 Mode::Rustc => "-rustc",
811 Mode::Codegen => "-codegen",
812 Mode::ToolBootstrap => "-bootstrap-tools",
813 Mode::ToolStd | Mode::ToolRustc => "-tools",
814 };
815 self.out.join(&*compiler.host.triple).join(format!("stage{}{}", compiler.stage, suffix))
816 }
817
818 /// Returns the root output directory for all Cargo output in a given stage,
819 /// running a particular compiler, whether or not we're building the
820 /// standard library, and targeting the specified architecture.
cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf821 fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
822 self.stage_out(compiler, mode).join(&*target.triple).join(self.cargo_dir())
823 }
824
825 /// Directory where the extracted `rustc-dev` component is stored.
ci_rustc_dir(&self, target: TargetSelection) -> PathBuf826 fn ci_rustc_dir(&self, target: TargetSelection) -> PathBuf {
827 self.out.join(&*target.triple).join("ci-rustc")
828 }
829
830 /// Root output directory for LLVM compiled for `target`
831 ///
832 /// Note that if LLVM is configured externally then the directory returned
833 /// will likely be empty.
llvm_out(&self, target: TargetSelection) -> PathBuf834 fn llvm_out(&self, target: TargetSelection) -> PathBuf {
835 self.out.join(&*target.triple).join("llvm")
836 }
837
lld_out(&self, target: TargetSelection) -> PathBuf838 fn lld_out(&self, target: TargetSelection) -> PathBuf {
839 self.out.join(&*target.triple).join("lld")
840 }
841
842 /// Output directory for all documentation for a target
doc_out(&self, target: TargetSelection) -> PathBuf843 fn doc_out(&self, target: TargetSelection) -> PathBuf {
844 self.out.join(&*target.triple).join("doc")
845 }
846
847 /// Output directory for all JSON-formatted documentation for a target
json_doc_out(&self, target: TargetSelection) -> PathBuf848 fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
849 self.out.join(&*target.triple).join("json-doc")
850 }
851
test_out(&self, target: TargetSelection) -> PathBuf852 fn test_out(&self, target: TargetSelection) -> PathBuf {
853 self.out.join(&*target.triple).join("test")
854 }
855
856 /// Output directory for all documentation for a target
compiler_doc_out(&self, target: TargetSelection) -> PathBuf857 fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
858 self.out.join(&*target.triple).join("compiler-doc")
859 }
860
861 /// Output directory for some generated md crate documentation for a target (temporary)
md_doc_out(&self, target: TargetSelection) -> Interned<PathBuf>862 fn md_doc_out(&self, target: TargetSelection) -> Interned<PathBuf> {
863 INTERNER.intern_path(self.out.join(&*target.triple).join("md-doc"))
864 }
865
866 /// Returns `true` if no custom `llvm-config` is set for the specified target.
867 ///
868 /// If no custom `llvm-config` was specified then Rust's llvm will be used.
is_rust_llvm(&self, target: TargetSelection) -> bool869 fn is_rust_llvm(&self, target: TargetSelection) -> bool {
870 match self.config.target_config.get(&target) {
871 Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
872 Some(Target { llvm_config, .. }) => {
873 // If the user set llvm-config we assume Rust is not patched,
874 // but first check to see if it was configured by llvm-from-ci.
875 (self.config.llvm_from_ci && target == self.config.build) || llvm_config.is_none()
876 }
877 None => true,
878 }
879 }
880
881 /// Returns the path to `FileCheck` binary for the specified target
llvm_filecheck(&self, target: TargetSelection) -> PathBuf882 fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {
883 let target_config = self.config.target_config.get(&target);
884 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
885 s.to_path_buf()
886 } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
887 let llvm_bindir = output(Command::new(s).arg("--bindir"));
888 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
889 if filecheck.exists() {
890 filecheck
891 } else {
892 // On Fedora the system LLVM installs FileCheck in the
893 // llvm subdirectory of the libdir.
894 let llvm_libdir = output(Command::new(s).arg("--libdir"));
895 let lib_filecheck =
896 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
897 if lib_filecheck.exists() {
898 lib_filecheck
899 } else {
900 // Return the most normal file name, even though
901 // it doesn't exist, so that any error message
902 // refers to that.
903 filecheck
904 }
905 }
906 } else {
907 let base = self.llvm_out(target).join("build");
908 let base = if !self.ninja() && target.contains("msvc") {
909 if self.config.llvm_optimize {
910 if self.config.llvm_release_debuginfo {
911 base.join("RelWithDebInfo")
912 } else {
913 base.join("Release")
914 }
915 } else {
916 base.join("Debug")
917 }
918 } else {
919 base
920 };
921 base.join("bin").join(exe("FileCheck", target))
922 }
923 }
924
925 /// Directory for libraries built from C/C++ code and shared between stages.
native_dir(&self, target: TargetSelection) -> PathBuf926 fn native_dir(&self, target: TargetSelection) -> PathBuf {
927 self.out.join(&*target.triple).join("native")
928 }
929
930 /// Root output directory for rust_test_helpers library compiled for
931 /// `target`
test_helpers_out(&self, target: TargetSelection) -> PathBuf932 fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {
933 self.native_dir(target).join("rust-test-helpers")
934 }
935
936 /// Adds the `RUST_TEST_THREADS` env var if necessary
add_rust_test_threads(&self, cmd: &mut Command)937 fn add_rust_test_threads(&self, cmd: &mut Command) {
938 if env::var_os("RUST_TEST_THREADS").is_none() {
939 cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
940 }
941 }
942
943 /// Returns the libdir of the snapshot compiler.
rustc_snapshot_libdir(&self) -> PathBuf944 fn rustc_snapshot_libdir(&self) -> PathBuf {
945 self.rustc_snapshot_sysroot().join(libdir(self.config.build))
946 }
947
948 /// Returns the sysroot of the snapshot compiler.
rustc_snapshot_sysroot(&self) -> &Path949 fn rustc_snapshot_sysroot(&self) -> &Path {
950 static SYSROOT_CACHE: OnceCell<PathBuf> = once_cell::sync::OnceCell::new();
951 SYSROOT_CACHE.get_or_init(|| {
952 let mut rustc = Command::new(&self.initial_rustc);
953 rustc.args(&["--print", "sysroot"]);
954 output(&mut rustc).trim().into()
955 })
956 }
957
958 /// Runs a command, printing out nice contextual information if it fails.
run(&self, cmd: &mut Command)959 fn run(&self, cmd: &mut Command) {
960 if self.config.dry_run() {
961 return;
962 }
963 self.verbose(&format!("running: {:?}", cmd));
964 run(cmd, self.is_verbose())
965 }
966
967 /// Runs a command, printing out nice contextual information if it fails.
run_quiet(&self, cmd: &mut Command)968 fn run_quiet(&self, cmd: &mut Command) {
969 if self.config.dry_run() {
970 return;
971 }
972 self.verbose(&format!("running: {:?}", cmd));
973 run_suppressed(cmd)
974 }
975
976 /// Runs a command, printing out nice contextual information if it fails.
977 /// Exits if the command failed to execute at all, otherwise returns its
978 /// `status.success()`.
try_run_quiet(&self, cmd: &mut Command) -> bool979 fn try_run_quiet(&self, cmd: &mut Command) -> bool {
980 if self.config.dry_run() {
981 return true;
982 }
983 self.verbose(&format!("running: {:?}", cmd));
984 try_run_suppressed(cmd)
985 }
986
is_verbose_than(&self, level: usize) -> bool987 pub fn is_verbose_than(&self, level: usize) -> bool {
988 self.verbosity > level
989 }
990
991 /// Prints a message if this build is configured in more verbose mode than `level`.
verbose_than(&self, level: usize, msg: &str)992 fn verbose_than(&self, level: usize, msg: &str) {
993 if self.is_verbose_than(level) {
994 println!("{}", msg);
995 }
996 }
997
info(&self, msg: &str)998 fn info(&self, msg: &str) {
999 match self.config.dry_run {
1000 DryRun::SelfCheck => return,
1001 DryRun::Disabled | DryRun::UserSelected => {
1002 println!("{}", msg);
1003 }
1004 }
1005 }
1006
msg_check( &self, what: impl Display, target: impl Into<Option<TargetSelection>>, ) -> Option<gha::Group>1007 fn msg_check(
1008 &self,
1009 what: impl Display,
1010 target: impl Into<Option<TargetSelection>>,
1011 ) -> Option<gha::Group> {
1012 self.msg(Kind::Check, self.config.stage, what, self.config.build, target)
1013 }
1014
msg_doc( &self, compiler: Compiler, what: impl Display, target: impl Into<Option<TargetSelection>> + Copy, ) -> Option<gha::Group>1015 fn msg_doc(
1016 &self,
1017 compiler: Compiler,
1018 what: impl Display,
1019 target: impl Into<Option<TargetSelection>> + Copy,
1020 ) -> Option<gha::Group> {
1021 self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
1022 }
1023
msg_build( &self, compiler: Compiler, what: impl Display, target: impl Into<Option<TargetSelection>>, ) -> Option<gha::Group>1024 fn msg_build(
1025 &self,
1026 compiler: Compiler,
1027 what: impl Display,
1028 target: impl Into<Option<TargetSelection>>,
1029 ) -> Option<gha::Group> {
1030 self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
1031 }
1032
1033 /// Return a `Group` guard for a [`Step`] that is built for each `--stage`.
1034 ///
1035 /// [`Step`]: crate::builder::Step
msg( &self, action: impl Into<Kind>, stage: u32, what: impl Display, host: impl Into<Option<TargetSelection>>, target: impl Into<Option<TargetSelection>>, ) -> Option<gha::Group>1036 fn msg(
1037 &self,
1038 action: impl Into<Kind>,
1039 stage: u32,
1040 what: impl Display,
1041 host: impl Into<Option<TargetSelection>>,
1042 target: impl Into<Option<TargetSelection>>,
1043 ) -> Option<gha::Group> {
1044 let action = action.into().description();
1045 let msg = |fmt| format!("{action} stage{stage} {what}{fmt}");
1046 let msg = if let Some(target) = target.into() {
1047 let host = host.into().unwrap();
1048 if host == target {
1049 msg(format_args!(" ({target})"))
1050 } else {
1051 msg(format_args!(" ({host} -> {target})"))
1052 }
1053 } else {
1054 msg(format_args!(""))
1055 };
1056 self.group(&msg)
1057 }
1058
1059 /// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
1060 ///
1061 /// [`Step`]: crate::builder::Step
msg_unstaged( &self, action: impl Into<Kind>, what: impl Display, target: TargetSelection, ) -> Option<gha::Group>1062 fn msg_unstaged(
1063 &self,
1064 action: impl Into<Kind>,
1065 what: impl Display,
1066 target: TargetSelection,
1067 ) -> Option<gha::Group> {
1068 let action = action.into().description();
1069 let msg = format!("{action} {what} for {target}");
1070 self.group(&msg)
1071 }
1072
msg_sysroot_tool( &self, action: impl Into<Kind>, stage: u32, what: impl Display, host: TargetSelection, target: TargetSelection, ) -> Option<gha::Group>1073 fn msg_sysroot_tool(
1074 &self,
1075 action: impl Into<Kind>,
1076 stage: u32,
1077 what: impl Display,
1078 host: TargetSelection,
1079 target: TargetSelection,
1080 ) -> Option<gha::Group> {
1081 let action = action.into().description();
1082 let msg = |fmt| format!("{action} {what} {fmt}");
1083 let msg = if host == target {
1084 msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
1085 } else {
1086 msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
1087 };
1088 self.group(&msg)
1089 }
1090
group(&self, msg: &str) -> Option<gha::Group>1091 fn group(&self, msg: &str) -> Option<gha::Group> {
1092 match self.config.dry_run {
1093 DryRun::SelfCheck => None,
1094 DryRun::Disabled | DryRun::UserSelected => Some(gha::group(&msg)),
1095 }
1096 }
1097
1098 /// Returns the number of parallel jobs that have been configured for this
1099 /// build.
jobs(&self) -> u321100 fn jobs(&self) -> u32 {
1101 self.config.jobs.unwrap_or_else(|| {
1102 std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1103 })
1104 }
1105
debuginfo_map_to(&self, which: GitRepo) -> Option<String>1106 fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
1107 if !self.config.rust_remap_debuginfo {
1108 return None;
1109 }
1110
1111 match which {
1112 GitRepo::Rustc => {
1113 let sha = self.rust_sha().unwrap_or(&self.version);
1114 Some(format!("/rustc/{}", sha))
1115 }
1116 GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1117 }
1118 }
1119
1120 /// Returns the path to the C compiler for the target specified.
cc(&self, target: TargetSelection) -> PathBuf1121 fn cc(&self, target: TargetSelection) -> PathBuf {
1122 if self.config.dry_run() {
1123 return PathBuf::new();
1124 }
1125 self.cc.borrow()[&target].path().into()
1126 }
1127
1128 /// Returns a list of flags to pass to the C compiler for the target
1129 /// specified.
cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec<String>1130 fn cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec<String> {
1131 if self.config.dry_run() {
1132 return Vec::new();
1133 }
1134 let base = match c {
1135 CLang::C => self.cc.borrow()[&target].clone(),
1136 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1137 };
1138
1139 // Filter out -O and /O (the optimization flags) that we picked up from
1140 // cc-rs because the build scripts will determine that for themselves.
1141 let mut base = base
1142 .args()
1143 .iter()
1144 .map(|s| s.to_string_lossy().into_owned())
1145 .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1146 .collect::<Vec<String>>();
1147
1148 // If we're compiling on macOS then we add a few unconditional flags
1149 // indicating that we want libc++ (more filled out than libstdc++) and
1150 // we want to compile for 10.7. This way we can ensure that
1151 // LLVM/etc are all properly compiled.
1152 if target.contains("apple-darwin") {
1153 base.push("-stdlib=libc++".into());
1154 }
1155
1156 // Work around an apparently bad MinGW / GCC optimization,
1157 // See: https://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html
1158 // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936
1159 if &*target.triple == "i686-pc-windows-gnu" {
1160 base.push("-fno-omit-frame-pointer".into());
1161 }
1162
1163 if let Some(map_to) = self.debuginfo_map_to(which) {
1164 let map = format!("{}={}", self.src.display(), map_to);
1165 let cc = self.cc(target);
1166 if cc.ends_with("clang") || cc.ends_with("gcc") {
1167 base.push(format!("-fdebug-prefix-map={}", map));
1168 } else if cc.ends_with("clang-cl.exe") {
1169 base.push("-Xclang".into());
1170 base.push(format!("-fdebug-prefix-map={}", map));
1171 }
1172 }
1173 base
1174 }
1175
1176 /// Returns the path to the `ar` archive utility for the target specified.
ar(&self, target: TargetSelection) -> Option<PathBuf>1177 fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1178 if self.config.dry_run() {
1179 return None;
1180 }
1181 self.ar.borrow().get(&target).cloned()
1182 }
1183
1184 /// Returns the path to the `ranlib` utility for the target specified.
ranlib(&self, target: TargetSelection) -> Option<PathBuf>1185 fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1186 if self.config.dry_run() {
1187 return None;
1188 }
1189 self.ranlib.borrow().get(&target).cloned()
1190 }
1191
1192 /// Returns the path to the C++ compiler for the target specified.
cxx(&self, target: TargetSelection) -> Result<PathBuf, String>1193 fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1194 if self.config.dry_run() {
1195 return Ok(PathBuf::new());
1196 }
1197 match self.cxx.borrow().get(&target) {
1198 Some(p) => Ok(p.path().into()),
1199 None => {
1200 Err(format!("target `{}` is not configured as a host, only as a target", target))
1201 }
1202 }
1203 }
1204
1205 /// Returns the path to the linker for the given target if it needs to be overridden.
linker(&self, target: TargetSelection) -> Option<PathBuf>1206 fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1207 if self.config.dry_run() {
1208 return Some(PathBuf::new());
1209 }
1210 if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1211 {
1212 Some(linker)
1213 } else if target.contains("vxworks") {
1214 // need to use CXX compiler as linker to resolve the exception functions
1215 // that are only existed in CXX libraries
1216 Some(self.cxx.borrow()[&target].path().into())
1217 } else if target != self.config.build
1218 && util::use_host_linker(target)
1219 && !target.contains("msvc")
1220 {
1221 Some(self.cc(target))
1222 } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target {
1223 Some(self.initial_lld.clone())
1224 } else {
1225 None
1226 }
1227 }
1228
1229 // LLD is used through `-fuse-ld=lld` rather than directly.
1230 // Only MSVC targets use LLD directly at the moment.
is_fuse_ld_lld(&self, target: TargetSelection) -> bool1231 fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool {
1232 self.config.use_lld && !target.contains("msvc")
1233 }
1234
lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String>1235 fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
1236 let mut options = [None, None];
1237
1238 if self.config.use_lld {
1239 if self.is_fuse_ld_lld(target) {
1240 options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
1241 }
1242
1243 let no_threads = util::lld_flag_no_threads(target.contains("windows"));
1244 options[1] = Some(format!("-Clink-arg=-Wl,{}", no_threads));
1245 }
1246
1247 IntoIterator::into_iter(options).flatten()
1248 }
1249
1250 /// Returns if this target should statically link the C runtime, if specified
crt_static(&self, target: TargetSelection) -> Option<bool>1251 fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1252 if target.contains("pc-windows-msvc") {
1253 Some(true)
1254 } else {
1255 self.config.target_config.get(&target).and_then(|t| t.crt_static)
1256 }
1257 }
1258
1259 /// Returns the "musl root" for this `target`, if defined
musl_root(&self, target: TargetSelection) -> Option<&Path>1260 fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1261 self.config
1262 .target_config
1263 .get(&target)
1264 .and_then(|t| t.musl_root.as_ref())
1265 .or_else(|| self.config.musl_root.as_ref())
1266 .map(|p| &**p)
1267 }
1268
1269 /// Returns the "musl libdir" for this `target`.
musl_libdir(&self, target: TargetSelection) -> Option<PathBuf>1270 fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1271 let t = self.config.target_config.get(&target)?;
1272 if let libdir @ Some(_) = &t.musl_libdir {
1273 return libdir.clone();
1274 }
1275 self.musl_root(target).map(|root| root.join("lib"))
1276 }
1277
1278 /// Returns the sysroot for the wasi target, if defined
wasi_root(&self, target: TargetSelection) -> Option<&Path>1279 fn wasi_root(&self, target: TargetSelection) -> Option<&Path> {
1280 self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p)
1281 }
1282
1283 /// Returns `true` if this is a no-std `target`, if defined
no_std(&self, target: TargetSelection) -> Option<bool>1284 fn no_std(&self, target: TargetSelection) -> Option<bool> {
1285 self.config.target_config.get(&target).map(|t| t.no_std)
1286 }
1287
1288 /// Returns `true` if the target will be tested using the `remote-test-client`
1289 /// and `remote-test-server` binaries.
remote_tested(&self, target: TargetSelection) -> bool1290 fn remote_tested(&self, target: TargetSelection) -> bool {
1291 self.qemu_rootfs(target).is_some()
1292 || target.contains("android")
1293 || env::var_os("TEST_DEVICE_ADDR").is_some()
1294 }
1295
1296 /// Returns the root of the "rootfs" image that this target will be using,
1297 /// if one was configured.
1298 ///
1299 /// If `Some` is returned then that means that tests for this target are
1300 /// emulated with QEMU and binaries will need to be shipped to the emulator.
qemu_rootfs(&self, target: TargetSelection) -> Option<&Path>1301 fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1302 self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1303 }
1304
1305 /// Path to the python interpreter to use
python(&self) -> &Path1306 fn python(&self) -> &Path {
1307 if self.config.build.ends_with("apple-darwin") {
1308 // Force /usr/bin/python3 on macOS for LLDB tests because we're loading the
1309 // LLDB plugin's compiled module which only works with the system python
1310 // (namely not Homebrew-installed python)
1311 Path::new("/usr/bin/python3")
1312 } else {
1313 self.config
1314 .python
1315 .as_ref()
1316 .expect("python is required for running LLDB or rustdoc tests")
1317 }
1318 }
1319
1320 /// Temporary directory that extended error information is emitted to.
extended_error_dir(&self) -> PathBuf1321 fn extended_error_dir(&self) -> PathBuf {
1322 self.out.join("tmp/extended-error-metadata")
1323 }
1324
1325 /// Tests whether the `compiler` compiling for `target` should be forced to
1326 /// use a stage1 compiler instead.
1327 ///
1328 /// Currently, by default, the build system does not perform a "full
1329 /// bootstrap" by default where we compile the compiler three times.
1330 /// Instead, we compile the compiler two times. The final stage (stage2)
1331 /// just copies the libraries from the previous stage, which is what this
1332 /// method detects.
1333 ///
1334 /// Here we return `true` if:
1335 ///
1336 /// * The build isn't performing a full bootstrap
1337 /// * The `compiler` is in the final stage, 2
1338 /// * We're not cross-compiling, so the artifacts are already available in
1339 /// stage1
1340 ///
1341 /// When all of these conditions are met the build will lift artifacts from
1342 /// the previous stage forward.
force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool1343 fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1344 !self.config.full_bootstrap
1345 && !self.config.download_rustc()
1346 && stage >= 2
1347 && (self.hosts.iter().any(|h| *h == target) || target == self.build)
1348 }
1349
1350 /// Checks whether the `compiler` compiling for `target` should be forced to
1351 /// use a stage2 compiler instead.
1352 ///
1353 /// When we download the pre-compiled version of rustc and compiler stage is >= 2,
1354 /// it should be forced to use a stage2 compiler.
force_use_stage2(&self, stage: u32) -> bool1355 fn force_use_stage2(&self, stage: u32) -> bool {
1356 self.config.download_rustc() && stage >= 2
1357 }
1358
1359 /// Given `num` in the form "a.b.c" return a "release string" which
1360 /// describes the release version number.
1361 ///
1362 /// For example on nightly this returns "a.b.c-nightly", on beta it returns
1363 /// "a.b.c-beta.1" and on stable it just returns "a.b.c".
release(&self, num: &str) -> String1364 fn release(&self, num: &str) -> String {
1365 match &self.config.channel[..] {
1366 "stable" => num.to_string(),
1367 "beta" => {
1368 if !self.config.omit_git_hash {
1369 format!("{}-beta.{}", num, self.beta_prerelease_version())
1370 } else {
1371 format!("{}-beta", num)
1372 }
1373 }
1374 "nightly" => format!("{}-nightly", num),
1375 _ => format!("{}-dev", num),
1376 }
1377 }
1378
beta_prerelease_version(&self) -> u321379 fn beta_prerelease_version(&self) -> u32 {
1380 fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1381 let version = fs::read_to_string(version_file).ok()?;
1382
1383 extract_beta_rev(&version)
1384 }
1385
1386 if let Some(s) = self.prerelease_version.get() {
1387 return s;
1388 }
1389
1390 // First check if there is a version file available.
1391 // If available, we read the beta revision from that file.
1392 // This only happens when building from a source tarball when Git should not be used.
1393 let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1394 // Figure out how many merge commits happened since we branched off master.
1395 // That's our beta number!
1396 // (Note that we use a `..` range, not the `...` symmetric difference.)
1397 output(self.config.git().arg("rev-list").arg("--count").arg("--merges").arg(format!(
1398 "refs/remotes/origin/{}..HEAD",
1399 self.config.stage0_metadata.config.nightly_branch
1400 )))
1401 });
1402 let n = count.trim().parse().unwrap();
1403 self.prerelease_version.set(Some(n));
1404 n
1405 }
1406
1407 /// Returns the value of `release` above for Rust itself.
rust_release(&self) -> String1408 fn rust_release(&self) -> String {
1409 self.release(&self.version)
1410 }
1411
1412 /// Returns the "package version" for a component given the `num` release
1413 /// number.
1414 ///
1415 /// The package version is typically what shows up in the names of tarballs.
1416 /// For channels like beta/nightly it's just the channel name, otherwise
1417 /// it's the `num` provided.
package_vers(&self, num: &str) -> String1418 fn package_vers(&self, num: &str) -> String {
1419 match &self.config.channel[..] {
1420 "stable" => num.to_string(),
1421 "beta" => "beta".to_string(),
1422 "nightly" => "nightly".to_string(),
1423 _ => format!("{}-dev", num),
1424 }
1425 }
1426
1427 /// Returns the value of `package_vers` above for Rust itself.
rust_package_vers(&self) -> String1428 fn rust_package_vers(&self) -> String {
1429 self.package_vers(&self.version)
1430 }
1431
1432 /// Returns the `version` string associated with this compiler for Rust
1433 /// itself.
1434 ///
1435 /// Note that this is a descriptive string which includes the commit date,
1436 /// sha, version, etc.
rust_version(&self) -> String1437 fn rust_version(&self) -> String {
1438 let mut version = self.rust_info().version(self, &self.version);
1439 if let Some(ref s) = self.config.description {
1440 version.push_str(" (");
1441 version.push_str(s);
1442 version.push(')');
1443 }
1444 version
1445 }
1446
1447 /// Returns the full commit hash.
rust_sha(&self) -> Option<&str>1448 fn rust_sha(&self) -> Option<&str> {
1449 self.rust_info().sha()
1450 }
1451
1452 /// Returns the `a.b.c` version that the given package is at.
release_num(&self, package: &str) -> String1453 fn release_num(&self, package: &str) -> String {
1454 let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package));
1455 let toml = t!(fs::read_to_string(&toml_file_name));
1456 for line in toml.lines() {
1457 if let Some(stripped) =
1458 line.strip_prefix("version = \"").and_then(|s| s.strip_suffix("\""))
1459 {
1460 return stripped.to_owned();
1461 }
1462 }
1463
1464 panic!("failed to find version in {}'s Cargo.toml", package)
1465 }
1466
1467 /// Returns `true` if unstable features should be enabled for the compiler
1468 /// we're building.
unstable_features(&self) -> bool1469 fn unstable_features(&self) -> bool {
1470 match &self.config.channel[..] {
1471 "stable" | "beta" => false,
1472 "nightly" | _ => true,
1473 }
1474 }
1475
1476 /// Returns a Vec of all the dependencies of the given root crate,
1477 /// including transitive dependencies and the root itself. Only includes
1478 /// "local" crates (those in the local source tree, not from a registry).
in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate>1479 fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1480 let mut ret = Vec::new();
1481 let mut list = vec![INTERNER.intern_str(root)];
1482 let mut visited = HashSet::new();
1483 while let Some(krate) = list.pop() {
1484 let krate = self
1485 .crates
1486 .get(&krate)
1487 .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1488 ret.push(krate);
1489 for dep in &krate.deps {
1490 if !self.crates.contains_key(dep) {
1491 // Ignore non-workspace members.
1492 continue;
1493 }
1494 // Don't include optional deps if their features are not
1495 // enabled. Ideally this would be computed from `cargo
1496 // metadata --features …`, but that is somewhat slow. In
1497 // the future, we may want to consider just filtering all
1498 // build and dev dependencies in metadata::build.
1499 if visited.insert(dep)
1500 && (dep != "profiler_builtins"
1501 || target
1502 .map(|t| self.config.profiler_enabled(t))
1503 .unwrap_or_else(|| self.config.any_profiler_enabled()))
1504 && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled())
1505 {
1506 list.push(*dep);
1507 }
1508 }
1509 }
1510 ret
1511 }
1512
read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)>1513 fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> {
1514 if self.config.dry_run() {
1515 return Vec::new();
1516 }
1517
1518 if !stamp.exists() {
1519 eprintln!(
1520 "Error: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1521 stamp.display()
1522 );
1523 crate::detail_exit_macro!(1);
1524 }
1525
1526 let mut paths = Vec::new();
1527 let contents = t!(fs::read(stamp), &stamp);
1528 // This is the method we use for extracting paths from the stamp file passed to us. See
1529 // run_cargo for more information (in compile.rs).
1530 for part in contents.split(|b| *b == 0) {
1531 if part.is_empty() {
1532 continue;
1533 }
1534 let dependency_type = match part[0] as char {
1535 'h' => DependencyType::Host,
1536 's' => DependencyType::TargetSelfContained,
1537 't' => DependencyType::Target,
1538 _ => unreachable!(),
1539 };
1540 let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1541 paths.push((path, dependency_type));
1542 }
1543 paths
1544 }
1545
1546 /// Copies a file from `src` to `dst`
copy(&self, src: &Path, dst: &Path)1547 pub fn copy(&self, src: &Path, dst: &Path) {
1548 self.copy_internal(src, dst, false);
1549 }
1550
copy_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool)1551 fn copy_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1552 if self.config.dry_run() {
1553 return;
1554 }
1555 self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst));
1556 if src == dst {
1557 return;
1558 }
1559 let _ = fs::remove_file(&dst);
1560 let metadata = t!(src.symlink_metadata());
1561 let mut src = src.to_path_buf();
1562 if metadata.file_type().is_symlink() {
1563 if dereference_symlinks {
1564 src = t!(fs::canonicalize(src));
1565 } else {
1566 let link = t!(fs::read_link(src));
1567 t!(self.symlink_file(link, dst));
1568 return;
1569 }
1570 }
1571 if let Ok(()) = fs::hard_link(&src, dst) {
1572 // Attempt to "easy copy" by creating a hard link
1573 // (symlinks don't work on windows), but if that fails
1574 // just fall back to a slow `copy` operation.
1575 } else {
1576 if let Err(e) = fs::copy(&src, dst) {
1577 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1578 }
1579 t!(fs::set_permissions(dst, metadata.permissions()));
1580 let atime = FileTime::from_last_access_time(&metadata);
1581 let mtime = FileTime::from_last_modification_time(&metadata);
1582 t!(filetime::set_file_times(dst, atime, mtime));
1583 }
1584 }
1585
1586 /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
1587 /// when this function is called.
cp_r(&self, src: &Path, dst: &Path)1588 pub fn cp_r(&self, src: &Path, dst: &Path) {
1589 if self.config.dry_run() {
1590 return;
1591 }
1592 for f in self.read_dir(src) {
1593 let path = f.path();
1594 let name = path.file_name().unwrap();
1595 let dst = dst.join(name);
1596 if t!(f.file_type()).is_dir() {
1597 t!(fs::create_dir_all(&dst));
1598 self.cp_r(&path, &dst);
1599 } else {
1600 let _ = fs::remove_file(&dst);
1601 self.copy(&path, &dst);
1602 }
1603 }
1604 }
1605
1606 /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
1607 /// when this function is called. Unwanted files or directories can be skipped
1608 /// by returning `false` from the filter function.
cp_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool)1609 pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1610 // Immediately recurse with an empty relative path
1611 self.recurse_(src, dst, Path::new(""), filter)
1612 }
1613
1614 // Inner function does the actual work
recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &dyn Fn(&Path) -> bool)1615 fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &dyn Fn(&Path) -> bool) {
1616 for f in self.read_dir(src) {
1617 let path = f.path();
1618 let name = path.file_name().unwrap();
1619 let dst = dst.join(name);
1620 let relative = relative.join(name);
1621 // Only copy file or directory if the filter function returns true
1622 if filter(&relative) {
1623 if t!(f.file_type()).is_dir() {
1624 let _ = fs::remove_dir_all(&dst);
1625 self.create_dir(&dst);
1626 self.recurse_(&path, &dst, &relative, filter);
1627 } else {
1628 let _ = fs::remove_file(&dst);
1629 self.copy(&path, &dst);
1630 }
1631 }
1632 }
1633 }
1634
copy_to_folder(&self, src: &Path, dest_folder: &Path)1635 fn copy_to_folder(&self, src: &Path, dest_folder: &Path) {
1636 let file_name = src.file_name().unwrap();
1637 let dest = dest_folder.join(file_name);
1638 self.copy(src, &dest);
1639 }
1640
install(&self, src: &Path, dstdir: &Path, perms: u32)1641 fn install(&self, src: &Path, dstdir: &Path, perms: u32) {
1642 if self.config.dry_run() {
1643 return;
1644 }
1645 let dst = dstdir.join(src.file_name().unwrap());
1646 self.verbose_than(1, &format!("Install {:?} to {:?}", src, dst));
1647 t!(fs::create_dir_all(dstdir));
1648 if !src.exists() {
1649 panic!("Error: File \"{}\" not found!", src.display());
1650 }
1651 self.copy_internal(src, &dst, true);
1652 chmod(&dst, perms);
1653 }
1654
read(&self, path: &Path) -> String1655 fn read(&self, path: &Path) -> String {
1656 if self.config.dry_run() {
1657 return String::new();
1658 }
1659 t!(fs::read_to_string(path))
1660 }
1661
create_dir(&self, dir: &Path)1662 fn create_dir(&self, dir: &Path) {
1663 if self.config.dry_run() {
1664 return;
1665 }
1666 t!(fs::create_dir_all(dir))
1667 }
1668
remove_dir(&self, dir: &Path)1669 fn remove_dir(&self, dir: &Path) {
1670 if self.config.dry_run() {
1671 return;
1672 }
1673 t!(fs::remove_dir_all(dir))
1674 }
1675
read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry>1676 fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1677 let iter = match fs::read_dir(dir) {
1678 Ok(v) => v,
1679 Err(_) if self.config.dry_run() => return vec![].into_iter(),
1680 Err(err) => panic!("could not read dir {:?}: {:?}", dir, err),
1681 };
1682 iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1683 }
1684
symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()>1685 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1686 #[cfg(unix)]
1687 use std::os::unix::fs::symlink as symlink_file;
1688 #[cfg(windows)]
1689 use std::os::windows::fs::symlink_file;
1690 if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1691 }
1692
1693 /// Returns if config.ninja is enabled, and checks for ninja existence,
1694 /// exiting with a nicer error message if not.
ninja(&self) -> bool1695 fn ninja(&self) -> bool {
1696 let mut cmd_finder = crate::sanity::Finder::new();
1697
1698 if self.config.ninja_in_file {
1699 // Some Linux distros rename `ninja` to `ninja-build`.
1700 // CMake can work with either binary name.
1701 if cmd_finder.maybe_have("ninja-build").is_none()
1702 && cmd_finder.maybe_have("ninja").is_none()
1703 {
1704 eprintln!(
1705 "
1706 Couldn't find required command: ninja (or ninja-build)
1707
1708 You should install ninja as described at
1709 <https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1710 or set `ninja = false` in the `[llvm]` section of `config.toml`.
1711 Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1712 to download LLVM rather than building it.
1713 "
1714 );
1715 detail_exit_macro!(1);
1716 }
1717 }
1718
1719 // If ninja isn't enabled but we're building for MSVC then we try
1720 // doubly hard to enable it. It was realized in #43767 that the msbuild
1721 // CMake generator for MSVC doesn't respect configuration options like
1722 // disabling LLVM assertions, which can often be quite important!
1723 //
1724 // In these cases we automatically enable Ninja if we find it in the
1725 // environment.
1726 if !self.config.ninja_in_file && self.config.build.contains("msvc") {
1727 if cmd_finder.maybe_have("ninja").is_some() {
1728 return true;
1729 }
1730 }
1731
1732 self.config.ninja_in_file
1733 }
1734
colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R1735 pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1736 self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
1737 }
1738
colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R1739 pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1740 self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
1741 }
1742
colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R where C: Fn(ColorChoice) -> StandardStream, F: FnOnce(&mut dyn WriteColor) -> R,1743 fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
1744 where
1745 C: Fn(ColorChoice) -> StandardStream,
1746 F: FnOnce(&mut dyn WriteColor) -> R,
1747 {
1748 let choice = match self.config.color {
1749 flags::Color::Always => ColorChoice::Always,
1750 flags::Color::Never => ColorChoice::Never,
1751 flags::Color::Auto if !is_tty => ColorChoice::Never,
1752 flags::Color::Auto => ColorChoice::Auto,
1753 };
1754 let mut stream = constructor(choice);
1755 let result = f(&mut stream);
1756 stream.reset().unwrap();
1757 result
1758 }
1759 }
1760
1761 /// Extract the beta revision from the full version string.
1762 ///
1763 /// The full version string looks like "a.b.c-beta.y". And we need to extract
1764 /// the "y" part from the string.
extract_beta_rev(version: &str) -> Option<String>1765 pub fn extract_beta_rev(version: &str) -> Option<String> {
1766 let parts = version.splitn(2, "-beta.").collect::<Vec<_>>();
1767 let count = parts.get(1).and_then(|s| s.find(' ').map(|p| (&s[..p]).to_string()));
1768
1769 count
1770 }
1771
1772 #[cfg(unix)]
chmod(path: &Path, perms: u32)1773 fn chmod(path: &Path, perms: u32) {
1774 use std::os::unix::fs::*;
1775 t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
1776 }
1777 #[cfg(windows)]
chmod(_path: &Path, _perms: u32)1778 fn chmod(_path: &Path, _perms: u32) {}
1779
1780 impl Compiler {
with_stage(mut self, stage: u32) -> Compiler1781 pub fn with_stage(mut self, stage: u32) -> Compiler {
1782 self.stage = stage;
1783 self
1784 }
1785
1786 /// Returns `true` if this is a snapshot compiler for `build`'s configuration
is_snapshot(&self, build: &Build) -> bool1787 pub fn is_snapshot(&self, build: &Build) -> bool {
1788 self.stage == 0 && self.host == build.build
1789 }
1790
1791 /// Returns if this compiler should be treated as a final stage one in the
1792 /// current build session.
1793 /// This takes into account whether we're performing a full bootstrap or
1794 /// not; don't directly compare the stage with `2`!
is_final_stage(&self, build: &Build) -> bool1795 pub fn is_final_stage(&self, build: &Build) -> bool {
1796 let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
1797 self.stage >= final_stage
1798 }
1799 }
1800
envify(s: &str) -> String1801 fn envify(s: &str) -> String {
1802 s.chars()
1803 .map(|c| match c {
1804 '-' => '_',
1805 c => c,
1806 })
1807 .flat_map(|c| c.to_uppercase())
1808 .collect()
1809 }
1810