src/bootstrap/src/lib.rs RUST 2,147 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,147.
1//! Implementation of bootstrap, the Rust build system.2//!3//! This module, and its descendants, are the implementation of the Rust build4//! system. Most of this build system is backed by Cargo but the outer layer5//! here serves as the ability to orchestrate calling Cargo, sequencing Cargo6//! builds, building artifacts like LLVM, etc. The goals of bootstrap are:7//!8//! * To be an easily understandable, easily extensible, and maintainable build9//!   system.10//! * Leverage standard tools in the Rust ecosystem to build the compiler, aka11//!   crates.io and Cargo.12//! * A standard interface to build across all platforms, including MSVC13//!14//! ## Further information15//!16//! More documentation can be found in each respective module below, and you can17//! also check out the `src/bootstrap/README.md` file for more information.18#![cfg_attr(test, allow(unused))]1920use 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};2728use 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;3536use crate::core::builder;37use crate::core::builder::Kind;38use crate::core::config::{BootstrapOverrideLld, DryRun, LlvmLibunwind, TargetSelection, flags};39use crate::utils::exec::{BootstrapCommand, command};40use crate::utils::helpers::{self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo};4142mod core;43mod utils;4445pub 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};5051#[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;5960use crate::core::build_steps::vendor::VENDOR_DIR;6162const LLVM_TOOLS: &[&str] = &[63    "llvm-cov",      // used to generate coverage report64    "llvm-nm",       // used to inspect binaries; it shows symbol names, their sizes and visibility65    "llvm-objcopy",  // used to transform ELFs into binary format which flashing tools consume66    "llvm-objdump",  // used to disassemble programs67    "llvm-profdata", // used to inspect and merge files generated by profiles68    "llvm-readobj",  // used to get information from ELFs/objects that the other tools don't provide69    "llvm-size",     // used to prints the size of the linker sections of a program70    "llvm-strip",    // used to discard symbols from binary files to reduce their size71    "llvm-ar",       // used for creating and modifying archive files72    "llvm-as",       // used to convert LLVM assembly to LLVM bitcode73    "llvm-dis",      // used to disassemble LLVM bitcode74    "llvm-link",     // Used to link LLVM bitcode75    "llc",           // used to compile LLVM bytecode76    "opt",           // used to optimize LLVM bytecode77];7879/// LLD file names for all flavors.80const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];8182/// Extra `--check-cfg` to add when building the compiler or tools83/// (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::ToolRustcPrivate), "bootstrap", None),89    (Some(Mode::ToolStd), "bootstrap", None),90    (Some(Mode::ToolRustcPrivate), "rust_analyzer", None),91    (Some(Mode::ToolStd), "rust_analyzer", None),92    // Any library specific cfgs like `target_os`, `target_arch` should be put in93    // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table94    // in the appropriate `library/{std,alloc,core}/Cargo.toml`95];9697/// A structure representing a Rust compiler.98///99/// Each compiler has a `stage` that it is associated with and a `host` that100/// corresponds to the platform the compiler runs on. This structure is used as101/// a parameter to many methods below.102#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)]103pub struct Compiler {104    stage: u32,105    host: TargetSelection,106    /// Indicates whether the compiler was forced to use a specific stage.107    /// This field is ignored in `Hash` and `PartialEq` implementations as only the `stage`108    /// and `host` fields are relevant for those.109    forced_compiler: bool,110}111112impl std::hash::Hash for Compiler {113    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {114        self.stage.hash(state);115        self.host.hash(state);116    }117}118119impl PartialEq for Compiler {120    fn eq(&self, other: &Self) -> bool {121        self.stage == other.stage && self.host == other.host122    }123}124125/// Represents a codegen backend.126#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]127pub enum CodegenBackendKind {128    #[default]129    Llvm,130    Cranelift,131    Gcc,132    Custom(String),133}134135impl CodegenBackendKind {136    /// Name of the codegen backend, as identified in the `compiler` directory137    /// (`rustc_codegen_<name>`).138    pub fn name(&self) -> &str {139        match self {140            CodegenBackendKind::Llvm => "llvm",141            CodegenBackendKind::Cranelift => "cranelift",142            CodegenBackendKind::Gcc => "gcc",143            CodegenBackendKind::Custom(name) => name,144        }145    }146147    /// Name of the codegen backend's crate, e.g. `rustc_codegen_cranelift`.148    pub fn crate_name(&self) -> String {149        format!("rustc_codegen_{}", self.name())150    }151152    pub fn is_llvm(&self) -> bool {153        matches!(self, Self::Llvm)154    }155156    pub fn is_cranelift(&self) -> bool {157        matches!(self, Self::Cranelift)158    }159160    pub fn is_gcc(&self) -> bool {161        matches!(self, Self::Gcc)162    }163}164165impl std::str::FromStr for CodegenBackendKind {166    type Err = &'static str;167168    fn from_str(s: &str) -> Result<Self, Self::Err> {169        match s.to_lowercase().as_str() {170            "" => Err("Invalid empty backend name"),171            "gcc" => Ok(Self::Gcc),172            "llvm" => Ok(Self::Llvm),173            "cranelift" => Ok(Self::Cranelift),174            _ => Ok(Self::Custom(s.to_string())),175        }176    }177}178179#[derive(PartialEq, Eq, Copy, Clone, Debug)]180pub enum TestTarget {181    /// Run unit, integration and doc tests (default).182    Default,183    /// Run unit, integration, doc tests, examples, bins, benchmarks (no doc tests).184    AllTargets,185    /// Only run doc tests.186    DocOnly,187    /// Only run unit and integration tests.188    Tests,189}190191impl TestTarget {192    fn runs_doctests(&self) -> bool {193        matches!(self, TestTarget::DocOnly | TestTarget::Default)194    }195}196197pub enum GitRepo {198    Rustc,199    Llvm,200}201202/// Global configuration for the build system.203///204/// This structure transitively contains all configuration for the build system.205/// All filesystem-encoded configuration is in `config`, all flags are in206/// `flags`, and then parsed or probed information is listed in the keys below.207///208/// This structure is a parameter of almost all methods in the build system,209/// although most functions are implemented as free functions rather than210/// methods specifically on this structure itself (to make it easier to211/// organize).212pub struct Build {213    /// User-specified configuration from `bootstrap.toml`.214    config: Config,215216    // Version information217    version: String,218219    // Properties derived from the above configuration220    src: PathBuf,221    out: PathBuf,222    bootstrap_out: PathBuf,223    cargo_info: GitInfo,224    rust_analyzer_info: GitInfo,225    clippy_info: GitInfo,226    miri_info: GitInfo,227    rustfmt_info: GitInfo,228    enzyme_info: GitInfo,229    in_tree_llvm_info: GitInfo,230    in_tree_gcc_info: GitInfo,231    local_rebuild: bool,232    fail_fast: bool,233    test_target: TestTarget,234    verbosity: usize,235236    /// Build triple for the pre-compiled snapshot compiler.237    host_target: TargetSelection,238    /// Which triples to produce a compiler toolchain for.239    hosts: Vec<TargetSelection>,240    /// Which triples to build libraries (core/alloc/std/test/proc_macro) for.241    targets: Vec<TargetSelection>,242243    initial_rustc: PathBuf,244    initial_rustdoc: PathBuf,245    initial_cargo: PathBuf,246    initial_lld: PathBuf,247    initial_relative_libdir: PathBuf,248    initial_sysroot: PathBuf,249250    // Runtime state filled in later on251    // C/C++ compilers and archiver for all targets252    cc: HashMap<TargetSelection, cc::Tool>,253    cxx: HashMap<TargetSelection, cc::Tool>,254    ar: HashMap<TargetSelection, PathBuf>,255    ranlib: HashMap<TargetSelection, PathBuf>,256    wasi_sdk_path: Option<PathBuf>,257258    // Miscellaneous259    // allow bidirectional lookups: both name -> path and path -> name260    crates: HashMap<String, Crate>,261    crate_paths: HashMap<PathBuf, String>,262    is_sudo: bool,263    prerelease_version: Cell<Option<u32>>,264265    #[cfg(feature = "build-metrics")]266    metrics: crate::utils::metrics::BuildMetrics,267268    #[cfg(feature = "tracing")]269    step_graph: std::cell::RefCell<crate::utils::step_graph::StepGraph>,270}271272#[derive(Debug, Clone)]273struct Crate {274    name: String,275    deps: HashSet<String>,276    path: PathBuf,277    features: Vec<String>,278}279280impl Crate {281    fn local_path(&self, build: &Build) -> PathBuf {282        self.path.strip_prefix(&build.config.src).unwrap().into()283    }284}285286/// When building Rust various objects are handled differently.287#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]288pub enum DependencyType {289    /// Libraries originating from proc-macros.290    Host,291    /// Typical Rust libraries.292    Target,293    /// Non Rust libraries and objects shipped to ease usage of certain targets.294    TargetSelfContained,295}296297/// The various "modes" of invoking Cargo.298///299/// These entries currently correspond to the various output directories of the300/// build system, with each mod generating output in a different directory.301#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]302pub enum Mode {303    /// Build the standard library, placing output in the "stageN-std" directory.304    Std,305306    /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.307    Rustc,308309    /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory.310    Codegen,311312    /// Build a tool, placing output in the "bootstrap-tools"313    /// directory. This is for miscellaneous sets of tools that extend314    /// bootstrap.315    ///316    /// These tools are intended to be only executed on the host system that317    /// invokes bootstrap, and they thus cannot be cross-compiled.318    ///319    /// They are always built using the stage0 compiler, and they320    /// can be compiled with stable Rust.321    ///322    /// These tools also essentially do not participate in staging.323    ToolBootstrap,324325    /// Build a cross-compilable helper tool. These tools do not depend on unstable features or326    /// compiler internals, but they might be cross-compilable (so we cannot build them using the327    /// stage0 compiler, unlike `ToolBootstrap`).328    ///329    /// Some of these tools are also shipped in our `dist` archives.330    /// While we could compile them using the stage0 compiler when not cross-compiling, we instead331    /// use the in-tree compiler (and std) to build them, so that we can ship e.g. std security332    /// fixes and avoid depending fully on stage0 for the artifacts that we ship.333    ///334    /// This mode is used e.g. for linkers and linker tools invoked by rustc on its host target.335    ToolTarget,336337    /// Build a tool which uses the locally built std, placing output in the338    /// "stageN-tools" directory. Its usage is quite rare; historically it was339    /// needed by compiletest, but now it is mainly used by `test-float-parse`.340    ToolStd,341342    /// Build a tool which uses the `rustc_private` mechanism, and thus343    /// the locally built rustc rlib artifacts,344    /// placing the output in the "stageN-tools" directory. This is used for345    /// everything that links to rustc as a library, such as rustdoc, clippy,346    /// rustfmt, miri, etc.347    ToolRustcPrivate,348}349350impl Mode {351    pub fn must_support_dlopen(&self) -> bool {352        match self {353            Mode::Std | Mode::Codegen => true,354            Mode::ToolBootstrap355            | Mode::ToolRustcPrivate356            | Mode::ToolStd357            | Mode::ToolTarget358            | Mode::Rustc => false,359        }360    }361}362363/// When `rust.rust_remap_debuginfo` is requested, the compiler needs to know how to364/// opportunistically unremap compiler vs non-compiler sources. We use two schemes,365/// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`].366pub enum RemapScheme {367    /// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`.368    Compiler,369    /// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`.370    NonCompiler,371}372373#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]374pub enum CLang {375    C,376    Cxx,377}378379#[derive(Debug, Clone, Copy, PartialEq, Eq)]380pub enum FileType {381    /// An executable binary file (like a `.exe`).382    Executable,383    /// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`).384    NativeLibrary,385    /// An executable (non-binary) script file (like a `.py` or `.sh`).386    Script,387    /// Any other regular file that is non-executable.388    Regular,389}390391impl FileType {392    /// Get Unix permissions appropriate for this file type.393    pub fn perms(self) -> u32 {394        match self {395            FileType::Executable | FileType::Script => 0o755,396            FileType::Regular | FileType::NativeLibrary => 0o644,397        }398    }399400    pub fn could_have_split_debuginfo(self) -> bool {401        match self {402            FileType::Executable | FileType::NativeLibrary => true,403            FileType::Script | FileType::Regular => false,404        }405    }406}407408macro_rules! forward {409    ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {410        impl Build {411            $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {412                self.config.$fn( $($param),* )413            } )+414        }415    }416}417418forward! {419    do_if_verbose(f: impl Fn()),420    is_verbose() -> bool,421    create(path: &Path, s: &str),422    remove(f: &Path),423    tempdir() -> PathBuf,424    llvm_link_shared() -> bool,425    download_rustc() -> bool,426}427428/// An alternative way of specifying what target and stage is involved in some bootstrap activity.429/// Ideally using a `Compiler` directly should be preferred.430struct TargetAndStage {431    target: TargetSelection,432    stage: u32,433}434435impl From<(TargetSelection, u32)> for TargetAndStage {436    fn from((target, stage): (TargetSelection, u32)) -> Self {437        Self { target, stage }438    }439}440441impl From<Compiler> for TargetAndStage {442    fn from(compiler: Compiler) -> Self {443        Self { target: compiler.host, stage: compiler.stage }444    }445}446447impl Build {448    /// Creates a new set of build configuration from the `flags` on the command449    /// line and the filesystem `config`.450    ///451    /// By default all build output will be placed in the current directory.452    pub fn new(mut config: Config) -> Build {453        let src = config.src.clone();454        let out = config.out.clone();455456        #[cfg(unix)]457        // keep this consistent with the equivalent check in x.py:458        // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797459        let is_sudo = match env::var_os("SUDO_USER") {460            Some(_sudo_user) => {461                // SAFETY: getuid() system call is always successful and no return value is reserved462                // to indicate an error.463                //464                // For more context, see https://man7.org/linux/man-pages/man2/geteuid.2.html465                let uid = unsafe { libc::getuid() };466                uid == 0467            }468            None => false,469        };470        #[cfg(not(unix))]471        let is_sudo = false;472473        let rust_info = config.rust_info.clone();474        let cargo_info = config.cargo_info.clone();475        let rust_analyzer_info = config.rust_analyzer_info.clone();476        let clippy_info = config.clippy_info.clone();477        let miri_info = config.miri_info.clone();478        let rustfmt_info = config.rustfmt_info.clone();479        let enzyme_info = config.enzyme_info.clone();480        let in_tree_llvm_info = config.in_tree_llvm_info.clone();481        let in_tree_gcc_info = config.in_tree_gcc_info.clone();482483        let initial_target_libdir = command(&config.initial_rustc)484            .run_in_dry_run()485            .args(["--print", "target-libdir"])486            .run_capture_stdout(&config)487            .stdout()488            .trim()489            .to_owned();490491        let initial_target_dir = Path::new(&initial_target_libdir)492            .parent()493            .unwrap_or_else(|| panic!("{initial_target_libdir} has no parent"));494495        let initial_lld = initial_target_dir.join("bin").join("rust-lld");496497        let initial_relative_libdir = if cfg!(test) {498            // On tests, bootstrap uses the shim rustc, not the one from the stage0 toolchain.499            PathBuf::default()500        } else {501            let ancestor = initial_target_dir.ancestors().nth(2).unwrap_or_else(|| {502                panic!("Not enough ancestors for {}", initial_target_dir.display())503            });504505            ancestor506                .strip_prefix(&config.initial_sysroot)507                .unwrap_or_else(|_| {508                    panic!(509                        "Couldn’t resolve the initial relative libdir from {}",510                        initial_target_dir.display()511                    )512                })513                .to_path_buf()514        };515516        let version = std::fs::read_to_string(src.join("src").join("version"))517            .expect("failed to read src/version");518        let version = version.trim();519520        let mut bootstrap_out = std::env::current_exe()521            .expect("could not determine path to running process")522            .parent()523            .unwrap()524            .to_path_buf();525        // Since bootstrap is hardlink to deps/bootstrap-*, Solaris can sometimes give526        // path with deps/ which is bad and needs to be avoided.527        if bootstrap_out.ends_with("deps") {528            bootstrap_out.pop();529        }530        if !bootstrap_out.join(exe("rustc", config.host_target)).exists() && !cfg!(test) {531            // this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented532            panic!(533                "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",534                bootstrap_out.display()535            )536        }537538        if rust_info.is_from_tarball() && config.description.is_none() {539            config.description = Some("built from a source tarball".to_owned());540        }541542        let mut build = Build {543            initial_lld,544            initial_relative_libdir,545            initial_rustc: config.initial_rustc.clone(),546            initial_rustdoc: config.initial_rustdoc.clone(),547            initial_cargo: config.initial_cargo.clone(),548            initial_sysroot: config.initial_sysroot.clone(),549            local_rebuild: config.local_rebuild,550            fail_fast: config.cmd.fail_fast(),551            test_target: config.cmd.test_target(),552            verbosity: config.exec_ctx.verbosity as usize,553554            host_target: config.host_target,555            hosts: config.hosts.clone(),556            targets: config.targets.clone(),557558            config,559            version: version.to_string(),560            src,561            out,562            bootstrap_out,563564            cargo_info,565            rust_analyzer_info,566            clippy_info,567            miri_info,568            rustfmt_info,569            enzyme_info,570            in_tree_llvm_info,571            in_tree_gcc_info,572            cc: HashMap::new(),573            cxx: HashMap::new(),574            ar: HashMap::new(),575            ranlib: HashMap::new(),576            wasi_sdk_path: env::var_os("WASI_SDK_PATH").map(PathBuf::from),577            crates: HashMap::new(),578            crate_paths: HashMap::new(),579            is_sudo,580            prerelease_version: Cell::new(None),581582            #[cfg(feature = "build-metrics")]583            metrics: crate::utils::metrics::BuildMetrics::init(),584585            #[cfg(feature = "tracing")]586            step_graph: std::cell::RefCell::new(crate::utils::step_graph::StepGraph::default()),587        };588589        // If local-rust is the same major.minor as the current version, then force a590        // local-rebuild591        let local_version_verbose = command(&build.initial_rustc)592            .run_in_dry_run()593            .args(["--version", "--verbose"])594            .run_capture_stdout(&build)595            .stdout();596        let local_release = local_version_verbose597            .lines()598            .filter_map(|x| x.strip_prefix("release:"))599            .next()600            .unwrap()601            .trim();602        if local_release.split('.').take(2).eq(version.split('.').take(2)) {603            build.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}"));604            build.local_rebuild = true;605        }606607        build.do_if_verbose(|| println!("finding compilers"));608        utils::cc_detect::fill_compilers(&mut build);609        // When running `setup`, the profile is about to change, so any requirements we have now may610        // be different on the next invocation. Don't check for them until the next time x.py is611        // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.612        //613        // Similarly, for `setup` we don't actually need submodules or cargo metadata.614        if !matches!(build.config.cmd, Subcommand::Setup { .. }) {615            build.do_if_verbose(|| println!("running sanity check"));616            crate::core::sanity::check(&mut build);617618            // Make sure we update these before gathering metadata so we don't get an error about missing619            // Cargo.toml files.620            let rust_submodules = ["library/backtrace"];621            for s in rust_submodules {622                build.require_submodule(623                    s,624                    Some(625                        "The submodule is required for the standard library \626                         and the main Cargo workspace.",627                    ),628                );629            }630            // Now, update all existing submodules.631            build.update_existing_submodules();632633            build.do_if_verbose(|| println!("learning about cargo"));634            crate::core::metadata::build(&mut build);635        }636637        // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file).638        let build_triple = build.out.join(build.host_target);639        t!(fs::create_dir_all(&build_triple));640        let host = build.out.join("host");641        if host.is_symlink() {642            // Left over from a previous build; overwrite it.643            // This matters if `build.build` has changed between invocations.644            #[cfg(windows)]645            t!(fs::remove_dir(&host));646            #[cfg(not(windows))]647            t!(fs::remove_file(&host));648        }649        t!(650            symlink_dir(&build.config, &build_triple, &host),651            format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())652        );653654        build655    }656657    /// Updates a submodule, and exits with a failure if submodule management658    /// is disabled and the submodule does not exist.659    ///660    /// The given submodule name should be its path relative to the root of661    /// the main repository.662    ///663    /// The given `err_hint` will be shown to the user if the submodule is not664    /// checked out and submodule management is disabled.665    #[cfg_attr(666        feature = "tracing",667        instrument(668            level = "trace",669            name = "Build::require_submodule",670            skip_all,671            fields(submodule = submodule),672        ),673    )]674    pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {675        if self.rust_info().is_from_tarball() {676            return;677        }678679        if self.config.dry_run() {680            return;681        }682683        // When testing bootstrap itself, it is much faster to ignore684        // submodules. Almost all Steps work fine without their submodules.685        if cfg!(test) && !self.config.submodules() {686            return;687        }688        self.config.update_submodule(submodule);689        let absolute_path = self.config.src.join(submodule);690        if !absolute_path.exists() || dir_is_empty(&absolute_path) {691            let maybe_enable = if !self.config.submodules()692                && self.config.rust_info.is_managed_git_subrepository()693            {694                "\nConsider setting `build.submodules = true` or manually initializing the submodules."695            } else {696                ""697            };698            let err_hint = err_hint.map_or_else(String::new, |e| format!("\n{e}"));699            eprintln!(700                "submodule {submodule} does not appear to be checked out, \701                 but it is required for this step{maybe_enable}{err_hint}"702            );703            exit!(1);704        }705    }706707    /// If any submodule has been initialized already, sync it unconditionally.708    /// This avoids contributors checking in a submodule change by accident.709    fn update_existing_submodules(&self) {710        // Avoid running git when there isn't a git checkout, or the user has711        // explicitly disabled submodules in `bootstrap.toml`.712        if !self.config.submodules() {713            return;714        }715        let output = helpers::git(Some(&self.src))716            .args(["config", "--file"])717            .arg(".gitmodules")718            .args(["--get-regexp", "path"])719            .run_capture(self)720            .stdout();721        std::thread::scope(|s| {722            // Look for `submodule.$name.path = $path`723            // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`724            for line in output.lines() {725                let submodule = line.split_once(' ').unwrap().1;726                let config = self.config.clone();727                s.spawn(move || {728                    Self::update_existing_submodule(&config, submodule);729                });730            }731        });732    }733734    /// Updates the given submodule only if it's initialized already; nothing happens otherwise.735    pub fn update_existing_submodule(config: &Config, submodule: &str) {736        // Avoid running git when there isn't a git checkout.737        if !config.submodules() {738            return;739        }740741        if config.git_info(false, Path::new(submodule)).is_managed_git_subrepository() {742            config.update_submodule(submodule);743        }744    }745746    /// Executes the entire build, as configured by the flags and configuration.747    #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))]748    pub fn build(&mut self) {749        trace!("setting up job management");750        unsafe {751            crate::utils::job::setup(self);752        }753754        // Handle hard-coded subcommands.755        {756            #[cfg(feature = "tracing")]757            let _hardcoded_span =758                span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Perf)")759                    .entered();760761            match &self.config.cmd {762                Subcommand::Format { check, all } => {763                    return core::build_steps::format::format(764                        &builder::Builder::new(self),765                        *check,766                        *all,767                        &self.config.paths,768                    );769                }770                Subcommand::Perf(args) => {771                    return core::build_steps::perf::perf(&builder::Builder::new(self), args);772                }773                _cmd => {774                    debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");775                }776            }777778            debug!("handling subcommand normally");779        }780781        if !self.config.dry_run() {782            #[cfg(feature = "tracing")]783            let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered();784785            // We first do a dry-run. This is a sanity-check to ensure that786            // steps don't do anything expensive in the dry-run.787            {788                #[cfg(feature = "tracing")]789                let _sanity_check_span =790                    span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered();791                self.config.set_dry_run(DryRun::SelfCheck);792                let builder = builder::Builder::new(self);793                builder.execute_cli();794            }795796            // Actual run.797            {798                #[cfg(feature = "tracing")]799                let _actual_run_span =800                    span!(tracing::Level::DEBUG, "(2) executing actual run").entered();801                self.config.set_dry_run(DryRun::Disabled);802                let builder = builder::Builder::new(self);803                builder.execute_cli();804            }805        } else {806            #[cfg(feature = "tracing")]807            let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered();808809            let builder = builder::Builder::new(self);810            builder.execute_cli();811        }812813        #[cfg(feature = "tracing")]814        debug!("checking for postponed test failures from `test  --no-fail-fast`");815816        // Check for postponed failures from `test --no-fail-fast`.817        self.config.exec_ctx().report_failures_and_exit();818819        #[cfg(feature = "build-metrics")]820        self.metrics.persist(self);821    }822823    fn rust_info(&self) -> &GitInfo {824        &self.config.rust_info825    }826827    /// Gets the space-separated set of activated features for the standard library.828    /// This can be configured with the `std-features` key in bootstrap.toml.829    fn std_features(&self, target: TargetSelection) -> String {830        let mut features: BTreeSet<&str> =831            self.config.rust_std_features.iter().map(|s| s.as_str()).collect();832833        match self.config.llvm_libunwind(target) {834            LlvmLibunwind::InTree => features.insert("llvm-libunwind"),835            LlvmLibunwind::System => features.insert("system-llvm-libunwind"),836            LlvmLibunwind::No => false,837        };838839        if self.config.backtrace {840            features.insert("backtrace");841        }842843        if self.config.profiler_enabled(target) {844            features.insert("profiler");845        }846847        // If zkvm target, generate memcpy, etc.848        if target.contains("zkvm") {849            features.insert("compiler-builtins-mem");850        }851852        if self.config.llvm_enzyme {853            features.insert("llvm_enzyme");854        }855856        features.into_iter().collect::<Vec<_>>().join(" ")857    }858859    /// Gets the space-separated set of activated features for the compiler.860    fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {861        let possible_features_by_crates: HashSet<_> = crates862            .iter()863            .flat_map(|krate| &self.crates[krate].features)864            .map(std::ops::Deref::deref)865            .collect();866        let check = |feature: &str| -> bool {867            crates.is_empty() || possible_features_by_crates.contains(feature)868        };869        let mut features = vec![];870        if self.config.jemalloc(target) && check("jemalloc") {871            features.push("jemalloc");872        }873        if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {874            features.push("llvm");875        }876        if self.config.llvm_enzyme {877            features.push("llvm_enzyme");878        }879        if self.config.llvm_offload {880            features.push("llvm_offload");881        }882        // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`883        if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {884            features.push("rustc_randomized_layouts");885        }886        if self.config.compile_time_deps && kind == Kind::Check {887            features.push("check_only");888        }889890        if crates.iter().any(|c| c == "rustc_transmute") {891            // for `x test rustc_transmute`, this feature isn't enabled automatically by a892            // dependent crate.893            features.push("rustc");894        }895896        // If debug logging is on, then we want the default for tracing:897        // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26898        // which is everything (including debug/trace/etc.)899        // if its unset, if debug_assertions is on, then debug_logging will also be on900        // as well as tracing *ignoring* this feature when debug_assertions is on901        if !self.config.rust_debug_logging && check("max_level_info") {902            features.push("max_level_info");903        }904905        features.join(" ")906    }907908    /// Component directory that Cargo will produce output into (e.g.909    /// release/debug)910    fn cargo_dir(&self, mode: Mode) -> &'static str {911        match (mode, self.config.rust_optimize.is_release()) {912            (Mode::Std, _) => "dist",913            (_, true) => "release",914            (_, false) => "debug",915        }916    }917918    fn tools_dir(&self, build_compiler: Compiler) -> PathBuf {919        let out = self920            .out921            .join(build_compiler.host)922            .join(format!("stage{}-tools-bin", build_compiler.stage + 1));923        t!(fs::create_dir_all(&out));924        out925    }926927    /// Returns the root directory for all output generated in a particular928    /// stage when being built with a particular build compiler.929    ///930    /// The mode indicates what the root directory is for.931    fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf {932        use std::fmt::Write;933934        fn bootstrap_tool() -> (Option<u32>, &'static str) {935            (None, "bootstrap-tools")936        }937        fn staged_tool(build_compiler: Compiler) -> (Option<u32>, &'static str) {938            (Some(build_compiler.stage + 1), "tools")939        }940941        let (stage, suffix) = match mode {942            // Std is special, stage N std is built with stage N rustc943            Mode::Std => (Some(build_compiler.stage), "std"),944            // The rest of things are built with stage N-1 rustc945            Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"),946            Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"),947            Mode::ToolBootstrap => bootstrap_tool(),948            Mode::ToolStd | Mode::ToolRustcPrivate => (Some(build_compiler.stage + 1), "tools"),949            Mode::ToolTarget => {950                // If we're not cross-compiling (the common case), share the target directory with951                // bootstrap tools to reuse the build cache.952                if build_compiler.stage == 0 {953                    bootstrap_tool()954                } else {955                    staged_tool(build_compiler)956                }957            }958        };959        let path = self.out.join(build_compiler.host);960        let mut dir_name = String::new();961        if let Some(stage) = stage {962            write!(dir_name, "stage{stage}-").unwrap();963        }964        dir_name.push_str(suffix);965        path.join(dir_name)966    }967968    /// Returns the root output directory for all Cargo output in a given stage,969    /// running a particular compiler, whether or not we're building the970    /// standard library, and targeting the specified architecture.971    fn cargo_out(&self, build_compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {972        self.stage_out(build_compiler, mode).join(target).join(self.cargo_dir(mode))973    }974975    /// Root output directory of LLVM for `target`976    ///977    /// Note that if LLVM is configured externally then the directory returned978    /// will likely be empty.979    fn llvm_out(&self, target: TargetSelection) -> PathBuf {980        if self.config.llvm_from_ci && self.config.is_host_target(target) {981            self.config.ci_llvm_root()982        } else {983            self.out.join(target).join("llvm")984        }985    }986987    fn enzyme_out(&self, target: TargetSelection) -> PathBuf {988        self.out.join(&*target.triple).join("enzyme")989    }990991    fn offload_out(&self, target: TargetSelection) -> PathBuf {992        self.out.join(&*target.triple).join("offload")993    }994995    fn lld_out(&self, target: TargetSelection) -> PathBuf {996        self.out.join(target).join("lld")997    }998999    /// Output directory for all documentation for a target1000    fn doc_out(&self, target: TargetSelection) -> PathBuf {1001        self.out.join(target).join("doc")1002    }10031004    /// Output directory for all JSON-formatted documentation for a target1005    fn json_doc_out(&self, target: TargetSelection) -> PathBuf {1006        self.out.join(target).join("json-doc")1007    }10081009    fn test_out(&self, target: TargetSelection) -> PathBuf {1010        self.out.join(target).join("test")1011    }10121013    /// Output directory for all documentation for a target1014    fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {1015        self.out.join(target).join("compiler-doc")1016    }10171018    /// Output directory for some generated md crate documentation for a target (temporary)1019    fn md_doc_out(&self, target: TargetSelection) -> PathBuf {1020        self.out.join(target).join("md-doc")1021    }10221023    /// Path to the vendored Rust crates.1024    fn vendored_crates_path(&self) -> Option<PathBuf> {1025        if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }1026    }10271028    /// Returns the path to `FileCheck` binary for the specified target1029    fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {1030        let target_config = self.config.target_config.get(&target);1031        if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {1032            s.to_path_buf()1033        } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {1034            let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(self).stdout();1035            let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));1036            if filecheck.exists() {1037                filecheck1038            } else {1039                // On Fedora the system LLVM installs FileCheck in the1040                // llvm subdirectory of the libdir.1041                let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(self).stdout();1042                let lib_filecheck =1043                    Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));1044                if lib_filecheck.exists() {1045                    lib_filecheck1046                } else {1047                    // Return the most normal file name, even though1048                    // it doesn't exist, so that any error message1049                    // refers to that.1050                    filecheck1051                }1052            }1053        } else {1054            let base = self.llvm_out(target).join("build");1055            let base = if !self.ninja() && target.is_msvc() {1056                if self.config.llvm_optimize {1057                    if self.config.llvm_release_debuginfo {1058                        base.join("RelWithDebInfo")1059                    } else {1060                        base.join("Release")1061                    }1062                } else {1063                    base.join("Debug")1064                }1065            } else {1066                base1067            };1068            base.join("bin").join(exe("FileCheck", target))1069        }1070    }10711072    /// Directory for libraries built from C/C++ code and shared between stages.1073    fn native_dir(&self, target: TargetSelection) -> PathBuf {1074        self.out.join(target).join("native")1075    }10761077    /// Root output directory for rust_test_helpers library compiled for1078    /// `target`1079    fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {1080        self.native_dir(target).join("rust-test-helpers")1081    }10821083    /// Adds the `RUST_TEST_THREADS` env var if necessary1084    fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {1085        if env::var_os("RUST_TEST_THREADS").is_none() {1086            cmd.env("RUST_TEST_THREADS", self.jobs().to_string());1087        }1088    }10891090    /// Returns the libdir of the snapshot compiler.1091    fn rustc_snapshot_libdir(&self) -> PathBuf {1092        self.rustc_snapshot_sysroot().join(libdir(self.config.host_target))1093    }10941095    /// Returns the sysroot of the snapshot compiler.1096    fn rustc_snapshot_sysroot(&self) -> &Path {1097        static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();1098        SYSROOT_CACHE.get_or_init(|| {1099            command(&self.initial_rustc)1100                .run_in_dry_run()1101                .args(["--print", "sysroot"])1102                .run_capture_stdout(self)1103                .stdout()1104                .trim()1105                .to_owned()1106                .into()1107        })1108    }11091110    fn info(&self, msg: &str) {1111        match self.config.get_dry_run() {1112            DryRun::SelfCheck => (),1113            DryRun::Disabled | DryRun::UserSelected => {1114                println!("{msg}");1115            }1116        }1117    }11181119    /// Return a `Group` guard for a [`Step`] that:1120    /// - Performs `action`1121    ///   - If the action is `Kind::Test`, use [`Build::msg_test`] instead.1122    /// - On `what`1123    ///   - Where `what` possibly corresponds to a `mode`1124    /// - `action` is performed with/on the given compiler (`target_and_stage`).1125    ///   - Since for some steps it is not possible to pass a single compiler here, it is also1126    ///     possible to pass the host and stage explicitly.1127    /// - With a given `target`.1128    ///1129    /// [`Step`]: crate::core::builder::Step1130    #[must_use = "Groups should not be dropped until the Step finishes running"]1131    #[track_caller]1132    fn msg(1133        &self,1134        action: impl Into<Kind>,1135        what: impl Display,1136        mode: impl Into<Option<Mode>>,1137        target_and_stage: impl Into<TargetAndStage>,1138        target: impl Into<Option<TargetSelection>>,1139    ) -> Option<gha::Group> {1140        let target_and_stage = target_and_stage.into();1141        let action = action.into();1142        assert!(1143            action != Kind::Test,1144            "Please use `Build::msg_test` instead of `Build::msg(Kind::Test)`"1145        );11461147        let actual_stage = match mode.into() {1148            // Std has the same stage as the compiler that builds it1149            Some(Mode::Std) => target_and_stage.stage,1150            // Other things have stage corresponding to their build compiler + 11151            Some(1152                Mode::Rustc1153                | Mode::Codegen1154                | Mode::ToolBootstrap1155                | Mode::ToolTarget1156                | Mode::ToolStd1157                | Mode::ToolRustcPrivate,1158            )1159            | None => target_and_stage.stage + 1,1160        };11611162        let action = action.description();1163        let what = what.to_string();1164        let msg = |fmt| {1165            let space = if !what.is_empty() { " " } else { "" };1166            format!("{action} stage{actual_stage} {what}{space}{fmt}")1167        };1168        let msg = if let Some(target) = target.into() {1169            let build_stage = target_and_stage.stage;1170            let host = target_and_stage.target;1171            if host == target {1172                msg(format_args!("(stage{build_stage} -> stage{actual_stage}, {target})"))1173            } else {1174                msg(format_args!("(stage{build_stage}:{host} -> stage{actual_stage}:{target})"))1175            }1176        } else {1177            msg(format_args!(""))1178        };1179        self.group(&msg)1180    }11811182    /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`.1183    /// Use this instead of [`Build::msg`] for test steps, because for them it is not always clear1184    /// what exactly is a build compiler.1185    ///1186    /// [`Step`]: crate::core::builder::Step1187    #[must_use = "Groups should not be dropped until the Step finishes running"]1188    #[track_caller]1189    fn msg_test(1190        &self,1191        what: impl Display,1192        target: TargetSelection,1193        stage: u32,1194    ) -> Option<gha::Group> {1195        let action = Kind::Test.description();1196        let msg = format!("{action} stage{stage} {what} ({target})");1197        self.group(&msg)1198    }11991200    /// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.1201    ///1202    /// [`Step`]: crate::core::builder::Step1203    #[must_use = "Groups should not be dropped until the Step finishes running"]1204    #[track_caller]1205    fn msg_unstaged(1206        &self,1207        action: impl Into<Kind>,1208        what: impl Display,1209        target: TargetSelection,1210    ) -> Option<gha::Group> {1211        let action = action.into().description();1212        let msg = format!("{action} {what} for {target}");1213        self.group(&msg)1214    }12151216    #[track_caller]1217    fn group(&self, msg: &str) -> Option<gha::Group> {1218        match self.config.get_dry_run() {1219            DryRun::SelfCheck => None,1220            DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),1221        }1222    }12231224    /// Returns the number of parallel jobs that have been configured for this1225    /// build.1226    fn jobs(&self) -> u32 {1227        self.config.jobs.unwrap_or_else(|| {1228            std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u321229        })1230    }12311232    fn debuginfo_map_to(&self, which: GitRepo, remap_scheme: RemapScheme) -> Option<String> {1233        if !self.config.rust_remap_debuginfo {1234            return None;1235        }12361237        match which {1238            GitRepo::Rustc => {1239                let sha = self.rust_sha().unwrap_or(&self.version);12401241                match remap_scheme {1242                    RemapScheme::Compiler => {1243                        // For compiler sources, remap via `/rustc-dev/{sha}` to allow1244                        // distinguishing between compiler sources vs library sources, since1245                        // `rustc-dev` dist component places them under1246                        // `$sysroot/lib/rustlib/rustc-src/rust` as opposed to `rust-src`'s1247                        // `$sysroot/lib/rustlib/src/rust`.1248                        //1249                        // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s1250                        // `try_to_translate_virtual_to_real`.1251                        Some(format!("/rustc-dev/{sha}"))1252                    }1253                    RemapScheme::NonCompiler => {1254                        // For non-compiler sources, use `/rustc/{sha}` remapping scheme.1255                        Some(format!("/rustc/{sha}"))1256                    }1257                }1258            }1259            GitRepo::Llvm => Some(String::from("/rustc/llvm")),1260        }1261    }12621263    /// Returns the path to the C compiler for the target specified.1264    fn cc(&self, target: TargetSelection) -> PathBuf {1265        if self.config.dry_run() {1266            return PathBuf::new();1267        }1268        self.cc[&target].path().into()1269    }12701271    /// Returns the internal `cc::Tool` for the C compiler.1272    fn cc_tool(&self, target: TargetSelection) -> Tool {1273        self.cc[&target].clone()1274    }12751276    /// Returns the internal `cc::Tool` for the C++ compiler.1277    fn cxx_tool(&self, target: TargetSelection) -> Tool {1278        self.cxx[&target].clone()1279    }12801281    /// Returns C flags that `cc-rs` thinks should be enabled for the1282    /// specified target by default.1283    fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {1284        if self.config.dry_run() {1285            return Vec::new();1286        }1287        let base = match c {1288            CLang::C => self.cc[&target].clone(),1289            CLang::Cxx => self.cxx[&target].clone(),1290        };12911292        // Filter out -O and /O (the optimization flags) that we picked up1293        // from cc-rs, that's up to the caller to figure out.1294        base.args()1295            .iter()1296            .map(|s| s.to_string_lossy().into_owned())1297            .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))1298            .collect::<Vec<String>>()1299    }13001301    /// Returns extra C flags that `cc-rs` doesn't handle.1302    fn cc_unhandled_cflags(1303        &self,1304        target: TargetSelection,1305        which: GitRepo,1306        c: CLang,1307    ) -> Vec<String> {1308        let mut base = Vec::new();13091310        // If we're compiling C++ on macOS then we add a flag indicating that1311        // we want libc++ (more filled out than libstdc++), ensuring that1312        // LLVM/etc are all properly compiled.1313        if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {1314            base.push("-stdlib=libc++".into());1315        }13161317        // Work around an apparently bad MinGW / GCC optimization,1318        // See: https://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html1319        // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=789361320        if &*target.triple == "i686-pc-windows-gnu" {1321            base.push("-fno-omit-frame-pointer".into());1322        }13231324        if let Some(map_to) = self.debuginfo_map_to(which, RemapScheme::NonCompiler) {1325            let map = format!("{}={}", self.src.display(), map_to);1326            let cc = self.cc(target);1327            if cc.ends_with("clang") || cc.ends_with("gcc") {1328                base.push(format!("-fdebug-prefix-map={map}"));1329            } else if cc.ends_with("clang-cl.exe") {1330                base.push("-Xclang".into());1331                base.push(format!("-fdebug-prefix-map={map}"));1332            }1333        }1334        base1335    }13361337    /// Returns the path to the `ar` archive utility for the target specified.1338    fn ar(&self, target: TargetSelection) -> Option<PathBuf> {1339        if self.config.dry_run() {1340            return None;1341        }1342        self.ar.get(&target).cloned()1343    }13441345    /// Returns the path to the `ranlib` utility for the target specified.1346    fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {1347        if self.config.dry_run() {1348            return None;1349        }1350        self.ranlib.get(&target).cloned()1351    }13521353    /// Returns the path to the C++ compiler for the target specified.1354    fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {1355        if self.config.dry_run() {1356            return Ok(PathBuf::new());1357        }1358        match self.cxx.get(&target) {1359            Some(p) => Ok(p.path().into()),1360            None => Err(format!("target `{target}` is not configured as a host, only as a target")),1361        }1362    }13631364    /// Returns the path to the linker for the given target if it needs to be overridden.1365    fn linker(&self, target: TargetSelection) -> Option<PathBuf> {1366        if self.config.dry_run() {1367            return Some(PathBuf::new());1368        }1369        if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())1370        {1371            Some(linker)1372        } else if target.contains("vxworks") {1373            // need to use CXX compiler as linker to resolve the exception functions1374            // that are only existed in CXX libraries1375            Some(self.cxx[&target].path().into())1376        } else if !self.config.is_host_target(target)1377            && helpers::use_host_linker(target)1378            && !target.is_msvc()1379        {1380            Some(self.cc(target))1381        } else if self.config.bootstrap_override_lld.is_used()1382            && self.is_lld_direct_linker(target)1383            && self.host_target == target1384        {1385            match self.config.bootstrap_override_lld {1386                BootstrapOverrideLld::SelfContained => Some(self.initial_lld.clone()),1387                BootstrapOverrideLld::External => Some("lld".into()),1388                BootstrapOverrideLld::None => None,1389            }1390        } else {1391            None1392        }1393    }13941395    // Is LLD configured directly through `-Clinker`?1396    // Only MSVC targets use LLD directly at the moment.1397    fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {1398        target.is_msvc()1399    }14001401    /// Returns if this target should statically link the C runtime, if specified1402    fn crt_static(&self, target: TargetSelection) -> Option<bool> {1403        if target.contains("pc-windows-msvc") {1404            Some(true)1405        } else {1406            self.config.target_config.get(&target).and_then(|t| t.crt_static)1407        }1408    }14091410    /// Returns the "musl root" for this `target`, if defined.1411    ///1412    /// If this is a native target (host is also musl) and no musl-root is given,1413    /// it falls back to the system toolchain in /usr.1414    fn musl_root(&self, target: TargetSelection) -> Option<&Path> {1415        let configured_root = self1416            .config1417            .target_config1418            .get(&target)1419            .and_then(|t| t.musl_root.as_ref())1420            .or(self.config.musl_root.as_ref())1421            .map(|p| &**p);14221423        if self.config.is_host_target(target) && configured_root.is_none() {1424            Some(Path::new("/usr"))1425        } else {1426            configured_root1427        }1428    }14291430    /// Returns the "musl libdir" for this `target`.1431    fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {1432        self.config1433            .target_config1434            .get(&target)1435            .and_then(|t| t.musl_libdir.clone())1436            .or_else(|| self.musl_root(target).map(|root| root.join("lib")))1437    }14381439    /// Returns the `lib` directory for the WASI target specified, if1440    /// configured.1441    ///1442    /// This first consults `wasi-root` as configured in per-target1443    /// configuration, and failing that it assumes that `$WASI_SDK_PATH` is1444    /// set in the environment, and failing that `None` is returned.1445    fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {1446        let configured =1447            self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);1448        if let Some(path) = configured {1449            return Some(path.join("lib").join(target.to_string()));1450        }1451        let mut env_root = self.wasi_sdk_path.clone()?;1452        env_root.push("share");1453        env_root.push("wasi-sysroot");1454        env_root.push("lib");1455        env_root.push(target.to_string());1456        Some(env_root)1457    }14581459    /// Returns `true` if this is a no-std `target`, if defined1460    fn no_std(&self, target: TargetSelection) -> Option<bool> {1461        self.config.target_config.get(&target).map(|t| t.no_std)1462    }14631464    /// Returns `true` if the target will be tested using the `remote-test-client`1465    /// and `remote-test-server` binaries.1466    fn remote_tested(&self, target: TargetSelection) -> bool {1467        self.qemu_rootfs(target).is_some()1468            || target.contains("android")1469            || env::var_os("TEST_DEVICE_ADDR").is_some()1470    }14711472    /// Returns an optional "runner" to pass to `compiletest` when executing1473    /// test binaries.1474    ///1475    /// An example of this would be a WebAssembly runtime when testing the wasm1476    /// targets.1477    fn runner(&self, target: TargetSelection) -> Option<String> {1478        let configured_runner =1479            self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);1480        if let Some(runner) = configured_runner {1481            return Some(runner.to_owned());1482        }14831484        if target.starts_with("wasm") && target.contains("wasi") {1485            self.default_wasi_runner(target)1486        } else {1487            None1488        }1489    }14901491    /// When a `runner` configuration is not provided and a WASI-looking target1492    /// is being tested this is consulted to prove the environment to see if1493    /// there's a runtime already lying around that seems reasonable to use.1494    fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {1495        let mut finder = crate::core::sanity::Finder::new();14961497        // Look for Wasmtime, and for its default options be sure to disable1498        // its caching system since we're executing quite a lot of tests and1499        // ideally shouldn't pollute the cache too much.1500        if let Some(path) = finder.maybe_have("wasmtime")1501            && let Ok(mut path) = path.into_os_string().into_string()1502        {1503            path.push_str(" run -C cache=n --dir .");1504            // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is1505            // required for libtest to work on beta/stable channels.1506            //1507            // NB: with Wasmtime 20 this can change to `-S inherit-env` to1508            // inherit the entire environment rather than just this single1509            // environment variable.1510            path.push_str(" --env RUSTC_BOOTSTRAP");15111512            if target.contains("wasip2") {1513                path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");1514            }15151516            return Some(path);1517        }15181519        None1520    }15211522    /// Returns whether the specified tool is configured as part of this build.1523    ///1524    /// This requires that both the `extended` key is set and the `tools` key is1525    /// either unset or specifically contains the specified tool.1526    fn tool_enabled(&self, tool: &str) -> bool {1527        if !self.config.extended {1528            return false;1529        }1530        match &self.config.tools {1531            Some(set) => set.contains(tool),1532            None => true,1533        }1534    }15351536    /// Returns the root of the "rootfs" image that this target will be using,1537    /// if one was configured.1538    ///1539    /// If `Some` is returned then that means that tests for this target are1540    /// emulated with QEMU and binaries will need to be shipped to the emulator.1541    fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {1542        self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)1543    }15441545    /// Temporary directory that extended error information is emitted to.1546    fn extended_error_dir(&self) -> PathBuf {1547        self.out.join("tmp/extended-error-metadata")1548    }15491550    /// Tests whether the `compiler` compiling for `target` should be forced to1551    /// use a stage1 compiler instead.1552    ///1553    /// Currently, by default, the build system does not perform a "full1554    /// bootstrap" by default where we compile the compiler three times.1555    /// Instead, we compile the compiler two times. The final stage (stage2)1556    /// just copies the libraries from the previous stage, which is what this1557    /// method detects.1558    ///1559    /// Here we return `true` if:1560    ///1561    /// * The build isn't performing a full bootstrap1562    /// * The `compiler` is in the final stage, 21563    /// * We're not cross-compiling, so the artifacts are already available in1564    ///   stage11565    ///1566    /// When all of these conditions are met the build will lift artifacts from1567    /// the previous stage forward.1568    fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {1569        !self.config.full_bootstrap1570            && !self.config.download_rustc()1571            && stage >= 21572            && (self.hosts.contains(&target) || target == self.host_target)1573    }15741575    /// Checks whether the `compiler` compiling for `target` should be forced to1576    /// use a stage2 compiler instead.1577    ///1578    /// When we download the pre-compiled version of rustc and compiler stage is >= 2,1579    /// it should be forced to use a stage2 compiler.1580    fn force_use_stage2(&self, stage: u32) -> bool {1581        self.config.download_rustc() && stage >= 21582    }15831584    /// Given `num` in the form "a.b.c" return a "release string" which1585    /// describes the release version number.1586    ///1587    /// For example on nightly this returns "a.b.c-nightly", on beta it returns1588    /// "a.b.c-beta.1" and on stable it just returns "a.b.c".1589    fn release(&self, num: &str) -> String {1590        match &self.config.channel[..] {1591            "stable" => num.to_string(),1592            "beta" => {1593                if !self.config.omit_git_hash {1594                    format!("{}-beta.{}", num, self.beta_prerelease_version())1595                } else {1596                    format!("{num}-beta")1597                }1598            }1599            "nightly" => format!("{num}-nightly"),1600            _ => format!("{num}-dev"),1601        }1602    }16031604    fn beta_prerelease_version(&self) -> u32 {1605        fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {1606            let version = fs::read_to_string(version_file).ok()?;16071608            helpers::extract_beta_rev(&version)1609        }16101611        if let Some(s) = self.prerelease_version.get() {1612            return s;1613        }16141615        // First check if there is a version file available.1616        // If available, we read the beta revision from that file.1617        // This only happens when building from a source tarball when Git should not be used.1618        let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {1619            // Figure out how many merge commits happened since we branched off main.1620            // That's our beta number!1621            // (Note that we use a `..` range, not the `...` symmetric difference.)1622            helpers::git(Some(&self.src))1623                .arg("rev-list")1624                .arg("--count")1625                .arg("--merges")1626                .arg(format!(1627                    "refs/remotes/origin/{}..HEAD",1628                    self.config.stage0_metadata.config.nightly_branch1629                ))1630                .run_in_dry_run()1631                .run_capture(self)1632                .stdout()1633        });1634        let n = count.trim().parse().unwrap();1635        self.prerelease_version.set(Some(n));1636        n1637    }16381639    /// Returns the value of `release` above for Rust itself.1640    fn rust_release(&self) -> String {1641        self.release(&self.version)1642    }16431644    /// Returns the "package version" for a component.1645    ///1646    /// The package version is typically what shows up in the names of tarballs.1647    /// For channels like beta/nightly it's just the channel name, otherwise it's the release1648    /// version.1649    fn rust_package_vers(&self) -> String {1650        match &self.config.channel[..] {1651            "stable" => self.version.to_string(),1652            "beta" => "beta".to_string(),1653            "nightly" => "nightly".to_string(),1654            _ => format!("{}-dev", self.version),1655        }1656    }16571658    /// Returns the `version` string associated with this compiler for Rust1659    /// itself.1660    ///1661    /// Note that this is a descriptive string which includes the commit date,1662    /// sha, version, etc.1663    fn rust_version(&self) -> String {1664        let mut version = self.rust_info().version(self, &self.version);1665        if let Some(ref s) = self.config.description1666            && !s.is_empty()1667        {1668            version.push_str(" (");1669            version.push_str(s);1670            version.push(')');1671        }1672        version1673    }16741675    /// Returns the full commit hash.1676    fn rust_sha(&self) -> Option<&str> {1677        self.rust_info().sha()1678    }16791680    /// Returns the `a.b.c` version that the given package is at.1681    fn release_num(&self, package: &str) -> String {1682        let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));1683        let toml = t!(fs::read_to_string(toml_file_name));1684        for line in toml.lines() {1685            if let Some(stripped) =1686                line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))1687            {1688                return stripped.to_owned();1689            }1690        }16911692        panic!("failed to find version in {package}'s Cargo.toml")1693    }16941695    /// Returns `true` if unstable features should be enabled for the compiler1696    /// we're building.1697    fn unstable_features(&self) -> bool {1698        !matches!(&self.config.channel[..], "stable" | "beta")1699    }17001701    /// Returns a Vec of all the dependencies of the given root crate,1702    /// including transitive dependencies and the root itself. Only includes1703    /// "local" crates (those in the local source tree, not from a registry).1704    fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {1705        let mut ret = Vec::new();1706        let mut list = vec![root.to_owned()];1707        let mut visited = HashSet::new();1708        while let Some(krate) = list.pop() {1709            let krate = self1710                .crates1711                .get(&krate)1712                .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));1713            ret.push(krate);1714            for dep in &krate.deps {1715                if !self.crates.contains_key(dep) {1716                    // Ignore non-workspace members.1717                    continue;1718                }1719                // Don't include optional deps if their features are not1720                // enabled. Ideally this would be computed from `cargo1721                // metadata --features …`, but that is somewhat slow. In1722                // the future, we may want to consider just filtering all1723                // build and dev dependencies in metadata::build.1724                if visited.insert(dep)1725                    && (dep != "profiler_builtins"1726                        || target1727                            .map(|t| self.config.profiler_enabled(t))1728                            .unwrap_or_else(|| self.config.any_profiler_enabled()))1729                    && (dep != "rustc_codegen_llvm"1730                        || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))1731                {1732                    list.push(dep.clone());1733                }1734            }1735        }1736        ret.sort_unstable_by_key(|krate| krate.name.clone()); // reproducible order needed for tests1737        ret1738    }17391740    fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {1741        if self.config.dry_run() {1742            return Vec::new();1743        }17441745        if !stamp.path().exists() {1746            eprintln!(1747                "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",1748                stamp.path().display()1749            );1750            crate::exit!(1);1751        }17521753        let mut paths = Vec::new();1754        let contents = t!(fs::read(stamp.path()), stamp.path());1755        // This is the method we use for extracting paths from the stamp file passed to us. See1756        // run_cargo for more information (in compile.rs).1757        for part in contents.split(|b| *b == 0) {1758            if part.is_empty() {1759                continue;1760            }1761            let dependency_type = match part[0] as char {1762                'h' => DependencyType::Host,1763                's' => DependencyType::TargetSelfContained,1764                't' => DependencyType::Target,1765                _ => unreachable!(),1766            };1767            let path = PathBuf::from(t!(str::from_utf8(&part[1..])));1768            paths.push((path, dependency_type));1769        }1770        paths1771    }17721773    /// Copies a file from `src` to `dst`.1774    ///1775    /// If `src` is a symlink, `src` will be resolved to the actual path1776    /// and copied to `dst` instead of the symlink itself.1777    #[track_caller]1778    pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {1779        self.copy_link_internal(src, dst, true);1780    }17811782    /// Links a file from `src` to `dst`.1783    /// Attempts to use hard links if possible, falling back to copying.1784    /// You can neither rely on this being a copy nor it being a link,1785    /// so do not write to dst.1786    #[track_caller]1787    pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {1788        self.copy_link_internal(src, dst, false);17891790        if file_type.could_have_split_debuginfo()1791            && let Some(dbg_file) = split_debuginfo(src)1792        {1793            self.copy_link_internal(1794                &dbg_file,1795                &dst.with_extension(dbg_file.extension().unwrap()),1796                false,1797            );1798        }1799    }18001801    #[track_caller]1802    fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {1803        if self.config.dry_run() {1804            return;1805        }1806        if src == dst {1807            return;1808        }18091810        #[cfg(feature = "tracing")]1811        let _span = trace_io!("file-copy-link", ?src, ?dst);18121813        if let Err(e) = fs::remove_file(dst)1814            && cfg!(windows)1815            && e.kind() != io::ErrorKind::NotFound1816        {1817            // workaround for https://github.com/rust-lang/rust/issues/1271261818            // if removing the file fails, attempt to rename it instead.1819            let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));1820            let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));1821        }1822        let mut metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));1823        let mut src = src.to_path_buf();1824        if metadata.file_type().is_symlink() {1825            if dereference_symlinks {1826                src = t!(fs::canonicalize(src));1827                metadata = t!(fs::metadata(&src), format!("target = {}", src.display()));1828            } else {1829                let link = t!(fs::read_link(src));1830                t!(self.symlink_file(link, dst));1831                return;1832            }1833        }1834        if let Ok(()) = fs::hard_link(&src, dst) {1835            // Attempt to "easy copy" by creating a hard link (symlinks are privileged on windows),1836            // but if that fails just fall back to a slow `copy` operation.1837        } else {1838            if let Err(e) = fs::copy(&src, dst) {1839                panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)1840            }1841            t!(fs::set_permissions(dst, metadata.permissions()));18421843            // Restore file times because changing permissions on e.g. Linux using `chmod` can cause1844            // file access time to change.1845            let file_times = fs::FileTimes::new()1846                .set_accessed(t!(metadata.accessed()))1847                .set_modified(t!(metadata.modified()));1848            t!(set_file_times(dst, file_times));1849        }1850    }18511852    /// Links the `src` directory recursively to `dst`. Both are assumed to exist1853    /// when this function is called.1854    /// Will attempt to use hard links if possible and fall back to copying.1855    #[track_caller]1856    pub fn cp_link_r(&self, src: &Path, dst: &Path) {1857        if self.config.dry_run() {1858            return;1859        }1860        for f in self.read_dir(src) {1861            let path = f.path();1862            let name = path.file_name().unwrap();1863            let dst = dst.join(name);1864            if t!(f.file_type()).is_dir() {1865                t!(fs::create_dir_all(&dst));1866                self.cp_link_r(&path, &dst);1867            } else {1868                self.copy_link(&path, &dst, FileType::Regular);1869            }1870        }1871    }18721873    /// Copies the `src` directory recursively to `dst`. Both are assumed to exist1874    /// when this function is called.1875    /// Will attempt to use hard links if possible and fall back to copying.1876    /// Unwanted files or directories can be skipped1877    /// by returning `false` from the filter function.1878    #[track_caller]1879    pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {1880        // Immediately recurse with an empty relative path1881        self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)1882    }18831884    // Inner function does the actual work1885    #[track_caller]1886    fn cp_link_filtered_recurse(1887        &self,1888        src: &Path,1889        dst: &Path,1890        relative: &Path,1891        filter: &dyn Fn(&Path) -> bool,1892    ) {1893        for f in self.read_dir(src) {1894            let path = f.path();1895            let name = path.file_name().unwrap();1896            let dst = dst.join(name);1897            let relative = relative.join(name);1898            // Only copy file or directory if the filter function returns true1899            if filter(&relative) {1900                if t!(f.file_type()).is_dir() {1901                    let _ = fs::remove_dir_all(&dst);1902                    self.create_dir(&dst);1903                    self.cp_link_filtered_recurse(&path, &dst, &relative, filter);1904                } else {1905                    self.copy_link(&path, &dst, FileType::Regular);1906                }1907            }1908        }1909    }19101911    fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {1912        let file_name = src.file_name().unwrap();1913        let dest = dest_folder.join(file_name);1914        self.copy_link(src, &dest, FileType::Regular);1915    }19161917    fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {1918        if self.config.dry_run() {1919            return;1920        }1921        let dst = dstdir.join(src.file_name().unwrap());19221923        #[cfg(feature = "tracing")]1924        let _span = trace_io!("install", ?src, ?dst);19251926        t!(fs::create_dir_all(dstdir));1927        if !src.exists() {1928            panic!("ERROR: File \"{}\" not found!", src.display());1929        }19301931        self.copy_link_internal(src, &dst, true);1932        chmod(&dst, file_type.perms());19331934        // If this file can have debuginfo, look for split debuginfo and install it too.1935        if file_type.could_have_split_debuginfo()1936            && let Some(dbg_file) = split_debuginfo(src)1937        {1938            self.install(&dbg_file, dstdir, FileType::Regular);1939        }1940    }19411942    fn read(&self, path: &Path) -> String {1943        if self.config.dry_run() {1944            return String::new();1945        }1946        t!(fs::read_to_string(path))1947    }19481949    #[track_caller]1950    fn create_dir(&self, dir: &Path) {1951        if self.config.dry_run() {1952            return;1953        }19541955        #[cfg(feature = "tracing")]1956        let _span = trace_io!("dir-create", ?dir);19571958        t!(fs::create_dir_all(dir))1959    }19601961    fn remove_dir(&self, dir: &Path) {1962        if self.config.dry_run() {1963            return;1964        }19651966        #[cfg(feature = "tracing")]1967        let _span = trace_io!("dir-remove", ?dir);19681969        t!(fs::remove_dir_all(dir))1970    }19711972    /// Make sure that `dir` will be an empty existing directory after this function ends.1973    /// If it existed before, it will be first deleted.1974    fn clear_dir(&self, dir: &Path) {1975        if self.config.dry_run() {1976            return;1977        }19781979        #[cfg(feature = "tracing")]1980        let _span = trace_io!("dir-clear", ?dir);19811982        let _ = std::fs::remove_dir_all(dir);1983        self.create_dir(dir);1984    }19851986    fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {1987        let iter = match fs::read_dir(dir) {1988            Ok(v) => v,1989            Err(_) if self.config.dry_run() => return vec![].into_iter(),1990            Err(err) => panic!("could not read dir {dir:?}: {err:?}"),1991        };1992        iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()1993    }19941995    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {1996        #[cfg(unix)]1997        use std::os::unix::fs::symlink as symlink_file;1998        #[cfg(windows)]1999        use std::os::windows::fs::symlink_file;2000        if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }

Code quality findings 58

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let uid = unsafe { libc::getuid() };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.path.strip_prefix(&build.config.src).unwrap().into()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`.
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("failed to read src/version");
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("could not determine path to running process")
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap()
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap()
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let submodule = line.split_once(' ').unwrap().1;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
.flat_map(|krate| &self.crates[krate].features)
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
write!(dir_name, "stage{stage}-").unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// Return a `Group` guard for a [`Step`] that:
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// - If the action is `Kind::Test`, use [`Build::msg_test`] instead.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// Use this instead of [`Build::msg`] for test steps, because for them it is not always clear
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.cc[&target].path().into()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.cc[&target].clone()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.cxx[&target].clone()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
CLang::C => self.cc[&target].clone(),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
CLang::Cxx => self.cxx[&target].clone(),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
Some(self.cxx[&target].path().into())
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
match &self.config.channel[..] {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let n = count.trim().parse().unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
match &self.config.channel[..] {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
!matches!(&self.config.channel[..], "stable" | "beta")
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let dependency_type = match part[0] as char {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
&dst.with_extension(dbg_file.extension().unwrap()),
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let name = path.file_name().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let name = path.file_name().unwrap();
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = fs::remove_dir_all(&dst);
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let file_name = src.file_name().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let dst = dstdir.join(src.file_name().unwrap());
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = std::fs::remove_dir_all(dir);
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
stream.reset().unwrap();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match s.to_lowercase().as_str() {
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let version = std::fs::read_to_string(src.join("src").join("version"))
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
build.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}"));
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
build.do_if_verbose(|| println!("finding compilers"));
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
build.do_if_verbose(|| println!("running sanity check"));
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
build.do_if_verbose(|| println!("learning about cargo"));
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let config = self.config.clone();
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
println!("{msg}");
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.config.channel[..] {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.config.channel[..] {
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let toml = t!(fs::read_to_string(toml_file_name));
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ret.push(krate);
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let contents = t!(fs::read(stamp.path()), stamp.path());
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let dependency_type = match part[0] as char {
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
t!(fs::read_to_string(path))
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use std::os::unix::fs::*;

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.