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