bootstrap/
lib.rs

1//! Implementation of bootstrap, 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 bootstrap 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#![cfg_attr(test, allow(unused))]
19
20use std::cell::Cell;
21use std::collections::{BTreeSet, HashMap, HashSet};
22use std::fmt::Display;
23use std::path::{Path, PathBuf};
24use std::sync::OnceLock;
25use std::time::{Instant, SystemTime};
26use std::{env, fs, io, str};
27
28use build_helper::ci::gha;
29use build_helper::exit;
30use cc::Tool;
31use termcolor::{ColorChoice, StandardStream, WriteColor};
32use utils::build_stamp::BuildStamp;
33use utils::channel::GitInfo;
34use utils::exec::ExecutionContext;
35
36use crate::core::builder;
37use crate::core::builder::Kind;
38use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags};
39use crate::utils::exec::{BootstrapCommand, command};
40use crate::utils::helpers::{
41    self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir,
42};
43
44mod core;
45mod utils;
46
47pub use core::builder::PathSet;
48pub use core::config::flags::{Flags, Subcommand};
49pub use core::config::{ChangeId, Config};
50
51#[cfg(feature = "tracing")]
52use tracing::{instrument, span};
53pub use utils::change_tracker::{
54    CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes,
55};
56pub use utils::helpers::PanicTracker;
57
58use crate::core::build_steps::vendor::VENDOR_DIR;
59
60const LLVM_TOOLS: &[&str] = &[
61    "llvm-cov",      // used to generate coverage report
62    "llvm-nm",       // used to inspect binaries; it shows symbol names, their sizes and visibility
63    "llvm-objcopy",  // used to transform ELFs into binary format which flashing tools consume
64    "llvm-objdump",  // used to disassemble programs
65    "llvm-profdata", // used to inspect and merge files generated by profiles
66    "llvm-readobj",  // used to get information from ELFs/objects that the other tools don't provide
67    "llvm-size",     // used to prints the size of the linker sections of a program
68    "llvm-strip",    // used to discard symbols from binary files to reduce their size
69    "llvm-ar",       // used for creating and modifying archive files
70    "llvm-as",       // used to convert LLVM assembly to LLVM bitcode
71    "llvm-dis",      // used to disassemble LLVM bitcode
72    "llvm-link",     // Used to link LLVM bitcode
73    "llc",           // used to compile LLVM bytecode
74    "opt",           // used to optimize LLVM bytecode
75];
76
77/// LLD file names for all flavors.
78const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
79
80/// Extra `--check-cfg` to add when building the compiler or tools
81/// (Mode restriction, config name, config values (if any))
82#[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above.
83const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
84    (Some(Mode::Rustc), "bootstrap", None),
85    (Some(Mode::Codegen), "bootstrap", None),
86    (Some(Mode::ToolRustc), "bootstrap", None),
87    (Some(Mode::ToolStd), "bootstrap", None),
88    (Some(Mode::Rustc), "llvm_enzyme", None),
89    (Some(Mode::Codegen), "llvm_enzyme", None),
90    (Some(Mode::ToolRustc), "llvm_enzyme", None),
91    (Some(Mode::ToolRustc), "rust_analyzer", None),
92    (Some(Mode::ToolStd), "rust_analyzer", None),
93    // Any library specific cfgs like `target_os`, `target_arch` should be put in
94    // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table
95    // in the appropriate `library/{std,alloc,core}/Cargo.toml`
96];
97
98/// A structure representing a Rust compiler.
99///
100/// Each compiler has a `stage` that it is associated with and a `host` that
101/// corresponds to the platform the compiler runs on. This structure is used as
102/// a parameter to many methods below.
103#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)]
104pub struct Compiler {
105    stage: u32,
106    host: TargetSelection,
107    /// Indicates whether the compiler was forced to use a specific stage.
108    /// This field is ignored in `Hash` and `PartialEq` implementations as only the `stage`
109    /// and `host` fields are relevant for those.
110    forced_compiler: bool,
111}
112
113impl std::hash::Hash for Compiler {
114    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
115        self.stage.hash(state);
116        self.host.hash(state);
117    }
118}
119
120impl PartialEq for Compiler {
121    fn eq(&self, other: &Self) -> bool {
122        self.stage == other.stage && self.host == other.host
123    }
124}
125
126/// Represents a codegen backend.
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
128pub enum CodegenBackendKind {
129    #[default]
130    Llvm,
131    Cranelift,
132    Gcc,
133    Custom(String),
134}
135
136impl CodegenBackendKind {
137    /// Name of the codegen backend, as identified in the `compiler` directory
138    /// (`rustc_codegen_<name>`).
139    pub fn name(&self) -> &str {
140        match self {
141            CodegenBackendKind::Llvm => "llvm",
142            CodegenBackendKind::Cranelift => "cranelift",
143            CodegenBackendKind::Gcc => "gcc",
144            CodegenBackendKind::Custom(name) => name,
145        }
146    }
147
148    /// Name of the codegen backend's crate, e.g. `rustc_codegen_cranelift`.
149    pub fn crate_name(&self) -> String {
150        format!("rustc_codegen_{}", self.name())
151    }
152
153    pub fn is_llvm(&self) -> bool {
154        matches!(self, Self::Llvm)
155    }
156
157    pub fn is_cranelift(&self) -> bool {
158        matches!(self, Self::Cranelift)
159    }
160
161    pub fn is_gcc(&self) -> bool {
162        matches!(self, Self::Gcc)
163    }
164}
165
166#[derive(PartialEq, Eq, Copy, Clone, Debug)]
167pub enum DocTests {
168    /// Run normal tests and doc tests (default).
169    Yes,
170    /// Do not run any doc tests.
171    No,
172    /// Only run doc tests.
173    Only,
174}
175
176pub enum GitRepo {
177    Rustc,
178    Llvm,
179}
180
181/// Global configuration for the build system.
182///
183/// This structure transitively contains all configuration for the build system.
184/// All filesystem-encoded configuration is in `config`, all flags are in
185/// `flags`, and then parsed or probed information is listed in the keys below.
186///
187/// This structure is a parameter of almost all methods in the build system,
188/// although most functions are implemented as free functions rather than
189/// methods specifically on this structure itself (to make it easier to
190/// organize).
191pub struct Build {
192    /// User-specified configuration from `bootstrap.toml`.
193    config: Config,
194
195    // Version information
196    version: String,
197
198    // Properties derived from the above configuration
199    src: PathBuf,
200    out: PathBuf,
201    bootstrap_out: PathBuf,
202    cargo_info: GitInfo,
203    rust_analyzer_info: GitInfo,
204    clippy_info: GitInfo,
205    miri_info: GitInfo,
206    rustfmt_info: GitInfo,
207    enzyme_info: GitInfo,
208    in_tree_llvm_info: GitInfo,
209    in_tree_gcc_info: GitInfo,
210    local_rebuild: bool,
211    fail_fast: bool,
212    doc_tests: DocTests,
213    verbosity: usize,
214
215    /// Build triple for the pre-compiled snapshot compiler.
216    host_target: TargetSelection,
217    /// Which triples to produce a compiler toolchain for.
218    hosts: Vec<TargetSelection>,
219    /// Which triples to build libraries (core/alloc/std/test/proc_macro) for.
220    targets: Vec<TargetSelection>,
221
222    initial_rustc: PathBuf,
223    initial_rustdoc: PathBuf,
224    initial_cargo: PathBuf,
225    initial_lld: PathBuf,
226    initial_relative_libdir: PathBuf,
227    initial_sysroot: PathBuf,
228
229    // Runtime state filled in later on
230    // C/C++ compilers and archiver for all targets
231    cc: HashMap<TargetSelection, cc::Tool>,
232    cxx: HashMap<TargetSelection, cc::Tool>,
233    ar: HashMap<TargetSelection, PathBuf>,
234    ranlib: HashMap<TargetSelection, PathBuf>,
235    wasi_sdk_path: Option<PathBuf>,
236
237    // Miscellaneous
238    // allow bidirectional lookups: both name -> path and path -> name
239    crates: HashMap<String, Crate>,
240    crate_paths: HashMap<PathBuf, String>,
241    is_sudo: bool,
242    prerelease_version: Cell<Option<u32>>,
243
244    #[cfg(feature = "build-metrics")]
245    metrics: crate::utils::metrics::BuildMetrics,
246
247    #[cfg(feature = "tracing")]
248    step_graph: std::cell::RefCell<crate::utils::step_graph::StepGraph>,
249}
250
251#[derive(Debug, Clone)]
252struct Crate {
253    name: String,
254    deps: HashSet<String>,
255    path: PathBuf,
256    features: Vec<String>,
257}
258
259impl Crate {
260    fn local_path(&self, build: &Build) -> PathBuf {
261        self.path.strip_prefix(&build.config.src).unwrap().into()
262    }
263}
264
265/// When building Rust various objects are handled differently.
266#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
267pub enum DependencyType {
268    /// Libraries originating from proc-macros.
269    Host,
270    /// Typical Rust libraries.
271    Target,
272    /// Non Rust libraries and objects shipped to ease usage of certain targets.
273    TargetSelfContained,
274}
275
276/// The various "modes" of invoking Cargo.
277///
278/// These entries currently correspond to the various output directories of the
279/// build system, with each mod generating output in a different directory.
280#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
281pub enum Mode {
282    /// Build the standard library, placing output in the "stageN-std" directory.
283    Std,
284
285    /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.
286    Rustc,
287
288    /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory.
289    Codegen,
290
291    /// Build a tool, placing output in the "bootstrap-tools"
292    /// directory. This is for miscellaneous sets of tools that extend
293    /// bootstrap.
294    ///
295    /// These tools are intended to be only executed on the host system that
296    /// invokes bootstrap, and they thus cannot be cross-compiled.
297    ///
298    /// They are always built using the stage0 compiler, and they
299    /// can be compiled with stable Rust.
300    ///
301    /// These tools also essentially do not participate in staging.
302    ToolBootstrap,
303
304    /// Build a cross-compilable helper tool. These tools do not depend on unstable features or
305    /// compiler internals, but they might be cross-compilable (so we cannot build them using the
306    /// stage0 compiler, unlike `ToolBootstrap`).
307    ///
308    /// Some of these tools are also shipped in our `dist` archives.
309    /// While we could compile them using the stage0 compiler when not cross-compiling, we instead
310    /// use the in-tree compiler (and std) to build them, so that we can ship e.g. std security
311    /// fixes and avoid depending fully on stage0 for the artifacts that we ship.
312    ///
313    /// This mode is used e.g. for linkers and linker tools invoked by rustc on its host target.
314    ToolTarget,
315
316    /// Build a tool which uses the locally built std, placing output in the
317    /// "stageN-tools" directory. Its usage is quite rare, mainly used by
318    /// compiletest which needs libtest.
319    ToolStd,
320
321    /// Build a tool which uses the locally built rustc and the target std,
322    /// placing the output in the "stageN-tools" directory. This is used for
323    /// anything that needs a fully functional rustc, such as rustdoc, clippy,
324    /// cargo, rustfmt, miri, etc.
325    ToolRustc,
326}
327
328impl Mode {
329    pub fn is_tool(&self) -> bool {
330        match self {
331            Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true,
332            Mode::Std | Mode::Codegen | Mode::Rustc => false,
333        }
334    }
335
336    pub fn must_support_dlopen(&self) -> bool {
337        match self {
338            Mode::Std | Mode::Codegen => true,
339            Mode::ToolBootstrap
340            | Mode::ToolRustc
341            | Mode::ToolStd
342            | Mode::ToolTarget
343            | Mode::Rustc => false,
344        }
345    }
346}
347
348/// When `rust.rust_remap_debuginfo` is requested, the compiler needs to know how to
349/// opportunistically unremap compiler vs non-compiler sources. We use two schemes,
350/// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`].
351pub enum RemapScheme {
352    /// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`.
353    Compiler,
354    /// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`.
355    NonCompiler,
356}
357
358#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
359pub enum CLang {
360    C,
361    Cxx,
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
365pub enum FileType {
366    /// An executable binary file (like a `.exe`).
367    Executable,
368    /// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`).
369    NativeLibrary,
370    /// An executable (non-binary) script file (like a `.py` or `.sh`).
371    Script,
372    /// Any other regular file that is non-executable.
373    Regular,
374}
375
376impl FileType {
377    /// Get Unix permissions appropriate for this file type.
378    pub fn perms(self) -> u32 {
379        match self {
380            FileType::Executable | FileType::Script => 0o755,
381            FileType::Regular | FileType::NativeLibrary => 0o644,
382        }
383    }
384
385    pub fn could_have_split_debuginfo(self) -> bool {
386        match self {
387            FileType::Executable | FileType::NativeLibrary => true,
388            FileType::Script | FileType::Regular => false,
389        }
390    }
391}
392
393macro_rules! forward {
394    ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
395        impl Build {
396            $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {
397                self.config.$fn( $($param),* )
398            } )+
399        }
400    }
401}
402
403forward! {
404    verbose(f: impl Fn()),
405    is_verbose() -> bool,
406    create(path: &Path, s: &str),
407    remove(f: &Path),
408    tempdir() -> PathBuf,
409    llvm_link_shared() -> bool,
410    download_rustc() -> bool,
411}
412
413/// A mostly temporary helper struct before we can migrate everything in bootstrap to use
414/// the concept of a build compiler.
415struct HostAndStage {
416    host: TargetSelection,
417    stage: u32,
418}
419
420impl From<(TargetSelection, u32)> for HostAndStage {
421    fn from((host, stage): (TargetSelection, u32)) -> Self {
422        Self { host, stage }
423    }
424}
425
426impl From<Compiler> for HostAndStage {
427    fn from(compiler: Compiler) -> Self {
428        Self { host: compiler.host, stage: compiler.stage }
429    }
430}
431
432impl Build {
433    /// Creates a new set of build configuration from the `flags` on the command
434    /// line and the filesystem `config`.
435    ///
436    /// By default all build output will be placed in the current directory.
437    pub fn new(mut config: Config) -> Build {
438        let src = config.src.clone();
439        let out = config.out.clone();
440
441        #[cfg(unix)]
442        // keep this consistent with the equivalent check in x.py:
443        // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797
444        let is_sudo = match env::var_os("SUDO_USER") {
445            Some(_sudo_user) => {
446                // SAFETY: getuid() system call is always successful and no return value is reserved
447                // to indicate an error.
448                //
449                // For more context, see https://man7.org/linux/man-pages/man2/geteuid.2.html
450                let uid = unsafe { libc::getuid() };
451                uid == 0
452            }
453            None => false,
454        };
455        #[cfg(not(unix))]
456        let is_sudo = false;
457
458        let rust_info = config.rust_info.clone();
459        let cargo_info = config.cargo_info.clone();
460        let rust_analyzer_info = config.rust_analyzer_info.clone();
461        let clippy_info = config.clippy_info.clone();
462        let miri_info = config.miri_info.clone();
463        let rustfmt_info = config.rustfmt_info.clone();
464        let enzyme_info = config.enzyme_info.clone();
465        let in_tree_llvm_info = config.in_tree_llvm_info.clone();
466        let in_tree_gcc_info = config.in_tree_gcc_info.clone();
467
468        let initial_target_libdir = command(&config.initial_rustc)
469            .run_in_dry_run()
470            .args(["--print", "target-libdir"])
471            .run_capture_stdout(&config)
472            .stdout()
473            .trim()
474            .to_owned();
475
476        let initial_target_dir = Path::new(&initial_target_libdir)
477            .parent()
478            .unwrap_or_else(|| panic!("{initial_target_libdir} has no parent"));
479
480        let initial_lld = initial_target_dir.join("bin").join("rust-lld");
481
482        let initial_relative_libdir = if cfg!(test) {
483            // On tests, bootstrap uses the shim rustc, not the one from the stage0 toolchain.
484            PathBuf::default()
485        } else {
486            let ancestor = initial_target_dir.ancestors().nth(2).unwrap_or_else(|| {
487                panic!("Not enough ancestors for {}", initial_target_dir.display())
488            });
489
490            ancestor
491                .strip_prefix(&config.initial_sysroot)
492                .unwrap_or_else(|_| {
493                    panic!(
494                        "Couldn’t resolve the initial relative libdir from {}",
495                        initial_target_dir.display()
496                    )
497                })
498                .to_path_buf()
499        };
500
501        let version = std::fs::read_to_string(src.join("src").join("version"))
502            .expect("failed to read src/version");
503        let version = version.trim();
504
505        let mut bootstrap_out = std::env::current_exe()
506            .expect("could not determine path to running process")
507            .parent()
508            .unwrap()
509            .to_path_buf();
510        // Since bootstrap is hardlink to deps/bootstrap-*, Solaris can sometimes give
511        // path with deps/ which is bad and needs to be avoided.
512        if bootstrap_out.ends_with("deps") {
513            bootstrap_out.pop();
514        }
515        if !bootstrap_out.join(exe("rustc", config.host_target)).exists() && !cfg!(test) {
516            // this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented
517            panic!(
518                "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",
519                bootstrap_out.display()
520            )
521        }
522
523        if rust_info.is_from_tarball() && config.description.is_none() {
524            config.description = Some("built from a source tarball".to_owned());
525        }
526
527        let mut build = Build {
528            initial_lld,
529            initial_relative_libdir,
530            initial_rustc: config.initial_rustc.clone(),
531            initial_rustdoc: config
532                .initial_rustc
533                .with_file_name(exe("rustdoc", config.host_target)),
534            initial_cargo: config.initial_cargo.clone(),
535            initial_sysroot: config.initial_sysroot.clone(),
536            local_rebuild: config.local_rebuild,
537            fail_fast: config.cmd.fail_fast(),
538            doc_tests: config.cmd.doc_tests(),
539            verbosity: config.exec_ctx.verbosity as usize,
540
541            host_target: config.host_target,
542            hosts: config.hosts.clone(),
543            targets: config.targets.clone(),
544
545            config,
546            version: version.to_string(),
547            src,
548            out,
549            bootstrap_out,
550
551            cargo_info,
552            rust_analyzer_info,
553            clippy_info,
554            miri_info,
555            rustfmt_info,
556            enzyme_info,
557            in_tree_llvm_info,
558            in_tree_gcc_info,
559            cc: HashMap::new(),
560            cxx: HashMap::new(),
561            ar: HashMap::new(),
562            ranlib: HashMap::new(),
563            wasi_sdk_path: env::var_os("WASI_SDK_PATH").map(PathBuf::from),
564            crates: HashMap::new(),
565            crate_paths: HashMap::new(),
566            is_sudo,
567            prerelease_version: Cell::new(None),
568
569            #[cfg(feature = "build-metrics")]
570            metrics: crate::utils::metrics::BuildMetrics::init(),
571
572            #[cfg(feature = "tracing")]
573            step_graph: std::cell::RefCell::new(crate::utils::step_graph::StepGraph::default()),
574        };
575
576        // If local-rust is the same major.minor as the current version, then force a
577        // local-rebuild
578        let local_version_verbose = command(&build.initial_rustc)
579            .run_in_dry_run()
580            .args(["--version", "--verbose"])
581            .run_capture_stdout(&build)
582            .stdout();
583        let local_release = local_version_verbose
584            .lines()
585            .filter_map(|x| x.strip_prefix("release:"))
586            .next()
587            .unwrap()
588            .trim();
589        if local_release.split('.').take(2).eq(version.split('.').take(2)) {
590            build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
591            build.local_rebuild = true;
592        }
593
594        build.verbose(|| println!("finding compilers"));
595        utils::cc_detect::fill_compilers(&mut build);
596        // When running `setup`, the profile is about to change, so any requirements we have now may
597        // be different on the next invocation. Don't check for them until the next time x.py is
598        // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
599        //
600        // Similarly, for `setup` we don't actually need submodules or cargo metadata.
601        if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
602            build.verbose(|| println!("running sanity check"));
603            crate::core::sanity::check(&mut build);
604
605            // Make sure we update these before gathering metadata so we don't get an error about missing
606            // Cargo.toml files.
607            let rust_submodules = ["library/backtrace"];
608            for s in rust_submodules {
609                build.require_submodule(
610                    s,
611                    Some(
612                        "The submodule is required for the standard library \
613                         and the main Cargo workspace.",
614                    ),
615                );
616            }
617            // Now, update all existing submodules.
618            build.update_existing_submodules();
619
620            build.verbose(|| println!("learning about cargo"));
621            crate::core::metadata::build(&mut build);
622        }
623
624        // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file).
625        let build_triple = build.out.join(build.host_target);
626        t!(fs::create_dir_all(&build_triple));
627        let host = build.out.join("host");
628        if host.is_symlink() {
629            // Left over from a previous build; overwrite it.
630            // This matters if `build.build` has changed between invocations.
631            #[cfg(windows)]
632            t!(fs::remove_dir(&host));
633            #[cfg(not(windows))]
634            t!(fs::remove_file(&host));
635        }
636        t!(
637            symlink_dir(&build.config, &build_triple, &host),
638            format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())
639        );
640
641        build
642    }
643
644    /// Updates a submodule, and exits with a failure if submodule management
645    /// is disabled and the submodule does not exist.
646    ///
647    /// The given submodule name should be its path relative to the root of
648    /// the main repository.
649    ///
650    /// The given `err_hint` will be shown to the user if the submodule is not
651    /// checked out and submodule management is disabled.
652    #[cfg_attr(
653        feature = "tracing",
654        instrument(
655            level = "trace",
656            name = "Build::require_submodule",
657            skip_all,
658            fields(submodule = submodule),
659        ),
660    )]
661    pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {
662        if self.rust_info().is_from_tarball() {
663            return;
664        }
665
666        // When testing bootstrap itself, it is much faster to ignore
667        // submodules. Almost all Steps work fine without their submodules.
668        if cfg!(test) && !self.config.submodules() {
669            return;
670        }
671        self.config.update_submodule(submodule);
672        let absolute_path = self.config.src.join(submodule);
673        if !absolute_path.exists() || dir_is_empty(&absolute_path) {
674            let maybe_enable = if !self.config.submodules()
675                && self.config.rust_info.is_managed_git_subrepository()
676            {
677                "\nConsider setting `build.submodules = true` or manually initializing the submodules."
678            } else {
679                ""
680            };
681            let err_hint = err_hint.map_or_else(String::new, |e| format!("\n{e}"));
682            eprintln!(
683                "submodule {submodule} does not appear to be checked out, \
684                 but it is required for this step{maybe_enable}{err_hint}"
685            );
686            exit!(1);
687        }
688    }
689
690    /// If any submodule has been initialized already, sync it unconditionally.
691    /// This avoids contributors checking in a submodule change by accident.
692    fn update_existing_submodules(&self) {
693        // Avoid running git when there isn't a git checkout, or the user has
694        // explicitly disabled submodules in `bootstrap.toml`.
695        if !self.config.submodules() {
696            return;
697        }
698        let output = helpers::git(Some(&self.src))
699            .args(["config", "--file"])
700            .arg(".gitmodules")
701            .args(["--get-regexp", "path"])
702            .run_capture(self)
703            .stdout();
704        std::thread::scope(|s| {
705            // Look for `submodule.$name.path = $path`
706            // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
707            for line in output.lines() {
708                let submodule = line.split_once(' ').unwrap().1;
709                let config = self.config.clone();
710                s.spawn(move || {
711                    Self::update_existing_submodule(&config, submodule);
712                });
713            }
714        });
715    }
716
717    /// Updates the given submodule only if it's initialized already; nothing happens otherwise.
718    pub fn update_existing_submodule(config: &Config, submodule: &str) {
719        // Avoid running git when there isn't a git checkout.
720        if !config.submodules() {
721            return;
722        }
723
724        if config.git_info(false, Path::new(submodule)).is_managed_git_subrepository() {
725            config.update_submodule(submodule);
726        }
727    }
728
729    /// Executes the entire build, as configured by the flags and configuration.
730    #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))]
731    pub fn build(&mut self) {
732        trace!("setting up job management");
733        unsafe {
734            crate::utils::job::setup(self);
735        }
736
737        // Handle hard-coded subcommands.
738        {
739            #[cfg(feature = "tracing")]
740            let _hardcoded_span =
741                span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Perf)")
742                    .entered();
743
744            match &self.config.cmd {
745                Subcommand::Format { check, all } => {
746                    return core::build_steps::format::format(
747                        &builder::Builder::new(self),
748                        *check,
749                        *all,
750                        &self.config.paths,
751                    );
752                }
753                Subcommand::Perf(args) => {
754                    return core::build_steps::perf::perf(&builder::Builder::new(self), args);
755                }
756                _cmd => {
757                    debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
758                }
759            }
760
761            debug!("handling subcommand normally");
762        }
763
764        if !self.config.dry_run() {
765            #[cfg(feature = "tracing")]
766            let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered();
767
768            // We first do a dry-run. This is a sanity-check to ensure that
769            // steps don't do anything expensive in the dry-run.
770            {
771                #[cfg(feature = "tracing")]
772                let _sanity_check_span =
773                    span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered();
774                self.config.set_dry_run(DryRun::SelfCheck);
775                let builder = builder::Builder::new(self);
776                builder.execute_cli();
777            }
778
779            // Actual run.
780            {
781                #[cfg(feature = "tracing")]
782                let _actual_run_span =
783                    span!(tracing::Level::DEBUG, "(2) executing actual run").entered();
784                self.config.set_dry_run(DryRun::Disabled);
785                let builder = builder::Builder::new(self);
786                builder.execute_cli();
787            }
788        } else {
789            #[cfg(feature = "tracing")]
790            let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered();
791
792            let builder = builder::Builder::new(self);
793            builder.execute_cli();
794        }
795
796        #[cfg(feature = "tracing")]
797        debug!("checking for postponed test failures from `test  --no-fail-fast`");
798
799        // Check for postponed failures from `test --no-fail-fast`.
800        self.config.exec_ctx().report_failures_and_exit();
801
802        #[cfg(feature = "build-metrics")]
803        self.metrics.persist(self);
804    }
805
806    fn rust_info(&self) -> &GitInfo {
807        &self.config.rust_info
808    }
809
810    /// Gets the space-separated set of activated features for the standard library.
811    /// This can be configured with the `std-features` key in bootstrap.toml.
812    fn std_features(&self, target: TargetSelection) -> String {
813        let mut features: BTreeSet<&str> =
814            self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
815
816        match self.config.llvm_libunwind(target) {
817            LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
818            LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
819            LlvmLibunwind::No => false,
820        };
821
822        if self.config.backtrace {
823            features.insert("backtrace");
824        }
825
826        if self.config.profiler_enabled(target) {
827            features.insert("profiler");
828        }
829
830        // If zkvm target, generate memcpy, etc.
831        if target.contains("zkvm") {
832            features.insert("compiler-builtins-mem");
833        }
834
835        features.into_iter().collect::<Vec<_>>().join(" ")
836    }
837
838    /// Gets the space-separated set of activated features for the compiler.
839    fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
840        let possible_features_by_crates: HashSet<_> = crates
841            .iter()
842            .flat_map(|krate| &self.crates[krate].features)
843            .map(std::ops::Deref::deref)
844            .collect();
845        let check = |feature: &str| -> bool {
846            crates.is_empty() || possible_features_by_crates.contains(feature)
847        };
848        let mut features = vec![];
849        if self.config.jemalloc(target) && check("jemalloc") {
850            features.push("jemalloc");
851        }
852        if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
853            features.push("llvm");
854        }
855        // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
856        if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
857            features.push("rustc_randomized_layouts");
858        }
859        if self.config.compile_time_deps && kind == Kind::Check {
860            features.push("check_only");
861        }
862
863        // If debug logging is on, then we want the default for tracing:
864        // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26
865        // which is everything (including debug/trace/etc.)
866        // if its unset, if debug_assertions is on, then debug_logging will also be on
867        // as well as tracing *ignoring* this feature when debug_assertions is on
868        if !self.config.rust_debug_logging && check("max_level_info") {
869            features.push("max_level_info");
870        }
871
872        features.join(" ")
873    }
874
875    /// Component directory that Cargo will produce output into (e.g.
876    /// release/debug)
877    fn cargo_dir(&self) -> &'static str {
878        if self.config.rust_optimize.is_release() { "release" } else { "debug" }
879    }
880
881    fn tools_dir(&self, build_compiler: Compiler) -> PathBuf {
882        let out = self
883            .out
884            .join(build_compiler.host)
885            .join(format!("stage{}-tools-bin", build_compiler.stage + 1));
886        t!(fs::create_dir_all(&out));
887        out
888    }
889
890    /// Returns the root directory for all output generated in a particular
891    /// stage when being built with a particular build compiler.
892    ///
893    /// The mode indicates what the root directory is for.
894    fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf {
895        use std::fmt::Write;
896
897        fn bootstrap_tool() -> (Option<u32>, &'static str) {
898            (None, "bootstrap-tools")
899        }
900        fn staged_tool(build_compiler: Compiler) -> (Option<u32>, &'static str) {
901            (Some(build_compiler.stage + 1), "tools")
902        }
903
904        let (stage, suffix) = match mode {
905            // Std is special, stage N std is built with stage N rustc
906            Mode::Std => (Some(build_compiler.stage), "std"),
907            // The rest of things are built with stage N-1 rustc
908            Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"),
909            Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"),
910            Mode::ToolBootstrap => bootstrap_tool(),
911            Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage + 1), "tools"),
912            Mode::ToolTarget => {
913                // If we're not cross-compiling (the common case), share the target directory with
914                // bootstrap tools to reuse the build cache.
915                if build_compiler.stage == 0 {
916                    bootstrap_tool()
917                } else {
918                    staged_tool(build_compiler)
919                }
920            }
921        };
922        let path = self.out.join(build_compiler.host);
923        let mut dir_name = String::new();
924        if let Some(stage) = stage {
925            write!(dir_name, "stage{stage}-").unwrap();
926        }
927        dir_name.push_str(suffix);
928        path.join(dir_name)
929    }
930
931    /// Returns the root output directory for all Cargo output in a given stage,
932    /// running a particular compiler, whether or not we're building the
933    /// standard library, and targeting the specified architecture.
934    fn cargo_out(&self, build_compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
935        self.stage_out(build_compiler, mode).join(target).join(self.cargo_dir())
936    }
937
938    /// Root output directory of LLVM for `target`
939    ///
940    /// Note that if LLVM is configured externally then the directory returned
941    /// will likely be empty.
942    fn llvm_out(&self, target: TargetSelection) -> PathBuf {
943        if self.config.llvm_from_ci && self.config.is_host_target(target) {
944            self.config.ci_llvm_root()
945        } else {
946            self.out.join(target).join("llvm")
947        }
948    }
949
950    fn enzyme_out(&self, target: TargetSelection) -> PathBuf {
951        self.out.join(&*target.triple).join("enzyme")
952    }
953
954    fn gcc_out(&self, target: TargetSelection) -> PathBuf {
955        self.out.join(&*target.triple).join("gcc")
956    }
957
958    fn lld_out(&self, target: TargetSelection) -> PathBuf {
959        self.out.join(target).join("lld")
960    }
961
962    /// Output directory for all documentation for a target
963    fn doc_out(&self, target: TargetSelection) -> PathBuf {
964        self.out.join(target).join("doc")
965    }
966
967    /// Output directory for all JSON-formatted documentation for a target
968    fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
969        self.out.join(target).join("json-doc")
970    }
971
972    fn test_out(&self, target: TargetSelection) -> PathBuf {
973        self.out.join(target).join("test")
974    }
975
976    /// Output directory for all documentation for a target
977    fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
978        self.out.join(target).join("compiler-doc")
979    }
980
981    /// Output directory for some generated md crate documentation for a target (temporary)
982    fn md_doc_out(&self, target: TargetSelection) -> PathBuf {
983        self.out.join(target).join("md-doc")
984    }
985
986    /// Path to the vendored Rust crates.
987    fn vendored_crates_path(&self) -> Option<PathBuf> {
988        if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }
989    }
990
991    /// Returns the path to `FileCheck` binary for the specified target
992    fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {
993        let target_config = self.config.target_config.get(&target);
994        if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
995            s.to_path_buf()
996        } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
997            let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(self).stdout();
998            let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
999            if filecheck.exists() {
1000                filecheck
1001            } else {
1002                // On Fedora the system LLVM installs FileCheck in the
1003                // llvm subdirectory of the libdir.
1004                let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(self).stdout();
1005                let lib_filecheck =
1006                    Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
1007                if lib_filecheck.exists() {
1008                    lib_filecheck
1009                } else {
1010                    // Return the most normal file name, even though
1011                    // it doesn't exist, so that any error message
1012                    // refers to that.
1013                    filecheck
1014                }
1015            }
1016        } else {
1017            let base = self.llvm_out(target).join("build");
1018            let base = if !self.ninja() && target.is_msvc() {
1019                if self.config.llvm_optimize {
1020                    if self.config.llvm_release_debuginfo {
1021                        base.join("RelWithDebInfo")
1022                    } else {
1023                        base.join("Release")
1024                    }
1025                } else {
1026                    base.join("Debug")
1027                }
1028            } else {
1029                base
1030            };
1031            base.join("bin").join(exe("FileCheck", target))
1032        }
1033    }
1034
1035    /// Directory for libraries built from C/C++ code and shared between stages.
1036    fn native_dir(&self, target: TargetSelection) -> PathBuf {
1037        self.out.join(target).join("native")
1038    }
1039
1040    /// Root output directory for rust_test_helpers library compiled for
1041    /// `target`
1042    fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {
1043        self.native_dir(target).join("rust-test-helpers")
1044    }
1045
1046    /// Adds the `RUST_TEST_THREADS` env var if necessary
1047    fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {
1048        if env::var_os("RUST_TEST_THREADS").is_none() {
1049            cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
1050        }
1051    }
1052
1053    /// Returns the libdir of the snapshot compiler.
1054    fn rustc_snapshot_libdir(&self) -> PathBuf {
1055        self.rustc_snapshot_sysroot().join(libdir(self.config.host_target))
1056    }
1057
1058    /// Returns the sysroot of the snapshot compiler.
1059    fn rustc_snapshot_sysroot(&self) -> &Path {
1060        static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
1061        SYSROOT_CACHE.get_or_init(|| {
1062            command(&self.initial_rustc)
1063                .run_in_dry_run()
1064                .args(["--print", "sysroot"])
1065                .run_capture_stdout(self)
1066                .stdout()
1067                .trim()
1068                .to_owned()
1069                .into()
1070        })
1071    }
1072
1073    /// Check if verbosity is greater than the `level`
1074    pub fn is_verbose_than(&self, level: usize) -> bool {
1075        self.verbosity > level
1076    }
1077
1078    /// Runs a function if verbosity is greater than `level`.
1079    fn verbose_than(&self, level: usize, f: impl Fn()) {
1080        if self.is_verbose_than(level) {
1081            f()
1082        }
1083    }
1084
1085    fn info(&self, msg: &str) {
1086        match self.config.get_dry_run() {
1087            DryRun::SelfCheck => (),
1088            DryRun::Disabled | DryRun::UserSelected => {
1089                println!("{msg}");
1090            }
1091        }
1092    }
1093
1094    /// Return a `Group` guard for a [`Step`] that:
1095    /// - Performs `action`
1096    /// - On `what`
1097    ///   - Where `what` possibly corresponds to a `mode`
1098    /// - `action` is performed using the given build compiler (`host_and_stage`).
1099    ///   - Since some steps do not use the concept of a build compiler yet, it is also possible
1100    ///     to pass the host and stage explicitly.
1101    /// - With a given `target`.
1102    ///
1103    /// [`Step`]: crate::core::builder::Step
1104    #[must_use = "Groups should not be dropped until the Step finishes running"]
1105    #[track_caller]
1106    fn msg(
1107        &self,
1108        action: impl Into<Kind>,
1109        what: impl Display,
1110        mode: impl Into<Option<Mode>>,
1111        host_and_stage: impl Into<HostAndStage>,
1112        target: impl Into<Option<TargetSelection>>,
1113    ) -> Option<gha::Group> {
1114        let host_and_stage = host_and_stage.into();
1115        let actual_stage = match mode.into() {
1116            // Std has the same stage as the compiler that builds it
1117            Some(Mode::Std) => host_and_stage.stage,
1118            // Other things have stage corresponding to their build compiler + 1
1119            Some(
1120                Mode::Rustc
1121                | Mode::Codegen
1122                | Mode::ToolBootstrap
1123                | Mode::ToolTarget
1124                | Mode::ToolStd
1125                | Mode::ToolRustc,
1126            )
1127            | None => host_and_stage.stage + 1,
1128        };
1129
1130        let action = action.into().description();
1131        let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}");
1132        let msg = if let Some(target) = target.into() {
1133            let build_stage = host_and_stage.stage;
1134            let host = host_and_stage.host;
1135            if host == target {
1136                msg(format_args!(" (stage{build_stage} -> stage{actual_stage}, {target})"))
1137            } else {
1138                msg(format_args!(" (stage{build_stage}:{host} -> stage{actual_stage}:{target})"))
1139            }
1140        } else {
1141            msg(format_args!(""))
1142        };
1143        self.group(&msg)
1144    }
1145
1146    /// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
1147    ///
1148    /// [`Step`]: crate::core::builder::Step
1149    #[must_use = "Groups should not be dropped until the Step finishes running"]
1150    #[track_caller]
1151    fn msg_unstaged(
1152        &self,
1153        action: impl Into<Kind>,
1154        what: impl Display,
1155        target: TargetSelection,
1156    ) -> Option<gha::Group> {
1157        let action = action.into().description();
1158        let msg = format!("{action} {what} for {target}");
1159        self.group(&msg)
1160    }
1161
1162    #[track_caller]
1163    fn group(&self, msg: &str) -> Option<gha::Group> {
1164        match self.config.get_dry_run() {
1165            DryRun::SelfCheck => None,
1166            DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),
1167        }
1168    }
1169
1170    /// Returns the number of parallel jobs that have been configured for this
1171    /// build.
1172    fn jobs(&self) -> u32 {
1173        self.config.jobs.unwrap_or_else(|| {
1174            std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1175        })
1176    }
1177
1178    fn debuginfo_map_to(&self, which: GitRepo, remap_scheme: RemapScheme) -> Option<String> {
1179        if !self.config.rust_remap_debuginfo {
1180            return None;
1181        }
1182
1183        match which {
1184            GitRepo::Rustc => {
1185                let sha = self.rust_sha().unwrap_or(&self.version);
1186
1187                match remap_scheme {
1188                    RemapScheme::Compiler => {
1189                        // For compiler sources, remap via `/rustc-dev/{sha}` to allow
1190                        // distinguishing between compiler sources vs library sources, since
1191                        // `rustc-dev` dist component places them under
1192                        // `$sysroot/lib/rustlib/rustc-src/rust` as opposed to `rust-src`'s
1193                        // `$sysroot/lib/rustlib/src/rust`.
1194                        //
1195                        // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s
1196                        // `try_to_translate_virtual_to_real`.
1197                        Some(format!("/rustc-dev/{sha}"))
1198                    }
1199                    RemapScheme::NonCompiler => {
1200                        // For non-compiler sources, use `/rustc/{sha}` remapping scheme.
1201                        Some(format!("/rustc/{sha}"))
1202                    }
1203                }
1204            }
1205            GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1206        }
1207    }
1208
1209    /// Returns the path to the C compiler for the target specified.
1210    fn cc(&self, target: TargetSelection) -> PathBuf {
1211        if self.config.dry_run() {
1212            return PathBuf::new();
1213        }
1214        self.cc[&target].path().into()
1215    }
1216
1217    /// Returns the internal `cc::Tool` for the C compiler.
1218    fn cc_tool(&self, target: TargetSelection) -> Tool {
1219        self.cc[&target].clone()
1220    }
1221
1222    /// Returns the internal `cc::Tool` for the C++ compiler.
1223    fn cxx_tool(&self, target: TargetSelection) -> Tool {
1224        self.cxx[&target].clone()
1225    }
1226
1227    /// Returns C flags that `cc-rs` thinks should be enabled for the
1228    /// specified target by default.
1229    fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
1230        if self.config.dry_run() {
1231            return Vec::new();
1232        }
1233        let base = match c {
1234            CLang::C => self.cc[&target].clone(),
1235            CLang::Cxx => self.cxx[&target].clone(),
1236        };
1237
1238        // Filter out -O and /O (the optimization flags) that we picked up
1239        // from cc-rs, that's up to the caller to figure out.
1240        base.args()
1241            .iter()
1242            .map(|s| s.to_string_lossy().into_owned())
1243            .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1244            .collect::<Vec<String>>()
1245    }
1246
1247    /// Returns extra C flags that `cc-rs` doesn't handle.
1248    fn cc_unhandled_cflags(
1249        &self,
1250        target: TargetSelection,
1251        which: GitRepo,
1252        c: CLang,
1253    ) -> Vec<String> {
1254        let mut base = Vec::new();
1255
1256        // If we're compiling C++ on macOS then we add a flag indicating that
1257        // we want libc++ (more filled out than libstdc++), ensuring that
1258        // LLVM/etc are all properly compiled.
1259        if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
1260            base.push("-stdlib=libc++".into());
1261        }
1262
1263        // Work around an apparently bad MinGW / GCC optimization,
1264        // See: https://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html
1265        // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936
1266        if &*target.triple == "i686-pc-windows-gnu" {
1267            base.push("-fno-omit-frame-pointer".into());
1268        }
1269
1270        if let Some(map_to) = self.debuginfo_map_to(which, RemapScheme::NonCompiler) {
1271            let map = format!("{}={}", self.src.display(), map_to);
1272            let cc = self.cc(target);
1273            if cc.ends_with("clang") || cc.ends_with("gcc") {
1274                base.push(format!("-fdebug-prefix-map={map}"));
1275            } else if cc.ends_with("clang-cl.exe") {
1276                base.push("-Xclang".into());
1277                base.push(format!("-fdebug-prefix-map={map}"));
1278            }
1279        }
1280        base
1281    }
1282
1283    /// Returns the path to the `ar` archive utility for the target specified.
1284    fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1285        if self.config.dry_run() {
1286            return None;
1287        }
1288        self.ar.get(&target).cloned()
1289    }
1290
1291    /// Returns the path to the `ranlib` utility for the target specified.
1292    fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1293        if self.config.dry_run() {
1294            return None;
1295        }
1296        self.ranlib.get(&target).cloned()
1297    }
1298
1299    /// Returns the path to the C++ compiler for the target specified.
1300    fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1301        if self.config.dry_run() {
1302            return Ok(PathBuf::new());
1303        }
1304        match self.cxx.get(&target) {
1305            Some(p) => Ok(p.path().into()),
1306            None => Err(format!("target `{target}` is not configured as a host, only as a target")),
1307        }
1308    }
1309
1310    /// Returns the path to the linker for the given target if it needs to be overridden.
1311    fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1312        if self.config.dry_run() {
1313            return Some(PathBuf::new());
1314        }
1315        if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1316        {
1317            Some(linker)
1318        } else if target.contains("vxworks") {
1319            // need to use CXX compiler as linker to resolve the exception functions
1320            // that are only existed in CXX libraries
1321            Some(self.cxx[&target].path().into())
1322        } else if !self.config.is_host_target(target)
1323            && helpers::use_host_linker(target)
1324            && !target.is_msvc()
1325        {
1326            Some(self.cc(target))
1327        } else if self.config.lld_mode.is_used()
1328            && self.is_lld_direct_linker(target)
1329            && self.host_target == target
1330        {
1331            match self.config.lld_mode {
1332                LldMode::SelfContained => Some(self.initial_lld.clone()),
1333                LldMode::External => Some("lld".into()),
1334                LldMode::Unused => None,
1335            }
1336        } else {
1337            None
1338        }
1339    }
1340
1341    // Is LLD configured directly through `-Clinker`?
1342    // Only MSVC targets use LLD directly at the moment.
1343    fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1344        target.is_msvc()
1345    }
1346
1347    /// Returns if this target should statically link the C runtime, if specified
1348    fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1349        if target.contains("pc-windows-msvc") {
1350            Some(true)
1351        } else {
1352            self.config.target_config.get(&target).and_then(|t| t.crt_static)
1353        }
1354    }
1355
1356    /// Returns the "musl root" for this `target`, if defined.
1357    ///
1358    /// If this is a native target (host is also musl) and no musl-root is given,
1359    /// it falls back to the system toolchain in /usr.
1360    fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1361        let configured_root = self
1362            .config
1363            .target_config
1364            .get(&target)
1365            .and_then(|t| t.musl_root.as_ref())
1366            .or(self.config.musl_root.as_ref())
1367            .map(|p| &**p);
1368
1369        if self.config.is_host_target(target) && configured_root.is_none() {
1370            Some(Path::new("/usr"))
1371        } else {
1372            configured_root
1373        }
1374    }
1375
1376    /// Returns the "musl libdir" for this `target`.
1377    fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1378        self.config
1379            .target_config
1380            .get(&target)
1381            .and_then(|t| t.musl_libdir.clone())
1382            .or_else(|| self.musl_root(target).map(|root| root.join("lib")))
1383    }
1384
1385    /// Returns the `lib` directory for the WASI target specified, if
1386    /// configured.
1387    ///
1388    /// This first consults `wasi-root` as configured in per-target
1389    /// configuration, and failing that it assumes that `$WASI_SDK_PATH` is
1390    /// set in the environment, and failing that `None` is returned.
1391    fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1392        let configured =
1393            self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);
1394        if let Some(path) = configured {
1395            return Some(path.join("lib").join(target.to_string()));
1396        }
1397        let mut env_root = self.wasi_sdk_path.clone()?;
1398        env_root.push("share");
1399        env_root.push("wasi-sysroot");
1400        env_root.push("lib");
1401        env_root.push(target.to_string());
1402        Some(env_root)
1403    }
1404
1405    /// Returns `true` if this is a no-std `target`, if defined
1406    fn no_std(&self, target: TargetSelection) -> Option<bool> {
1407        self.config.target_config.get(&target).map(|t| t.no_std)
1408    }
1409
1410    /// Returns `true` if the target will be tested using the `remote-test-client`
1411    /// and `remote-test-server` binaries.
1412    fn remote_tested(&self, target: TargetSelection) -> bool {
1413        self.qemu_rootfs(target).is_some()
1414            || target.contains("android")
1415            || env::var_os("TEST_DEVICE_ADDR").is_some()
1416    }
1417
1418    /// Returns an optional "runner" to pass to `compiletest` when executing
1419    /// test binaries.
1420    ///
1421    /// An example of this would be a WebAssembly runtime when testing the wasm
1422    /// targets.
1423    fn runner(&self, target: TargetSelection) -> Option<String> {
1424        let configured_runner =
1425            self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);
1426        if let Some(runner) = configured_runner {
1427            return Some(runner.to_owned());
1428        }
1429
1430        if target.starts_with("wasm") && target.contains("wasi") {
1431            self.default_wasi_runner(target)
1432        } else {
1433            None
1434        }
1435    }
1436
1437    /// When a `runner` configuration is not provided and a WASI-looking target
1438    /// is being tested this is consulted to prove the environment to see if
1439    /// there's a runtime already lying around that seems reasonable to use.
1440    fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {
1441        let mut finder = crate::core::sanity::Finder::new();
1442
1443        // Look for Wasmtime, and for its default options be sure to disable
1444        // its caching system since we're executing quite a lot of tests and
1445        // ideally shouldn't pollute the cache too much.
1446        if let Some(path) = finder.maybe_have("wasmtime")
1447            && let Ok(mut path) = path.into_os_string().into_string()
1448        {
1449            path.push_str(" run -C cache=n --dir .");
1450            // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is
1451            // required for libtest to work on beta/stable channels.
1452            //
1453            // NB: with Wasmtime 20 this can change to `-S inherit-env` to
1454            // inherit the entire environment rather than just this single
1455            // environment variable.
1456            path.push_str(" --env RUSTC_BOOTSTRAP");
1457
1458            if target.contains("wasip2") {
1459                path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
1460            }
1461
1462            return Some(path);
1463        }
1464
1465        None
1466    }
1467
1468    /// Returns whether the specified tool is configured as part of this build.
1469    ///
1470    /// This requires that both the `extended` key is set and the `tools` key is
1471    /// either unset or specifically contains the specified tool.
1472    fn tool_enabled(&self, tool: &str) -> bool {
1473        if !self.config.extended {
1474            return false;
1475        }
1476        match &self.config.tools {
1477            Some(set) => set.contains(tool),
1478            None => true,
1479        }
1480    }
1481
1482    /// Returns the root of the "rootfs" image that this target will be using,
1483    /// if one was configured.
1484    ///
1485    /// If `Some` is returned then that means that tests for this target are
1486    /// emulated with QEMU and binaries will need to be shipped to the emulator.
1487    fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1488        self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1489    }
1490
1491    /// Path to the python interpreter to use
1492    fn python(&self) -> &Path {
1493        if self.config.host_target.ends_with("apple-darwin") {
1494            // Force /usr/bin/python3 on macOS for LLDB tests because we're loading the
1495            // LLDB plugin's compiled module which only works with the system python
1496            // (namely not Homebrew-installed python)
1497            Path::new("/usr/bin/python3")
1498        } else {
1499            self.config
1500                .python
1501                .as_ref()
1502                .expect("python is required for running LLDB or rustdoc tests")
1503        }
1504    }
1505
1506    /// Temporary directory that extended error information is emitted to.
1507    fn extended_error_dir(&self) -> PathBuf {
1508        self.out.join("tmp/extended-error-metadata")
1509    }
1510
1511    /// Tests whether the `compiler` compiling for `target` should be forced to
1512    /// use a stage1 compiler instead.
1513    ///
1514    /// Currently, by default, the build system does not perform a "full
1515    /// bootstrap" by default where we compile the compiler three times.
1516    /// Instead, we compile the compiler two times. The final stage (stage2)
1517    /// just copies the libraries from the previous stage, which is what this
1518    /// method detects.
1519    ///
1520    /// Here we return `true` if:
1521    ///
1522    /// * The build isn't performing a full bootstrap
1523    /// * The `compiler` is in the final stage, 2
1524    /// * We're not cross-compiling, so the artifacts are already available in
1525    ///   stage1
1526    ///
1527    /// When all of these conditions are met the build will lift artifacts from
1528    /// the previous stage forward.
1529    fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1530        !self.config.full_bootstrap
1531            && !self.config.download_rustc()
1532            && stage >= 2
1533            && (self.hosts.contains(&target) || target == self.host_target)
1534    }
1535
1536    /// Checks whether the `compiler` compiling for `target` should be forced to
1537    /// use a stage2 compiler instead.
1538    ///
1539    /// When we download the pre-compiled version of rustc and compiler stage is >= 2,
1540    /// it should be forced to use a stage2 compiler.
1541    fn force_use_stage2(&self, stage: u32) -> bool {
1542        self.config.download_rustc() && stage >= 2
1543    }
1544
1545    /// Given `num` in the form "a.b.c" return a "release string" which
1546    /// describes the release version number.
1547    ///
1548    /// For example on nightly this returns "a.b.c-nightly", on beta it returns
1549    /// "a.b.c-beta.1" and on stable it just returns "a.b.c".
1550    fn release(&self, num: &str) -> String {
1551        match &self.config.channel[..] {
1552            "stable" => num.to_string(),
1553            "beta" => {
1554                if !self.config.omit_git_hash {
1555                    format!("{}-beta.{}", num, self.beta_prerelease_version())
1556                } else {
1557                    format!("{num}-beta")
1558                }
1559            }
1560            "nightly" => format!("{num}-nightly"),
1561            _ => format!("{num}-dev"),
1562        }
1563    }
1564
1565    fn beta_prerelease_version(&self) -> u32 {
1566        fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1567            let version = fs::read_to_string(version_file).ok()?;
1568
1569            helpers::extract_beta_rev(&version)
1570        }
1571
1572        if let Some(s) = self.prerelease_version.get() {
1573            return s;
1574        }
1575
1576        // First check if there is a version file available.
1577        // If available, we read the beta revision from that file.
1578        // This only happens when building from a source tarball when Git should not be used.
1579        let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1580            // Figure out how many merge commits happened since we branched off master.
1581            // That's our beta number!
1582            // (Note that we use a `..` range, not the `...` symmetric difference.)
1583            helpers::git(Some(&self.src))
1584                .arg("rev-list")
1585                .arg("--count")
1586                .arg("--merges")
1587                .arg(format!(
1588                    "refs/remotes/origin/{}..HEAD",
1589                    self.config.stage0_metadata.config.nightly_branch
1590                ))
1591                .run_in_dry_run()
1592                .run_capture(self)
1593                .stdout()
1594        });
1595        let n = count.trim().parse().unwrap();
1596        self.prerelease_version.set(Some(n));
1597        n
1598    }
1599
1600    /// Returns the value of `release` above for Rust itself.
1601    fn rust_release(&self) -> String {
1602        self.release(&self.version)
1603    }
1604
1605    /// Returns the "package version" for a component given the `num` release
1606    /// number.
1607    ///
1608    /// The package version is typically what shows up in the names of tarballs.
1609    /// For channels like beta/nightly it's just the channel name, otherwise
1610    /// it's the `num` provided.
1611    fn package_vers(&self, num: &str) -> String {
1612        match &self.config.channel[..] {
1613            "stable" => num.to_string(),
1614            "beta" => "beta".to_string(),
1615            "nightly" => "nightly".to_string(),
1616            _ => format!("{num}-dev"),
1617        }
1618    }
1619
1620    /// Returns the value of `package_vers` above for Rust itself.
1621    fn rust_package_vers(&self) -> String {
1622        self.package_vers(&self.version)
1623    }
1624
1625    /// Returns the `version` string associated with this compiler for Rust
1626    /// itself.
1627    ///
1628    /// Note that this is a descriptive string which includes the commit date,
1629    /// sha, version, etc.
1630    fn rust_version(&self) -> String {
1631        let mut version = self.rust_info().version(self, &self.version);
1632        if let Some(ref s) = self.config.description
1633            && !s.is_empty()
1634        {
1635            version.push_str(" (");
1636            version.push_str(s);
1637            version.push(')');
1638        }
1639        version
1640    }
1641
1642    /// Returns the full commit hash.
1643    fn rust_sha(&self) -> Option<&str> {
1644        self.rust_info().sha()
1645    }
1646
1647    /// Returns the `a.b.c` version that the given package is at.
1648    fn release_num(&self, package: &str) -> String {
1649        let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));
1650        let toml = t!(fs::read_to_string(toml_file_name));
1651        for line in toml.lines() {
1652            if let Some(stripped) =
1653                line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))
1654            {
1655                return stripped.to_owned();
1656            }
1657        }
1658
1659        panic!("failed to find version in {package}'s Cargo.toml")
1660    }
1661
1662    /// Returns `true` if unstable features should be enabled for the compiler
1663    /// we're building.
1664    fn unstable_features(&self) -> bool {
1665        !matches!(&self.config.channel[..], "stable" | "beta")
1666    }
1667
1668    /// Returns a Vec of all the dependencies of the given root crate,
1669    /// including transitive dependencies and the root itself. Only includes
1670    /// "local" crates (those in the local source tree, not from a registry).
1671    fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1672        let mut ret = Vec::new();
1673        let mut list = vec![root.to_owned()];
1674        let mut visited = HashSet::new();
1675        while let Some(krate) = list.pop() {
1676            let krate = self
1677                .crates
1678                .get(&krate)
1679                .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1680            ret.push(krate);
1681            for dep in &krate.deps {
1682                if !self.crates.contains_key(dep) {
1683                    // Ignore non-workspace members.
1684                    continue;
1685                }
1686                // Don't include optional deps if their features are not
1687                // enabled. Ideally this would be computed from `cargo
1688                // metadata --features …`, but that is somewhat slow. In
1689                // the future, we may want to consider just filtering all
1690                // build and dev dependencies in metadata::build.
1691                if visited.insert(dep)
1692                    && (dep != "profiler_builtins"
1693                        || target
1694                            .map(|t| self.config.profiler_enabled(t))
1695                            .unwrap_or_else(|| self.config.any_profiler_enabled()))
1696                    && (dep != "rustc_codegen_llvm"
1697                        || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
1698                {
1699                    list.push(dep.clone());
1700                }
1701            }
1702        }
1703        ret.sort_unstable_by_key(|krate| krate.name.clone()); // reproducible order needed for tests
1704        ret
1705    }
1706
1707    fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {
1708        if self.config.dry_run() {
1709            return Vec::new();
1710        }
1711
1712        if !stamp.path().exists() {
1713            eprintln!(
1714                "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1715                stamp.path().display()
1716            );
1717            crate::exit!(1);
1718        }
1719
1720        let mut paths = Vec::new();
1721        let contents = t!(fs::read(stamp.path()), stamp.path());
1722        // This is the method we use for extracting paths from the stamp file passed to us. See
1723        // run_cargo for more information (in compile.rs).
1724        for part in contents.split(|b| *b == 0) {
1725            if part.is_empty() {
1726                continue;
1727            }
1728            let dependency_type = match part[0] as char {
1729                'h' => DependencyType::Host,
1730                's' => DependencyType::TargetSelfContained,
1731                't' => DependencyType::Target,
1732                _ => unreachable!(),
1733            };
1734            let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1735            paths.push((path, dependency_type));
1736        }
1737        paths
1738    }
1739
1740    /// Copies a file from `src` to `dst`.
1741    ///
1742    /// If `src` is a symlink, `src` will be resolved to the actual path
1743    /// and copied to `dst` instead of the symlink itself.
1744    pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
1745        self.copy_link_internal(src, dst, true);
1746    }
1747
1748    /// Links a file from `src` to `dst`.
1749    /// Attempts to use hard links if possible, falling back to copying.
1750    /// You can neither rely on this being a copy nor it being a link,
1751    /// so do not write to dst.
1752    pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
1753        self.copy_link_internal(src, dst, false);
1754
1755        if file_type.could_have_split_debuginfo()
1756            && let Some(dbg_file) = split_debuginfo(src)
1757        {
1758            self.copy_link_internal(
1759                &dbg_file,
1760                &dst.with_extension(dbg_file.extension().unwrap()),
1761                false,
1762            );
1763        }
1764    }
1765
1766    fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1767        if self.config.dry_run() {
1768            return;
1769        }
1770        self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
1771        if src == dst {
1772            return;
1773        }
1774        if let Err(e) = fs::remove_file(dst)
1775            && cfg!(windows)
1776            && e.kind() != io::ErrorKind::NotFound
1777        {
1778            // workaround for https://github.com/rust-lang/rust/issues/127126
1779            // if removing the file fails, attempt to rename it instead.
1780            let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1781            let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1782        }
1783        let mut metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
1784        let mut src = src.to_path_buf();
1785        if metadata.file_type().is_symlink() {
1786            if dereference_symlinks {
1787                src = t!(fs::canonicalize(src));
1788                metadata = t!(fs::metadata(&src), format!("target = {}", src.display()));
1789            } else {
1790                let link = t!(fs::read_link(src));
1791                t!(self.symlink_file(link, dst));
1792                return;
1793            }
1794        }
1795        if let Ok(()) = fs::hard_link(&src, dst) {
1796            // Attempt to "easy copy" by creating a hard link (symlinks are privileged on windows),
1797            // but if that fails just fall back to a slow `copy` operation.
1798        } else {
1799            if let Err(e) = fs::copy(&src, dst) {
1800                panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1801            }
1802            t!(fs::set_permissions(dst, metadata.permissions()));
1803
1804            // Restore file times because changing permissions on e.g. Linux using `chmod` can cause
1805            // file access time to change.
1806            let file_times = fs::FileTimes::new()
1807                .set_accessed(t!(metadata.accessed()))
1808                .set_modified(t!(metadata.modified()));
1809            t!(set_file_times(dst, file_times));
1810        }
1811    }
1812
1813    /// Links the `src` directory recursively to `dst`. Both are assumed to exist
1814    /// when this function is called.
1815    /// Will attempt to use hard links if possible and fall back to copying.
1816    pub fn cp_link_r(&self, src: &Path, dst: &Path) {
1817        if self.config.dry_run() {
1818            return;
1819        }
1820        for f in self.read_dir(src) {
1821            let path = f.path();
1822            let name = path.file_name().unwrap();
1823            let dst = dst.join(name);
1824            if t!(f.file_type()).is_dir() {
1825                t!(fs::create_dir_all(&dst));
1826                self.cp_link_r(&path, &dst);
1827            } else {
1828                self.copy_link(&path, &dst, FileType::Regular);
1829            }
1830        }
1831    }
1832
1833    /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
1834    /// when this function is called.
1835    /// Will attempt to use hard links if possible and fall back to copying.
1836    /// Unwanted files or directories can be skipped
1837    /// by returning `false` from the filter function.
1838    pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1839        // Immediately recurse with an empty relative path
1840        self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
1841    }
1842
1843    // Inner function does the actual work
1844    fn cp_link_filtered_recurse(
1845        &self,
1846        src: &Path,
1847        dst: &Path,
1848        relative: &Path,
1849        filter: &dyn Fn(&Path) -> bool,
1850    ) {
1851        for f in self.read_dir(src) {
1852            let path = f.path();
1853            let name = path.file_name().unwrap();
1854            let dst = dst.join(name);
1855            let relative = relative.join(name);
1856            // Only copy file or directory if the filter function returns true
1857            if filter(&relative) {
1858                if t!(f.file_type()).is_dir() {
1859                    let _ = fs::remove_dir_all(&dst);
1860                    self.create_dir(&dst);
1861                    self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
1862                } else {
1863                    let _ = fs::remove_file(&dst);
1864                    self.copy_link(&path, &dst, FileType::Regular);
1865                }
1866            }
1867        }
1868    }
1869
1870    fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
1871        let file_name = src.file_name().unwrap();
1872        let dest = dest_folder.join(file_name);
1873        self.copy_link(src, &dest, FileType::Regular);
1874    }
1875
1876    fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
1877        if self.config.dry_run() {
1878            return;
1879        }
1880        let dst = dstdir.join(src.file_name().unwrap());
1881        self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
1882        t!(fs::create_dir_all(dstdir));
1883        if !src.exists() {
1884            panic!("ERROR: File \"{}\" not found!", src.display());
1885        }
1886
1887        self.copy_link_internal(src, &dst, true);
1888        chmod(&dst, file_type.perms());
1889
1890        // If this file can have debuginfo, look for split debuginfo and install it too.
1891        if file_type.could_have_split_debuginfo()
1892            && let Some(dbg_file) = split_debuginfo(src)
1893        {
1894            self.install(&dbg_file, dstdir, FileType::Regular);
1895        }
1896    }
1897
1898    fn read(&self, path: &Path) -> String {
1899        if self.config.dry_run() {
1900            return String::new();
1901        }
1902        t!(fs::read_to_string(path))
1903    }
1904
1905    fn create_dir(&self, dir: &Path) {
1906        if self.config.dry_run() {
1907            return;
1908        }
1909        t!(fs::create_dir_all(dir))
1910    }
1911
1912    fn remove_dir(&self, dir: &Path) {
1913        if self.config.dry_run() {
1914            return;
1915        }
1916        t!(fs::remove_dir_all(dir))
1917    }
1918
1919    fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1920        let iter = match fs::read_dir(dir) {
1921            Ok(v) => v,
1922            Err(_) if self.config.dry_run() => return vec![].into_iter(),
1923            Err(err) => panic!("could not read dir {dir:?}: {err:?}"),
1924        };
1925        iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1926    }
1927
1928    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1929        #[cfg(unix)]
1930        use std::os::unix::fs::symlink as symlink_file;
1931        #[cfg(windows)]
1932        use std::os::windows::fs::symlink_file;
1933        if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1934    }
1935
1936    /// Returns if config.ninja is enabled, and checks for ninja existence,
1937    /// exiting with a nicer error message if not.
1938    fn ninja(&self) -> bool {
1939        let mut cmd_finder = crate::core::sanity::Finder::new();
1940
1941        if self.config.ninja_in_file {
1942            // Some Linux distros rename `ninja` to `ninja-build`.
1943            // CMake can work with either binary name.
1944            if cmd_finder.maybe_have("ninja-build").is_none()
1945                && cmd_finder.maybe_have("ninja").is_none()
1946            {
1947                eprintln!(
1948                    "
1949Couldn't find required command: ninja (or ninja-build)
1950
1951You should install ninja as described at
1952<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1953or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`.
1954Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1955to download LLVM rather than building it.
1956"
1957                );
1958                exit!(1);
1959            }
1960        }
1961
1962        // If ninja isn't enabled but we're building for MSVC then we try
1963        // doubly hard to enable it. It was realized in #43767 that the msbuild
1964        // CMake generator for MSVC doesn't respect configuration options like
1965        // disabling LLVM assertions, which can often be quite important!
1966        //
1967        // In these cases we automatically enable Ninja if we find it in the
1968        // environment.
1969        if !self.config.ninja_in_file
1970            && self.config.host_target.is_msvc()
1971            && cmd_finder.maybe_have("ninja").is_some()
1972        {
1973            return true;
1974        }
1975
1976        self.config.ninja_in_file
1977    }
1978
1979    pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1980        self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
1981    }
1982
1983    pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1984        self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
1985    }
1986
1987    fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
1988    where
1989        C: Fn(ColorChoice) -> StandardStream,
1990        F: FnOnce(&mut dyn WriteColor) -> R,
1991    {
1992        let choice = match self.config.color {
1993            flags::Color::Always => ColorChoice::Always,
1994            flags::Color::Never => ColorChoice::Never,
1995            flags::Color::Auto if !is_tty => ColorChoice::Never,
1996            flags::Color::Auto => ColorChoice::Auto,
1997        };
1998        let mut stream = constructor(choice);
1999        let result = f(&mut stream);
2000        stream.reset().unwrap();
2001        result
2002    }
2003
2004    pub fn exec_ctx(&self) -> &ExecutionContext {
2005        &self.config.exec_ctx
2006    }
2007
2008    pub fn report_summary(&self, start_time: Instant) {
2009        self.config.exec_ctx.profiler().report_summary(start_time);
2010    }
2011
2012    #[cfg(feature = "tracing")]
2013    pub fn report_step_graph(self) {
2014        self.step_graph.into_inner().store_to_dot_files();
2015    }
2016}
2017
2018impl AsRef<ExecutionContext> for Build {
2019    fn as_ref(&self) -> &ExecutionContext {
2020        &self.config.exec_ctx
2021    }
2022}
2023
2024#[cfg(unix)]
2025fn chmod(path: &Path, perms: u32) {
2026    use std::os::unix::fs::*;
2027    t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
2028}
2029#[cfg(windows)]
2030fn chmod(_path: &Path, _perms: u32) {}
2031
2032impl Compiler {
2033    pub fn new(stage: u32, host: TargetSelection) -> Self {
2034        Self { stage, host, forced_compiler: false }
2035    }
2036
2037    pub fn forced_compiler(&mut self, forced_compiler: bool) {
2038        self.forced_compiler = forced_compiler;
2039    }
2040
2041    pub fn with_stage(mut self, stage: u32) -> Compiler {
2042        self.stage = stage;
2043        self
2044    }
2045
2046    /// Returns `true` if this is a snapshot compiler for `build`'s configuration
2047    pub fn is_snapshot(&self, build: &Build) -> bool {
2048        self.stage == 0 && self.host == build.host_target
2049    }
2050
2051    /// Indicates whether the compiler was forced to use a specific stage.
2052    pub fn is_forced_compiler(&self) -> bool {
2053        self.forced_compiler
2054    }
2055}
2056
2057fn envify(s: &str) -> String {
2058    s.chars()
2059        .map(|c| match c {
2060            '-' => '_',
2061            c => c,
2062        })
2063        .flat_map(|c| c.to_uppercase())
2064        .collect()
2065}
2066
2067/// Ensures that the behavior dump directory is properly initialized.
2068pub fn prepare_behaviour_dump_dir(build: &Build) {
2069    static INITIALIZED: OnceLock<bool> = OnceLock::new();
2070
2071    let dump_path = build.out.join("bootstrap-shims-dump");
2072
2073    let initialized = INITIALIZED.get().unwrap_or(&false);
2074    if !initialized {
2075        // clear old dumps
2076        if dump_path.exists() {
2077            t!(fs::remove_dir_all(&dump_path));
2078        }
2079
2080        t!(fs::create_dir_all(&dump_path));
2081
2082        t!(INITIALIZED.set(true));
2083    }
2084}