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