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

    1//! [`BuildRunner`] is the mutable state used during the build process.
    2
    3use std::collections::{BTreeSet, HashMap, HashSet};
    4use std::path::{Path, PathBuf};
    5use std::sync::{Arc, Mutex};
    6
    7use crate::core::compiler::compilation::{self, UnitOutput};
    8use crate::core::compiler::{self, artifact, Unit};
    9use crate::core::PackageId;
    10use crate::util::cache_lock::CacheLockMode;
    11use crate::util::errors::CargoResult;
    12use anyhow::{bail, Context as _};
    13use filetime::FileTime;
    14use itertools::Itertools;
    15use jobserver::Client;
    16
    17use super::build_plan::BuildPlan;
    18use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts};
    19use super::fingerprint::{Checksum, Fingerprint};
    20use super::job_queue::JobQueue;
    21use super::layout::Layout;
    22use super::lto::Lto;
    23use super::unit_graph::UnitDep;
    24use super::{
    25    BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor, RustDocFingerprint,
    26};
    27
    28mod compilation_files;
    29use self::compilation_files::CompilationFiles;
    30pub use self::compilation_files::{Metadata, OutputFile, UnitHash};
    31
    32/// Collection of all the stuff that is needed to perform a build.
    33///
    34/// Different from the [`BuildContext`], `Context` is a _mutable_ state used
    35/// throughout the entire build process. Everything is coordinated through this.
    36///
    37/// [`BuildContext`]: crate::core::compiler::BuildContext
    38pub struct BuildRunner<'a, 'gctx> {
    39    /// Mostly static information about the build task.
    40    pub bcx: &'a BuildContext<'a, 'gctx>,
    41    /// A large collection of information about the result of the entire compilation.
    42    pub compilation: Compilation<'gctx>,
    43    /// Output from build scripts, updated after each build script runs.
    44    pub build_script_outputs: Arc<Mutex<BuildScriptOutputs>>,
    45    /// Dependencies (like rerun-if-changed) declared by a build script.
    46    /// This is *only* populated from the output from previous runs.
    47    /// If the build script hasn't ever been run, then it must be run.
    48    pub build_explicit_deps: HashMap<Unit, BuildDeps>,
    49    /// Fingerprints used to detect if a unit is out-of-date.
    50    pub fingerprints: HashMap<Unit, Arc<Fingerprint>>,
    51    /// Cache of file mtimes to reduce filesystem hits.
    52    pub mtime_cache: HashMap<PathBuf, FileTime>,
    53    /// Cache of file checksums to reduce filesystem reads.
    54    pub checksum_cache: HashMap<PathBuf, Checksum>,
    55    /// A set used to track which units have been compiled.
    56    /// A unit may appear in the job graph multiple times as a dependency of
    57    /// multiple packages, but it only needs to run once.
    58    pub compiled: HashSet<Unit>,
    59    /// Linking information for each `Unit`.
    60    /// See `build_map` for details.
    61    pub build_scripts: HashMap<Unit, Arc<BuildScripts>>,
    62    /// Job server client to manage concurrency with other processes.
    63    pub jobserver: Client,
    64    /// "Primary" packages are the ones the user selected on the command-line
    65    /// with `-p` flags. If no flags are specified, then it is the defaults
    66    /// based on the current directory and the default workspace members.
    67    primary_packages: HashSet<PackageId>,
    68    /// An abstraction of the files and directories that will be generated by
    69    /// the compilation. This is `None` until after `unit_dependencies` has
    70    /// been computed.
    71    files: Option<CompilationFiles<'a, 'gctx>>,
    72
    73    /// A set of units which are compiling rlibs and are expected to produce
    74    /// metadata files in addition to the rlib itself.
    75    rmeta_required: HashSet<Unit>,
    76
    77    /// Map of the LTO-status of each unit. This indicates what sort of
    78    /// compilation is happening (only object, only bitcode, both, etc), and is
    79    /// precalculated early on.
    80    pub lto: HashMap<Unit, Lto>,
    81
    82    /// Map of Doc/Docscrape units to metadata for their -Cmetadata flag.
    83    /// See `Context::find_metadata_units` for more details.
    84    pub metadata_for_doc_units: HashMap<Unit, Metadata>,
    85
    86    /// Set of metadata of Docscrape units that fail before completion, e.g.
    87    /// because the target has a type error. This is in an Arc<Mutex<..>>
    88    /// because it is continuously updated as the job progresses.
    89    pub failed_scrape_units: Arc<Mutex<HashSet<UnitHash>>>,
    90}
    91
    92impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
    93    pub fn new(bcx: &'a BuildContext<'a, 'gctx>) -> CargoResult<Self> {
    94        // Load up the jobserver that we'll use to manage our parallelism. This
    95        // is the same as the GNU make implementation of a jobserver, and
    96        // intentionally so! It's hoped that we can interact with GNU make and
    97        // all share the same jobserver.
    98        //
    99        // Note that if we don't have a jobserver in our environment then we
    100        // create our own, and we create it with `n` tokens, but immediately
    101        // acquire one, because one token is ourself, a running process.
    102        let jobserver = match bcx.gctx.jobserver_from_env() {
    103            Some(c) => c.clone(),
    104            None => {
    105                let client =
    106                    Client::new(bcx.jobs() as usize).context("failed to create jobserver")?;
    107                client.acquire_raw()?;
    108                client
    109            }
    110        };
    111
    112        Ok(Self {
    113            bcx,
    114            compilation: Compilation::new(bcx)?,
    115            build_script_outputs: Arc::new(Mutex::new(BuildScriptOutputs::default())),
    116            fingerprints: HashMap::new(),
    117            mtime_cache: HashMap::new(),
    118            checksum_cache: HashMap::new(),
    119            compiled: HashSet::new(),
    120            build_scripts: HashMap::new(),
    121            build_explicit_deps: HashMap::new(),
    122            jobserver,
    123            primary_packages: HashSet::new(),
    124            files: None,
    125            rmeta_required: HashSet::new(),
    126            lto: HashMap::new(),
    127            metadata_for_doc_units: HashMap::new(),
    128            failed_scrape_units: Arc::new(Mutex::new(HashSet::new())),
    129        })
    130    }
    131
    132    /// Dry-run the compilation without actually running it.
    133    ///
    134    /// This is expected to collect information like the location of output artifacts.
    135    /// Please keep in sync with non-compilation part in [`BuildRunner::compile`].
    136    pub fn dry_run(mut self) -> CargoResult<Compilation<'gctx>> {
    137        let _lock = self
    138            .bcx
    139            .gctx
    140            .acquire_package_cache_lock(CacheLockMode::Shared)?;
    141        self.lto = super::lto::generate(self.bcx)?;
    142        self.prepare_units()?;
    143        self.prepare()?;
    144        self.check_collisions()?;
    145
    146        for unit in &self.bcx.roots {
    147            self.collect_tests_and_executables(unit)?;
    148        }
    149
    150        Ok(self.compilation)
    151    }
    152
    153    /// Starts compilation, waits for it to finish, and returns information
    154    /// about the result of compilation.
    155    ///
    156    /// See [`ops::cargo_compile`] for a higher-level view of the compile process.
    157    ///
    158    /// [`ops::cargo_compile`]: crate::ops::cargo_compile
    159    #[tracing::instrument(skip_all)]
    160    pub fn compile(mut self, exec: &Arc<dyn Executor>) -> CargoResult<Compilation<'gctx>> {
    161        // A shared lock is held during the duration of the build since rustc
    162        // needs to read from the `src` cache, and we don't want other
    163        // commands modifying the `src` cache while it is running.
    164        let _lock = self
    165            .bcx
    166            .gctx
    167            .acquire_package_cache_lock(CacheLockMode::Shared)?;
    168        let mut queue = JobQueue::new(self.bcx);
    169        let mut plan = BuildPlan::new();
    170        let build_plan = self.bcx.build_config.build_plan;
    171        self.lto = super::lto::generate(self.bcx)?;
    172        self.prepare_units()?;
    173        self.prepare()?;
    174        custom_build::build_map(&mut self)?;
    175        self.check_collisions()?;
    176        self.compute_metadata_for_doc_units();
    177
    178        // We need to make sure that if there were any previous docs
    179        // already compiled, they were compiled with the same Rustc version that we're currently
    180        // using. Otherwise we must remove the `doc/` folder and compile again forcing a rebuild.
    181        //
    182        // This is important because the `.js`/`.html` & `.css` files that are generated by Rustc don't have
    183        // any versioning (See https://github.com/rust-lang/cargo/issues/8461).
    184        // Therefore, we can end up with weird bugs and behaviours if we mix different
    185        // versions of these files.
    186        if self.bcx.build_config.intent.is_doc() {
    187            RustDocFingerprint::check_rustdoc_fingerprint(&self)?
    188        }
    189
    190        for unit in &self.bcx.roots {
    191            let force_rebuild = self.bcx.build_config.force_rebuild;
    192            super::compile(&mut self, &mut queue, &mut plan, unit, exec, force_rebuild)?;
    193        }
    194
    195        // Now that we've got the full job queue and we've done all our
    196        // fingerprint analysis to determine what to run, bust all the memoized
    197        // fingerprint hashes to ensure that during the build they all get the
    198        // most up-to-date values. In theory we only need to bust hashes that
    199        // transitively depend on a dirty build script, but it shouldn't matter
    200        // that much for performance anyway.
    201        for fingerprint in self.fingerprints.values() {
    202            fingerprint.clear_memoized();
    203        }
    204
    205        // Now that we've figured out everything that we're going to do, do it!
    206        queue.execute(&mut self, &mut plan)?;
    207
    208        if build_plan {
    209            plan.set_inputs(self.build_plan_inputs()?);
    210            plan.output_plan(self.bcx.gctx);
    211        }
    212
    213        // Add `OUT_DIR` to env vars if unit has a build script.
    214        let units_with_build_script = &self
    215            .bcx
    216            .roots
    217            .iter()
    218            .filter(|unit| self.build_scripts.contains_key(unit))
    219            .dedup_by(|x, y| x.pkg.package_id() == y.pkg.package_id())
    220            .collect::<Vec<_>>();
    221        for unit in units_with_build_script {
    222            for dep in &self.bcx.unit_graph[unit] {
    223                if dep.unit.mode.is_run_custom_build() {
    224                    let out_dir = self
    225                        .files()
    226                        .build_script_out_dir(&dep.unit)
    227                        .display()
    228                        .to_string();
    229                    let script_meta = self.get_run_build_script_metadata(&dep.unit);
    230                    self.compilation
    231                        .extra_env
    232                        .entry(script_meta)
    233                        .or_insert_with(Vec::new)
    234                        .push(("OUT_DIR".to_string(), out_dir));
    235                }
    236            }
    237        }
    238
    239        // Collect the result of the build into `self.compilation`.
    240        for unit in &self.bcx.roots {
    241            self.collect_tests_and_executables(unit)?;
    242
    243            // Collect information for `rustdoc --test`.
    244            if unit.mode.is_doc_test() {
    245                let mut unstable_opts = false;
    246                let mut args = compiler::extern_args(&self, unit, &mut unstable_opts)?;
    247                args.extend(compiler::lto_args(&self, unit));
    248                args.extend(compiler::features_args(unit));
    249                args.extend(compiler::check_cfg_args(unit));
    250
    251                let script_meta = self.find_build_script_metadata(unit);
    252                if let Some(meta) = script_meta {
    253                    if let Some(output) = self.build_script_outputs.lock().unwrap().get(meta) {
    254                        for cfg in &output.cfgs {
    255                            args.push("--cfg".into());
    256                            args.push(cfg.into());
    257                        }
    258
    259                        for check_cfg in &output.check_cfgs {
    260                            args.push("--check-cfg".into());
    261                            args.push(check_cfg.into());
    262                        }
    263
    264                        for (lt, arg) in &output.linker_args {
    265                            if lt.applies_to(&unit.target, unit.mode) {
    266                                args.push("-C".into());
    267                                args.push(format!("link-arg={}", arg).into());
    268                            }
    269                        }
    270                    }
    271                }
    272                args.extend(unit.rustdocflags.iter().map(Into::into));
    273
    274                use super::MessageFormat;
    275                let format = match self.bcx.build_config.message_format {
    276                    MessageFormat::Short => "short",
    277                    MessageFormat::Human => "human",
    278                    MessageFormat::Json { .. } => "json",
    279                };
    280                args.push("--error-format".into());
    281                args.push(format.into());
    282
    283                self.compilation.to_doc_test.push(compilation::Doctest {
    284                    unit: unit.clone(),
    285                    args,
    286                    unstable_opts,
    287                    linker: self.compilation.target_linker(unit.kind).clone(),
    288                    script_meta,
    289                    env: artifact::get_env(&self, self.unit_deps(unit))?,
    290                });
    291            }
    292
    293            super::output_depinfo(&mut self, unit)?;
    294        }
    295
    296        for (script_meta, output) in self.build_script_outputs.lock().unwrap().iter() {
    297            self.compilation
    298                .extra_env
    299                .entry(*script_meta)
    300                .or_insert_with(Vec::new)
    301                .extend(output.env.iter().cloned());
    302
    303            for dir in output.library_paths.iter() {
    304                self.compilation
    305                    .native_dirs
    306                    .insert(dir.clone().into_path_buf());
    307            }
    308        }
    309        Ok(self.compilation)
    310    }
    311
    312    fn collect_tests_and_executables(&mut self, unit: &Unit) -> CargoResult<()> {
    313        for output in self.outputs(unit)?.iter() {
    314            if matches!(
    315                output.flavor,
    316                FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
    317            ) {
    318                continue;
    319            }
    320
    321            let bindst = output.bin_dst();
    322
    323            if unit.mode == CompileMode::Test {
    324                self.compilation
    325                    .tests
    326                    .push(self.unit_output(unit, &output.path));
    327            } else if unit.target.is_executable() {
    328                self.compilation
    329                    .binaries
    330                    .push(self.unit_output(unit, bindst));
    331            } else if unit.target.is_cdylib()
    332                && !self.compilation.cdylibs.iter().any(|uo| uo.unit == *unit)
    333            {
    334                self.compilation
    335                    .cdylibs
    336                    .push(self.unit_output(unit, bindst));
    337            }
    338        }
    339        Ok(())
    340    }
    341
    342    /// Returns the executable for the specified unit (if any).
    343    pub fn get_executable(&mut self, unit: &Unit) -> CargoResult<Option<PathBuf>> {
    344        let is_binary = unit.target.is_executable();
    345        let is_test = unit.mode.is_any_test();
    346        if !unit.mode.generates_executable() || !(is_binary || is_test) {
    347            return Ok(None);
    348        }
    349        Ok(self
    350            .outputs(unit)?
    351            .iter()
    352            .find(|o| o.flavor == FileFlavor::Normal)
    353            .map(|output| output.bin_dst().clone()))
    354    }
    355
    356    #[tracing::instrument(skip_all)]
    357    pub fn prepare_units(&mut self) -> CargoResult<()> {
    358        let dest = self.bcx.profiles.get_dir_name();
    359        let host_layout = Layout::new(self.bcx.ws, None, &dest)?;
    360        let mut targets = HashMap::new();
    361        for kind in self.bcx.all_kinds.iter() {
    362            if let CompileKind::Target(target) = *kind {
    363                let layout = Layout::new(self.bcx.ws, Some(target), &dest)?;
    364                targets.insert(target, layout);
    365            }
    366        }
    367        self.primary_packages
    368            .extend(self.bcx.roots.iter().map(|u| u.pkg.package_id()));
    369        self.compilation
    370            .root_crate_names
    371            .extend(self.bcx.roots.iter().map(|u| u.target.crate_name()));
    372
    373        self.record_units_requiring_metadata();
    374
    375        let files = CompilationFiles::new(self, host_layout, targets);
    376        self.files = Some(files);
    377        Ok(())
    378    }
    379
    380    /// Prepare this context, ensuring that all filesystem directories are in
    381    /// place.
    382    #[tracing::instrument(skip_all)]
    383    pub fn prepare(&mut self) -> CargoResult<()> {
    384        self.files
    385            .as_mut()
    386            .unwrap()
    387            .host
    388            .prepare()
    389            .context("couldn't prepare build directories")?;
    390        for target in self.files.as_mut().unwrap().target.values_mut() {
    391            target
    392                .prepare()
    393                .context("couldn't prepare build directories")?;
    394        }
    395
    396        let files = self.files.as_ref().unwrap();
    397        for &kind in self.bcx.all_kinds.iter() {
    398            let layout = files.layout(kind);
    399            self.compilation
    400                .root_output
    401                .insert(kind, layout.dest().to_path_buf());
    402            self.compilation
    403                .deps_output
    404                .insert(kind, layout.deps().to_path_buf());
    405        }
    406        Ok(())
    407    }
    408
    409    pub fn files(&self) -> &CompilationFiles<'a, 'gctx> {
    410        self.files.as_ref().unwrap()
    411    }
    412
    413    /// Returns the filenames that the given unit will generate.
    414    pub fn outputs(&self, unit: &Unit) -> CargoResult<Arc<Vec<OutputFile>>> {
    415        self.files.as_ref().unwrap().outputs(unit, self.bcx)
    416    }
    417
    418    /// Direct dependencies for the given unit.
    419    pub fn unit_deps(&self, unit: &Unit) -> &[UnitDep] {
    420        &self.bcx.unit_graph[unit]
    421    }
    422
    423    /// Returns the `RunCustomBuild` Unit associated with the given Unit.
    424    ///
    425    /// If the package does not have a build script, this returns None.
    426    pub fn find_build_script_unit(&self, unit: &Unit) -> Option<Unit> {
    427        if unit.mode.is_run_custom_build() {
    428            return Some(unit.clone());
    429        }
    430        self.bcx.unit_graph[unit]
    431            .iter()
    432            .find(|unit_dep| {
    433                unit_dep.unit.mode.is_run_custom_build()
    434                    && unit_dep.unit.pkg.package_id() == unit.pkg.package_id()
    435            })
    436            .map(|unit_dep| unit_dep.unit.clone())
    437    }
    438
    439    /// Returns the metadata hash for the `RunCustomBuild` Unit associated with
    440    /// the given unit.
    441    ///
    442    /// If the package does not have a build script, this returns None.
    443    pub fn find_build_script_metadata(&self, unit: &Unit) -> Option<UnitHash> {
    444        let script_unit = self.find_build_script_unit(unit)?;
    445        Some(self.get_run_build_script_metadata(&script_unit))
    446    }
    447
    448    /// Returns the metadata hash for a `RunCustomBuild` unit.
    449    pub fn get_run_build_script_metadata(&self, unit: &Unit) -> UnitHash {
    450        assert!(unit.mode.is_run_custom_build());
    451        self.files().metadata(unit).unit_id()
    452    }
    453
    454    /// Returns the list of SBOM output file paths for a given [`Unit`].
    455    pub fn sbom_output_files(&self, unit: &Unit) -> CargoResult<Vec<PathBuf>> {
    456        Ok(self
    457            .outputs(unit)?
    458            .iter()
    459            .filter(|o| o.flavor == FileFlavor::Sbom)
    460            .map(|o| o.path.clone())
    461            .collect())
    462    }
    463
    464    pub fn is_primary_package(&self, unit: &Unit) -> bool {
    465        self.primary_packages.contains(&unit.pkg.package_id())
    466    }
    467
    468    /// Returns the list of filenames read by cargo to generate the [`BuildContext`]
    469    /// (all `Cargo.toml`, etc.).
    470    pub fn build_plan_inputs(&self) -> CargoResult<Vec<PathBuf>> {
    471        // Keep sorted for consistency.
    472        let mut inputs = BTreeSet::new();
    473        // Note: dev-deps are skipped if they are not present in the unit graph.
    474        for unit in self.bcx.unit_graph.keys() {
    475            inputs.insert(unit.pkg.manifest_path().to_path_buf());
    476        }
    477        Ok(inputs.into_iter().collect())
    478    }
    479
    480    /// Returns a [`UnitOutput`] which represents some information about the
    481    /// output of a unit.
    482    pub fn unit_output(&self, unit: &Unit, path: &Path) -> UnitOutput {
    483        let script_meta = self.find_build_script_metadata(unit);
    484        UnitOutput {
    485            unit: unit.clone(),
    486            path: path.to_path_buf(),
    487            script_meta,
    488        }
    489    }
    490
    491    /// Check if any output file name collision happens.
    492    /// See <https://github.com/rust-lang/cargo/issues/6313> for more.
    493    #[tracing::instrument(skip_all)]
    494    fn check_collisions(&self) -> CargoResult<()> {
    495        let mut output_collisions = HashMap::new();
    496        let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String {
    497            format!(
    498                "The {} target `{}` in package `{}` has the same output \
    499                     filename as the {} target `{}` in package `{}`.\n\
    500                     Colliding filename is: {}\n",
    501                unit.target.kind().description(),
    502                unit.target.name(),
    503                unit.pkg.package_id(),
    504                other_unit.target.kind().description(),
    505                other_unit.target.name(),
    506                other_unit.pkg.package_id(),
    507                path.display()
    508            )
    509        };
    510        let suggestion =
    511            "Consider changing their names to be unique or compiling them separately.\n\
    512             This may become a hard error in the future; see \
    513             <https://github.com/rust-lang/cargo/issues/6313>.";
    514        let rustdoc_suggestion =
    515            "This is a known bug where multiple crates with the same name use\n\
    516             the same path; see <https://github.com/rust-lang/cargo/issues/6313>.";
    517        let report_collision = |unit: &Unit,
    518                                other_unit: &Unit,
    519                                path: &PathBuf,
    520                                suggestion: &str|
    521         -> CargoResult<()> {
    522            if unit.target.name() == other_unit.target.name() {
    523                self.bcx.gctx.shell().warn(format!(
    524                    "output filename collision.\n\
    525                     {}\
    526                     The targets should have unique names.\n\
    527                     {}",
    528                    describe_collision(unit, other_unit, path),
    529                    suggestion
    530                ))
    531            } else {
    532                self.bcx.gctx.shell().warn(format!(
    533                    "output filename collision.\n\
    534                    {}\
    535                    The output filenames should be unique.\n\
    536                    {}\n\
    537                    If this looks unexpected, it may be a bug in Cargo. Please file a bug report at\n\
    538                    https://github.com/rust-lang/cargo/issues/ with as much information as you\n\
    539                    can provide.\n\
    540                    cargo {} running on `{}` target `{}`\n\
    541                    First unit: {:?}\n\
    542                    Second unit: {:?}",
    543                    describe_collision(unit, other_unit, path),
    544                    suggestion,
    545                    crate::version(),
    546                    self.bcx.host_triple(),
    547                    self.bcx.target_data.short_name(&unit.kind),
    548                    unit,
    549                    other_unit))
    550            }
    551        };
    552
    553        fn doc_collision_error(unit: &Unit, other_unit: &Unit) -> CargoResult<()> {
    554            bail!(
    555                "document output filename collision\n\
    556                 The {} `{}` in package `{}` has the same name as the {} `{}` in package `{}`.\n\
    557                 Only one may be documented at once since they output to the same path.\n\
    558                 Consider documenting only one, renaming one, \
    559                 or marking one with `doc = false` in Cargo.toml.",
    560                unit.target.kind().description(),
    561                unit.target.name(),
    562                unit.pkg,
    563                other_unit.target.kind().description(),
    564                other_unit.target.name(),
    565                other_unit.pkg,
    566            );
    567        }
    568
    569        let mut keys = self
    570            .bcx
    571            .unit_graph
    572            .keys()
    573            .filter(|unit| !unit.mode.is_run_custom_build())
    574            .collect::<Vec<_>>();
    575        // Sort for consistent error messages.
    576        keys.sort_unstable();
    577        // These are kept separate to retain compatibility with older
    578        // versions, which generated an error when there was a duplicate lib
    579        // or bin (but the old code did not check bin<->lib collisions). To
    580        // retain backwards compatibility, this only generates an error for
    581        // duplicate libs or duplicate bins (but not both). Ideally this
    582        // shouldn't be here, but since there isn't a complete workaround,
    583        // yet, this retains the old behavior.
    584        let mut doc_libs = HashMap::new();
    585        let mut doc_bins = HashMap::new();
    586        for unit in keys {
    587            if unit.mode.is_doc() && self.is_primary_package(unit) {
    588                // These situations have been an error since before 1.0, so it
    589                // is not a warning like the other situations.
    590                if unit.target.is_lib() {
    591                    if let Some(prev) = doc_libs.insert((unit.target.crate_name(), unit.kind), unit)
    592                    {
    593                        doc_collision_error(unit, prev)?;
    594                    }
    595                } else if let Some(prev) =
    596                    doc_bins.insert((unit.target.crate_name(), unit.kind), unit)
    597                {
    598                    doc_collision_error(unit, prev)?;
    599                }
    600            }
    601            for output in self.outputs(unit)?.iter() {
    602                if let Some(other_unit) = output_collisions.insert(output.path.clone(), unit) {
    603                    if unit.mode.is_doc() {
    604                        // See https://github.com/rust-lang/rust/issues/56169
    605                        // and https://github.com/rust-lang/rust/issues/61378
    606                        report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?;
    607                    } else {
    608                        report_collision(unit, other_unit, &output.path, suggestion)?;
    609                    }
    610                }
    611                if let Some(hardlink) = output.hardlink.as_ref() {
    612                    if let Some(other_unit) = output_collisions.insert(hardlink.clone(), unit) {
    613                        report_collision(unit, other_unit, hardlink, suggestion)?;
    614                    }
    615                }
    616                if let Some(ref export_path) = output.export_path {
    617                    if let Some(other_unit) = output_collisions.insert(export_path.clone(), unit) {
    618                        self.bcx.gctx.shell().warn(format!(
    619                            "`--artifact-dir` filename collision.\n\
    620                             {}\
    621                             The exported filenames should be unique.\n\
    622                             {}",
    623                            describe_collision(unit, other_unit, export_path),
    624                            suggestion
    625                        ))?;
    626                    }
    627                }
    628            }
    629        }
    630        Ok(())
    631    }
    632
    633    /// Records the list of units which are required to emit metadata.
    634    ///
    635    /// Units which depend only on the metadata of others requires the others to
    636    /// actually produce metadata, so we'll record that here.
    637    fn record_units_requiring_metadata(&mut self) {
    638        for (key, deps) in self.bcx.unit_graph.iter() {
    639            for dep in deps {
    640                if self.only_requires_rmeta(key, &dep.unit) {
    641                    self.rmeta_required.insert(dep.unit.clone());
    642                }
    643            }
    644        }
    645    }
    646
    647    /// Returns whether when `parent` depends on `dep` if it only requires the
    648    /// metadata file from `dep`.
    649    pub fn only_requires_rmeta(&self, parent: &Unit, dep: &Unit) -> bool {
    650        // We're only a candidate for requiring an `rmeta` file if we
    651        // ourselves are building an rlib,
    652        !parent.requires_upstream_objects()
    653            && parent.mode == CompileMode::Build
    654            // Our dependency must also be built as an rlib, otherwise the
    655            // object code must be useful in some fashion
    656            && !dep.requires_upstream_objects()
    657            && dep.mode == CompileMode::Build
    658    }
    659
    660    /// Returns whether when `unit` is built whether it should emit metadata as
    661    /// well because some compilations rely on that.
    662    pub fn rmeta_required(&self, unit: &Unit) -> bool {
    663        self.rmeta_required.contains(unit)
    664    }
    665
    666    /// Finds metadata for Doc/Docscrape units.
    667    ///
    668    /// rustdoc needs a -Cmetadata flag in order to recognize StableCrateIds that refer to
    669    /// items in the crate being documented. The -Cmetadata flag used by reverse-dependencies
    670    /// will be the metadata of the Cargo unit that generated the current library's rmeta file,
    671    /// which should be a Check unit.
    672    ///
    673    /// If the current crate has reverse-dependencies, such a Check unit should exist, and so
    674    /// we use that crate's metadata. If not, we use the crate's Doc unit so at least examples
    675    /// scraped from the current crate can be used when documenting the current crate.
    676    #[tracing::instrument(skip_all)]
    677    pub fn compute_metadata_for_doc_units(&mut self) {
    678        for unit in self.bcx.unit_graph.keys() {
    679            if !unit.mode.is_doc() && !unit.mode.is_doc_scrape() {
    680                continue;
    681            }
    682
    683            let matching_units = self
    684                .bcx
    685                .unit_graph
    686                .keys()
    687                .filter(|other| {
    688                    unit.pkg == other.pkg
    689                        && unit.target == other.target
    690                        && !other.mode.is_doc_scrape()
    691                })
    692                .collect::<Vec<_>>();
    693            let metadata_unit = matching_units
    694                .iter()
    695                .find(|other| other.mode.is_check())
    696                .or_else(|| matching_units.iter().find(|other| other.mode.is_doc()))
    697                .unwrap_or(&unit);
    698            self.metadata_for_doc_units
    699                .insert(unit.clone(), self.files().metadata(metadata_unit));
    700        }
    701    }
    702}