1use crate::util::data_structures::{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 let parse_pass_output = 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
152 && (compilation.lint_warning_count + parse_pass_output.lint_warning_count) > 0
153 {
154 anyhow::bail!("warnings are denied by `build.warnings` configuration")
155 }
156 Ok(compilation)
157}
158
159#[tracing::instrument(skip_all)]
161fn compile_ws<'a>(
162 ws: &Workspace<'a>,
163 options: &CompileOptions,
164 exec: &Arc<dyn Executor>,
165) -> CargoResult<Compilation<'a>> {
166 let interner = UnitInterner::new();
167 let logger = BuildLogger::maybe_new(ws, &options.build_config)?;
168
169 if let Some(ref logger) = logger {
170 let rustc = ws.gctx().load_global_rustc(Some(ws))?;
171 let num_cpus = std::thread::available_parallelism()
172 .ok()
173 .map(|x| x.get() as u64);
174 logger.log(LogMessage::BuildStarted {
175 command: std::env::args_os()
176 .map(|arg| arg.to_string_lossy().into_owned())
177 .collect(),
178 cwd: ws.gctx().cwd().to_path_buf(),
179 host: rustc.host.to_string(),
180 jobs: options.build_config.jobs,
181 num_cpus,
182 profile: options.build_config.requested_profile.to_string(),
183 rustc_version: rustc.version.to_string(),
184 rustc_version_verbose: rustc.verbose_version.clone(),
185 target_dir: ws.target_dir().as_path_unlocked().to_path_buf(),
186 workspace_root: ws.root().to_path_buf(),
187 });
188 }
189
190 let bcx = create_bcx(ws, options, &interner, logger.as_ref())?;
191
192 if options.build_config.unit_graph {
193 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.gctx())?;
194 return Compilation::new(&bcx);
195 }
196 crate::core::gc::auto_gc(bcx.gctx);
197 let build_runner = BuildRunner::new(&bcx)?;
198 if options.build_config.dry_run {
199 build_runner.dry_run()
200 } else {
201 build_runner.compile(exec)
202 }
203}
204
205pub fn print<'a>(
209 ws: &Workspace<'a>,
210 options: &CompileOptions,
211 print_opt_value: &str,
212) -> CargoResult<()> {
213 let CompileOptions {
214 ref build_config,
215 ref target_rustc_args,
216 ..
217 } = *options;
218 let gctx = ws.gctx();
219 let rustc = gctx.load_global_rustc(Some(ws))?;
220 for (index, kind) in build_config.requested_kinds.iter().enumerate() {
221 if index != 0 {
222 drop_println!(gctx);
223 }
224 let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?;
225 let mut process = rustc.process();
226 apply_env_config(gctx, &mut process)?;
227 process.args(&target_info.rustflags);
228 if let Some(args) = target_rustc_args {
229 process.args(args);
230 }
231 kind.add_target_arg(&mut process);
232 process.arg("--print").arg(print_opt_value);
233 process.exec()?;
234 }
235 Ok(())
236}
237
238#[tracing::instrument(skip_all)]
243pub fn create_bcx<'a, 'gctx>(
244 ws: &'a Workspace<'gctx>,
245 options: &'a CompileOptions,
246 interner: &'a UnitInterner,
247 logger: Option<&'a BuildLogger>,
248) -> CargoResult<BuildContext<'a, 'gctx>> {
249 let CompileOptions {
250 ref build_config,
251 ref spec,
252 ref cli_features,
253 ref filter,
254 ref target_rustdoc_args,
255 ref target_rustc_args,
256 ref target_rustc_crate_types,
257 rustdoc_document_private_items,
258 honor_rust_version,
259 } = *options;
260 let gctx = ws.gctx();
261
262 match build_config.intent {
264 UserIntent::Test | UserIntent::Build | UserIntent::Check { .. } | UserIntent::Bench => {
265 if ws.gctx().get_env("RUST_FLAGS").is_ok() {
266 gctx.shell().print_report(
267 &[Level::WARNING
268 .secondary_title("ignoring environment variable `RUST_FLAGS`")
269 .element(Level::HELP.message("rust flags are passed via `RUSTFLAGS`"))],
270 false,
271 )?;
272 }
273 }
274 UserIntent::Doc { .. } | UserIntent::Doctest => {
275 if ws.gctx().get_env("RUSTDOC_FLAGS").is_ok() {
276 gctx.shell().print_report(
277 &[Level::WARNING
278 .secondary_title("ignoring environment variable `RUSTDOC_FLAGS`")
279 .element(
280 Level::HELP.message("rustdoc flags are passed via `RUSTDOCFLAGS`"),
281 )],
282 false,
283 )?;
284 }
285 }
286 }
287 gctx.validate_term_config()?;
288
289 let mut target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
290
291 let specs = spec.to_package_id_specs(ws)?;
292 let has_dev_units = {
293 let any_pkg_has_scrape_enabled = ws
297 .members_with_features(&specs, cli_features)?
298 .iter()
299 .any(|(pkg, _)| {
300 pkg.targets()
301 .iter()
302 .any(|target| target.is_example() && target.doc_scrape_examples().is_enabled())
303 });
304
305 if filter.need_dev_deps(build_config.intent)
306 || (build_config.intent.is_doc() && any_pkg_has_scrape_enabled)
307 {
308 HasDevUnits::Yes
309 } else {
310 HasDevUnits::No
311 }
312 };
313 let dry_run = false;
314
315 if let Some(logger) = logger {
316 let elapsed = ws.gctx().invocation_instant().elapsed().as_secs_f64();
317 logger.log(LogMessage::ResolutionStarted { elapsed });
318 }
319
320 let resolve = ops::resolve_ws_with_opts(
321 ws,
322 &mut target_data,
323 &build_config.requested_kinds,
324 cli_features,
325 &specs,
326 has_dev_units,
327 ForceAllTargets::No,
328 dry_run,
329 )?;
330 let WorkspaceResolve {
331 mut pkg_set,
332 workspace_resolve,
333 targeted_resolve: resolve,
334 specs_and_features,
335 } = resolve;
336
337 if let Some(logger) = logger {
338 let elapsed = ws.gctx().invocation_instant().elapsed().as_secs_f64();
339 logger.log(LogMessage::ResolutionFinished { elapsed });
340 }
341
342 let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
343 let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
344 ws,
345 &mut target_data,
346 &build_config,
347 crates,
348 &build_config.requested_kinds,
349 )?;
350 pkg_set.add_set(std_package_set);
351 Some((std_resolve, std_features))
352 } else {
353 None
354 };
355
356 let to_build_ids = resolve.specs_to_ids(&specs)?;
360 let mut to_builds = pkg_set.get_many(to_build_ids)?;
364
365 to_builds.sort_by_key(|p| p.package_id());
369
370 for pkg in to_builds.iter() {
371 pkg.manifest().print_teapot(gctx);
372
373 if build_config.intent.is_any_test()
374 && !ws.is_member(pkg)
375 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
376 {
377 anyhow::bail!(
378 "package `{}` cannot be tested because it requires dev-dependencies \
379 and is not a member of the workspace",
380 pkg.name()
381 );
382 }
383 }
384
385 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
386 (Some(args), _) => (Some(args.clone()), "rustc"),
387 (_, Some(args)) => (Some(args.clone()), "rustdoc"),
388 _ => (None, ""),
389 };
390
391 if extra_args.is_some() && to_builds.len() != 1 {
392 panic!(
393 "`{}` should not accept multiple `-p` flags",
394 extra_args_name
395 );
396 }
397
398 let profiles = Profiles::new(ws, build_config.requested_profile)?;
399 profiles.validate_packages(
400 ws.profiles(),
401 &mut gctx.shell(),
402 workspace_resolve.as_ref().unwrap_or(&resolve),
403 )?;
404
405 let explicit_host_kind = CompileKind::Target(CompileTarget::new(
409 &target_data.rustc.host,
410 gctx.cli_unstable().json_target_spec,
411 )?);
412 let explicit_host_kinds: Vec<_> = build_config
413 .requested_kinds
414 .iter()
415 .map(|kind| match kind {
416 CompileKind::Host => explicit_host_kind,
417 CompileKind::Target(t) => CompileKind::Target(*t),
418 })
419 .collect();
420
421 let mut root_units = Vec::new();
422 let mut unit_graph = HashMap::default();
423 let mut scrape_units = Vec::new();
424
425 if let Some(logger) = logger {
426 let elapsed = ws.gctx().invocation_instant().elapsed().as_secs_f64();
427 logger.log(LogMessage::UnitGraphStarted { elapsed });
428 }
429
430 let mut selected_dep_kinds = DepKindSet::default();
431 for SpecsAndResolvedFeatures {
432 specs,
433 resolved_features,
434 } in &specs_and_features
435 {
436 let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
442 let packages = to_builds
443 .iter()
444 .filter(|package| spec_names.contains(&package.name().as_str()))
445 .cloned()
446 .collect::<Vec<_>>();
447 let generator = UnitGenerator {
448 ws,
449 packages: &packages,
450 spec,
451 target_data: &target_data,
452 filter,
453 requested_kinds: &build_config.requested_kinds,
454 explicit_host_kind,
455 intent: build_config.intent,
456 resolve: &resolve,
457 workspace_resolve: &workspace_resolve,
458 resolved_features: &resolved_features,
459 package_set: &pkg_set,
460 profiles: &profiles,
461 interner,
462 has_dev_units,
463 };
464 let (mut targeted_root_units, curr_selected_dep_kinds) = generator.generate_root_units()?;
465 selected_dep_kinds = curr_selected_dep_kinds;
467
468 if let Some(args) = target_rustc_crate_types {
469 override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
470 }
471
472 let should_scrape =
473 build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
474 let targeted_scrape_units = if should_scrape {
475 generator.generate_scrape_units(&targeted_root_units)?
476 } else {
477 Vec::new()
478 };
479
480 let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
481 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
482 standard_lib::generate_std_roots(
483 &crates,
484 &targeted_root_units,
485 std_resolve,
486 std_features,
487 &explicit_host_kinds,
488 &pkg_set,
489 interner,
490 &profiles,
491 &target_data,
492 )?
493 } else {
494 Default::default()
495 };
496
497 unit_graph.extend(build_unit_dependencies(
498 ws,
499 &pkg_set,
500 &resolve,
501 &resolved_features,
502 std_resolve_features.as_ref(),
503 &targeted_root_units,
504 &targeted_scrape_units,
505 &std_roots,
506 build_config.intent,
507 &target_data,
508 &profiles,
509 interner,
510 )?);
511 root_units.extend(targeted_root_units);
512 scrape_units.extend(targeted_scrape_units);
513 }
514
515 if build_config.intent.wants_deps_docs() {
518 remove_duplicate_doc(build_config, &root_units, &mut unit_graph);
519 }
520
521 let host_kind_requested = build_config
522 .requested_kinds
523 .iter()
524 .any(CompileKind::is_host);
525 let (root_units, scrape_units, unit_graph) = rebuild_unit_graph_shared(
531 interner,
532 unit_graph,
533 &root_units,
534 &scrape_units,
535 host_kind_requested.then_some(explicit_host_kind),
536 build_config.compile_time_deps_only,
537 );
538
539 let units: Vec<_> = unit_graph.keys().sorted().collect();
540 let unit_to_index: HashMap<_, _> = units
541 .iter()
542 .enumerate()
543 .map(|(i, &unit)| (unit.clone(), UnitIndex(i as u64)))
544 .collect();
545
546 if let Some(logger) = logger {
547 let root_unit_indexes: HashSet<_> =
548 root_units.iter().map(|unit| unit_to_index[&unit]).collect();
549
550 for (index, unit) in units.into_iter().enumerate() {
551 let index = UnitIndex(index as u64);
552 let dependencies = unit_graph
553 .get(unit)
554 .map(|deps| {
555 deps.iter()
556 .filter_map(|dep| unit_to_index.get(&dep.unit).copied())
557 .collect()
558 })
559 .unwrap_or_default();
560 logger.log(LogMessage::UnitRegistered {
561 package_id: unit.pkg.package_id().to_spec(),
562 target: (&unit.target).into(),
563 mode: unit.mode,
564 platform: target_data.short_name(&unit.kind).to_owned(),
565 index,
566 features: unit
567 .features
568 .iter()
569 .map(|s| s.as_str().to_owned())
570 .collect(),
571 requested: root_unit_indexes.contains(&index),
572 dependencies,
573 });
574 }
575 let elapsed = ws.gctx().invocation_instant().elapsed().as_secs_f64();
576 logger.log(LogMessage::UnitGraphFinished { elapsed });
577 }
578
579 let mut extra_compiler_args = HashMap::default();
580 if let Some(args) = extra_args {
581 if root_units.len() != 1 {
582 anyhow::bail!(
583 "extra arguments to `{}` can only be passed to one \
584 target, consider filtering\nthe package by passing, \
585 e.g., `--lib` or `--bin NAME` to specify a single target",
586 extra_args_name
587 );
588 }
589 extra_compiler_args.insert(root_units[0].clone(), args);
590 }
591
592 for unit in root_units
593 .iter()
594 .filter(|unit| unit.mode.is_doc() || unit.mode.is_doc_test())
595 .filter(|unit| rustdoc_document_private_items || unit.target.is_bin())
596 {
597 let mut args = vec!["--document-private-items".into()];
601 if unit.target.is_bin() {
602 args.push("-Arustdoc::private-intra-doc-links".into());
606 }
607 extra_compiler_args
608 .entry(unit.clone())
609 .or_default()
610 .extend(args);
611 }
612
613 let mut error_count: usize = 0;
615 for unit in &root_units {
616 if let Some(target_src_path) = unit.target.src_path().path() {
617 validate_target_path_as_source_file(
618 gctx,
619 target_src_path,
620 unit.target.name(),
621 unit.target.kind(),
622 unit.pkg.manifest_path(),
623 &mut error_count,
624 )?
625 }
626 }
627 if error_count > 0 {
628 let plural: &str = if error_count > 1 { "s" } else { "" };
629 anyhow::bail!(
630 "could not compile due to {error_count} previous target resolution error{plural}"
631 );
632 }
633
634 if honor_rust_version.unwrap_or(true) {
635 let rustc_version = target_data.rustc.version.clone().into();
636
637 let mut incompatible = Vec::new();
638 let mut local_incompatible = false;
639 for unit in unit_graph.keys() {
640 let Some(pkg_msrv) = unit.pkg.rust_version() else {
641 continue;
642 };
643
644 if pkg_msrv.is_compatible_with(&rustc_version) {
645 continue;
646 }
647
648 local_incompatible |= unit.is_local();
649 incompatible.push((unit, pkg_msrv));
650 }
651 if !incompatible.is_empty() {
652 use std::fmt::Write as _;
653
654 let plural = if incompatible.len() == 1 { "" } else { "s" };
655 let mut message = format!(
656 "rustc {rustc_version} is not supported by the following package{plural}:\n"
657 );
658 incompatible.sort_by_key(|(unit, _)| (unit.pkg.name(), unit.pkg.version()));
659 for (unit, msrv) in incompatible {
660 let name = &unit.pkg.name();
661 let version = &unit.pkg.version();
662 writeln!(&mut message, " {name}@{version} requires rustc {msrv}").unwrap();
663 }
664 if ws.is_ephemeral() {
665 if ws.ignore_lock() {
666 writeln!(
667 &mut message,
668 "Try re-running `cargo install` with `--locked`"
669 )
670 .unwrap();
671 }
672 } else if !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
703fn 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 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
802fn 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 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 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
874fn 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 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 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 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 let new_dep_hash = Hasher::finish(&dep_hash);
927
928 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 !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 unit_is_for_host
957 && to_host.is_some()
958 && profile.debuginfo.is_deferred()
959 && !unit.artifact.is_true()
960 {
961 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 profile.debuginfo = if unit_graph.contains_key(&unit_probe) {
986 canonical_debuginfo
989 } else {
990 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 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
1021fn remove_duplicate_doc(
1037 build_config: &BuildConfig,
1038 root_units: &[Unit],
1039 unit_graph: &mut UnitGraph,
1040) {
1041 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 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 for (_crate_name, mut units) in all_docs {
1073 if units.len() == 1 {
1074 continue;
1075 }
1076 if build_config
1078 .requested_kinds
1079 .iter()
1080 .all(CompileKind::is_host)
1081 {
1082 units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
1087 if units.len() == 1 {
1088 continue;
1089 }
1090 }
1091 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 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 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 }
1122 for unit_deps in unit_graph.values_mut() {
1124 unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
1125 }
1126 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
1142fn 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
1194pub 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 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}