1use std::collections::{HashMap, HashSet};
39use std::hash::{Hash, Hasher};
40use std::sync::Arc;
41
42use crate::core::compiler::UnitIndex;
43use crate::core::compiler::UserIntent;
44use crate::core::compiler::unit_dependencies::build_unit_dependencies;
45use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph};
46use crate::core::compiler::{BuildConfig, BuildContext, BuildRunner, Compilation};
47use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData, Unit};
48use crate::core::compiler::{CrateType, TargetInfo, apply_env_config, standard_lib};
49use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
50use crate::core::profiles::Profiles;
51use crate::core::resolver::features::{self, CliFeatures, FeaturesFor};
52use crate::core::resolver::{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 annotate_snippets::{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 ws.emit_warnings()?;
147 let compilation = compile_ws(ws, options, exec)?;
148 if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.lint_warning_count > 0
149 {
150 anyhow::bail!("warnings are denied by `build.warnings` configuration")
151 }
152 Ok(compilation)
153}
154
155#[tracing::instrument(skip_all)]
157pub fn compile_ws<'a>(
158 ws: &Workspace<'a>,
159 options: &CompileOptions,
160 exec: &Arc<dyn Executor>,
161) -> CargoResult<Compilation<'a>> {
162 let interner = UnitInterner::new();
163 let logger = BuildLogger::maybe_new(ws)?;
164
165 if let Some(ref logger) = logger {
166 let rustc = ws.gctx().load_global_rustc(Some(ws))?;
167 let num_cpus = std::thread::available_parallelism()
168 .ok()
169 .map(|x| x.get() as u64);
170 logger.log(LogMessage::BuildStarted {
171 cwd: ws.gctx().cwd().to_path_buf(),
172 host: rustc.host.to_string(),
173 jobs: options.build_config.jobs,
174 num_cpus,
175 profile: options.build_config.requested_profile.to_string(),
176 rustc_version: rustc.version.to_string(),
177 rustc_version_verbose: rustc.verbose_version.clone(),
178 target_dir: ws.target_dir().as_path_unlocked().to_path_buf(),
179 workspace_root: ws.root().to_path_buf(),
180 });
181 }
182
183 let bcx = create_bcx(ws, options, &interner, logger.as_ref())?;
184
185 if options.build_config.unit_graph {
186 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.gctx())?;
187 return Compilation::new(&bcx);
188 }
189 crate::core::gc::auto_gc(bcx.gctx);
190 let build_runner = BuildRunner::new(&bcx)?;
191 if options.build_config.dry_run {
192 build_runner.dry_run()
193 } else {
194 build_runner.compile(exec)
195 }
196}
197
198pub fn print<'a>(
202 ws: &Workspace<'a>,
203 options: &CompileOptions,
204 print_opt_value: &str,
205) -> CargoResult<()> {
206 let CompileOptions {
207 ref build_config,
208 ref target_rustc_args,
209 ..
210 } = *options;
211 let gctx = ws.gctx();
212 let rustc = gctx.load_global_rustc(Some(ws))?;
213 for (index, kind) in build_config.requested_kinds.iter().enumerate() {
214 if index != 0 {
215 drop_println!(gctx);
216 }
217 let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?;
218 let mut process = rustc.process();
219 apply_env_config(gctx, &mut process)?;
220 process.args(&target_info.rustflags);
221 if let Some(args) = target_rustc_args {
222 process.args(args);
223 }
224 if let CompileKind::Target(t) = kind {
225 process.arg("--target").arg(t.rustc_target());
226 }
227 process.arg("--print").arg(print_opt_value);
228 process.exec()?;
229 }
230 Ok(())
231}
232
233#[tracing::instrument(skip_all)]
238pub fn create_bcx<'a, 'gctx>(
239 ws: &'a Workspace<'gctx>,
240 options: &'a CompileOptions,
241 interner: &'a UnitInterner,
242 logger: Option<&'a BuildLogger>,
243) -> CargoResult<BuildContext<'a, 'gctx>> {
244 let CompileOptions {
245 ref build_config,
246 ref spec,
247 ref cli_features,
248 ref filter,
249 ref target_rustdoc_args,
250 ref target_rustc_args,
251 ref target_rustc_crate_types,
252 rustdoc_document_private_items,
253 honor_rust_version,
254 } = *options;
255 let gctx = ws.gctx();
256
257 match build_config.intent {
259 UserIntent::Test | UserIntent::Build | UserIntent::Check { .. } | UserIntent::Bench => {
260 if ws.gctx().get_env("RUST_FLAGS").is_ok() {
261 gctx.shell().print_report(
262 &[Level::WARNING
263 .secondary_title("ignoring environment variable `RUST_FLAGS`")
264 .element(Level::HELP.message("rust flags are passed via `RUSTFLAGS`"))],
265 false,
266 )?;
267 }
268 }
269 UserIntent::Doc { .. } | UserIntent::Doctest => {
270 if ws.gctx().get_env("RUSTDOC_FLAGS").is_ok() {
271 gctx.shell().print_report(
272 &[Level::WARNING
273 .secondary_title("ignoring environment variable `RUSTDOC_FLAGS`")
274 .element(
275 Level::HELP.message("rustdoc flags are passed via `RUSTDOCFLAGS`"),
276 )],
277 false,
278 )?;
279 }
280 }
281 }
282 gctx.validate_term_config()?;
283
284 let mut target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
285
286 let specs = spec.to_package_id_specs(ws)?;
287 let has_dev_units = {
288 let any_pkg_has_scrape_enabled = ws
292 .members_with_features(&specs, cli_features)?
293 .iter()
294 .any(|(pkg, _)| {
295 pkg.targets()
296 .iter()
297 .any(|target| target.is_example() && target.doc_scrape_examples().is_enabled())
298 });
299
300 if filter.need_dev_deps(build_config.intent)
301 || (build_config.intent.is_doc() && any_pkg_has_scrape_enabled)
302 {
303 HasDevUnits::Yes
304 } else {
305 HasDevUnits::No
306 }
307 };
308 let dry_run = false;
309
310 if let Some(logger) = logger {
311 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
312 logger.log(LogMessage::ResolutionStarted { elapsed });
313 }
314 let resolve = ops::resolve_ws_with_opts(
315 ws,
316 &mut target_data,
317 &build_config.requested_kinds,
318 cli_features,
319 &specs,
320 has_dev_units,
321 crate::core::resolver::features::ForceAllTargets::No,
322 dry_run,
323 )?;
324 let WorkspaceResolve {
325 mut pkg_set,
326 workspace_resolve,
327 targeted_resolve: resolve,
328 specs_and_features,
329 } = resolve;
330
331 if let Some(logger) = logger {
332 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
333 logger.log(LogMessage::ResolutionFinished { elapsed });
334 }
335
336 let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
337 let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
338 ws,
339 &mut target_data,
340 &build_config,
341 crates,
342 &build_config.requested_kinds,
343 )?;
344 pkg_set.add_set(std_package_set);
345 Some((std_resolve, std_features))
346 } else {
347 None
348 };
349
350 let to_build_ids = resolve.specs_to_ids(&specs)?;
354 let mut to_builds = pkg_set.get_many(to_build_ids)?;
358
359 to_builds.sort_by_key(|p| p.package_id());
363
364 for pkg in to_builds.iter() {
365 pkg.manifest().print_teapot(gctx);
366
367 if build_config.intent.is_any_test()
368 && !ws.is_member(pkg)
369 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
370 {
371 anyhow::bail!(
372 "package `{}` cannot be tested because it requires dev-dependencies \
373 and is not a member of the workspace",
374 pkg.name()
375 );
376 }
377 }
378
379 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
380 (Some(args), _) => (Some(args.clone()), "rustc"),
381 (_, Some(args)) => (Some(args.clone()), "rustdoc"),
382 _ => (None, ""),
383 };
384
385 if extra_args.is_some() && to_builds.len() != 1 {
386 panic!(
387 "`{}` should not accept multiple `-p` flags",
388 extra_args_name
389 );
390 }
391
392 let profiles = Profiles::new(ws, build_config.requested_profile)?;
393 profiles.validate_packages(
394 ws.profiles(),
395 &mut gctx.shell(),
396 workspace_resolve.as_ref().unwrap_or(&resolve),
397 )?;
398
399 let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
403 let explicit_host_kinds: Vec<_> = build_config
404 .requested_kinds
405 .iter()
406 .map(|kind| match kind {
407 CompileKind::Host => explicit_host_kind,
408 CompileKind::Target(t) => CompileKind::Target(*t),
409 })
410 .collect();
411
412 let mut root_units = Vec::new();
413 let mut unit_graph = HashMap::new();
414 let mut scrape_units = Vec::new();
415
416 if let Some(logger) = logger {
417 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
418 logger.log(LogMessage::UnitGraphStarted { elapsed });
419 }
420
421 for SpecsAndResolvedFeatures {
422 specs,
423 resolved_features,
424 } in &specs_and_features
425 {
426 let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
432 let packages = to_builds
433 .iter()
434 .filter(|package| spec_names.contains(&package.name().as_str()))
435 .cloned()
436 .collect::<Vec<_>>();
437 let generator = UnitGenerator {
438 ws,
439 packages: &packages,
440 spec,
441 target_data: &target_data,
442 filter,
443 requested_kinds: &build_config.requested_kinds,
444 explicit_host_kind,
445 intent: build_config.intent,
446 resolve: &resolve,
447 workspace_resolve: &workspace_resolve,
448 resolved_features: &resolved_features,
449 package_set: &pkg_set,
450 profiles: &profiles,
451 interner,
452 has_dev_units,
453 };
454 let mut targeted_root_units = generator.generate_root_units()?;
455
456 if let Some(args) = target_rustc_crate_types {
457 override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
458 }
459
460 let should_scrape =
461 build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
462 let targeted_scrape_units = if should_scrape {
463 generator.generate_scrape_units(&targeted_root_units)?
464 } else {
465 Vec::new()
466 };
467
468 let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
469 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
470 standard_lib::generate_std_roots(
471 &crates,
472 &targeted_root_units,
473 std_resolve,
474 std_features,
475 &explicit_host_kinds,
476 &pkg_set,
477 interner,
478 &profiles,
479 &target_data,
480 )?
481 } else {
482 Default::default()
483 };
484
485 unit_graph.extend(build_unit_dependencies(
486 ws,
487 &pkg_set,
488 &resolve,
489 &resolved_features,
490 std_resolve_features.as_ref(),
491 &targeted_root_units,
492 &targeted_scrape_units,
493 &std_roots,
494 build_config.intent,
495 &target_data,
496 &profiles,
497 interner,
498 )?);
499 root_units.extend(targeted_root_units);
500 scrape_units.extend(targeted_scrape_units);
501 }
502
503 if build_config.intent.wants_deps_docs() {
506 remove_duplicate_doc(build_config, &root_units, &mut unit_graph);
507 }
508
509 let host_kind_requested = build_config
510 .requested_kinds
511 .iter()
512 .any(CompileKind::is_host);
513 let (root_units, scrape_units, unit_graph) = rebuild_unit_graph_shared(
519 interner,
520 unit_graph,
521 &root_units,
522 &scrape_units,
523 host_kind_requested.then_some(explicit_host_kind),
524 build_config.compile_time_deps_only,
525 );
526
527 let units: Vec<_> = unit_graph.keys().sorted().collect();
528 let unit_to_index: HashMap<_, _> = units
529 .iter()
530 .enumerate()
531 .map(|(i, &unit)| (unit.clone(), UnitIndex(i as u64)))
532 .collect();
533 if let Some(logger) = logger {
534 let root_unit_indexes: HashSet<_> =
535 root_units.iter().map(|unit| unit_to_index[&unit]).collect();
536
537 for (index, unit) in units.into_iter().enumerate() {
538 let index = UnitIndex(index as u64);
539 let dependencies = unit_graph
540 .get(unit)
541 .map(|deps| {
542 deps.iter()
543 .filter_map(|dep| unit_to_index.get(&dep.unit).copied())
544 .collect()
545 })
546 .unwrap_or_default();
547 logger.log(LogMessage::UnitRegistered {
548 package_id: unit.pkg.package_id().to_spec(),
549 target: (&unit.target).into(),
550 mode: unit.mode,
551 platform: target_data.short_name(&unit.kind).to_owned(),
552 index,
553 features: unit
554 .features
555 .iter()
556 .map(|s| s.as_str().to_owned())
557 .collect(),
558 requested: root_unit_indexes.contains(&index),
559 dependencies,
560 });
561 }
562 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
563 logger.log(LogMessage::UnitGraphFinished { elapsed });
564 }
565
566 let mut extra_compiler_args = HashMap::new();
567 if let Some(args) = extra_args {
568 if root_units.len() != 1 {
569 anyhow::bail!(
570 "extra arguments to `{}` can only be passed to one \
571 target, consider filtering\nthe package by passing, \
572 e.g., `--lib` or `--bin NAME` to specify a single target",
573 extra_args_name
574 );
575 }
576 extra_compiler_args.insert(root_units[0].clone(), args);
577 }
578
579 for unit in root_units
580 .iter()
581 .filter(|unit| unit.mode.is_doc() || unit.mode.is_doc_test())
582 .filter(|unit| rustdoc_document_private_items || unit.target.is_bin())
583 {
584 let mut args = vec!["--document-private-items".into()];
588 if unit.target.is_bin() {
589 args.push("-Arustdoc::private-intra-doc-links".into());
593 }
594 extra_compiler_args
595 .entry(unit.clone())
596 .or_default()
597 .extend(args);
598 }
599
600 let mut error_count: usize = 0;
602 for unit in &root_units {
603 if let Some(target_src_path) = unit.target.src_path().path() {
604 validate_target_path_as_source_file(
605 gctx,
606 target_src_path,
607 unit.target.name(),
608 unit.target.kind(),
609 unit.pkg.manifest_path(),
610 &mut error_count,
611 )?
612 }
613 }
614 if error_count > 0 {
615 let plural: &str = if error_count > 1 { "s" } else { "" };
616 anyhow::bail!(
617 "could not compile due to {error_count} previous target resolution error{plural}"
618 );
619 }
620
621 if honor_rust_version.unwrap_or(true) {
622 let rustc_version = target_data.rustc.version.clone().into();
623
624 let mut incompatible = Vec::new();
625 let mut local_incompatible = false;
626 for unit in unit_graph.keys() {
627 let Some(pkg_msrv) = unit.pkg.rust_version() else {
628 continue;
629 };
630
631 if pkg_msrv.is_compatible_with(&rustc_version) {
632 continue;
633 }
634
635 local_incompatible |= unit.is_local();
636 incompatible.push((unit, pkg_msrv));
637 }
638 if !incompatible.is_empty() {
639 use std::fmt::Write as _;
640
641 let plural = if incompatible.len() == 1 { "" } else { "s" };
642 let mut message = format!(
643 "rustc {rustc_version} is not supported by the following package{plural}:\n"
644 );
645 incompatible.sort_by_key(|(unit, _)| (unit.pkg.name(), unit.pkg.version()));
646 for (unit, msrv) in incompatible {
647 let name = &unit.pkg.name();
648 let version = &unit.pkg.version();
649 writeln!(&mut message, " {name}@{version} requires rustc {msrv}").unwrap();
650 }
651 if ws.is_ephemeral() {
652 if ws.ignore_lock() {
653 writeln!(
654 &mut message,
655 "Try re-running `cargo install` with `--locked`"
656 )
657 .unwrap();
658 }
659 } else if !local_incompatible {
660 writeln!(
661 &mut message,
662 "Either upgrade rustc or select compatible dependency versions with
663`cargo update <name>@<current-ver> --precise <compatible-ver>`
664where `<compatible-ver>` is the latest version supporting rustc {rustc_version}",
665 )
666 .unwrap();
667 }
668 return Err(anyhow::Error::msg(message));
669 }
670 }
671
672 let bcx = BuildContext::new(
673 ws,
674 logger,
675 pkg_set,
676 build_config,
677 profiles,
678 extra_compiler_args,
679 target_data,
680 root_units,
681 unit_graph,
682 unit_to_index,
683 scrape_units,
684 )?;
685
686 Ok(bcx)
687}
688
689fn validate_target_path_as_source_file(
691 gctx: &GlobalContext,
692 target_path: &std::path::Path,
693 target_name: &str,
694 target_kind: &TargetKind,
695 unit_manifest_path: &std::path::Path,
696 error_count: &mut usize,
697) -> CargoResult<()> {
698 if !target_path.exists() {
699 *error_count += 1;
700
701 let err_msg = format!(
702 "can't find {} `{}` at path `{}`",
703 target_kind.description(),
704 target_name,
705 target_path.display()
706 );
707
708 let group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(Origin::path(
709 unit_manifest_path.to_str().unwrap_or_default(),
710 ));
711
712 gctx.shell().print_report(&[group], true)?;
713 } else if target_path.is_dir() {
714 *error_count += 1;
715
716 let main_rs = target_path.join("main.rs");
718 let lib_rs = target_path.join("lib.rs");
719
720 let suggested_files_opt = match target_kind {
721 TargetKind::Lib(_) => {
722 if lib_rs.exists() {
723 Some(format!("`{}`", lib_rs.display()))
724 } else {
725 None
726 }
727 }
728 TargetKind::Bin => {
729 if main_rs.exists() {
730 Some(format!("`{}`", main_rs.display()))
731 } else {
732 None
733 }
734 }
735 TargetKind::Test => {
736 if main_rs.exists() {
737 Some(format!("`{}`", main_rs.display()))
738 } else {
739 None
740 }
741 }
742 TargetKind::ExampleBin => {
743 if main_rs.exists() {
744 Some(format!("`{}`", main_rs.display()))
745 } else {
746 None
747 }
748 }
749 TargetKind::Bench => {
750 if main_rs.exists() {
751 Some(format!("`{}`", main_rs.display()))
752 } else {
753 None
754 }
755 }
756 TargetKind::ExampleLib(_) => {
757 if lib_rs.exists() {
758 Some(format!("`{}`", lib_rs.display()))
759 } else {
760 None
761 }
762 }
763 TargetKind::CustomBuild => None,
764 };
765
766 let err_msg = format!(
767 "path `{}` for {} `{}` is a directory, but a source file was expected.",
768 target_path.display(),
769 target_kind.description(),
770 target_name,
771 );
772 let mut group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(
773 Origin::path(unit_manifest_path.to_str().unwrap_or_default()),
774 );
775
776 if let Some(suggested_files) = suggested_files_opt {
777 group = group.element(
778 Level::HELP.message(format!("an entry point exists at {}", suggested_files)),
779 );
780 }
781
782 gctx.shell().print_report(&[group], true)?;
783 }
784
785 Ok(())
786}
787
788fn rebuild_unit_graph_shared(
823 interner: &UnitInterner,
824 unit_graph: UnitGraph,
825 roots: &[Unit],
826 scrape_units: &[Unit],
827 to_host: Option<CompileKind>,
828 compile_time_deps_only: bool,
829) -> (Vec<Unit>, Vec<Unit>, UnitGraph) {
830 let mut result = UnitGraph::new();
831 let mut memo = HashMap::new();
834 let new_roots = roots
835 .iter()
836 .map(|root| {
837 traverse_and_share(
838 interner,
839 &mut memo,
840 &mut result,
841 &unit_graph,
842 root,
843 true,
844 false,
845 to_host,
846 compile_time_deps_only,
847 )
848 })
849 .collect();
850 let new_scrape_units = scrape_units
854 .iter()
855 .map(|unit| memo.get(unit).unwrap().clone())
856 .collect();
857 (new_roots, new_scrape_units, result)
858}
859
860fn traverse_and_share(
866 interner: &UnitInterner,
867 memo: &mut HashMap<Unit, Unit>,
868 new_graph: &mut UnitGraph,
869 unit_graph: &UnitGraph,
870 unit: &Unit,
871 unit_is_root: bool,
872 unit_is_for_host: bool,
873 to_host: Option<CompileKind>,
874 compile_time_deps_only: bool,
875) -> Unit {
876 if let Some(new_unit) = memo.get(unit) {
877 return new_unit.clone();
879 }
880 let mut dep_hash = StableHasher::new();
881 let skip_non_compile_time_deps = compile_time_deps_only
882 && (!unit.target.is_compile_time_dependency() ||
883 unit_is_root);
886 let new_deps: Vec<_> = unit_graph[unit]
887 .iter()
888 .map(|dep| {
889 let new_dep_unit = traverse_and_share(
890 interner,
891 memo,
892 new_graph,
893 unit_graph,
894 &dep.unit,
895 false,
896 dep.unit_for.is_for_host(),
897 to_host,
898 skip_non_compile_time_deps,
902 );
903 new_dep_unit.hash(&mut dep_hash);
904 UnitDep {
905 unit: new_dep_unit,
906 ..dep.clone()
907 }
908 })
909 .collect();
910 let new_dep_hash = Hasher::finish(&dep_hash);
913
914 let canonical_kind = match to_host {
921 Some(to_host) if to_host == unit.kind => CompileKind::Host,
922 _ => unit.kind,
923 };
924
925 let mut profile = unit.profile.clone();
926 if profile.strip.is_deferred() {
927 if !profile.debuginfo.is_turned_on()
931 && new_deps
932 .iter()
933 .all(|dep| !dep.unit.profile.debuginfo.is_turned_on())
934 {
935 profile.strip = profile.strip.strip_debuginfo();
936 }
937 }
938
939 if unit_is_for_host
943 && to_host.is_some()
944 && profile.debuginfo.is_deferred()
945 && !unit.artifact.is_true()
946 {
947 let canonical_debuginfo = profile.debuginfo.finalize();
951 let mut canonical_profile = profile.clone();
952 canonical_profile.debuginfo = canonical_debuginfo;
953 let unit_probe = interner.intern(
954 &unit.pkg,
955 &unit.target,
956 canonical_profile,
957 to_host.unwrap(),
958 unit.mode,
959 unit.features.clone(),
960 unit.rustflags.clone(),
961 unit.rustdocflags.clone(),
962 unit.links_overrides.clone(),
963 unit.is_std,
964 unit.dep_hash,
965 unit.artifact,
966 unit.artifact_target_for_features,
967 unit.skip_non_compile_time_dep,
968 );
969
970 profile.debuginfo = if unit_graph.contains_key(&unit_probe) {
972 canonical_debuginfo
975 } else {
976 canonical_debuginfo.weaken()
979 }
980 }
981
982 let new_unit = interner.intern(
983 &unit.pkg,
984 &unit.target,
985 profile,
986 canonical_kind,
987 unit.mode,
988 unit.features.clone(),
989 unit.rustflags.clone(),
990 unit.rustdocflags.clone(),
991 unit.links_overrides.clone(),
992 unit.is_std,
993 new_dep_hash,
994 unit.artifact,
995 None,
998 skip_non_compile_time_deps,
999 );
1000 if !unit_is_root || !compile_time_deps_only {
1001 assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
1002 }
1003 new_graph.entry(new_unit.clone()).or_insert(new_deps);
1004 new_unit
1005}
1006
1007fn remove_duplicate_doc(
1023 build_config: &BuildConfig,
1024 root_units: &[Unit],
1025 unit_graph: &mut UnitGraph,
1026) {
1027 let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
1030 for unit in unit_graph.keys() {
1031 if unit.mode.is_doc() {
1032 all_docs
1033 .entry(unit.target.crate_name())
1034 .or_default()
1035 .push(unit.clone());
1036 }
1037 }
1038 let mut removed_units: HashSet<Unit> = HashSet::new();
1041 let mut remove = |units: Vec<Unit>, reason: &str, cb: &dyn Fn(&Unit) -> bool| -> Vec<Unit> {
1042 let (to_remove, remaining_units): (Vec<Unit>, Vec<Unit>) = units
1043 .into_iter()
1044 .partition(|unit| cb(unit) && !root_units.contains(unit));
1045 for unit in to_remove {
1046 tracing::debug!(
1047 "removing duplicate doc due to {} for package {} target `{}`",
1048 reason,
1049 unit.pkg,
1050 unit.target.name()
1051 );
1052 unit_graph.remove(&unit);
1053 removed_units.insert(unit);
1054 }
1055 remaining_units
1056 };
1057 for (_crate_name, mut units) in all_docs {
1059 if units.len() == 1 {
1060 continue;
1061 }
1062 if build_config
1064 .requested_kinds
1065 .iter()
1066 .all(CompileKind::is_host)
1067 {
1068 units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
1073 if units.len() == 1 {
1074 continue;
1075 }
1076 }
1077 let mut source_map: HashMap<(InternedString, SourceId, CompileKind), Vec<Unit>> =
1079 HashMap::new();
1080 for unit in units {
1081 let pkg_id = unit.pkg.package_id();
1082 source_map
1084 .entry((pkg_id.name(), pkg_id.source_id(), unit.kind))
1085 .or_default()
1086 .push(unit);
1087 }
1088 let mut remaining_units = Vec::new();
1089 for (_key, mut units) in source_map {
1090 if units.len() > 1 {
1091 units.sort_by(|a, b| a.pkg.version().partial_cmp(b.pkg.version()).unwrap());
1092 let newest_version = units.last().unwrap().pkg.version().clone();
1094 let keep_units = remove(units, "older version", &|unit| {
1095 unit.pkg.version() < &newest_version
1096 });
1097 remaining_units.extend(keep_units);
1098 } else {
1099 remaining_units.extend(units);
1100 }
1101 }
1102 if remaining_units.len() == 1 {
1103 continue;
1104 }
1105 }
1108 for unit_deps in unit_graph.values_mut() {
1110 unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
1111 }
1112 let mut visited = HashSet::new();
1114 fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
1115 if !visited.insert(unit.clone()) {
1116 return;
1117 }
1118 for dep in &graph[unit] {
1119 visit(&dep.unit, graph, visited);
1120 }
1121 }
1122 for unit in root_units {
1123 visit(unit, unit_graph, &mut visited);
1124 }
1125 unit_graph.retain(|unit, _| visited.contains(unit));
1126}
1127
1128fn override_rustc_crate_types(
1132 units: &mut [Unit],
1133 args: &[String],
1134 interner: &UnitInterner,
1135) -> CargoResult<()> {
1136 if units.len() != 1 {
1137 anyhow::bail!(
1138 "crate types to rustc can only be passed to one \
1139 target, consider filtering\nthe package by passing, \
1140 e.g., `--lib` or `--example` to specify a single target"
1141 );
1142 }
1143
1144 let unit = &units[0];
1145 let override_unit = |f: fn(Vec<CrateType>) -> TargetKind| {
1146 let crate_types = args.iter().map(|s| s.into()).collect();
1147 let mut target = unit.target.clone();
1148 target.set_kind(f(crate_types));
1149 interner.intern(
1150 &unit.pkg,
1151 &target,
1152 unit.profile.clone(),
1153 unit.kind,
1154 unit.mode,
1155 unit.features.clone(),
1156 unit.rustflags.clone(),
1157 unit.rustdocflags.clone(),
1158 unit.links_overrides.clone(),
1159 unit.is_std,
1160 unit.dep_hash,
1161 unit.artifact,
1162 unit.artifact_target_for_features,
1163 unit.skip_non_compile_time_dep,
1164 )
1165 };
1166 units[0] = match unit.target.kind() {
1167 TargetKind::Lib(_) => override_unit(TargetKind::Lib),
1168 TargetKind::ExampleLib(_) => override_unit(TargetKind::ExampleLib),
1169 _ => {
1170 anyhow::bail!(
1171 "crate types can only be specified for libraries and example libraries.\n\
1172 Binaries, tests, and benchmarks are always the `bin` crate type"
1173 );
1174 }
1175 };
1176
1177 Ok(())
1178}
1179
1180pub fn resolve_all_features(
1186 resolve_with_overrides: &Resolve,
1187 resolved_features: &features::ResolvedFeatures,
1188 package_set: &PackageSet<'_>,
1189 package_id: PackageId,
1190) -> HashSet<String> {
1191 let mut features: HashSet<String> = resolved_features
1192 .activated_features(package_id, FeaturesFor::NormalOrDev)
1193 .iter()
1194 .map(|s| s.to_string())
1195 .collect();
1196
1197 for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
1200 let is_proc_macro = package_set
1201 .get_one(dep_id)
1202 .expect("packages downloaded")
1203 .proc_macro();
1204 for dep in deps {
1205 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
1206 for feature in resolved_features
1207 .activated_features_unverified(dep_id, features_for)
1208 .unwrap_or_default()
1209 {
1210 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
1211 }
1212 }
1213 }
1214
1215 features
1216}