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::{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 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, &options.build_config)?;
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 kind.add_target_arg(&mut process);
225 process.arg("--print").arg(print_opt_value);
226 process.exec()?;
227 }
228 Ok(())
229}
230
231#[tracing::instrument(skip_all)]
236pub fn create_bcx<'a, 'gctx>(
237 ws: &'a Workspace<'gctx>,
238 options: &'a CompileOptions,
239 interner: &'a UnitInterner,
240 logger: Option<&'a BuildLogger>,
241) -> CargoResult<BuildContext<'a, 'gctx>> {
242 let CompileOptions {
243 ref build_config,
244 ref spec,
245 ref cli_features,
246 ref filter,
247 ref target_rustdoc_args,
248 ref target_rustc_args,
249 ref target_rustc_crate_types,
250 rustdoc_document_private_items,
251 honor_rust_version,
252 } = *options;
253 let gctx = ws.gctx();
254
255 match build_config.intent {
257 UserIntent::Test | UserIntent::Build | UserIntent::Check { .. } | UserIntent::Bench => {
258 if ws.gctx().get_env("RUST_FLAGS").is_ok() {
259 gctx.shell().print_report(
260 &[Level::WARNING
261 .secondary_title("ignoring environment variable `RUST_FLAGS`")
262 .element(Level::HELP.message("rust flags are passed via `RUSTFLAGS`"))],
263 false,
264 )?;
265 }
266 }
267 UserIntent::Doc { .. } | UserIntent::Doctest => {
268 if ws.gctx().get_env("RUSTDOC_FLAGS").is_ok() {
269 gctx.shell().print_report(
270 &[Level::WARNING
271 .secondary_title("ignoring environment variable `RUSTDOC_FLAGS`")
272 .element(
273 Level::HELP.message("rustdoc flags are passed via `RUSTDOCFLAGS`"),
274 )],
275 false,
276 )?;
277 }
278 }
279 }
280 gctx.validate_term_config()?;
281
282 let mut target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
283
284 let specs = spec.to_package_id_specs(ws)?;
285 let has_dev_units = {
286 let any_pkg_has_scrape_enabled = ws
290 .members_with_features(&specs, cli_features)?
291 .iter()
292 .any(|(pkg, _)| {
293 pkg.targets()
294 .iter()
295 .any(|target| target.is_example() && target.doc_scrape_examples().is_enabled())
296 });
297
298 if filter.need_dev_deps(build_config.intent)
299 || (build_config.intent.is_doc() && any_pkg_has_scrape_enabled)
300 {
301 HasDevUnits::Yes
302 } else {
303 HasDevUnits::No
304 }
305 };
306 let dry_run = false;
307
308 if let Some(logger) = logger {
309 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
310 logger.log(LogMessage::ResolutionStarted { elapsed });
311 }
312
313 let resolve = ops::resolve_ws_with_opts(
314 ws,
315 &mut target_data,
316 &build_config.requested_kinds,
317 cli_features,
318 &specs,
319 has_dev_units,
320 ForceAllTargets::No,
321 dry_run,
322 )?;
323 let WorkspaceResolve {
324 mut pkg_set,
325 workspace_resolve,
326 targeted_resolve: resolve,
327 specs_and_features,
328 } = resolve;
329
330 if let Some(logger) = logger {
331 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
332 logger.log(LogMessage::ResolutionFinished { elapsed });
333 }
334
335 let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
336 let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
337 ws,
338 &mut target_data,
339 &build_config,
340 crates,
341 &build_config.requested_kinds,
342 )?;
343 pkg_set.add_set(std_package_set);
344 Some((std_resolve, std_features))
345 } else {
346 None
347 };
348
349 let to_build_ids = resolve.specs_to_ids(&specs)?;
353 let mut to_builds = pkg_set.get_many(to_build_ids)?;
357
358 to_builds.sort_by_key(|p| p.package_id());
362
363 for pkg in to_builds.iter() {
364 pkg.manifest().print_teapot(gctx);
365
366 if build_config.intent.is_any_test()
367 && !ws.is_member(pkg)
368 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
369 {
370 anyhow::bail!(
371 "package `{}` cannot be tested because it requires dev-dependencies \
372 and is not a member of the workspace",
373 pkg.name()
374 );
375 }
376 }
377
378 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
379 (Some(args), _) => (Some(args.clone()), "rustc"),
380 (_, Some(args)) => (Some(args.clone()), "rustdoc"),
381 _ => (None, ""),
382 };
383
384 if extra_args.is_some() && to_builds.len() != 1 {
385 panic!(
386 "`{}` should not accept multiple `-p` flags",
387 extra_args_name
388 );
389 }
390
391 let profiles = Profiles::new(ws, build_config.requested_profile)?;
392 profiles.validate_packages(
393 ws.profiles(),
394 &mut gctx.shell(),
395 workspace_resolve.as_ref().unwrap_or(&resolve),
396 )?;
397
398 let explicit_host_kind = CompileKind::Target(CompileTarget::new(
402 &target_data.rustc.host,
403 gctx.cli_unstable().json_target_spec,
404 )?);
405 let explicit_host_kinds: Vec<_> = build_config
406 .requested_kinds
407 .iter()
408 .map(|kind| match kind {
409 CompileKind::Host => explicit_host_kind,
410 CompileKind::Target(t) => CompileKind::Target(*t),
411 })
412 .collect();
413
414 let mut root_units = Vec::new();
415 let mut unit_graph = HashMap::new();
416 let mut scrape_units = Vec::new();
417
418 if let Some(logger) = logger {
419 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
420 logger.log(LogMessage::UnitGraphStarted { elapsed });
421 }
422
423 for SpecsAndResolvedFeatures {
424 specs,
425 resolved_features,
426 } in &specs_and_features
427 {
428 let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
434 let packages = to_builds
435 .iter()
436 .filter(|package| spec_names.contains(&package.name().as_str()))
437 .cloned()
438 .collect::<Vec<_>>();
439 let generator = UnitGenerator {
440 ws,
441 packages: &packages,
442 spec,
443 target_data: &target_data,
444 filter,
445 requested_kinds: &build_config.requested_kinds,
446 explicit_host_kind,
447 intent: build_config.intent,
448 resolve: &resolve,
449 workspace_resolve: &workspace_resolve,
450 resolved_features: &resolved_features,
451 package_set: &pkg_set,
452 profiles: &profiles,
453 interner,
454 has_dev_units,
455 };
456 let mut targeted_root_units = generator.generate_root_units()?;
457
458 if let Some(args) = target_rustc_crate_types {
459 override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
460 }
461
462 let should_scrape =
463 build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
464 let targeted_scrape_units = if should_scrape {
465 generator.generate_scrape_units(&targeted_root_units)?
466 } else {
467 Vec::new()
468 };
469
470 let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
471 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
472 standard_lib::generate_std_roots(
473 &crates,
474 &targeted_root_units,
475 std_resolve,
476 std_features,
477 &explicit_host_kinds,
478 &pkg_set,
479 interner,
480 &profiles,
481 &target_data,
482 )?
483 } else {
484 Default::default()
485 };
486
487 unit_graph.extend(build_unit_dependencies(
488 ws,
489 &pkg_set,
490 &resolve,
491 &resolved_features,
492 std_resolve_features.as_ref(),
493 &targeted_root_units,
494 &targeted_scrape_units,
495 &std_roots,
496 build_config.intent,
497 &target_data,
498 &profiles,
499 interner,
500 )?);
501 root_units.extend(targeted_root_units);
502 scrape_units.extend(targeted_scrape_units);
503 }
504
505 if build_config.intent.wants_deps_docs() {
508 remove_duplicate_doc(build_config, &root_units, &mut unit_graph);
509 }
510
511 let host_kind_requested = build_config
512 .requested_kinds
513 .iter()
514 .any(CompileKind::is_host);
515 let (root_units, scrape_units, unit_graph) = rebuild_unit_graph_shared(
521 interner,
522 unit_graph,
523 &root_units,
524 &scrape_units,
525 host_kind_requested.then_some(explicit_host_kind),
526 build_config.compile_time_deps_only,
527 );
528
529 let units: Vec<_> = unit_graph.keys().sorted().collect();
530 let unit_to_index: HashMap<_, _> = units
531 .iter()
532 .enumerate()
533 .map(|(i, &unit)| (unit.clone(), UnitIndex(i as u64)))
534 .collect();
535
536 if let Some(logger) = logger {
537 let root_unit_indexes: HashSet<_> =
538 root_units.iter().map(|unit| unit_to_index[&unit]).collect();
539
540 for (index, unit) in units.into_iter().enumerate() {
541 let index = UnitIndex(index as u64);
542 let dependencies = unit_graph
543 .get(unit)
544 .map(|deps| {
545 deps.iter()
546 .filter_map(|dep| unit_to_index.get(&dep.unit).copied())
547 .collect()
548 })
549 .unwrap_or_default();
550 logger.log(LogMessage::UnitRegistered {
551 package_id: unit.pkg.package_id().to_spec(),
552 target: (&unit.target).into(),
553 mode: unit.mode,
554 platform: target_data.short_name(&unit.kind).to_owned(),
555 index,
556 features: unit
557 .features
558 .iter()
559 .map(|s| s.as_str().to_owned())
560 .collect(),
561 requested: root_unit_indexes.contains(&index),
562 dependencies,
563 });
564 }
565 let elapsed = ws.gctx().creation_time().elapsed().as_secs_f64();
566 logger.log(LogMessage::UnitGraphFinished { elapsed });
567 }
568
569 let mut extra_compiler_args = HashMap::new();
570 if let Some(args) = extra_args {
571 if root_units.len() != 1 {
572 anyhow::bail!(
573 "extra arguments to `{}` can only be passed to one \
574 target, consider filtering\nthe package by passing, \
575 e.g., `--lib` or `--bin NAME` to specify a single target",
576 extra_args_name
577 );
578 }
579 extra_compiler_args.insert(root_units[0].clone(), args);
580 }
581
582 for unit in root_units
583 .iter()
584 .filter(|unit| unit.mode.is_doc() || unit.mode.is_doc_test())
585 .filter(|unit| rustdoc_document_private_items || unit.target.is_bin())
586 {
587 let mut args = vec!["--document-private-items".into()];
591 if unit.target.is_bin() {
592 args.push("-Arustdoc::private-intra-doc-links".into());
596 }
597 extra_compiler_args
598 .entry(unit.clone())
599 .or_default()
600 .extend(args);
601 }
602
603 let mut error_count: usize = 0;
605 for unit in &root_units {
606 if let Some(target_src_path) = unit.target.src_path().path() {
607 validate_target_path_as_source_file(
608 gctx,
609 target_src_path,
610 unit.target.name(),
611 unit.target.kind(),
612 unit.pkg.manifest_path(),
613 &mut error_count,
614 )?
615 }
616 }
617 if error_count > 0 {
618 let plural: &str = if error_count > 1 { "s" } else { "" };
619 anyhow::bail!(
620 "could not compile due to {error_count} previous target resolution error{plural}"
621 );
622 }
623
624 if honor_rust_version.unwrap_or(true) {
625 let rustc_version = target_data.rustc.version.clone().into();
626
627 let mut incompatible = Vec::new();
628 let mut local_incompatible = false;
629 for unit in unit_graph.keys() {
630 let Some(pkg_msrv) = unit.pkg.rust_version() else {
631 continue;
632 };
633
634 if pkg_msrv.is_compatible_with(&rustc_version) {
635 continue;
636 }
637
638 local_incompatible |= unit.is_local();
639 incompatible.push((unit, pkg_msrv));
640 }
641 if !incompatible.is_empty() {
642 use std::fmt::Write as _;
643
644 let plural = if incompatible.len() == 1 { "" } else { "s" };
645 let mut message = format!(
646 "rustc {rustc_version} is not supported by the following package{plural}:\n"
647 );
648 incompatible.sort_by_key(|(unit, _)| (unit.pkg.name(), unit.pkg.version()));
649 for (unit, msrv) in incompatible {
650 let name = &unit.pkg.name();
651 let version = &unit.pkg.version();
652 writeln!(&mut message, " {name}@{version} requires rustc {msrv}").unwrap();
653 }
654 if ws.is_ephemeral() {
655 if ws.ignore_lock() {
656 writeln!(
657 &mut message,
658 "Try re-running `cargo install` with `--locked`"
659 )
660 .unwrap();
661 }
662 } else if !local_incompatible {
663 writeln!(
664 &mut message,
665 "Either upgrade rustc or select compatible dependency versions with
666`cargo update <name>@<current-ver> --precise <compatible-ver>`
667where `<compatible-ver>` is the latest version supporting rustc {rustc_version}",
668 )
669 .unwrap();
670 }
671 return Err(anyhow::Error::msg(message));
672 }
673 }
674
675 let bcx = BuildContext::new(
676 ws,
677 logger,
678 pkg_set,
679 build_config,
680 profiles,
681 extra_compiler_args,
682 target_data,
683 root_units,
684 unit_graph,
685 unit_to_index,
686 scrape_units,
687 )?;
688
689 Ok(bcx)
690}
691
692fn validate_target_path_as_source_file(
694 gctx: &GlobalContext,
695 target_path: &std::path::Path,
696 target_name: &str,
697 target_kind: &TargetKind,
698 unit_manifest_path: &std::path::Path,
699 error_count: &mut usize,
700) -> CargoResult<()> {
701 if !target_path.exists() {
702 *error_count += 1;
703
704 let err_msg = format!(
705 "can't find {} `{}` at path `{}`",
706 target_kind.description(),
707 target_name,
708 target_path.display()
709 );
710
711 let group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(Origin::path(
712 unit_manifest_path.to_str().unwrap_or_default(),
713 ));
714
715 gctx.shell().print_report(&[group], true)?;
716 } else if target_path.is_dir() {
717 *error_count += 1;
718
719 let main_rs = target_path.join("main.rs");
721 let lib_rs = target_path.join("lib.rs");
722
723 let suggested_files_opt = match target_kind {
724 TargetKind::Lib(_) => {
725 if lib_rs.exists() {
726 Some(format!("`{}`", lib_rs.display()))
727 } else {
728 None
729 }
730 }
731 TargetKind::Bin => {
732 if main_rs.exists() {
733 Some(format!("`{}`", main_rs.display()))
734 } else {
735 None
736 }
737 }
738 TargetKind::Test => {
739 if main_rs.exists() {
740 Some(format!("`{}`", main_rs.display()))
741 } else {
742 None
743 }
744 }
745 TargetKind::ExampleBin => {
746 if main_rs.exists() {
747 Some(format!("`{}`", main_rs.display()))
748 } else {
749 None
750 }
751 }
752 TargetKind::Bench => {
753 if main_rs.exists() {
754 Some(format!("`{}`", main_rs.display()))
755 } else {
756 None
757 }
758 }
759 TargetKind::ExampleLib(_) => {
760 if lib_rs.exists() {
761 Some(format!("`{}`", lib_rs.display()))
762 } else {
763 None
764 }
765 }
766 TargetKind::CustomBuild => None,
767 };
768
769 let err_msg = format!(
770 "path `{}` for {} `{}` is a directory, but a source file was expected.",
771 target_path.display(),
772 target_kind.description(),
773 target_name,
774 );
775 let mut group = Group::with_title(Level::ERROR.primary_title(err_msg)).element(
776 Origin::path(unit_manifest_path.to_str().unwrap_or_default()),
777 );
778
779 if let Some(suggested_files) = suggested_files_opt {
780 group = group.element(
781 Level::HELP.message(format!("an entry point exists at {}", suggested_files)),
782 );
783 }
784
785 gctx.shell().print_report(&[group], true)?;
786 }
787
788 Ok(())
789}
790
791fn rebuild_unit_graph_shared(
826 interner: &UnitInterner,
827 unit_graph: UnitGraph,
828 roots: &[Unit],
829 scrape_units: &[Unit],
830 to_host: Option<CompileKind>,
831 compile_time_deps_only: bool,
832) -> (Vec<Unit>, Vec<Unit>, UnitGraph) {
833 let mut result = UnitGraph::new();
834 let mut memo = HashMap::new();
837 let new_roots = roots
838 .iter()
839 .map(|root| {
840 traverse_and_share(
841 interner,
842 &mut memo,
843 &mut result,
844 &unit_graph,
845 root,
846 true,
847 false,
848 to_host,
849 compile_time_deps_only,
850 )
851 })
852 .collect();
853 let new_scrape_units = scrape_units
857 .iter()
858 .map(|unit| memo.get(unit).unwrap().clone())
859 .collect();
860 (new_roots, new_scrape_units, result)
861}
862
863fn traverse_and_share(
869 interner: &UnitInterner,
870 memo: &mut HashMap<Unit, Unit>,
871 new_graph: &mut UnitGraph,
872 unit_graph: &UnitGraph,
873 unit: &Unit,
874 unit_is_root: bool,
875 unit_is_for_host: bool,
876 to_host: Option<CompileKind>,
877 compile_time_deps_only: bool,
878) -> Unit {
879 if let Some(new_unit) = memo.get(unit) {
880 return new_unit.clone();
882 }
883 let mut dep_hash = StableHasher::new();
884 let skip_non_compile_time_deps = compile_time_deps_only
885 && (!unit.target.is_compile_time_dependency() ||
886 unit_is_root);
889 let new_deps: Vec<_> = unit_graph[unit]
890 .iter()
891 .map(|dep| {
892 let new_dep_unit = traverse_and_share(
893 interner,
894 memo,
895 new_graph,
896 unit_graph,
897 &dep.unit,
898 false,
899 dep.unit_for.is_for_host(),
900 to_host,
901 skip_non_compile_time_deps,
905 );
906 new_dep_unit.hash(&mut dep_hash);
907 UnitDep {
908 unit: new_dep_unit,
909 ..dep.clone()
910 }
911 })
912 .collect();
913 let new_dep_hash = Hasher::finish(&dep_hash);
916
917 let canonical_kind = match to_host {
924 Some(to_host) if to_host == unit.kind => CompileKind::Host,
925 _ => unit.kind,
926 };
927
928 let mut profile = unit.profile.clone();
929 if profile.strip.is_deferred() {
930 if !profile.debuginfo.is_turned_on()
934 && new_deps
935 .iter()
936 .all(|dep| !dep.unit.profile.debuginfo.is_turned_on())
937 {
938 profile.strip = profile.strip.strip_debuginfo();
939 }
940 }
941
942 if unit_is_for_host
946 && to_host.is_some()
947 && profile.debuginfo.is_deferred()
948 && !unit.artifact.is_true()
949 {
950 let canonical_debuginfo = profile.debuginfo.finalize();
954 let mut canonical_profile = profile.clone();
955 canonical_profile.debuginfo = canonical_debuginfo;
956 let unit_probe = interner.intern(
957 &unit.pkg,
958 &unit.target,
959 canonical_profile,
960 to_host.unwrap(),
961 unit.mode,
962 unit.features.clone(),
963 unit.rustflags.clone(),
964 unit.rustdocflags.clone(),
965 unit.links_overrides.clone(),
966 unit.is_std,
967 unit.dep_hash,
968 unit.artifact,
969 unit.artifact_target_for_features,
970 unit.skip_non_compile_time_dep,
971 );
972
973 profile.debuginfo = if unit_graph.contains_key(&unit_probe) {
975 canonical_debuginfo
978 } else {
979 canonical_debuginfo.weaken()
982 }
983 }
984
985 let new_unit = interner.intern(
986 &unit.pkg,
987 &unit.target,
988 profile,
989 canonical_kind,
990 unit.mode,
991 unit.features.clone(),
992 unit.rustflags.clone(),
993 unit.rustdocflags.clone(),
994 unit.links_overrides.clone(),
995 unit.is_std,
996 new_dep_hash,
997 unit.artifact,
998 None,
1001 skip_non_compile_time_deps,
1002 );
1003 if !unit_is_root || !compile_time_deps_only {
1004 assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
1005 }
1006 new_graph.entry(new_unit.clone()).or_insert(new_deps);
1007 new_unit
1008}
1009
1010fn remove_duplicate_doc(
1026 build_config: &BuildConfig,
1027 root_units: &[Unit],
1028 unit_graph: &mut UnitGraph,
1029) {
1030 let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
1033 for unit in unit_graph.keys() {
1034 if unit.mode.is_doc() {
1035 all_docs
1036 .entry(unit.target.crate_name())
1037 .or_default()
1038 .push(unit.clone());
1039 }
1040 }
1041 let mut removed_units: HashSet<Unit> = HashSet::new();
1044 let mut remove = |units: Vec<Unit>, reason: &str, cb: &dyn Fn(&Unit) -> bool| -> Vec<Unit> {
1045 let (to_remove, remaining_units): (Vec<Unit>, Vec<Unit>) = units
1046 .into_iter()
1047 .partition(|unit| cb(unit) && !root_units.contains(unit));
1048 for unit in to_remove {
1049 tracing::debug!(
1050 "removing duplicate doc due to {} for package {} target `{}`",
1051 reason,
1052 unit.pkg,
1053 unit.target.name()
1054 );
1055 unit_graph.remove(&unit);
1056 removed_units.insert(unit);
1057 }
1058 remaining_units
1059 };
1060 for (_crate_name, mut units) in all_docs {
1062 if units.len() == 1 {
1063 continue;
1064 }
1065 if build_config
1067 .requested_kinds
1068 .iter()
1069 .all(CompileKind::is_host)
1070 {
1071 units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
1076 if units.len() == 1 {
1077 continue;
1078 }
1079 }
1080 let mut source_map: HashMap<(InternedString, SourceId, CompileKind), Vec<Unit>> =
1082 HashMap::new();
1083 for unit in units {
1084 let pkg_id = unit.pkg.package_id();
1085 source_map
1087 .entry((pkg_id.name(), pkg_id.source_id(), unit.kind))
1088 .or_default()
1089 .push(unit);
1090 }
1091 let mut remaining_units = Vec::new();
1092 for (_key, mut units) in source_map {
1093 if units.len() > 1 {
1094 units.sort_by(|a, b| a.pkg.version().partial_cmp(b.pkg.version()).unwrap());
1095 let newest_version = units.last().unwrap().pkg.version().clone();
1097 let keep_units = remove(units, "older version", &|unit| {
1098 unit.pkg.version() < &newest_version
1099 });
1100 remaining_units.extend(keep_units);
1101 } else {
1102 remaining_units.extend(units);
1103 }
1104 }
1105 if remaining_units.len() == 1 {
1106 continue;
1107 }
1108 }
1111 for unit_deps in unit_graph.values_mut() {
1113 unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
1114 }
1115 let mut visited = HashSet::new();
1117 fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
1118 if !visited.insert(unit.clone()) {
1119 return;
1120 }
1121 for dep in &graph[unit] {
1122 visit(&dep.unit, graph, visited);
1123 }
1124 }
1125 for unit in root_units {
1126 visit(unit, unit_graph, &mut visited);
1127 }
1128 unit_graph.retain(|unit, _| visited.contains(unit));
1129}
1130
1131fn override_rustc_crate_types(
1135 units: &mut [Unit],
1136 args: &[String],
1137 interner: &UnitInterner,
1138) -> CargoResult<()> {
1139 if units.len() != 1 {
1140 anyhow::bail!(
1141 "crate types to rustc can only be passed to one \
1142 target, consider filtering\nthe package by passing, \
1143 e.g., `--lib` or `--example` to specify a single target"
1144 );
1145 }
1146
1147 let unit = &units[0];
1148 let override_unit = |f: fn(Vec<CrateType>) -> TargetKind| {
1149 let crate_types = args.iter().map(|s| s.into()).collect();
1150 let mut target = unit.target.clone();
1151 target.set_kind(f(crate_types));
1152 interner.intern(
1153 &unit.pkg,
1154 &target,
1155 unit.profile.clone(),
1156 unit.kind,
1157 unit.mode,
1158 unit.features.clone(),
1159 unit.rustflags.clone(),
1160 unit.rustdocflags.clone(),
1161 unit.links_overrides.clone(),
1162 unit.is_std,
1163 unit.dep_hash,
1164 unit.artifact,
1165 unit.artifact_target_for_features,
1166 unit.skip_non_compile_time_dep,
1167 )
1168 };
1169 units[0] = match unit.target.kind() {
1170 TargetKind::Lib(_) => override_unit(TargetKind::Lib),
1171 TargetKind::ExampleLib(_) => override_unit(TargetKind::ExampleLib),
1172 _ => {
1173 anyhow::bail!(
1174 "crate types can only be specified for libraries and example libraries.\n\
1175 Binaries, tests, and benchmarks are always the `bin` crate type"
1176 );
1177 }
1178 };
1179
1180 Ok(())
1181}
1182
1183pub fn resolve_all_features(
1189 resolve_with_overrides: &Resolve,
1190 resolved_features: &features::ResolvedFeatures,
1191 package_set: &PackageSet<'_>,
1192 package_id: PackageId,
1193 has_dev_units: HasDevUnits,
1194 requested_kinds: &[CompileKind],
1195 target_data: &RustcTargetData<'_>,
1196 force_all_targets: ForceAllTargets,
1197) -> HashSet<String> {
1198 let mut features: HashSet<String> = resolved_features
1199 .activated_features(package_id, FeaturesFor::NormalOrDev)
1200 .iter()
1201 .map(|s| s.to_string())
1202 .collect();
1203
1204 let filtered_deps = PackageSet::filter_deps(
1207 package_id,
1208 resolve_with_overrides,
1209 has_dev_units,
1210 requested_kinds,
1211 target_data,
1212 force_all_targets,
1213 );
1214 for (dep_id, deps) in filtered_deps {
1215 let is_proc_macro = package_set
1216 .get_one(dep_id)
1217 .expect("packages downloaded")
1218 .proc_macro();
1219 for dep in deps {
1220 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
1221 for feature in resolved_features
1222 .activated_features_unverified(dep_id, features_for)
1223 .unwrap_or_default()
1224 {
1225 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
1226 }
1227 }
1228 }
1229
1230 features
1231}