Skip to main content

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