#
cargo/core/compiler/build_runner/
compilation_files.rs

    1//! See [`CompilationFiles`].
    2
    3use std::cell::OnceCell;
    4use std::collections::HashMap;
    5use std::fmt;
    6use std::hash::{Hash, Hasher};
    7use std::path::{Path, PathBuf};
    8use std::sync::Arc;
    9
    10use tracing::debug;
    11
    12use super::{BuildContext, BuildRunner, CompileKind, FileFlavor, Layout};
    13use crate::core::compiler::{CompileMode, CompileTarget, CrateType, FileType, Unit};
    14use crate::core::{Target, TargetKind, Workspace};
    15use crate::util::{self, CargoResult, OnceExt, StableHasher};
    16
    17/// This is a generic version number that can be changed to make
    18/// backwards-incompatible changes to any file structures in the output
    19/// directory. For example, the fingerprint files or the build-script
    20/// output files.
    21///
    22/// Normally cargo updates ship with rustc updates which will
    23/// cause a new hash due to the rustc version changing, but this allows
    24/// cargo to be extra careful to deal with different versions of cargo that
    25/// use the same rustc version.
    26const METADATA_VERSION: u8 = 2;
    27
    28/// Uniquely identify a [`Unit`] under specific circumstances, see [`Metadata`] for more.
    29#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
    30pub struct UnitHash(u64);
    31
    32impl fmt::Display for UnitHash {
    33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    34        write!(f, "{:016x}", self.0)
    35    }
    36}
    37
    38impl fmt::Debug for UnitHash {
    39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    40        write!(f, "UnitHash({:016x})", self.0)
    41    }
    42}
    43
    44/// [`Metadata`] tracks several [`UnitHash`]s, including
    45/// [`Metadata::unit_id`], [`Metadata::c_metadata`], and [`Metadata::c_extra_filename`].
    46///
    47/// We use a hash because it is an easy way to guarantee
    48/// that all the inputs can be converted to a valid path.
    49///
    50/// [`Metadata::unit_id`] is used to uniquely identify a unit in the build graph.
    51/// This serves as a similar role as [`Metadata::c_extra_filename`] in that it uniquely identifies output
    52/// on the filesystem except that its always present.
    53///
    54/// [`Metadata::c_extra_filename`] is needed for cases like:
    55/// - A project may depend on crate `A` and crate `B`, so the package name must be in the file name.
    56/// - Similarly a project may depend on two versions of `A`, so the version must be in the file name.
    57///
    58/// This also acts as the main layer of caching provided by Cargo
    59/// so this must include all things that need to be distinguished in different parts of
    60/// the same build. This is absolutely required or we override things before
    61/// we get chance to use them.
    62///
    63/// For example, we want to cache `cargo build` and `cargo doc` separately, so that running one
    64/// does not invalidate the artifacts for the other. We do this by including [`CompileMode`] in the
    65/// hash, thus the artifacts go in different folders and do not override each other.
    66/// If we don't add something that we should have, for this reason, we get the
    67/// correct output but rebuild more than is needed.
    68///
    69/// Some things that need to be tracked to ensure the correct output should definitely *not*
    70/// go in the `Metadata`. For example, the modification time of a file, should be tracked to make a
    71/// rebuild when the file changes. However, it would be wasteful to include in the `Metadata`. The
    72/// old artifacts are never going to be needed again. We can save space by just overwriting them.
    73/// If we add something that we should not have, for this reason, we get the correct output but take
    74/// more space than needed. This makes not including something in `Metadata`
    75/// a form of cache invalidation.
    76///
    77/// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a
    78/// rebuild is needed.
    79///
    80/// [`Metadata::c_metadata`] is used for symbol mangling, because if you have two versions of
    81/// the same crate linked together, their symbols need to be differentiated.
    82///
    83/// You should avoid anything that would interfere with reproducible
    84/// builds. For example, *any* absolute path should be avoided. This is one
    85/// reason that `RUSTFLAGS` is not in [`Metadata::c_metadata`], because it often has
    86/// absolute paths (like `--remap-path-prefix` which is fundamentally used for
    87/// reproducible builds and has absolute paths in it). Also, in some cases the
    88/// mangled symbols need to be stable between different builds with different
    89/// settings. For example, profile-guided optimizations need to swap
    90/// `RUSTFLAGS` between runs, but needs to keep the same symbol names.
    91#[derive(Copy, Clone, Debug)]
    92pub struct Metadata {
    93    unit_id: UnitHash,
    94    c_metadata: UnitHash,
    95    c_extra_filename: bool,
    96    pkg_dir: bool,
    97}
    98
    99impl Metadata {
    100    /// A hash to identify a given [`Unit`] in the build graph
    101    pub fn unit_id(&self) -> UnitHash {
    102        self.unit_id
    103    }
    104
    105    /// A hash to add to symbol naming through `-C metadata`
    106    pub fn c_metadata(&self) -> UnitHash {
    107        self.c_metadata
    108    }
    109
    110    /// A hash to add to file names through `-C extra-filename`
    111    pub fn c_extra_filename(&self) -> Option<UnitHash> {
    112        self.c_extra_filename.then_some(self.unit_id)
    113    }
    114
    115    /// A hash to add to Cargo directory names
    116    pub fn pkg_dir(&self) -> Option<UnitHash> {
    117        self.pkg_dir.then_some(self.unit_id)
    118    }
    119}
    120
    121/// Collection of information about the files emitted by the compiler, and the
    122/// output directory structure.
    123pub struct CompilationFiles<'a, 'gctx> {
    124    /// The target directory layout for the host (and target if it is the same as host).
    125    pub(super) host: Layout,
    126    /// The target directory layout for the target (if different from then host).
    127    pub(super) target: HashMap<CompileTarget, Layout>,
    128    /// Additional directory to include a copy of the outputs.
    129    export_dir: Option<PathBuf>,
    130    /// The root targets requested by the user on the command line (does not
    131    /// include dependencies).
    132    roots: Vec<Unit>,
    133    ws: &'a Workspace<'gctx>,
    134    /// Metadata hash to use for each unit.
    135    metas: HashMap<Unit, Metadata>,
    136    /// For each Unit, a list all files produced.
    137    outputs: HashMap<Unit, OnceCell<Arc<Vec<OutputFile>>>>,
    138}
    139
    140/// Info about a single file emitted by the compiler.
    141#[derive(Debug)]
    142pub struct OutputFile {
    143    /// Absolute path to the file that will be produced by the build process.
    144    pub path: PathBuf,
    145    /// If it should be linked into `target`, and what it should be called
    146    /// (e.g., without metadata).
    147    pub hardlink: Option<PathBuf>,
    148    /// If `--artifact-dir` is specified, the absolute path to the exported file.
    149    pub export_path: Option<PathBuf>,
    150    /// Type of the file (library / debug symbol / else).
    151    pub flavor: FileFlavor,
    152}
    153
    154impl OutputFile {
    155    /// Gets the hard link if present; otherwise, returns the path.
    156    pub fn bin_dst(&self) -> &PathBuf {
    157        match self.hardlink {
    158            Some(ref link_dst) => link_dst,
    159            None => &self.path,
    160        }
    161    }
    162}
    163
    164impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
    165    pub(super) fn new(
    166        build_runner: &BuildRunner<'a, 'gctx>,
    167        host: Layout,
    168        target: HashMap<CompileTarget, Layout>,
    169    ) -> CompilationFiles<'a, 'gctx> {
    170        let mut metas = HashMap::new();
    171        for unit in &build_runner.bcx.roots {
    172            metadata_of(unit, build_runner, &mut metas);
    173        }
    174        let outputs = metas
    175            .keys()
    176            .cloned()
    177            .map(|unit| (unit, OnceCell::new()))
    178            .collect();
    179        CompilationFiles {
    180            ws: build_runner.bcx.ws,
    181            host,
    182            target,
    183            export_dir: build_runner.bcx.build_config.export_dir.clone(),
    184            roots: build_runner.bcx.roots.clone(),
    185            metas,
    186            outputs,
    187        }
    188    }
    189
    190    /// Returns the appropriate directory layout for either a plugin or not.
    191    pub fn layout(&self, kind: CompileKind) -> &Layout {
    192        match kind {
    193            CompileKind::Host => &self.host,
    194            CompileKind::Target(target) => &self.target[&target],
    195        }
    196    }
    197
    198    /// Gets the metadata for the given unit.
    199    ///
    200    /// See [`Metadata`] and [`fingerprint`] module for more.
    201    ///
    202    /// [`fingerprint`]: super::super::fingerprint#fingerprints-and-metadata
    203    pub fn metadata(&self, unit: &Unit) -> Metadata {
    204        self.metas[unit]
    205    }
    206
    207    /// Gets the short hash based only on the `PackageId`.
    208    /// Used for the metadata when `c_extra_filename` returns `None`.
    209    fn target_short_hash(&self, unit: &Unit) -> String {
    210        let hashable = unit.pkg.package_id().stable_hash(self.ws.root());
    211        util::short_hash(&(METADATA_VERSION, hashable))
    212    }
    213
    214    /// Returns the directory where the artifacts for the given unit are
    215    /// initially created.
    216    pub fn out_dir(&self, unit: &Unit) -> PathBuf {
    217        // Docscrape units need to have doc/ set as the out_dir so sources for reverse-dependencies
    218        // will be put into doc/ and not into deps/ where the *.examples files are stored.
    219        if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
    220            self.layout(unit.kind)
    221                .artifact_dir()
    222                .expect("artifact-dir was not locked")
    223                .doc()
    224                .to_path_buf()
    225        } else if unit.mode.is_doc_test() {
    226            panic!("doc tests do not have an out dir");
    227        } else if unit.target.is_custom_build() {
    228            self.build_script_dir(unit)
    229        } else if unit.target.is_example() && !self.ws.gctx().cli_unstable().build_dir_new_layout {
    230            self.layout(unit.kind).build_dir().examples().to_path_buf()
    231        } else if unit.artifact.is_true() {
    232            self.artifact_dir(unit)
    233        } else {
    234            self.deps_dir(unit).to_path_buf()
    235        }
    236    }
    237
    238    /// Additional export directory from `--artifact-dir`.
    239    pub fn export_dir(&self) -> Option<PathBuf> {
    240        self.export_dir.clone()
    241    }
    242
    243    /// Directory name to use for a package in the form `{NAME}/{HASH}`.
    244    ///
    245    /// Note that some units may share the same directory, so care should be
    246    /// taken in those cases!
    247    fn pkg_dir(&self, unit: &Unit) -> String {
    248        let separator = match self.ws.gctx().cli_unstable().build_dir_new_layout {
    249            true => "/",
    250            false => "-",
    251        };
    252        let name = unit.pkg.package_id().name();
    253        let hash = self.unit_hash(unit);
    254        format!("{name}{separator}{hash}")
    255    }
    256
    257    /// The directory hash to use for a given unit
    258    pub fn unit_hash(&self, unit: &Unit) -> String {
    259        self.metas[unit]
    260            .pkg_dir()
    261            .map(|h| h.to_string())
    262            .unwrap_or_else(|| self.target_short_hash(unit))
    263    }
    264
    265    /// Returns the final artifact path for the host (`/…/target/debug`)
    266    pub fn host_dest(&self) -> Option<&Path> {
    267        self.host.artifact_dir().map(|v| v.dest())
    268    }
    269
    270    /// Returns the root of the build output tree for the host (`/…/build-dir`)
    271    pub fn host_build_root(&self) -> &Path {
    272        self.host.build_dir().root()
    273    }
    274
    275    /// Returns the host `deps` directory path.
    276    pub fn host_deps(&self, unit: &Unit) -> PathBuf {
    277        let dir = self.pkg_dir(unit);
    278        self.host.build_dir().deps(&dir)
    279    }
    280
    281    /// Returns the directories where Rust crate dependencies are found for the
    282    /// specified unit.
    283    pub fn deps_dir(&self, unit: &Unit) -> PathBuf {
    284        let dir = self.pkg_dir(unit);
    285        self.layout(unit.kind).build_dir().deps(&dir)
    286    }
    287
    288    /// Returns the directories where Rust crate dependencies are found for the
    289    /// specified unit. (new layout)
    290    ///
    291    /// New features should consider using this so we can avoid their migrations.
    292    pub fn deps_dir_new_layout(&self, unit: &Unit) -> PathBuf {
    293        let dir = self.pkg_dir(unit);
    294        self.layout(unit.kind).build_dir().deps_new_layout(&dir)
    295    }
    296
    297    /// Directory where the fingerprint for the given unit should go.
    298    pub fn fingerprint_dir(&self, unit: &Unit) -> PathBuf {
    299        let dir = self.pkg_dir(unit);
    300        self.layout(unit.kind).build_dir().fingerprint(&dir)
    301    }
    302
    303    /// The lock location for a given build unit.
    304    pub fn build_unit_lock(&self, unit: &Unit) -> PathBuf {
    305        let dir = self.pkg_dir(unit);
    306        self.layout(unit.kind)
    307            .build_dir()
    308            .build_unit(&dir)
    309            .join(".lock")
    310    }
    311
    312    /// Directory where incremental output for the given unit should go.
    313    pub fn incremental_dir(&self, unit: &Unit) -> &Path {
    314        self.layout(unit.kind).build_dir().incremental()
    315    }
    316
    317    /// Directory where timing output should go.
    318    pub fn timings_dir(&self) -> Option<&Path> {
    319        self.host.artifact_dir().map(|v| v.timings())
    320    }
    321
    322    /// Returns the path for a file in the fingerprint directory.
    323    ///
    324    /// The "prefix" should be something to distinguish the file from other
    325    /// files in the fingerprint directory.
    326    pub fn fingerprint_file_path(&self, unit: &Unit, prefix: &str) -> PathBuf {
    327        // Different targets need to be distinguished in the
    328        let kind = unit.target.kind().description();
    329        let flavor = if unit.mode.is_any_test() {
    330            "test-"
    331        } else if unit.mode.is_doc() {
    332            "doc-"
    333        } else if unit.mode.is_run_custom_build() {
    334            "run-"
    335        } else {
    336            ""
    337        };
    338        let name = format!("{}{}{}-{}", prefix, flavor, kind, unit.target.name());
    339        self.fingerprint_dir(unit).join(name)
    340    }
    341
    342    /// Path where compiler output is cached.
    343    pub fn message_cache_path(&self, unit: &Unit) -> PathBuf {
    344        self.fingerprint_file_path(unit, "output-")
    345    }
    346
    347    /// Returns the directory where a compiled build script is stored.
    348    /// `/path/to/target/{debug,release}/build/PKG-HASH`
    349    pub fn build_script_dir(&self, unit: &Unit) -> PathBuf {
    350        assert!(unit.target.is_custom_build());
    351        assert!(!unit.mode.is_run_custom_build());
    352        assert!(self.metas.contains_key(unit));
    353        let dir = self.pkg_dir(unit);
    354        self.layout(CompileKind::Host)
    355            .build_dir()
    356            .build_script(&dir)
    357    }
    358
    359    /// Returns the directory for compiled artifacts files.
    360    /// `/path/to/target/{debug,release}/deps/artifact/KIND/PKG-HASH`
    361    fn artifact_dir(&self, unit: &Unit) -> PathBuf {
    362        assert!(self.metas.contains_key(unit));
    363        assert!(unit.artifact.is_true());
    364        let dir = self.pkg_dir(unit);
    365        let kind = match unit.target.kind() {
    366            TargetKind::Bin => "bin",
    367            TargetKind::Lib(lib_kinds) => match lib_kinds.as_slice() {
    368                &[CrateType::Cdylib] => "cdylib",
    369                &[CrateType::Staticlib] => "staticlib",
    370                invalid => unreachable!(
    371                    "BUG: unexpected artifact library type(s): {:?} - these should have been split",
    372                    invalid
    373                ),
    374            },
    375            invalid => unreachable!(
    376                "BUG: {:?} are not supposed to be used as artifacts",
    377                invalid
    378            ),
    379        };
    380        self.layout(unit.kind)
    381            .build_dir()
    382            .artifact()
    383            .join(dir)
    384            .join(kind)
    385    }
    386
    387    /// Returns the directory where information about running a build script
    388    /// is stored.
    389    /// `/path/to/target/{debug,release}/build/PKG-HASH`
    390    pub fn build_script_run_dir(&self, unit: &Unit) -> PathBuf {
    391        assert!(unit.target.is_custom_build());
    392        assert!(unit.mode.is_run_custom_build());
    393        let dir = self.pkg_dir(unit);
    394        self.layout(unit.kind)
    395            .build_dir()
    396            .build_script_execution(&dir)
    397    }
    398
    399    /// Returns the "`OUT_DIR`" directory for running a build script.
    400    /// `/path/to/target/{debug,release}/build/PKG-HASH/out`
    401    pub fn build_script_out_dir(&self, unit: &Unit) -> PathBuf {
    402        self.build_script_run_dir(unit).join("out")
    403    }
    404
    405    /// Returns the path to the executable binary for the given bin target.
    406    ///
    407    /// This should only to be used when a `Unit` is not available.
    408    pub fn bin_link_for_target(
    409        &self,
    410        target: &Target,
    411        kind: CompileKind,
    412        bcx: &BuildContext<'_, '_>,
    413    ) -> CargoResult<Option<PathBuf>> {
    414        assert!(target.is_bin());
    415        let Some(dest) = self.layout(kind).artifact_dir().map(|v| v.dest()) else {
    416            return Ok(None);
    417        };
    418        let info = bcx.target_data.info(kind);
    419        let (file_types, _) = info
    420            .rustc_outputs(
    421                CompileMode::Build,
    422                &TargetKind::Bin,
    423                bcx.target_data.short_name(&kind),
    424                bcx.gctx,
    425            )
    426            .expect("target must support `bin`");
    427
    428        let file_type = file_types
    429            .iter()
    430            .find(|file_type| file_type.flavor == FileFlavor::Normal)
    431            .expect("target must support `bin`");
    432
    433        Ok(Some(dest.join(file_type.uplift_filename(target))))
    434    }
    435
    436    /// Returns the filenames that the given unit will generate.
    437    ///
    438    /// Note: It is not guaranteed that all of the files will be generated.
    439    pub(super) fn outputs(
    440        &self,
    441        unit: &Unit,
    442        bcx: &BuildContext<'a, 'gctx>,
    443    ) -> CargoResult<Arc<Vec<OutputFile>>> {
    444        self.outputs[unit]
    445            .try_borrow_with(|| self.calc_outputs(unit, bcx))
    446            .map(Arc::clone)
    447    }
    448
    449    /// Returns the path where the output for the given unit and `FileType`
    450    /// should be uplifted to.
    451    ///
    452    /// Returns `None` if the unit shouldn't be uplifted (for example, a
    453    /// dependent rlib).
    454    fn uplift_to(&self, unit: &Unit, file_type: &FileType, from_path: &Path) -> Option<PathBuf> {
    455        // Tests, check, doc, etc. should not be uplifted.
    456        if unit.mode != CompileMode::Build || file_type.flavor == FileFlavor::Rmeta {
    457            return None;
    458        }
    459
    460        // Artifact dependencies are never uplifted.
    461        if unit.artifact.is_true() {
    462            return None;
    463        }
    464
    465        // - Binaries: The user always wants to see these, even if they are
    466        //   implicitly built (for example for integration tests).
    467        // - dylibs: This ensures that the dynamic linker pulls in all the
    468        //   latest copies (even if the dylib was built from a previous cargo
    469        //   build). There are complex reasons for this, see #8139, #6167, #6162.
    470        // - Things directly requested from the command-line (the "roots").
    471        //   This one is a little questionable for rlibs (see #6131), but is
    472        //   historically how Cargo has operated. This is primarily useful to
    473        //   give the user access to staticlibs and cdylibs.
    474        if !unit.target.is_bin()
    475            && !unit.target.is_custom_build()
    476            && file_type.crate_type != Some(CrateType::Dylib)
    477            && !self.roots.contains(unit)
    478        {
    479            return None;
    480        }
    481
    482        let filename = file_type.uplift_filename(&unit.target);
    483        let uplift_path = if unit.target.is_example() {
    484            // Examples live in their own little world.
    485            self.layout(unit.kind)
    486                .artifact_dir()?
    487                .examples()
    488                .join(filename)
    489        } else if unit.target.is_custom_build() {
    490            self.build_script_dir(unit).join(filename)
    491        } else {
    492            self.layout(unit.kind).artifact_dir()?.dest().join(filename)
    493        };
    494        if from_path == uplift_path {
    495            // This can happen with things like examples that reside in the
    496            // same directory, do not have a metadata hash (like on Windows),
    497            // and do not have hyphens.
    498            return None;
    499        }
    500        Some(uplift_path)
    501    }
    502
    503    /// Calculates the filenames that the given unit will generate.
    504    /// Should use [`CompilationFiles::outputs`] instead
    505    /// as it caches the result of this function.
    506    fn calc_outputs(
    507        &self,
    508        unit: &Unit,
    509        bcx: &BuildContext<'a, 'gctx>,
    510    ) -> CargoResult<Arc<Vec<OutputFile>>> {
    511        let ret = match unit.mode {
    512            _ if unit.skip_non_compile_time_dep => {
    513                // This skips compilations so no outputs
    514                vec![]
    515            }
    516            CompileMode::Doc => {
    517                let path = if bcx.build_config.intent.wants_doc_json_output() {
    518                    self.out_dir(unit)
    519                        .join(format!("{}.json", unit.target.crate_name()))
    520                } else {
    521                    self.out_dir(unit)
    522                        .join(unit.target.crate_name())
    523                        .join("index.html")
    524                };
    525
    526                let mut outputs = vec![OutputFile {
    527                    path,
    528                    hardlink: None,
    529                    export_path: None,
    530                    flavor: FileFlavor::Normal,
    531                }];
    532
    533                if bcx.gctx.cli_unstable().rustdoc_mergeable_info {
    534                    // `-Zrustdoc-mergeable-info` always uses the new layout.
    535                    outputs.push(OutputFile {
    536                        path: self
    537                            .deps_dir_new_layout(unit)
    538                            .join(unit.target.crate_name())
    539                            .with_extension("json"),
    540                        hardlink: None,
    541                        export_path: None,
    542                        flavor: FileFlavor::DocParts,
    543                    })
    544                }
    545
    546                outputs
    547            }
    548            CompileMode::RunCustomBuild => {
    549                // At this time, this code path does not handle build script
    550                // outputs.
    551                vec![]
    552            }
    553            CompileMode::Doctest => {
    554                // Doctests are built in a temporary directory and then
    555                // deleted. There is the `--persist-doctests` unstable flag,
    556                // but Cargo does not know about that.
    557                vec![]
    558            }
    559            CompileMode::Docscrape => {
    560                // The file name needs to be stable across Cargo sessions.
    561                // This originally used unit.buildkey(), but that isn't stable,
    562                // so we use metadata instead (prefixed with name for debugging).
    563                let file_name = format!(
    564                    "{}-{}.examples",
    565                    unit.pkg.name(),
    566                    self.metadata(unit).unit_id()
    567                );
    568                let path = self.deps_dir(unit).join(file_name);
    569                vec![OutputFile {
    570                    path,
    571                    hardlink: None,
    572                    export_path: None,
    573                    flavor: FileFlavor::Normal,
    574                }]
    575            }
    576            CompileMode::Test | CompileMode::Build | CompileMode::Check { .. } => {
    577                let mut outputs = self.calc_outputs_rustc(unit, bcx)?;
    578                if bcx.build_config.sbom && bcx.gctx.cli_unstable().sbom {
    579                    let sbom_files: Vec<_> = outputs
    580                        .iter()
    581                        .filter(|o| matches!(o.flavor, FileFlavor::Normal | FileFlavor::Linkable))
    582                        .map(|output| OutputFile {
    583                            path: Self::append_sbom_suffix(&output.path),
    584                            hardlink: output.hardlink.as_ref().map(Self::append_sbom_suffix),
    585                            export_path: output.export_path.as_ref().map(Self::append_sbom_suffix),
    586                            flavor: FileFlavor::Sbom,
    587                        })
    588                        .collect();
    589                    outputs.extend(sbom_files.into_iter());
    590                }
    591                outputs
    592            }
    593        };
    594        debug!("Target filenames: {:?}", ret);
    595
    596        Ok(Arc::new(ret))
    597    }
    598
    599    /// Append the SBOM suffix to the file name.
    600    fn append_sbom_suffix(link: &PathBuf) -> PathBuf {
    601        const SBOM_FILE_EXTENSION: &str = ".cargo-sbom.json";
    602        let mut link_buf = link.clone().into_os_string();
    603        link_buf.push(SBOM_FILE_EXTENSION);
    604        PathBuf::from(link_buf)
    605    }
    606
    607    /// Computes the actual, full pathnames for all the files generated by rustc.
    608    ///
    609    /// The `OutputFile` also contains the paths where those files should be
    610    /// "uplifted" to.
    611    fn calc_outputs_rustc(
    612        &self,
    613        unit: &Unit,
    614        bcx: &BuildContext<'a, 'gctx>,
    615    ) -> CargoResult<Vec<OutputFile>> {
    616        let out_dir = self.out_dir(unit);
    617
    618        let info = bcx.target_data.info(unit.kind);
    619        let triple = bcx.target_data.short_name(&unit.kind);
    620        let (file_types, unsupported) =
    621            info.rustc_outputs(unit.mode, unit.target.kind(), triple, bcx.gctx)?;
    622        if file_types.is_empty() {
    623            if !unsupported.is_empty() {
    624                let unsupported_strs: Vec<_> = unsupported.iter().map(|ct| ct.as_str()).collect();
    625                anyhow::bail!(
    626                    "cannot produce {} for `{}` as the target `{}` \
    627                     does not support these crate types",
    628                    unsupported_strs.join(", "),
    629                    unit.pkg,
    630                    triple,
    631                )
    632            }
    633            anyhow::bail!(
    634                "cannot compile `{}` as the target `{}` does not \
    635                 support any of the output crate types",
    636                unit.pkg,
    637                triple,
    638            );
    639        }
    640
    641        // Convert FileType to OutputFile.
    642        let mut outputs = Vec::new();
    643        for file_type in file_types {
    644            let meta = self.metas[unit];
    645            let meta_opt = meta.c_extra_filename().map(|h| h.to_string());
    646            let path = out_dir.join(file_type.output_filename(&unit.target, meta_opt.as_deref()));
    647
    648            // If, the `different_binary_name` feature is enabled, the name of the hardlink will
    649            // be the name of the binary provided by the user in `Cargo.toml`.
    650            let hardlink = self.uplift_to(unit, &file_type, &path);
    651            let export_path = if unit.target.is_custom_build() {
    652                None
    653            } else {
    654                self.export_dir.as_ref().and_then(|export_dir| {
    655                    hardlink
    656                        .as_ref()
    657                        .map(|hardlink| export_dir.join(hardlink.file_name().unwrap()))
    658                })
    659            };
    660            outputs.push(OutputFile {
    661                path,
    662                hardlink,
    663                export_path,
    664                flavor: file_type.flavor,
    665            });
    666        }
    667        Ok(outputs)
    668    }
    669}
    670
    671/// Gets the metadata hash for the given [`Unit`].
    672///
    673/// When a metadata hash doesn't exist for the given unit,
    674/// this calls itself recursively to compute metadata hashes of all its dependencies.
    675/// See [`compute_metadata`] for how a single metadata hash is computed.
    676fn metadata_of<'a>(
    677    unit: &Unit,
    678    build_runner: &BuildRunner<'_, '_>,
    679    metas: &'a mut HashMap<Unit, Metadata>,
    680) -> &'a Metadata {
    681    if !metas.contains_key(unit) {
    682        let meta = compute_metadata(unit, build_runner, metas);
    683        metas.insert(unit.clone(), meta);
    684        for dep in build_runner.unit_deps(unit) {
    685            metadata_of(&dep.unit, build_runner, metas);
    686        }
    687    }
    688    &metas[unit]
    689}
    690
    691/// Computes the metadata hash for the given [`Unit`].
    692fn compute_metadata(
    693    unit: &Unit,
    694    build_runner: &BuildRunner<'_, '_>,
    695    metas: &mut HashMap<Unit, Metadata>,
    696) -> Metadata {
    697    let bcx = &build_runner.bcx;
    698    let deps_metadata = build_runner
    699        .unit_deps(unit)
    700        .iter()
    701        .map(|dep| *metadata_of(&dep.unit, build_runner, metas))
    702        .collect::<Vec<_>>();
    703    let c_extra_filename = use_extra_filename(bcx, unit);
    704    let pkg_dir = use_pkg_dir(bcx, unit);
    705
    706    let mut shared_hasher = StableHasher::new();
    707
    708    METADATA_VERSION.hash(&mut shared_hasher);
    709
    710    let ws_root = if unit.is_std {
    711        // SourceId for stdlib crates is an absolute path inside the sysroot.
    712        // Pass the sysroot as workspace root so that we hash a relative path.
    713        // This avoids the metadata hash changing depending on where the user installed rustc.
    714        &bcx.target_data.get_info(unit.kind).unwrap().sysroot
    715    } else {
    716        bcx.ws.root()
    717    };
    718
    719    // Unique metadata per (name, source, version) triple. This'll allow us
    720    // to pull crates from anywhere without worrying about conflicts.
    721    unit.pkg
    722        .package_id()
    723        .stable_hash(ws_root)
    724        .hash(&mut shared_hasher);
    725
    726    // Also mix in enabled features to our metadata. This'll ensure that
    727    // when changing feature sets each lib is separately cached.
    728    unit.features.hash(&mut shared_hasher);
    729
    730    // Throw in the profile we're compiling with. This helps caching
    731    // `panic=abort` and `panic=unwind` artifacts, additionally with various
    732    // settings like debuginfo and whatnot.
    733    unit.profile.hash(&mut shared_hasher);
    734    unit.mode.hash(&mut shared_hasher);
    735    build_runner.lto[unit].hash(&mut shared_hasher);
    736
    737    // Artifacts compiled for the host should have a different
    738    // metadata piece than those compiled for the target, so make sure
    739    // we throw in the unit's `kind` as well.  Use `fingerprint_hash`
    740    // so that the StableHash doesn't change based on the pathnames
    741    // of the custom target JSON spec files.
    742    unit.kind.fingerprint_hash().hash(&mut shared_hasher);
    743
    744    // Finally throw in the target name/kind. This ensures that concurrent
    745    // compiles of targets in the same crate don't collide.
    746    unit.target.name().hash(&mut shared_hasher);
    747    unit.target.kind().hash(&mut shared_hasher);
    748
    749    hash_rustc_version(bcx, &mut shared_hasher, unit);
    750
    751    if build_runner.bcx.ws.is_member(&unit.pkg) {
    752        // This is primarily here for clippy. This ensures that the clippy
    753        // artifacts are separate from the `check` ones.
    754        if let Some(path) = &build_runner.bcx.rustc().workspace_wrapper {
    755            path.hash(&mut shared_hasher);
    756        }
    757    }
    758
    759    // Seed the contents of `__CARGO_DEFAULT_LIB_METADATA` to the hasher if present.
    760    // This should be the release channel, to get a different hash for each channel.
    761    if let Ok(ref channel) = build_runner
    762        .bcx
    763        .gctx
    764        .get_env("__CARGO_DEFAULT_LIB_METADATA")
    765    {
    766        channel.hash(&mut shared_hasher);
    767    }
    768
    769    // std units need to be kept separate from user dependencies. std crates
    770    // are differentiated in the Unit with `is_std` (for things like
    771    // `-Zforce-unstable-if-unmarked`), so they are always built separately.
    772    // This isn't strictly necessary for build dependencies which probably
    773    // don't need unstable support. A future experiment might be to set
    774    // `is_std` to false for build dependencies so that they can be shared
    775    // with user dependencies.
    776    unit.is_std.hash(&mut shared_hasher);
    777
    778    // While we don't hash RUSTFLAGS because it may contain absolute paths that
    779    // hurts reproducibility, we track whether a unit's RUSTFLAGS is from host
    780    // config, so that we can generate a different metadata hash for runtime
    781    // and compile-time units.
    782    //
    783    // HACK: This is a temporary hack for fixing rust-lang/cargo#14253
    784    // Need to find a long-term solution to replace this fragile workaround.
    785    // See https://github.com/rust-lang/cargo/pull/14432#discussion_r1725065350
    786    if unit.kind.is_host() && !bcx.gctx.target_applies_to_host().unwrap_or_default() {
    787        let host_info = bcx.target_data.info(CompileKind::Host);
    788        let target_configs_are_different = unit.rustflags != host_info.rustflags
    789            || unit.rustdocflags != host_info.rustdocflags
    790            || bcx
    791                .target_data
    792                .target_config(CompileKind::Host)
    793                .links_overrides
    794                != unit.links_overrides;
    795        target_configs_are_different.hash(&mut shared_hasher);
    796    }
    797
    798    let mut c_metadata_hasher = shared_hasher.clone();
    799    // Mix in the target-metadata of all the dependencies of this target.
    800    let mut dep_c_metadata_hashes = deps_metadata
    801        .iter()
    802        .map(|m| m.c_metadata)
    803        .collect::<Vec<_>>();
    804    dep_c_metadata_hashes.sort();
    805    dep_c_metadata_hashes.hash(&mut c_metadata_hasher);
    806
    807    let mut unit_id_hasher = shared_hasher.clone();
    808    // Mix in the target-metadata of all the dependencies of this target.
    809    let mut dep_unit_id_hashes = deps_metadata.iter().map(|m| m.unit_id).collect::<Vec<_>>();
    810    dep_unit_id_hashes.sort();
    811    dep_unit_id_hashes.hash(&mut unit_id_hasher);
    812    // Avoid trashing the caches on RUSTFLAGS changing via `unit_id`
    813    //
    814    // Limited to `unit_id` to help with reproducible build / PGO issues.
    815    let default = Vec::new();
    816    let extra_args = build_runner.bcx.extra_args_for(unit).unwrap_or(&default);
    817    if !has_remap_path_prefix(&extra_args) {
    818        extra_args.hash(&mut unit_id_hasher);
    819    }
    820    if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
    821        if !has_remap_path_prefix(&unit.rustdocflags) {
    822            unit.rustdocflags.hash(&mut unit_id_hasher);
    823        }
    824    } else {
    825        if !has_remap_path_prefix(&unit.rustflags) {
    826            unit.rustflags.hash(&mut unit_id_hasher);
    827        }
    828    }
    829
    830    let c_metadata = UnitHash(Hasher::finish(&c_metadata_hasher));
    831    let unit_id = UnitHash(Hasher::finish(&unit_id_hasher));
    832
    833    Metadata {
    834        unit_id,
    835        c_metadata,
    836        c_extra_filename,
    837        pkg_dir,
    838    }
    839}
    840
    841/// HACK: Detect the *potential* presence of `--remap-path-prefix`
    842///
    843/// As CLI parsing is contextual and dependent on the CLI definition to understand the context, we
    844/// can't say for sure whether `--remap-path-prefix` is present, so we guess if anything looks like
    845/// it.
    846/// If we could, we'd strip it out for hashing.
    847/// Instead, we use this to avoid hashing rustflags if it might be present to avoid the risk of taking
    848/// a flag that is trying to make things reproducible and making things less reproducible by the
    849/// `-Cextra-filename` showing up in the rlib, even with `split-debuginfo`.
    850fn has_remap_path_prefix(args: &[String]) -> bool {
    851    args.iter()
    852        .any(|s| s.starts_with("--remap-path-prefix=") || s == "--remap-path-prefix")
    853}
    854
    855/// Hash the version of rustc being used during the build process.
    856fn hash_rustc_version(bcx: &BuildContext<'_, '_>, hasher: &mut StableHasher, unit: &Unit) {
    857    let vers = &bcx.rustc().version;
    858    if vers.pre.is_empty() || bcx.gctx.cli_unstable().separate_nightlies {
    859        // For stable, keep the artifacts separate. This helps if someone is
    860        // testing multiple versions, to avoid recompiles. Note though that for
    861        // cross-compiled builds the `host:` line of `verbose_version` is
    862        // omitted since rustc should produce the same output for each target
    863        // regardless of the host.
    864        for line in bcx.rustc().verbose_version.lines() {
    865            if unit.kind.is_host() || !line.starts_with("host: ") {
    866                line.hash(hasher);
    867            }
    868        }
    869        return;
    870    }
    871    // On "nightly"/"beta"/"dev"/etc, keep each "channel" separate. Don't hash
    872    // the date/git information, so that whenever someone updates "nightly",
    873    // they won't have a bunch of stale artifacts in the target directory.
    874    //
    875    // This assumes that the first segment is the important bit ("nightly",
    876    // "beta", "dev", etc.). Skip other parts like the `.3` in `-beta.3`.
    877    vers.pre.split('.').next().hash(hasher);
    878    // Keep "host" since some people switch hosts to implicitly change
    879    // targets, (like gnu vs musl or gnu vs msvc). In the future, we may want
    880    // to consider hashing `unit.kind.short_name()` instead.
    881    if unit.kind.is_host() {
    882        bcx.rustc().host.hash(hasher);
    883    }
    884    // None of the other lines are important. Currently they are:
    885    // binary: rustc  <-- or "rustdoc"
    886    // commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a
    887    // commit-date: 2020-03-21
    888    // host: x86_64-apple-darwin
    889    // release: 1.44.0-nightly
    890    // LLVM version: 9.0
    891    //
    892    // The backend version ("LLVM version") might become more relevant in
    893    // the future when cranelift sees more use, and people want to switch
    894    // between different backends without recompiling.
    895}
    896
    897/// Returns whether or not this unit should use a hash in the filename to make it unique.
    898fn use_extra_filename(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
    899    if unit.mode.is_doc_test() || unit.mode.is_doc() {
    900        // Doc tests do not have metadata.
    901        return false;
    902    }
    903    if bcx.gctx.cli_unstable().build_dir_new_layout {
    904        if unit.mode.is_any_test() || unit.mode.is_check() {
    905            // These always use metadata.
    906            return true;
    907        }
    908        // No metadata in these cases:
    909        //
    910        // - dylib, cdylib, executable: `pkg_dir` avoids collisions for us and rustc isn't looking these
    911        //   up by `-Cextra-filename`
    912        //
    913        // The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to
    914        // force metadata in the hash. This is only used for building libstd. For
    915        // example, if libstd is placed in a common location, we don't want a file
    916        // named /usr/lib/libstd.so which could conflict with other rustc
    917        // installs. In addition it prevents accidentally loading a libstd of a
    918        // different compiler at runtime.
    919        // See https://github.com/rust-lang/cargo/issues/3005
    920        if (unit.target.is_dylib() || unit.target.is_cdylib() || unit.target.is_executable())
    921            && bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err()
    922        {
    923            return false;
    924        }
    925    } else {
    926        if unit.mode.is_any_test() || unit.mode.is_check() {
    927            // These always use metadata.
    928            return true;
    929        }
    930        // No metadata in these cases:
    931        //
    932        // - dylibs:
    933        //   - if any dylib names are encoded in executables, so they can't be renamed.
    934        //   - TODO: Maybe use `-install-name` on macOS or `-soname` on other UNIX systems
    935        //     to specify the dylib name to be used by the linker instead of the filename.
    936        // - Windows MSVC executables: The path to the PDB is embedded in the
    937        //   executable, and we don't want the PDB path to include the hash in it.
    938        // - wasm32-unknown-emscripten executables: When using emscripten, the path to the
    939        //   .wasm file is embedded in the .js file, so we don't want the hash in there.
    940        //
    941        // This is only done for local packages, as we don't expect to export
    942        // dependencies.
    943        //
    944        // The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to
    945        // force metadata in the hash. This is only used for building libstd. For
    946        // example, if libstd is placed in a common location, we don't want a file
    947        // named /usr/lib/libstd.so which could conflict with other rustc
    948        // installs. In addition it prevents accidentally loading a libstd of a
    949        // different compiler at runtime.
    950        // See https://github.com/rust-lang/cargo/issues/3005
    951        let short_name = bcx.target_data.short_name(&unit.kind);
    952        if (unit.target.is_dylib()
    953            || unit.target.is_cdylib()
    954            || (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten")
    955            || (unit.target.is_executable() && short_name.contains("msvc")))
    956            && unit.pkg.package_id().source_id().is_path()
    957            && bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err()
    958        {
    959            return false;
    960        }
    961    }
    962    true
    963}
    964
    965/// Returns whether or not this unit should use a hash in the pkg_dir to make it unique.
    966fn use_pkg_dir(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
    967    if unit.mode.is_doc_test() || unit.mode.is_doc() {
    968        // Doc tests do not have metadata.
    969        return false;
    970    }
    971    if bcx.gctx.cli_unstable().build_dir_new_layout {
    972        // These always use metadata.
    973        return true;
    974    }
    975    if unit.mode.is_any_test() || unit.mode.is_check() {
    976        // These always use metadata.
    977        return true;
    978    }
    979    // No metadata in these cases:
    980    //
    981    // - dylibs:
    982    //   - if any dylib names are encoded in executables, so they can't be renamed.
    983    //   - TODO: Maybe use `-install-name` on macOS or `-soname` on other UNIX systems
    984    //     to specify the dylib name to be used by the linker instead of the filename.
    985    // - Windows MSVC executables: The path to the PDB is embedded in the
    986    //   executable, and we don't want the PDB path to include the hash in it.
    987    // - wasm32-unknown-emscripten executables: When using emscripten, the path to the
    988    //   .wasm file is embedded in the .js file, so we don't want the hash in there.
    989    //
    990    // This is only done for local packages, as we don't expect to export
    991    // dependencies.
    992    //
    993    // The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to
    994    // force metadata in the hash. This is only used for building libstd. For
    995    // example, if libstd is placed in a common location, we don't want a file
    996    // named /usr/lib/libstd.so which could conflict with other rustc
    997    // installs. In addition it prevents accidentally loading a libstd of a
    998    // different compiler at runtime.
    999    // See https://github.com/rust-lang/cargo/issues/3005
    1000    let short_name = bcx.target_data.short_name(&unit.kind);
    1001    if (unit.target.is_dylib()
    1002        || unit.target.is_cdylib()
    1003        || (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten")
    1004        || (unit.target.is_executable() && short_name.contains("msvc")))
    1005        && unit.pkg.package_id().source_id().is_path()
    1006        && bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err()
    1007    {
    1008        return false;
    1009    }
    1010    true
    1011}