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