1use std::collections::{HashMap, HashSet};
39use std::hash::{Hash, Hasher};
40use std::sync::Arc;
41
42use crate::core::compiler::UserIntent;
43use crate::core::compiler::unit_dependencies::build_unit_dependencies;
44use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph};
45use crate::core::compiler::{BuildConfig, BuildContext, BuildRunner, Compilation};
46use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData, Unit};
47use crate::core::compiler::{CrateType, TargetInfo, apply_env_config, standard_lib};
48use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
49use crate::core::compiler::{DepKindSet, UnitIndex};
50use crate::core::profiles::Profiles;
51use crate::core::resolver::features::{self, CliFeatures, FeaturesFor};
52use crate::core::resolver::{ForceAllTargets, HasDevUnits, Resolve};
53use crate::core::{PackageId, PackageSet, SourceId, TargetKind, Workspace};
54use crate::drop_println;
55use crate::ops;
56use crate::ops::resolve::{SpecsAndResolvedFeatures, WorkspaceResolve};
57use crate::util::BuildLogger;
58use crate::util::context::{GlobalContext, WarningHandling};
59use crate::util::interning::InternedString;
60use crate::util::log_message::LogMessage;
61use crate::util::{CargoResult, StableHasher};
62
63mod compile_filter;
64use cargo_util_terminal::report::{Group, Level, Origin};
65pub use compile_filter::{CompileFilter, FilterRule, LibRule};
66
67pub(super) mod unit_generator;
68use itertools::Itertools as _;
69use unit_generator::UnitGenerator;
70
71mod packages;
72
73pub use packages::Packages;
74
75#[derive(Debug, Clone)]
84pub struct CompileOptions {
85 pub build_config: BuildConfig,
87 pub cli_features: CliFeatures,
89 pub spec: Packages,
91 pub filter: CompileFilter,
94 pub target_rustdoc_args: Option<Vec<String>>,
96 pub target_rustc_args: Option<Vec<String>>,
99 pub target_rustc_crate_types: Option<Vec<String>>,
101 pub rustdoc_document_private_items: bool,
104 pub honor_rust_version: Option<bool>,
107}
108
109impl CompileOptions {
110 pub fn new(gctx: &GlobalContext, intent: UserIntent) -> CargoResult<CompileOptions> {
111 let jobs = None;
112 let keep_going = false;
113 Ok(CompileOptions {
114 build_config: BuildConfig::new(gctx, jobs, keep_going, &[], intent)?,
115 cli_features: CliFeatures::new_all(false),
116 spec: ops::Packages::Packages(Vec::new()),
117 filter: CompileFilter::Default {
118 required_features_filterable: false,
119 },
120 target_rustdoc_args: None,
121 target_rustc_args: None,
122 target_rustc_crate_types: None,
123 rustdoc_document_private_items: false,
124 honor_rust_version: None,
125 })
126 }
127}
128
129pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> {
133 let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
134 compile_with_exec(ws, options, &exec)
135}
136
137pub fn compile_with_exec<'a>(
142 ws: &Workspace<'a>,
143 options: &CompileOptions,
144 exec: &Arc<dyn Executor>,
145) -> CargoResult<Compilation<'a>> {
146 crate::diagnostics::passes::emit_parse_diagnostics(
147 ws,
148 crate::diagnostics::rules::PARSE_PASS_RULES,
149 )?;
150 let compilation = compile_ws(ws, options, exec)?;
151 if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.lint_warning_count > 0
152 {
153 anyhow::bail!("warnings are denied by `build.warnings` configuration")
154 }
155 Ok(compilation)
156}
157
158#[tracing::instrument(skip_all)]
160pub fn compile_ws<'a>(
161 ws: &Workspace<'a>,
162 options: &CompileOptions,
163 exec: &Arc<dyn Executor>,
164) -> CargoResult<Compilation<'a>> {
165 let interner = UnitInterner::new();
166 let logger = BuildLogger::maybe_new(ws, &options.build_config)?;
167
168 if let Some(ref logger) = logger {
169 let rustc = ws.gctx().load_global_rustc(Some(ws))?;
170 let num_cpus = std::thread::available_parallelism()
171 .ok()
172 .map(|x| x.get() as u64);
173 logger.log(LogMessage::BuildStarted {
174 command: std::env::args_os()
175 .map(|arg| arg.to_string_lossy().into_owned())
176 .collect(),
177 cwd: ws.gctx().cwd().to_path_buf(),
178 host: rustc.host.to_string(),
179 jobs: options.build_config.jobs,
180 num_cpus,
181 profile: options.build_config.requested_profile.to_string(),
182 rustc_version: rustc.version.to_string(),
183 rustc_version_verbose: rustc.verbose_version.clone(),
184 target_dir: ws.target_dir().as_path_unlocked().to_path_buf(),
185 workspace_root: ws.root().to_path_buf(),
186 });
187 }
188
189 let bcx = create_bcx(ws, options, &interner, logger.as_ref())?;
190
191 if options.build_config.unit_graph {
192 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.gctx())?;
193 return Compilation::new(&bcx);
194 }
195 crate::core::gc::auto_gc(bcx.gctx);
196 let build_runner = BuildRunner::new(&bcx)?;
197 if options.build_config.dry_run {
198 build_runner.dry_run()
199 } else {
200 build_runner.compile(exec)
201 }
202}
203
204pub fn print<'a>(
208 ws: &Workspace<'a>,
209 options: &CompileOptions,
210 print_opt_value: &str,
211) -> CargoResult<()> {
212 let CompileOptions {
213 ref build_config,
214 ref target_rustc_args,
215 ..
216 } = *options;
217 let gctx = ws.gctx();
218 let rustc = gctx.load_global_rustc(Some(ws))?;
219 for (index, kind) in build_config.requested_kinds.iter().enumerate() {
220 if index != 0 {
221 drop_println!(gctx);
222 }
223 let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?;
224 let mut process = rustc.process();
225 apply_env_config(gctx, &mut process)?;
226 process.args(&target_info.rustflags);
227 if let Some(args) = target_rustc_args {
228 process.args(args);
229 }
230 kind.add_target_arg(&mut process);
231 process.arg("--print").arg(print_opt_value);
232 process.exec()?;
233 }
234 Ok(())
235}
236
237#[tracing::instrument(skip_all)]
242pub fn create_bcx<'a, 'gctx>(
243 ws: &'a Workspace<'gctx>,
244 options: &'a CompileOptions,
245 interner: &'a UnitInterner,
246 logger: Option<&'a BuildLogger>,
247) -> CargoResult<BuildContext<'a, 'gctx>> {
248 let CompileOptions {
249 ref build_config,
250 ref spec,
251 ref cli_features,
252 ref filter,
253 ref target_rustdoc_args,
254 ref target_rustc_args,
255 ref target_rustc_crate_types,
256 rustdoc_document_private_items,
257 honor_rust_version,
258 } = *options;
259 let gctx = ws.gctx();
260
261 match build_config.intent {
263 UserIntent::Test | UserIntent::Build | UserIntent::Check { .. } | UserIntent::Bench => {
264 if ws.gctx().get_env("RUST_FLAGS").is_ok() {
265 gctx.shell().print_report(
266 &[Level::WARNING
267 .secondary_title("ignoring environment variable `RUST_FLAGS`")
268 .element(Level::HELP.message("rust flags are passed via `RUSTFLAGS`"))],
269 false,
270 )?;
271 }
272 }
273 UserIntent::Doc { .. } | UserIntent::Doctest => {
274 if ws.gctx().get_env("RUSTDOC_FLAGS").is_ok() {
275 gctx.shell().print_report(
276 &[Level::WARNING
277 .secondary_title("ignoring environment variable `RUSTDOC_FLAGS`")
278 .element(
279 Level::HELP.message("rustdoc flags are passed via `RUSTDOCFLAGS`"),
280 )],
281 false,
282 )?;
283 }
284 }
285 }
286 gctx.validate_term_config()?;
287
288 let mut target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
289
290 let specs = spec.to_package_id_specs(ws)?;
291 let has_dev_units = {
292 let any_pkg_has_scrape_enabled = ws
296 .members_with_features(&specs, cli_features)?
297 .iter()
298 .any(|(pkg, _)| {
299 pkg.targets()
300 .iter()
301 .any(|target| target.is_example() && target.doc_scrape_examples().is_enabled())
302 });
303
304 if filter.need_dev_deps(build_config.intent)
305 || (build_config.intent.is_doc() && any_pkg_has_scrape_enabled)
306 {
307 HasDevUnits::Yes
308 } else {
309 HasDevUnits::No
310 }
311 };
312 let dry_run = false;
313
314 if let Some(logger) = logger {
315 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
316 logger.log(LogMessage::ResolutionStarted { elapsed });
317 }
318
319 let resolve = ops::resolve_ws_with_opts(
320 ws,
321 &mut target_data,
322 &build_config.requested_kinds,
323 cli_features,
324 &specs,
325 has_dev_units,
326 ForceAllTargets::No,
327 dry_run,
328 )?;
329 let WorkspaceResolve {
330 mut pkg_set,
331 workspace_resolve,
332 targeted_resolve: resolve,
333 specs_and_features,
334 } = resolve;
335
336 if let Some(logger) = logger {
337 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
338 logger.log(LogMessage::ResolutionFinished { elapsed });
339 }
340
341 let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
342 let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
343 ws,
344 &mut target_data,
345 &build_config,
346 crates,
347 &build_config.requested_kinds,
348 )?;
349 pkg_set.add_set(std_package_set);
350 Some((std_resolve, std_features))
351 } else {
352 None
353 };
354
355 let to_build_ids = resolve.specs_to_ids(&specs)?;
359 let mut to_builds = pkg_set.get_many(to_build_ids)?;
363
364 to_builds.sort_by_key(|p| p.package_id());
368
369 for pkg in to_builds.iter() {
370 pkg.manifest().print_teapot(gctx);
371
372 if build_config.intent.is_any_test()
373 && !ws.is_member(pkg)
374 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
375 {
376 anyhow::bail!(
377 "package `{}` cannot be tested because it requires dev-dependencies \
378 and is not a member of the workspace",
379 pkg.name()
380 );
381 }
382 }
383
384 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
385 (Some(args), _) => (Some(args.clone()), "rustc"),
386 (_, Some(args)) => (Some(args.clone()), "rustdoc"),
387 _ => (None, ""),
388 };
389
390 if extra_args.is_some() && to_builds.len() != 1 {
391 panic!(
392 "`{}` should not accept multiple `-p` flags",
393 extra_args_name
394 );
395 }
396
397 let profiles = Profiles::new(ws, build_config.requested_profile)?;
398 profiles.validate_packages(
399 ws.profiles(),
400 &mut gctx.shell(),
401 workspace_resolve.as_ref().unwrap_or(&resolve),
402 )?;
403
404 let explicit_host_kind = CompileKind::Target(CompileTarget::new(
408 &target_data.rustc.host,
409 gctx.cli_unstable().json_target_spec,
410 )?);
411 let explicit_host_kinds: Vec<_> = build_config
412 .requested_kinds
413 .iter()
414 .map(|kind| match kind {
415 CompileKind::Host => explicit_host_kind,
416 CompileKind::Target(t) => CompileKind::Target(*t),
417 })
418 .collect();
419
420 let mut root_units = Vec::new();
421 let mut unit_graph = HashMap::new();
422 let mut scrape_units = Vec::new();
423
424 if let Some(logger) = logger {
425 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
426 logger.log(LogMessage::UnitGraphStarted { elapsed });
427 }
428
429 let mut selected_dep_kinds = DepKindSet::default();
430 for SpecsAndResolvedFeatures {
431 specs,
432 resolved_features,
433 } in &specs_and_features
434 {
435 let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
441 let packages = to_builds
442 .iter()
443 .filter(|package| spec_names.contains(&package.name().as_str()))
444 .cloned()
445 .collect::<Vec<_>>();
446 let generator = UnitGenerator {
447 ws,
448 packages: &packages,
449 spec,
450 target_data: &target_data,
451 filter,
452 requested_kinds: &build_config.requested_kinds,
453 explicit_host_kind,
454 intent: build_config.intent,
455 resolve: &resolve,
456 workspace_resolve: &workspace_resolve,
457 resolved_features: &resolved_features,
458 package_set: &pkg_set,
459 profiles: &profiles,
460 interner,
461 has_dev_units,
462 };
463 let (mut targeted_root_units, curr_selected_dep_kinds) = generator.generate_root_units()?;
464 selected_dep_kinds = curr_selected_dep_kinds;
466
467 if let Some(args) = target_rustc_crate_types {
468 override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
469 }
470
471 let should_scrape =
472 build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
473 let targeted_scrape_units = if should_scrape {
474 generator.generate_scrape_units(&targeted_root_units)?
475 } else {
476 Vec::new()
477 };
478
479 let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
480 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
481 standard_lib::generate_std_roots(
482 &crates,
483 &targeted_root_units,
484 std_resolve,
485 std_features,
486 &explicit_host_kinds,
487 &pkg_set,
488 interner,
489 &profiles,
490 &target_data,
491 )?
492 } else {
493 Default::default()
494 };
495
496 unit_graph.extend(build_unit_dependencies(
497 ws,
498 &pkg_set,
499 &resolve,
500 &resolved_features,
501 std_resolve_features.as_ref(),
502 &targeted_root_units,
503 &targeted_scrape_units,
504 &std_roots,
505 build_config.intent,
506 &target_data,
507 &profiles,
508 interner,
509 )?);
510 root_units.extend(targeted_root_units);
511 scrape_units.extend(targeted_scrape_units);
512 }
513
514 if build_config.intent.wants_deps_docs() {
517 remove_duplicate_doc(build_config, &root_units, &mut unit_graph);
518 }
519
520 let host_kind_requested = build_config
521 .requested_kinds
522 .iter()
523 .any(CompileKind::is_host);
524 let (root_units, scrape_units, unit_graph) = rebuild_unit_graph_shared(
530 interner,
531 unit_graph,
532 &root_units,
533 &scrape_units,
534 host_kind_requested.then_some(explicit_host_kind),
535 build_config.compile_time_deps_only,
536 );
537
538 let units: Vec<_> = unit_graph.keys().sorted().collect();
539 let unit_to_index: HashMap<_, _> = units
540 .iter()
541 .enumerate()
542 .map(|(i, &unit)| (unit.clone(), UnitIndex(i as u64)))
543 .collect();
544
545 if let Some(logger) = logger {
546 let root_unit_indexes: HashSet<_> =
547 root_units.iter().map(|unit| unit_to_index[&unit]).collect();
548
549 for (index, unit) in units.into_iter().enumerate() {
550 let index = UnitIndex(index as u64);
551 let dependencies = unit_graph
552 .get(unit)
553 .map(|deps| {
554 deps.iter()
555 .filter_map(|dep| unit_to_index.get(&dep.unit).copied())
556 .collect()
557 })
558 .unwrap_or_default();
559 logger.log(LogMessage::UnitRegistered {
560 package_id: unit.pkg.package_id().to_spec(),
561 target: (&unit.target).into(),
562 mode: unit.mode,
563 platform: target_data.short_name(&unit.kind).to_owned(),
564 index,
565 features: unit
566 .features
567 .iter()
568 .map(|s| s.as_str().to_owned())
569 .collect(),
570 requested: root_unit_indexes.contains(&index),
571 dependencies,
572 });
573 }
574 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
575 logger.log(LogMessage::UnitGraphFinished { elapsed });
576 }
577
578 let mut extra_compiler_args = HashMap::new();
579 if let Some(args) = extra_args {
580 if root_units.len() != 1 {
581 anyhow::bail!(
582 "extra arguments to `{}` can only be passed to one \
583 target, consider filtering\nthe package by passing, \
584 e.g., `--lib` or `--bin NAME` to specify a single target",
585 extra_args_name
586 );
587 }
588 extra_compiler_args.insert(root_units[0].clone(), args);
589 }
590
591 for unit in root_units
592 .iter()
593 .filter(|unit| unit.mode.is_doc() || unit.mode.is_doc_test())
594 .filter(|unit| rustdoc_document_private_items || unit.target.is_bin())
595 {
596 let mut args = vec!["--document-private-items".into()];
600 if unit.target.is_bin() {
601 args.push("-Arustdoc::private-intra-doc-links".into());
605 }
606 extra_compiler_args
607 .entry(unit.clone())
608 .or_default()
609 .extend(args);
610 }
611
612 let mut error_count: usize = 0;
614 for unit in &root_units {
615 if let Some(target_src_path) = unit.target.src_path().path() {
616 validate_target_path_as_source_file(
617 gctx,
618 target_src_path,
619 unit.target.name(),
620 unit.target.kind(),
621 unit.pkg.manifest_path(),
622 &mut error_count,
623 )?
624 }
625 }
626 if error_count > 0 {
627 let plural: &str = if error_count > 1 { "s" } else { "" };
628 anyhow::bail!(
629 "could not compile due to {error_count} previous target resolution error{plural}"
630 );
631 }
632
633 if honor_rust_version.unwrap_or(true) {
634 let rustc_version = target_data.rustc.version.clone().into();
635
636 let mut incompatible = Vec::new();
637 let mut local_incompatible = false;
638 for unit in unit_graph.keys() {
639 let Some(pkg_msrv) = unit.pkg.rust_version() else {
640 continue;
641 };
642
643 if pkg_msrv.is_compatible_with(&rustc_version) {
644 continue;
645 }
646
647 local_incompatible |= unit.is_local();
648 incompatible.push((unit, pkg_msrv));
649 }
650 if !incompatible.is_empty() {
651 use std::fmt::Write as _;
652
653 let plural = if incompatible.len() == 1 { "" } else { "s" };
654 let mut message = format!(
655 "rustc {rustc_version} is not supported by the following package{plural}:\n"
656 );
657 incompatible.sort_by_key(|(unit, _)| (unit.pkg.name(), unit.pkg.version()));
658 for (unit, msrv) in incompatible {
659 let name = &unit.pkg.name();
660 let version = &unit.pkg.version();
661 writeln!(&mut message, " {name}@{version} requires rustc {msrv}").unwrap();
662 }
663 if ws.is_ephemeral() {
664 if ws.ignore_lock() {
665 writeln!(
666 &mut message,
667 "Try re-running `cargo install` with `--locked`"
668 )
669 .unwrap();
670 }
671 } else if !local_incompatible {
672 writeln!(
673 &mut message,
674 "Either upgrade rustc or select compatible dependency versions with
675`cargo update <name>@<current-ver> --precise <compatible-ver>`
676where `<compatible-ver>` is the latest version supporting rustc {rustc_version}",
677 )
678 .unwrap();
679 }
680 return Err(anyhow::Error::msg(message));
681 }
682 }
683
684 let bcx = BuildContext::new(
685 ws,
686 logger,
687 pkg_set,
688 build_config,
689 selected_dep_kinds,
690 profiles,
691 extra_compiler_args,
692 target_data,
693 root_units,
694 unit_graph,
695 unit_to_index,
696 scrape_units,
697 )?;
698
699 Ok(bcx)
700}
701
702fn validate_target_path_as_source_file(
704 gctx: &GlobalContext,
705 target_path: &std::path::Path,
706 target_name: &str,
707 target_kind: &TargetKind,
708 unit_manifest_path: &std::path::Path,
709 error_count: &mut usize,
710) -> CargoResult<()> {
711 if !target_path.exists() {
712 *error_count += 1;
713
714 let err_msg = format!(
715 "can't find {} `{}` at path `{}`",
716 target_kind.description(),
717 target_name,
718 target_path.display()
719 );
720
721 let group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(Origin::path(
722 unit_manifest_path.to_str().unwrap_or_default(),
723 ));
724
725 gctx.shell().print_report(&[group], true)?;
726 } else if target_path.is_dir() {
727 *error_count += 1;
728
729 let main_rs = target_path.join("main.rs");
731 let lib_rs = target_path.join("lib.rs");
732
733 let suggested_files_opt = match target_kind {
734 TargetKind::Lib(_) => {
735 if lib_rs.exists() {
736 Some(format!("`{}`", lib_rs.display()))
737 } else {
738 None
739 }
740 }
741 TargetKind::Bin => {
742 if main_rs.exists() {
743 Some(format!("`{}`", main_rs.display()))
744 } else {
745 None
746 }
747 }
748 TargetKind::Test => {
749 if main_rs.exists() {
750 Some(format!("`{}`", main_rs.display()))
751 } else {
752 None
753 }
754 }
755 TargetKind::ExampleBin => {
756 if main_rs.exists() {
757 Some(format!("`{}`", main_rs.display()))
758 } else {
759 None
760 }
761 }
762 TargetKind::Bench => {
763 if main_rs.exists() {
764 Some(format!("`{}`", main_rs.display()))
765 } else {
766 None
767 }
768 }
769 TargetKind::ExampleLib(_) => {
770 if lib_rs.exists() {
771 Some(format!("`{}`", lib_rs.display()))
772 } else {
773 None
774 }
775 }
776 TargetKind::CustomBuild => None,
777 };
778
779 let err_msg = format!(
780 "path `{}` for {} `{}` is a directory, but a source file was expected.",
781 target_path.display(),
782 target_kind.description(),
783 target_name,
784 );
785 let mut group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(
786 Origin::path(unit_manifest_path.to_str().unwrap_or_default()),
787 );
788
789 if let Some(suggested_files) = suggested_files_opt {
790 group = group.element(
791 Level::HELP.message(format!("an entry point exists at {}", suggested_files)),
792 );
793 }
794
795 gctx.shell().print_report(&[group], true)?;
796 }
797
798 Ok(())
799}
800
801fn rebuild_unit_graph_shared(
836 interner: &UnitInterner,
837 unit_graph: UnitGraph,
838 roots: &[Unit],
839 scrape_units: &[Unit],
840 to_host: Option<CompileKind>,
841 compile_time_deps_only: bool,
842) -> (Vec<Unit>, Vec<Unit>, UnitGraph) {
843 let mut result = UnitGraph::new();
844 let mut memo = HashMap::new();
847 let new_roots = roots
848 .iter()
849 .map(|root| {
850 traverse_and_share(
851 interner,
852 &mut memo,
853 &mut result,
854 &unit_graph,
855 root,
856 true,
857 false,
858 to_host,
859 compile_time_deps_only,
860 )
861 })
862 .collect();
863 let new_scrape_units = scrape_units
867 .iter()
868 .map(|unit| memo.get(unit).unwrap().clone())
869 .collect();
870 (new_roots, new_scrape_units, result)
871}
872
873fn traverse_and_share(
879 interner: &UnitInterner,
880 memo: &mut HashMap<Unit, Unit>,
881 new_graph: &mut UnitGraph,
882 unit_graph: &UnitGraph,
883 unit: &Unit,
884 unit_is_root: bool,
885 unit_is_for_host: bool,
886 to_host: Option<CompileKind>,
887 compile_time_deps_only: bool,
888) -> Unit {
889 if let Some(new_unit) = memo.get(unit) {
890 return new_unit.clone();
892 }
893 let mut dep_hash = StableHasher::new();
894 let skip_non_compile_time_deps = compile_time_deps_only
895 && (!unit.target.is_compile_time_dependency() ||
896 unit_is_root);
899 let new_deps: Vec<_> = unit_graph[unit]
900 .iter()
901 .map(|dep| {
902 let new_dep_unit = traverse_and_share(
903 interner,
904 memo,
905 new_graph,
906 unit_graph,
907 &dep.unit,
908 false,
909 dep.unit_for.is_for_host(),
910 to_host,
911 skip_non_compile_time_deps,
915 );
916 new_dep_unit.hash(&mut dep_hash);
917 UnitDep {
918 unit: new_dep_unit,
919 ..dep.clone()
920 }
921 })
922 .collect();
923 let new_dep_hash = Hasher::finish(&dep_hash);
926
927 let canonical_kind = match to_host {
934 Some(to_host) if to_host == unit.kind => CompileKind::Host,
935 _ => unit.kind,
936 };
937
938 let mut profile = unit.profile.clone();
939 if profile.strip.is_deferred() {
940 if !profile.debuginfo.is_turned_on()
944 && new_deps
945 .iter()
946 .all(|dep| !dep.unit.profile.debuginfo.is_turned_on())
947 {
948 profile.strip = profile.strip.strip_debuginfo();
949 }
950 }
951
952 if unit_is_for_host
956 && to_host.is_some()
957 && profile.debuginfo.is_deferred()
958 && !unit.artifact.is_true()
959 {
960 let canonical_debuginfo = profile.debuginfo.finalize();
964 let mut canonical_profile = profile.clone();
965 canonical_profile.debuginfo = canonical_debuginfo;
966 let unit_probe = interner.intern(
967 &unit.pkg,
968 &unit.target,
969 canonical_profile,
970 to_host.unwrap(),
971 unit.mode,
972 unit.features.clone(),
973 unit.rustflags.clone(),
974 unit.rustdocflags.clone(),
975 unit.links_overrides.clone(),
976 unit.is_std,
977 unit.dep_hash,
978 unit.artifact,
979 unit.artifact_target_for_features,
980 unit.skip_non_compile_time_dep,
981 );
982
983 profile.debuginfo = if unit_graph.contains_key(&unit_probe) {
985 canonical_debuginfo
988 } else {
989 canonical_debuginfo.weaken()
992 }
993 }
994
995 let new_unit = interner.intern(
996 &unit.pkg,
997 &unit.target,
998 profile,
999 canonical_kind,
1000 unit.mode,
1001 unit.features.clone(),
1002 unit.rustflags.clone(),
1003 unit.rustdocflags.clone(),
1004 unit.links_overrides.clone(),
1005 unit.is_std,
1006 new_dep_hash,
1007 unit.artifact,
1008 None,
1011 skip_non_compile_time_deps,
1012 );
1013 if !unit_is_root || !compile_time_deps_only {
1014 assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
1015 }
1016 new_graph.entry(new_unit.clone()).or_insert(new_deps);
1017 new_unit
1018}
1019
1020fn remove_duplicate_doc(
1036 build_config: &BuildConfig,
1037 root_units: &[Unit],
1038 unit_graph: &mut UnitGraph,
1039) {
1040 let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
1043 for unit in unit_graph.keys() {
1044 if unit.mode.is_doc() {
1045 all_docs
1046 .entry(unit.target.crate_name())
1047 .or_default()
1048 .push(unit.clone());
1049 }
1050 }
1051 let mut removed_units: HashSet<Unit> = HashSet::new();
1054 let mut remove = |units: Vec<Unit>, reason: &str, cb: &dyn Fn(&Unit) -> bool| -> Vec<Unit> {
1055 let (to_remove, remaining_units): (Vec<Unit>, Vec<Unit>) = units
1056 .into_iter()
1057 .partition(|unit| cb(unit) && !root_units.contains(unit));
1058 for unit in to_remove {
1059 tracing::debug!(
1060 "removing duplicate doc due to {} for package {} target `{}`",
1061 reason,
1062 unit.pkg,
1063 unit.target.name()
1064 );
1065 unit_graph.remove(&unit);
1066 removed_units.insert(unit);
1067 }
1068 remaining_units
1069 };
1070 for (_crate_name, mut units) in all_docs {
1072 if units.len() == 1 {
1073 continue;
1074 }
1075 if build_config
1077 .requested_kinds
1078 .iter()
1079 .all(CompileKind::is_host)
1080 {
1081 units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
1086 if units.len() == 1 {
1087 continue;
1088 }
1089 }
1090 let mut source_map: HashMap<(InternedString, SourceId, CompileKind), Vec<Unit>> =
1092 HashMap::new();
1093 for unit in units {
1094 let pkg_id = unit.pkg.package_id();
1095 source_map
1097 .entry((pkg_id.name(), pkg_id.source_id(), unit.kind))
1098 .or_default()
1099 .push(unit);
1100 }
1101 let mut remaining_units = Vec::new();
1102 for (_key, mut units) in source_map {
1103 if units.len() > 1 {
1104 units.sort_by(|a, b| a.pkg.version().partial_cmp(b.pkg.version()).unwrap());
1105 let newest_version = units.last().unwrap().pkg.version().clone();
1107 let keep_units = remove(units, "older version", &|unit| {
1108 unit.pkg.version() < &newest_version
1109 });
1110 remaining_units.extend(keep_units);
1111 } else {
1112 remaining_units.extend(units);
1113 }
1114 }
1115 if remaining_units.len() == 1 {
1116 continue;
1117 }
1118 }
1121 for unit_deps in unit_graph.values_mut() {
1123 unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
1124 }
1125 let mut visited = HashSet::new();
1127 fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
1128 if !visited.insert(unit.clone()) {
1129 return;
1130 }
1131 for dep in &graph[unit] {
1132 visit(&dep.unit, graph, visited);
1133 }
1134 }
1135 for unit in root_units {
1136 visit(unit, unit_graph, &mut visited);
1137 }
1138 unit_graph.retain(|unit, _| visited.contains(unit));
1139}
1140
1141fn override_rustc_crate_types(
1145 units: &mut [Unit],
1146 args: &[String],
1147 interner: &UnitInterner,
1148) -> CargoResult<()> {
1149 if units.len() != 1 {
1150 anyhow::bail!(
1151 "crate types to rustc can only be passed to one \
1152 target, consider filtering\nthe package by passing, \
1153 e.g., `--lib` or `--example` to specify a single target"
1154 );
1155 }
1156
1157 let unit = &units[0];
1158 let override_unit = |f: fn(Vec<CrateType>) -> TargetKind| {
1159 let crate_types = args.iter().map(|s| s.into()).collect();
1160 let mut target = unit.target.clone();
1161 target.set_kind(f(crate_types));
1162 interner.intern(
1163 &unit.pkg,
1164 &target,
1165 unit.profile.clone(),
1166 unit.kind,
1167 unit.mode,
1168 unit.features.clone(),
1169 unit.rustflags.clone(),
1170 unit.rustdocflags.clone(),
1171 unit.links_overrides.clone(),
1172 unit.is_std,
1173 unit.dep_hash,
1174 unit.artifact,
1175 unit.artifact_target_for_features,
1176 unit.skip_non_compile_time_dep,
1177 )
1178 };
1179 units[0] = match unit.target.kind() {
1180 TargetKind::Lib(_) => override_unit(TargetKind::Lib),
1181 TargetKind::ExampleLib(_) => override_unit(TargetKind::ExampleLib),
1182 _ => {
1183 anyhow::bail!(
1184 "crate types can only be specified for libraries and example libraries.\n\
1185 Binaries, tests, and benchmarks are always the `bin` crate type"
1186 );
1187 }
1188 };
1189
1190 Ok(())
1191}
1192
1193pub fn resolve_all_features(
1199 resolve_with_overrides: &Resolve,
1200 resolved_features: &features::ResolvedFeatures,
1201 package_set: &PackageSet<'_>,
1202 package_id: PackageId,
1203 has_dev_units: HasDevUnits,
1204 requested_kinds: &[CompileKind],
1205 target_data: &RustcTargetData<'_>,
1206 force_all_targets: ForceAllTargets,
1207) -> HashSet<String> {
1208 let mut features: HashSet<String> = resolved_features
1209 .activated_features(package_id, FeaturesFor::NormalOrDev)
1210 .iter()
1211 .map(|s| s.to_string())
1212 .collect();
1213
1214 let filtered_deps = PackageSet::filter_deps(
1217 package_id,
1218 resolve_with_overrides,
1219 has_dev_units,
1220 requested_kinds,
1221 target_data,
1222 force_all_targets,
1223 );
1224 for (dep_id, deps) in filtered_deps {
1225 let is_proc_macro = package_set
1226 .get_one(dep_id)
1227 .expect("packages downloaded")
1228 .proc_macro();
1229 for dep in deps {
1230 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
1231 for feature in resolved_features
1232 .activated_features_unverified(dep_id, features_for)
1233 .unwrap_or_default()
1234 {
1235 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
1236 }
1237 }
1238 }
1239
1240 features
1241}