Skip to main content

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