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