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

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