#
cargo/ops/cargo_compile/
mod.rs

    1//! The entry point for starting the compilation process for commands like
    2//! `build`, `test`, `doc`, `rustc`, etc.
    3//!
    4//! The [`compile`] function will do all the work to compile a workspace. A
    5//! rough outline is:
    6//!
    7//! 1. Resolve the dependency graph (see [`ops::resolve`]).
    8//! 2. Download any packages needed (see [`PackageSet`]).
    9//! 3. Generate a list of top-level "units" of work for the targets the user
    10//!   requested on the command-line. Each [`Unit`] corresponds to a compiler
    11//!   invocation. This is done in this module ([`UnitGenerator::generate_root_units`]).
    12//! 4. Starting from the root [`Unit`]s, generate the [`UnitGraph`] by walking the dependency graph
    13//!   from the resolver.  See also [`unit_dependencies`].
    14//! 5. Construct the [`BuildContext`] with all of the information collected so
    15//!   far. This is the end of the "front end" of compilation.
    16//! 6. Create a [`BuildRunner`] which coordinates the compilation process
    17//!   and will perform the following steps:
    18//!     1. Prepare the `target` directory (see [`Layout`]).
    19//!     2. Create a [`JobQueue`]. The queue checks the
    20//!       fingerprint of each `Unit` to determine if it should run or be
    21//!       skipped.
    22//!     3. Execute the queue via [`drain_the_queue`]. Each leaf in the queue's dependency graph is
    23//!        executed, and then removed from the graph when finished. This repeats until the queue is
    24//!        empty.  Note that this is the only point in cargo that currently uses threads.
    25//! 7. The result of the compilation is stored in the [`Compilation`] struct. This can be used for
    26//!    various things, such as running tests after the compilation  has finished.
    27//!
    28//! **Note**: "target" inside this module generally refers to ["Cargo Target"],
    29//! which corresponds to artifact that will be built in a package. Not to be
    30//! confused with target-triple or target architecture.
    31//!
    32//! [`unit_dependencies`]: crate::core::compiler::unit_dependencies
    33//! [`Layout`]: crate::core::compiler::Layout
    34//! [`JobQueue`]: crate::core::compiler::job_queue
    35//! [`drain_the_queue`]: crate::core::compiler::job_queue
    36//! ["Cargo Target"]: https://doc.rust-lang.org/nightly/cargo/reference/cargo-targets.html
    37
    38use std::collections::{HashMap, HashSet};
    39use std::hash::{Hash, Hasher};
    40use std::sync::Arc;
    41
    42use crate::core::compiler::UserIntent;
    43use crate::core::compiler::unit_dependencies::build_unit_dependencies;
    44use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph};
    45use crate::core::compiler::{BuildConfig, BuildContext, BuildRunner, Compilation};
    46use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData, Unit};
    47use crate::core::compiler::{CrateType, TargetInfo, apply_env_config, standard_lib};
    48use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
    49use crate::core::profiles::Profiles;
    50use crate::core::resolver::features::{self, CliFeatures, FeaturesFor};
    51use crate::core::resolver::{HasDevUnits, Resolve};
    52use crate::core::{PackageId, PackageSet, SourceId, TargetKind, Workspace};
    53use crate::drop_println;
    54use crate::ops;
    55use crate::ops::resolve::{SpecsAndResolvedFeatures, WorkspaceResolve};
    56use crate::util::BuildLogger;
    57use crate::util::context::{GlobalContext, WarningHandling};
    58use crate::util::interning::InternedString;
    59use crate::util::log_message::LogMessage;
    60use crate::util::{CargoResult, StableHasher};
    61
    62mod compile_filter;
    63use annotate_snippets::Level;
    64pub use compile_filter::{CompileFilter, FilterRule, LibRule};
    65
    66pub(super) mod unit_generator;
    67use unit_generator::UnitGenerator;
    68
    69mod packages;
    70
    71pub use packages::Packages;
    72
    73/// Contains information about how a package should be compiled.
    74///
    75/// Note on distinction between `CompileOptions` and [`BuildConfig`]:
    76/// `BuildConfig` contains values that need to be retained after
    77/// [`BuildContext`] is created. The other fields are no longer necessary. Think
    78/// of it as `CompileOptions` are high-level settings requested on the
    79/// command-line, and `BuildConfig` are low-level settings for actually
    80/// driving `rustc`.
    81#[derive(Debug, Clone)]
    82pub struct CompileOptions {
    83    /// Configuration information for a rustc build
    84    pub build_config: BuildConfig,
    85    /// Feature flags requested by the user.
    86    pub cli_features: CliFeatures,
    87    /// A set of packages to build.
    88    pub spec: Packages,
    89    /// Filter to apply to the root package to select which targets will be
    90    /// built.
    91    pub filter: CompileFilter,
    92    /// Extra arguments to be passed to rustdoc (single target only)
    93    pub target_rustdoc_args: Option<Vec<String>>,
    94    /// The specified target will be compiled with all the available arguments,
    95    /// note that this only accounts for the *final* invocation of rustc
    96    pub target_rustc_args: Option<Vec<String>>,
    97    /// Crate types to be passed to rustc (single target only)
    98    pub target_rustc_crate_types: Option<Vec<String>>,
    99    /// Whether the `--document-private-items` flags was specified and should
    100    /// be forwarded to `rustdoc`.
    101    pub rustdoc_document_private_items: bool,
    102    /// Whether the build process should check the minimum Rust version
    103    /// defined in the cargo metadata for a crate.
    104    pub honor_rust_version: Option<bool>,
    105}
    106
    107impl CompileOptions {
    108    pub fn new(gctx: &GlobalContext, intent: UserIntent) -> CargoResult<CompileOptions> {
    109        let jobs = None;
    110        let keep_going = false;
    111        Ok(CompileOptions {
    112            build_config: BuildConfig::new(gctx, jobs, keep_going, &[], intent)?,
    113            cli_features: CliFeatures::new_all(false),
    114            spec: ops::Packages::Packages(Vec::new()),
    115            filter: CompileFilter::Default {
    116                required_features_filterable: false,
    117            },
    118            target_rustdoc_args: None,
    119            target_rustc_args: None,
    120            target_rustc_crate_types: None,
    121            rustdoc_document_private_items: false,
    122            honor_rust_version: None,
    123        })
    124    }
    125}
    126
    127/// Compiles!
    128///
    129/// This uses the [`DefaultExecutor`]. To use a custom [`Executor`], see [`compile_with_exec`].
    130pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> {
    131    let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
    132    compile_with_exec(ws, options, &exec)
    133}
    134
    135/// Like [`compile`] but allows specifying a custom [`Executor`]
    136/// that will be able to intercept build calls and add custom logic.
    137///
    138/// [`compile`] uses [`DefaultExecutor`] which just passes calls through.
    139pub fn compile_with_exec<'a>(
    140    ws: &Workspace<'a>,
    141    options: &CompileOptions,
    142    exec: &Arc<dyn Executor>,
    143) -> CargoResult<Compilation<'a>> {
    144    ws.emit_warnings()?;
    145    let compilation = compile_ws(ws, options, exec)?;
    146    if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.lint_warning_count > 0
    147    {
    148        anyhow::bail!("warnings are denied by `build.warnings` configuration")
    149    }
    150    Ok(compilation)
    151}
    152
    153/// Like [`compile_with_exec`] but without warnings from manifest parsing.
    154#[tracing::instrument(skip_all)]
    155pub fn compile_ws<'a>(
    156    ws: &Workspace<'a>,
    157    options: &CompileOptions,
    158    exec: &Arc<dyn Executor>,
    159) -> CargoResult<Compilation<'a>> {
    160    let interner = UnitInterner::new();
    161    let logger = BuildLogger::maybe_new(ws)?;
    162
    163    if let Some(ref logger) = logger {
    164        let rustc = ws.gctx().load_global_rustc(Some(ws))?;
    165        logger.log(LogMessage::BuildStarted {
    166            cwd: ws.gctx().cwd().to_path_buf(),
    167            host: rustc.host.to_string(),
    168            jobs: options.build_config.jobs,
    169            profile: options.build_config.requested_profile.to_string(),
    170            rustc_version: rustc.version.to_string(),
    171            rustc_version_verbose: rustc.verbose_version.clone(),
    172            target_dir: ws.target_dir().as_path_unlocked().to_path_buf(),
    173            workspace_root: ws.root().to_path_buf(),
    174        });
    175    }
    176
    177    let bcx = create_bcx(ws, options, &interner, logger.as_ref())?;
    178
    179    if options.build_config.unit_graph {
    180        unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.gctx())?;
    181        return Compilation::new(&bcx);
    182    }
    183    crate::core::gc::auto_gc(bcx.gctx);
    184    let build_runner = BuildRunner::new(&bcx)?;
    185    if options.build_config.dry_run {
    186        build_runner.dry_run()
    187    } else {
    188        build_runner.compile(exec)
    189    }
    190}
    191
    192/// Executes `rustc --print <VALUE>`.
    193///
    194/// * `print_opt_value` is the VALUE passed through.
    195pub fn print<'a>(
    196    ws: &Workspace<'a>,
    197    options: &CompileOptions,
    198    print_opt_value: &str,
    199) -> CargoResult<()> {
    200    let CompileOptions {
    201        ref build_config,
    202        ref target_rustc_args,
    203        ..
    204    } = *options;
    205    let gctx = ws.gctx();
    206    let rustc = gctx.load_global_rustc(Some(ws))?;
    207    for (index, kind) in build_config.requested_kinds.iter().enumerate() {
    208        if index != 0 {
    209            drop_println!(gctx);
    210        }
    211        let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?;
    212        let mut process = rustc.process();
    213        apply_env_config(gctx, &mut process)?;
    214        process.args(&target_info.rustflags);
    215        if let Some(args) = target_rustc_args {
    216            process.args(args);
    217        }
    218        if let CompileKind::Target(t) = kind {
    219            process.arg("--target").arg(t.rustc_target());
    220        }
    221        process.arg("--print").arg(print_opt_value);
    222        process.exec()?;
    223    }
    224    Ok(())
    225}
    226
    227/// Prepares all required information for the actual compilation.
    228///
    229/// For how it works and what data it collects,
    230/// please see the [module-level documentation](self).
    231#[tracing::instrument(skip_all)]
    232pub fn create_bcx<'a, 'gctx>(
    233    ws: &'a Workspace<'gctx>,
    234    options: &'a CompileOptions,
    235    interner: &'a UnitInterner,
    236    logger: Option<&'a BuildLogger>,
    237) -> CargoResult<BuildContext<'a, 'gctx>> {
    238    let CompileOptions {
    239        ref build_config,
    240        ref spec,
    241        ref cli_features,
    242        ref filter,
    243        ref target_rustdoc_args,
    244        ref target_rustc_args,
    245        ref target_rustc_crate_types,
    246        rustdoc_document_private_items,
    247        honor_rust_version,
    248    } = *options;
    249    let gctx = ws.gctx();
    250
    251    // Perform some pre-flight validation.
    252    match build_config.intent {
    253        UserIntent::Test | UserIntent::Build | UserIntent::Check { .. } | UserIntent::Bench => {
    254            if ws.gctx().get_env("RUST_FLAGS").is_ok() {
    255                gctx.shell().print_report(
    256                    &[Level::WARNING
    257                        .secondary_title("ignoring environment variable `RUST_FLAGS`")
    258                        .element(Level::HELP.message("rust flags are passed via `RUSTFLAGS`"))],
    259                    false,
    260                )?;
    261            }
    262        }
    263        UserIntent::Doc { .. } | UserIntent::Doctest => {
    264            if ws.gctx().get_env("RUSTDOC_FLAGS").is_ok() {
    265                gctx.shell().print_report(
    266                    &[Level::WARNING
    267                        .secondary_title("ignoring environment variable `RUSTDOC_FLAGS`")
    268                        .element(
    269                            Level::HELP.message("rustdoc flags are passed via `RUSTDOCFLAGS`"),
    270                        )],
    271                    false,
    272                )?;
    273            }
    274        }
    275    }
    276    gctx.validate_term_config()?;
    277
    278    let mut target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
    279
    280    let specs = spec.to_package_id_specs(ws)?;
    281    let has_dev_units = {
    282        // Rustdoc itself doesn't need dev-dependencies. But to scrape examples from packages in the
    283        // workspace, if any of those packages need dev-dependencies, then we need include dev-dependencies
    284        // to scrape those packages.
    285        let any_pkg_has_scrape_enabled = ws
    286            .members_with_features(&specs, cli_features)?
    287            .iter()
    288            .any(|(pkg, _)| {
    289                pkg.targets()
    290                    .iter()
    291                    .any(|target| target.is_example() && target.doc_scrape_examples().is_enabled())
    292            });
    293
    294        if filter.need_dev_deps(build_config.intent)
    295            || (build_config.intent.is_doc() && any_pkg_has_scrape_enabled)
    296        {
    297            HasDevUnits::Yes
    298        } else {
    299            HasDevUnits::No
    300        }
    301    };
    302    let dry_run = false;
    303    let resolve = ops::resolve_ws_with_opts(
    304        ws,
    305        &mut target_data,
    306        &build_config.requested_kinds,
    307        cli_features,
    308        &specs,
    309        has_dev_units,
    310        crate::core::resolver::features::ForceAllTargets::No,
    311        dry_run,
    312    )?;
    313    let WorkspaceResolve {
    314        mut pkg_set,
    315        workspace_resolve,
    316        targeted_resolve: resolve,
    317        specs_and_features,
    318    } = resolve;
    319
    320    let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
    321        let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
    322            ws,
    323            &mut target_data,
    324            &build_config,
    325            crates,
    326            &build_config.requested_kinds,
    327        )?;
    328        pkg_set.add_set(std_package_set);
    329        Some((std_resolve, std_features))
    330    } else {
    331        None
    332    };
    333
    334    // Find the packages in the resolver that the user wants to build (those
    335    // passed in with `-p` or the defaults from the workspace), and convert
    336    // Vec<PackageIdSpec> to a Vec<PackageId>.
    337    let to_build_ids = resolve.specs_to_ids(&specs)?;
    338    // Now get the `Package` for each `PackageId`. This may trigger a download
    339    // if the user specified `-p` for a dependency that is not downloaded.
    340    // Dependencies will be downloaded during build_unit_dependencies.
    341    let mut to_builds = pkg_set.get_many(to_build_ids)?;
    342
    343    // The ordering here affects some error messages coming out of cargo, so
    344    // let's be test and CLI friendly by always printing in the same order if
    345    // there's an error.
    346    to_builds.sort_by_key(|p| p.package_id());
    347
    348    for pkg in to_builds.iter() {
    349        pkg.manifest().print_teapot(gctx);
    350
    351        if build_config.intent.is_any_test()
    352            && !ws.is_member(pkg)
    353            && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
    354        {
    355            anyhow::bail!(
    356                "package `{}` cannot be tested because it requires dev-dependencies \
    357                 and is not a member of the workspace",
    358                pkg.name()
    359            );
    360        }
    361    }
    362
    363    let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
    364        (Some(args), _) => (Some(args.clone()), "rustc"),
    365        (_, Some(args)) => (Some(args.clone()), "rustdoc"),
    366        _ => (None, ""),
    367    };
    368
    369    if extra_args.is_some() && to_builds.len() != 1 {
    370        panic!(
    371            "`{}` should not accept multiple `-p` flags",
    372            extra_args_name
    373        );
    374    }
    375
    376    let profiles = Profiles::new(ws, build_config.requested_profile)?;
    377    profiles.validate_packages(
    378        ws.profiles(),
    379        &mut gctx.shell(),
    380        workspace_resolve.as_ref().unwrap_or(&resolve),
    381    )?;
    382
    383    // If `--target` has not been specified, then the unit graph is built
    384    // assuming `--target $HOST` was specified. See
    385    // `rebuild_unit_graph_shared` for more on why this is done.
    386    let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
    387    let explicit_host_kinds: Vec<_> = build_config
    388        .requested_kinds
    389        .iter()
    390        .map(|kind| match kind {
    391            CompileKind::Host => explicit_host_kind,
    392            CompileKind::Target(t) => CompileKind::Target(*t),
    393        })
    394        .collect();
    395
    396    let mut units = Vec::new();
    397    let mut unit_graph = HashMap::new();
    398    let mut scrape_units = Vec::new();
    399
    400    for SpecsAndResolvedFeatures {
    401        specs,
    402        resolved_features,
    403    } in &specs_and_features
    404    {
    405        // Passing `build_config.requested_kinds` instead of
    406        // `explicit_host_kinds` here so that `generate_root_units` can do
    407        // its own special handling of `CompileKind::Host`. It will
    408        // internally replace the host kind by the `explicit_host_kind`
    409        // before setting as a unit.
    410        let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
    411        let packages = to_builds
    412            .iter()
    413            .filter(|package| spec_names.contains(&package.name().as_str()))
    414            .cloned()
    415            .collect::<Vec<_>>();
    416        let generator = UnitGenerator {
    417            ws,
    418            packages: &packages,
    419            spec,
    420            target_data: &target_data,
    421            filter,
    422            requested_kinds: &build_config.requested_kinds,
    423            explicit_host_kind,
    424            intent: build_config.intent,
    425            resolve: &resolve,
    426            workspace_resolve: &workspace_resolve,
    427            resolved_features: &resolved_features,
    428            package_set: &pkg_set,
    429            profiles: &profiles,
    430            interner,
    431            has_dev_units,
    432        };
    433        let mut targeted_root_units = generator.generate_root_units()?;
    434
    435        if let Some(args) = target_rustc_crate_types {
    436            override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
    437        }
    438
    439        let should_scrape =
    440            build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
    441        let targeted_scrape_units = if should_scrape {
    442            generator.generate_scrape_units(&targeted_root_units)?
    443        } else {
    444            Vec::new()
    445        };
    446
    447        let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
    448            let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
    449            standard_lib::generate_std_roots(
    450                &crates,
    451                &targeted_root_units,
    452                std_resolve,
    453                std_features,
    454                &explicit_host_kinds,
    455                &pkg_set,
    456                interner,
    457                &profiles,
    458                &target_data,
    459            )?
    460        } else {
    461            Default::default()
    462        };
    463
    464        unit_graph.extend(build_unit_dependencies(
    465            ws,
    466            &pkg_set,
    467            &resolve,
    468            &resolved_features,
    469            std_resolve_features.as_ref(),
    470            &targeted_root_units,
    471            &targeted_scrape_units,
    472            &std_roots,
    473            build_config.intent,
    474            &target_data,
    475            &profiles,
    476            interner,
    477        )?);
    478        units.extend(targeted_root_units);
    479        scrape_units.extend(targeted_scrape_units);
    480    }
    481
    482    // TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
    483    // what heuristics to use in that case.
    484    if build_config.intent.wants_deps_docs() {
    485        remove_duplicate_doc(build_config, &units, &mut unit_graph);
    486    }
    487
    488    let host_kind_requested = build_config
    489        .requested_kinds
    490        .iter()
    491        .any(CompileKind::is_host);
    492    // Rebuild the unit graph, replacing the explicit host targets with
    493    // CompileKind::Host, removing `artifact_target_for_features` and merging any dependencies
    494    // shared with build and artifact dependencies.
    495    (units, scrape_units, unit_graph) = rebuild_unit_graph_shared(
    496        interner,
    497        unit_graph,
    498        &units,
    499        &scrape_units,
    500        host_kind_requested.then_some(explicit_host_kind),
    501        build_config.compile_time_deps_only,
    502    );
    503
    504    let mut extra_compiler_args = HashMap::new();
    505    if let Some(args) = extra_args {
    506        if units.len() != 1 {
    507            anyhow::bail!(
    508                "extra arguments to `{}` can only be passed to one \
    509                 target, consider filtering\nthe package by passing, \
    510                 e.g., `--lib` or `--bin NAME` to specify a single target",
    511                extra_args_name
    512            );
    513        }
    514        extra_compiler_args.insert(units[0].clone(), args);
    515    }
    516
    517    for unit in units
    518        .iter()
    519        .filter(|unit| unit.mode.is_doc() || unit.mode.is_doc_test())
    520        .filter(|unit| rustdoc_document_private_items || unit.target.is_bin())
    521    {
    522        // Add `--document-private-items` rustdoc flag if requested or if
    523        // the target is a binary. Binary crates get their private items
    524        // documented by default.
    525        let mut args = vec!["--document-private-items".into()];
    526        if unit.target.is_bin() {
    527            // This warning only makes sense if it's possible to document private items
    528            // sometimes and ignore them at other times. But cargo consistently passes
    529            // `--document-private-items`, so the warning isn't useful.
    530            args.push("-Arustdoc::private-intra-doc-links".into());
    531        }
    532        extra_compiler_args
    533            .entry(unit.clone())
    534            .or_default()
    535            .extend(args);
    536    }
    537
    538    if honor_rust_version.unwrap_or(true) {
    539        let rustc_version = target_data.rustc.version.clone().into();
    540
    541        let mut incompatible = Vec::new();
    542        let mut local_incompatible = false;
    543        for unit in unit_graph.keys() {
    544            let Some(pkg_msrv) = unit.pkg.rust_version() else {
    545                continue;
    546            };
    547
    548            if pkg_msrv.is_compatible_with(&rustc_version) {
    549                continue;
    550            }
    551
    552            local_incompatible |= unit.is_local();
    553            incompatible.push((unit, pkg_msrv));
    554        }
    555        if !incompatible.is_empty() {
    556            use std::fmt::Write as _;
    557
    558            let plural = if incompatible.len() == 1 { "" } else { "s" };
    559            let mut message = format!(
    560                "rustc {rustc_version} is not supported by the following package{plural}:\n"
    561            );
    562            incompatible.sort_by_key(|(unit, _)| (unit.pkg.name(), unit.pkg.version()));
    563            for (unit, msrv) in incompatible {
    564                let name = &unit.pkg.name();
    565                let version = &unit.pkg.version();
    566                writeln!(&mut message, "  {name}@{version} requires rustc {msrv}").unwrap();
    567            }
    568            if ws.is_ephemeral() {
    569                if ws.ignore_lock() {
    570                    writeln!(
    571                        &mut message,
    572                        "Try re-running `cargo install` with `--locked`"
    573                    )
    574                    .unwrap();
    575                }
    576            } else if !local_incompatible {
    577                writeln!(
    578                    &mut message,
    579                    "Either upgrade rustc or select compatible dependency versions with
    580`cargo update <name>@<current-ver> --precise <compatible-ver>`
    581where `<compatible-ver>` is the latest version supporting rustc {rustc_version}",
    582                )
    583                .unwrap();
    584            }
    585            return Err(anyhow::Error::msg(message));
    586        }
    587    }
    588
    589    let bcx = BuildContext::new(
    590        ws,
    591        logger,
    592        pkg_set,
    593        build_config,
    594        profiles,
    595        extra_compiler_args,
    596        target_data,
    597        units,
    598        unit_graph,
    599        scrape_units,
    600    )?;
    601
    602    Ok(bcx)
    603}
    604
    605/// This is used to rebuild the unit graph, sharing host dependencies if possible,
    606/// and applying other unit adjustments based on the whole graph.
    607///
    608/// This will translate any unit's `CompileKind::Target(host)` to
    609/// `CompileKind::Host` if `to_host` is not `None` and the kind is equal to `to_host`.
    610/// This also handles generating the unit `dep_hash`, and merging shared units if possible.
    611///
    612/// This is necessary because if normal dependencies used `CompileKind::Host`,
    613/// there would be no way to distinguish those units from build-dependency
    614/// units or artifact dependency units.
    615/// This can cause a problem if a shared normal/build/artifact dependency needs
    616/// to link to another dependency whose features differ based on whether or
    617/// not it is a normal, build or artifact dependency. If all units used
    618/// `CompileKind::Host`, then they would end up being identical, causing a
    619/// collision in the `UnitGraph`, and Cargo would end up randomly choosing one
    620/// value or the other.
    621///
    622/// The solution is to keep normal, build and artifact dependencies separate when
    623/// building the unit graph, and then run this second pass which will try to
    624/// combine shared dependencies safely. By adding a hash of the dependencies
    625/// to the `Unit`, this allows the `CompileKind` to be changed back to `Host`
    626/// and `artifact_target_for_features` to be removed without fear of an unwanted
    627/// collision for build or artifact dependencies.
    628///
    629/// This is also responsible for adjusting the `strip` profile option to
    630/// opportunistically strip if debug is 0 for all dependencies. This helps
    631/// remove debuginfo added by the standard library.
    632///
    633/// This is also responsible for adjusting the `debug` setting for host
    634/// dependencies, turning off debug if the user has not explicitly enabled it,
    635/// and the unit is not shared with a target unit.
    636///
    637/// This is also responsible for adjusting whether each unit should be compiled
    638/// or not regarding `--compile-time-deps` flag.
    639fn rebuild_unit_graph_shared(
    640    interner: &UnitInterner,
    641    unit_graph: UnitGraph,
    642    roots: &[Unit],
    643    scrape_units: &[Unit],
    644    to_host: Option<CompileKind>,
    645    compile_time_deps_only: bool,
    646) -> (Vec<Unit>, Vec<Unit>, UnitGraph) {
    647    let mut result = UnitGraph::new();
    648    // Map of the old unit to the new unit, used to avoid recursing into units
    649    // that have already been computed to improve performance.
    650    let mut memo = HashMap::new();
    651    let new_roots = roots
    652        .iter()
    653        .map(|root| {
    654            traverse_and_share(
    655                interner,
    656                &mut memo,
    657                &mut result,
    658                &unit_graph,
    659                root,
    660                true,
    661                false,
    662                to_host,
    663                compile_time_deps_only,
    664            )
    665        })
    666        .collect();
    667    // If no unit in the unit graph ended up having scrape units attached as dependencies,
    668    // then they won't have been discovered in traverse_and_share and hence won't be in
    669    // memo. So we filter out missing scrape units.
    670    let new_scrape_units = scrape_units
    671        .iter()
    672        .map(|unit| memo.get(unit).unwrap().clone())
    673        .collect();
    674    (new_roots, new_scrape_units, result)
    675}
    676
    677/// Recursive function for rebuilding the graph.
    678///
    679/// This walks `unit_graph`, starting at the given `unit`. It inserts the new
    680/// units into `new_graph`, and returns a new updated version of the given
    681/// unit (`dep_hash` is filled in, and `kind` switched if necessary).
    682fn traverse_and_share(
    683    interner: &UnitInterner,
    684    memo: &mut HashMap<Unit, Unit>,
    685    new_graph: &mut UnitGraph,
    686    unit_graph: &UnitGraph,
    687    unit: &Unit,
    688    unit_is_root: bool,
    689    unit_is_for_host: bool,
    690    to_host: Option<CompileKind>,
    691    compile_time_deps_only: bool,
    692) -> Unit {
    693    if let Some(new_unit) = memo.get(unit) {
    694        // Already computed, no need to recompute.
    695        return new_unit.clone();
    696    }
    697    let mut dep_hash = StableHasher::new();
    698    let skip_non_compile_time_deps = compile_time_deps_only
    699        && (!unit.target.is_compile_time_dependency() ||
    700            // Root unit is not a dependency unless other units are dependant
    701            // to it.
    702            unit_is_root);
    703    let new_deps: Vec<_> = unit_graph[unit]
    704        .iter()
    705        .map(|dep| {
    706            let new_dep_unit = traverse_and_share(
    707                interner,
    708                memo,
    709                new_graph,
    710                unit_graph,
    711                &dep.unit,
    712                false,
    713                dep.unit_for.is_for_host(),
    714                to_host,
    715                // If we should compile the current unit, we should also compile
    716                // its dependencies. And if not, we should compile compile time
    717                // dependencies only.
    718                skip_non_compile_time_deps,
    719            );
    720            new_dep_unit.hash(&mut dep_hash);
    721            UnitDep {
    722                unit: new_dep_unit,
    723                ..dep.clone()
    724            }
    725        })
    726        .collect();
    727    // Here, we have recursively traversed this unit's dependencies, and hashed them: we can
    728    // finalize the dep hash.
    729    let new_dep_hash = Hasher::finish(&dep_hash);
    730
    731    // This is the key part of the sharing process: if the unit is a runtime dependency, whose
    732    // target is the same as the host, we canonicalize the compile kind to `CompileKind::Host`.
    733    // A possible host dependency counterpart to this unit would have that kind, and if such a unit
    734    // exists in the current `unit_graph`, they will unify in the new unit graph map `new_graph`.
    735    // The resulting unit graph will be optimized with less units, thanks to sharing these host
    736    // dependencies.
    737    let canonical_kind = match to_host {
    738        Some(to_host) if to_host == unit.kind => CompileKind::Host,
    739        _ => unit.kind,
    740    };
    741
    742    let mut profile = unit.profile.clone();
    743    if profile.strip.is_deferred() {
    744        // If strip was not manually set, and all dependencies of this unit together
    745        // with this unit have debuginfo turned off, we enable debuginfo stripping.
    746        // This will remove pre-existing debug symbols coming from the standard library.
    747        if !profile.debuginfo.is_turned_on()
    748            && new_deps
    749                .iter()
    750                .all(|dep| !dep.unit.profile.debuginfo.is_turned_on())
    751        {
    752            profile.strip = profile.strip.strip_debuginfo();
    753        }
    754    }
    755
    756    // If this is a build dependency, and it's not shared with runtime dependencies, we can weaken
    757    // its debuginfo level to optimize build times. We do nothing if it's an artifact dependency,
    758    // as it and its debuginfo may end up embedded in the main program.
    759    if unit_is_for_host
    760        && to_host.is_some()
    761        && profile.debuginfo.is_deferred()
    762        && !unit.artifact.is_true()
    763    {
    764        // We create a "probe" test to see if a unit with the same explicit debuginfo level exists
    765        // in the graph. This is the level we'd expect if it was set manually or the default value
    766        // set by a profile for a runtime dependency: its canonical value.
    767        let canonical_debuginfo = profile.debuginfo.finalize();
    768        let mut canonical_profile = profile.clone();
    769        canonical_profile.debuginfo = canonical_debuginfo;
    770        let unit_probe = interner.intern(
    771            &unit.pkg,
    772            &unit.target,
    773            canonical_profile,
    774            to_host.unwrap(),
    775            unit.mode,
    776            unit.features.clone(),
    777            unit.rustflags.clone(),
    778            unit.rustdocflags.clone(),
    779            unit.links_overrides.clone(),
    780            unit.is_std,
    781            unit.dep_hash,
    782            unit.artifact,
    783            unit.artifact_target_for_features,
    784            unit.skip_non_compile_time_dep,
    785        );
    786
    787        // We can now turn the deferred value into its actual final value.
    788        profile.debuginfo = if unit_graph.contains_key(&unit_probe) {
    789            // The unit is present in both build time and runtime subgraphs: we canonicalize its
    790            // level to the other unit's, thus ensuring reuse between the two to optimize build times.
    791            canonical_debuginfo
    792        } else {
    793            // The unit is only present in the build time subgraph, we can weaken its debuginfo
    794            // level to optimize build times.
    795            canonical_debuginfo.weaken()
    796        }
    797    }
    798
    799    let new_unit = interner.intern(
    800        &unit.pkg,
    801        &unit.target,
    802        profile,
    803        canonical_kind,
    804        unit.mode,
    805        unit.features.clone(),
    806        unit.rustflags.clone(),
    807        unit.rustdocflags.clone(),
    808        unit.links_overrides.clone(),
    809        unit.is_std,
    810        new_dep_hash,
    811        unit.artifact,
    812        // Since `dep_hash` is now filled in, there's no need to specify the artifact target
    813        // for target-dependent feature resolution
    814        None,
    815        skip_non_compile_time_deps,
    816    );
    817    if !unit_is_root || !compile_time_deps_only {
    818        assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
    819    }
    820    new_graph.entry(new_unit.clone()).or_insert(new_deps);
    821    new_unit
    822}
    823
    824/// Removes duplicate `CompileMode::Doc` units that would cause problems with
    825/// filename collisions.
    826///
    827/// Rustdoc only separates units by crate name in the file directory
    828/// structure. If any two units with the same crate name exist, this would
    829/// cause a filename collision, causing different rustdoc invocations to stomp
    830/// on one another's files.
    831///
    832/// Unfortunately this does not remove all duplicates, as some of them are
    833/// either user error, or difficult to remove. Cases that I can think of:
    834///
    835/// - Same target name in different packages. See the `collision_doc` test.
    836/// - Different sources. See `collision_doc_sources` test.
    837///
    838/// Ideally this would not be necessary.
    839fn remove_duplicate_doc(
    840    build_config: &BuildConfig,
    841    root_units: &[Unit],
    842    unit_graph: &mut UnitGraph,
    843) {
    844    // First, create a mapping of crate_name -> Unit so we can see where the
    845    // duplicates are.
    846    let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
    847    for unit in unit_graph.keys() {
    848        if unit.mode.is_doc() {
    849            all_docs
    850                .entry(unit.target.crate_name())
    851                .or_default()
    852                .push(unit.clone());
    853        }
    854    }
    855    // Keep track of units to remove so that they can be efficiently removed
    856    // from the unit_deps.
    857    let mut removed_units: HashSet<Unit> = HashSet::new();
    858    let mut remove = |units: Vec<Unit>, reason: &str, cb: &dyn Fn(&Unit) -> bool| -> Vec<Unit> {
    859        let (to_remove, remaining_units): (Vec<Unit>, Vec<Unit>) = units
    860            .into_iter()
    861            .partition(|unit| cb(unit) && !root_units.contains(unit));
    862        for unit in to_remove {
    863            tracing::debug!(
    864                "removing duplicate doc due to {} for package {} target `{}`",
    865                reason,
    866                unit.pkg,
    867                unit.target.name()
    868            );
    869            unit_graph.remove(&unit);
    870            removed_units.insert(unit);
    871        }
    872        remaining_units
    873    };
    874    // Iterate over the duplicates and try to remove them from unit_graph.
    875    for (_crate_name, mut units) in all_docs {
    876        if units.len() == 1 {
    877            continue;
    878        }
    879        // Prefer target over host if --target was not specified.
    880        if build_config
    881            .requested_kinds
    882            .iter()
    883            .all(CompileKind::is_host)
    884        {
    885            // Note these duplicates may not be real duplicates, since they
    886            // might get merged in rebuild_unit_graph_shared. Either way, it
    887            // shouldn't hurt to remove them early (although the report in the
    888            // log might be confusing).
    889            units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
    890            if units.len() == 1 {
    891                continue;
    892            }
    893        }
    894        // Prefer newer versions over older.
    895        let mut source_map: HashMap<(InternedString, SourceId, CompileKind), Vec<Unit>> =
    896            HashMap::new();
    897        for unit in units {
    898            let pkg_id = unit.pkg.package_id();
    899            // Note, this does not detect duplicates from different sources.
    900            source_map
    901                .entry((pkg_id.name(), pkg_id.source_id(), unit.kind))
    902                .or_default()
    903                .push(unit);
    904        }
    905        let mut remaining_units = Vec::new();
    906        for (_key, mut units) in source_map {
    907            if units.len() > 1 {
    908                units.sort_by(|a, b| a.pkg.version().partial_cmp(b.pkg.version()).unwrap());
    909                // Remove any entries with version < newest.
    910                let newest_version = units.last().unwrap().pkg.version().clone();
    911                let keep_units = remove(units, "older version", &|unit| {
    912                    unit.pkg.version() < &newest_version
    913                });
    914                remaining_units.extend(keep_units);
    915            } else {
    916                remaining_units.extend(units);
    917            }
    918        }
    919        if remaining_units.len() == 1 {
    920            continue;
    921        }
    922        // Are there other heuristics to remove duplicates that would make
    923        // sense? Maybe prefer path sources over all others?
    924    }
    925    // Also remove units from the unit_deps so there aren't any dangling edges.
    926    for unit_deps in unit_graph.values_mut() {
    927        unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
    928    }
    929    // Remove any orphan units that were detached from the graph.
    930    let mut visited = HashSet::new();
    931    fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
    932        if !visited.insert(unit.clone()) {
    933            return;
    934        }
    935        for dep in &graph[unit] {
    936            visit(&dep.unit, graph, visited);
    937        }
    938    }
    939    for unit in root_units {
    940        visit(unit, unit_graph, &mut visited);
    941    }
    942    unit_graph.retain(|unit, _| visited.contains(unit));
    943}
    944
    945/// Override crate types for given units.
    946///
    947/// This is primarily used by `cargo rustc --crate-type`.
    948fn override_rustc_crate_types(
    949    units: &mut [Unit],
    950    args: &[String],
    951    interner: &UnitInterner,
    952) -> CargoResult<()> {
    953    if units.len() != 1 {
    954        anyhow::bail!(
    955            "crate types to rustc can only be passed to one \
    956            target, consider filtering\nthe package by passing, \
    957            e.g., `--lib` or `--example` to specify a single target"
    958        );
    959    }
    960
    961    let unit = &units[0];
    962    let override_unit = |f: fn(Vec<CrateType>) -> TargetKind| {
    963        let crate_types = args.iter().map(|s| s.into()).collect();
    964        let mut target = unit.target.clone();
    965        target.set_kind(f(crate_types));
    966        interner.intern(
    967            &unit.pkg,
    968            &target,
    969            unit.profile.clone(),
    970            unit.kind,
    971            unit.mode,
    972            unit.features.clone(),
    973            unit.rustflags.clone(),
    974            unit.rustdocflags.clone(),
    975            unit.links_overrides.clone(),
    976            unit.is_std,
    977            unit.dep_hash,
    978            unit.artifact,
    979            unit.artifact_target_for_features,
    980            unit.skip_non_compile_time_dep,
    981        )
    982    };
    983    units[0] = match unit.target.kind() {
    984        TargetKind::Lib(_) => override_unit(TargetKind::Lib),
    985        TargetKind::ExampleLib(_) => override_unit(TargetKind::ExampleLib),
    986        _ => {
    987            anyhow::bail!(
    988                "crate types can only be specified for libraries and example libraries.\n\
    989                Binaries, tests, and benchmarks are always the `bin` crate type"
    990            );
    991        }
    992    };
    993
    994    Ok(())
    995}
    996
    997/// Gets all of the features enabled for a package, plus its dependencies'
    998/// features.
    999///
    1000/// Dependencies are added as `dep_name/feat_name` because `required-features`
    1001/// wants to support that syntax.
    1002pub fn resolve_all_features(
    1003    resolve_with_overrides: &Resolve,
    1004    resolved_features: &features::ResolvedFeatures,
    1005    package_set: &PackageSet<'_>,
    1006    package_id: PackageId,
    1007) -> HashSet<String> {
    1008    let mut features: HashSet<String> = resolved_features
    1009        .activated_features(package_id, FeaturesFor::NormalOrDev)
    1010        .iter()
    1011        .map(|s| s.to_string())
    1012        .collect();
    1013
    1014    // Include features enabled for use by dependencies so targets can also use them with the
    1015    // required-features field when deciding whether to be built or skipped.
    1016    for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
    1017        let is_proc_macro = package_set
    1018            .get_one(dep_id)
    1019            .expect("packages downloaded")
    1020            .proc_macro();
    1021        for dep in deps {
    1022            let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
    1023            for feature in resolved_features
    1024                .activated_features_unverified(dep_id, features_for)
    1025                .unwrap_or_default()
    1026            {
    1027                features.insert(format!("{}/{}", dep.name_in_toml(), feature));
    1028            }
    1029        }
    1030    }
    1031
    1032    features
    1033}