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