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