diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index 387098a2e..e042f21e2 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -50,6 +50,8 @@ pub struct BuildConfig { pub timing_outputs: Vec, /// Output SBOM precursor files. pub sbom: bool, + /// Build compile time dependencies only, e.g., build scripts and proc macros + pub compile_time_deps_only: bool, } fn default_parallelism() -> CargoResult { @@ -129,6 +131,7 @@ impl BuildConfig { future_incompat_report: false, timing_outputs: Vec::new(), sbom, + compile_time_deps_only: false, }) } diff --git a/src/cargo/core/compiler/build_runner/compilation_files.rs b/src/cargo/core/compiler/build_runner/compilation_files.rs index 1892e8775..f70a870cf 100644 --- a/src/cargo/core/compiler/build_runner/compilation_files.rs +++ b/src/cargo/core/compiler/build_runner/compilation_files.rs @@ -448,6 +448,10 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> { bcx: &BuildContext<'a, 'gctx>, ) -> CargoResult>> { let ret = match unit.mode { + _ if unit.skip_non_compile_time_dep => { + // This skips compilations so no outputs + vec![] + } CompileMode::Doc => { let path = if bcx.build_config.intent.wants_doc_json_output() { self.out_dir(unit) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 63857fea8..9bd925a20 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -180,50 +180,55 @@ fn compile<'gctx>( return Ok(()); } - // Build up the work to be done to compile this unit, enqueuing it once - // we've got everything constructed. - fingerprint::prepare_init(build_runner, unit)?; + // If we are in `--compile-time-deps` and the given unit is not a compile time + // dependency, skip compling the unit and jumps to dependencies, which still + // have chances to be compile time dependencies + if !unit.skip_non_compile_time_dep { + // Build up the work to be done to compile this unit, enqueuing it once + // we've got everything constructed. + fingerprint::prepare_init(build_runner, unit)?; - let job = if unit.mode.is_run_custom_build() { - custom_build::prepare(build_runner, unit)? - } else if unit.mode.is_doc_test() { - // We run these targets later, so this is just a no-op for now. - Job::new_fresh() - } else if build_plan { - Job::new_dirty( - rustc(build_runner, unit, &exec.clone())?, - DirtyReason::FreshBuild, - ) - } else { - let force = exec.force_rebuild(unit) || force_rebuild; - let mut job = fingerprint::prepare_target(build_runner, unit, force)?; - job.before(if job.freshness().is_dirty() { - let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { - rustdoc(build_runner, unit)? - } else { - rustc(build_runner, unit, exec)? - }; - work.then(link_targets(build_runner, unit, false)?) + let job = if unit.mode.is_run_custom_build() { + custom_build::prepare(build_runner, unit)? + } else if unit.mode.is_doc_test() { + // We run these targets later, so this is just a no-op for now. + Job::new_fresh() + } else if build_plan { + Job::new_dirty( + rustc(build_runner, unit, &exec.clone())?, + DirtyReason::FreshBuild, + ) } else { - // We always replay the output cache, - // since it might contain future-incompat-report messages - let show_diagnostics = unit.show_warnings(bcx.gctx) - && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow; - let work = replay_output_cache( - unit.pkg.package_id(), - PathBuf::from(unit.pkg.manifest_path()), - &unit.target, - build_runner.files().message_cache_path(unit), - build_runner.bcx.build_config.message_format, - show_diagnostics, - ); - // Need to link targets on both the dirty and fresh. - work.then(link_targets(build_runner, unit, true)?) - }); + let force = exec.force_rebuild(unit) || force_rebuild; + let mut job = fingerprint::prepare_target(build_runner, unit, force)?; + job.before(if job.freshness().is_dirty() { + let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { + rustdoc(build_runner, unit)? + } else { + rustc(build_runner, unit, exec)? + }; + work.then(link_targets(build_runner, unit, false)?) + } else { + // We always replay the output cache, + // since it might contain future-incompat-report messages + let show_diagnostics = unit.show_warnings(bcx.gctx) + && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow; + let work = replay_output_cache( + unit.pkg.package_id(), + PathBuf::from(unit.pkg.manifest_path()), + &unit.target, + build_runner.files().message_cache_path(unit), + build_runner.bcx.build_config.message_format, + show_diagnostics, + ); + // Need to link targets on both the dirty and fresh. + work.then(link_targets(build_runner, unit, true)?) + }); - job - }; - jobs.enqueue(build_runner, unit, job)?; + job + }; + jobs.enqueue(build_runner, unit, job)?; + } // Be sure to compile all dependencies of this target as well. let deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow. diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 18253b5d4..30f7c1815 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -210,6 +210,7 @@ fn generate_roots( /*dep_hash*/ 0, IsArtifact::No, None, + false, )); } } diff --git a/src/cargo/core/compiler/unit.rs b/src/cargo/core/compiler/unit.rs index 631353aae..43068b7f5 100644 --- a/src/cargo/core/compiler/unit.rs +++ b/src/cargo/core/compiler/unit.rs @@ -112,6 +112,13 @@ pub struct UnitInner { /// /// [`FeaturesFor::ArtifactDep`]: crate::core::resolver::features::FeaturesFor::ArtifactDep pub artifact_target_for_features: Option, + + /// Skip compiling this unit because `--compile-time-deps` flag is set and + /// this is not a compile time dependency. + /// + /// Since dependencies of this unit might be compile time dependencies, we + /// set this field instead of completely dropping out this unit from unit graph. + pub skip_non_compile_time_dep: bool, } impl UnitInner { @@ -245,6 +252,7 @@ impl UnitInterner { dep_hash: u64, artifact: IsArtifact, artifact_target_for_features: Option, + skip_non_compile_time_dep: bool, ) -> Unit { let target = match (is_std, target.kind()) { // This is a horrible hack to support build-std. `libstd` declares @@ -281,6 +289,7 @@ impl UnitInterner { dep_hash, artifact, artifact_target_for_features, + skip_non_compile_time_dep, }); Unit { inner } } diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 3255c8208..8f7115610 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -866,6 +866,7 @@ fn new_unit_dep_with_profile( /*dep_hash*/ 0, artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), artifact_target, + false, ); Ok(UnitDep { unit, diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index f85f0ff73..18bd62102 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -1079,6 +1079,11 @@ impl Target { *self.kind() == TargetKind::CustomBuild } + /// Returns `true` if it is a compile time depencencies, e.g., build script or proc macro + pub fn is_compile_time_dependency(&self) -> bool { + self.is_custom_build() || self.proc_macro() + } + /// Returns the arguments suitable for `--crate-type` to pass to rustc. pub fn rustc_crate_types(&self) -> Vec { self.kind().rustc_crate_types() diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index 6a406cc92..366056ef2 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -449,6 +449,7 @@ pub fn create_bcx<'a, 'gctx>( &units, &scrape_units, host_kind_requested.then_some(explicit_host_kind), + build_config.compile_time_deps_only, ); let mut extra_compiler_args = HashMap::new(); @@ -582,12 +583,16 @@ where `` is the latest version supporting rustc {rustc_version}" /// This is also responsible for adjusting the `debug` setting for host /// dependencies, turning off debug if the user has not explicitly enabled it, /// and the unit is not shared with a target unit. +/// +/// This is also responsible for adjusting whether each unit should be compiled +/// or not regarding `--compile-time-deps` flag. fn rebuild_unit_graph_shared( interner: &UnitInterner, unit_graph: UnitGraph, roots: &[Unit], scrape_units: &[Unit], to_host: Option, + compile_time_deps_only: bool, ) -> (Vec, Vec, UnitGraph) { let mut result = UnitGraph::new(); // Map of the old unit to the new unit, used to avoid recursing into units @@ -602,8 +607,10 @@ fn rebuild_unit_graph_shared( &mut result, &unit_graph, root, + true, false, to_host, + compile_time_deps_only, ) }) .collect(); @@ -628,14 +635,21 @@ fn traverse_and_share( new_graph: &mut UnitGraph, unit_graph: &UnitGraph, unit: &Unit, + unit_is_root: bool, unit_is_for_host: bool, to_host: Option, + compile_time_deps_only: bool, ) -> Unit { if let Some(new_unit) = memo.get(unit) { // Already computed, no need to recompute. return new_unit.clone(); } let mut dep_hash = StableHasher::new(); + let skip_non_compile_time_deps = compile_time_deps_only + && (!unit.target.is_compile_time_dependency() || + // Root unit is not a dependency unless other units are dependant + // to it. + unit_is_root); let new_deps: Vec<_> = unit_graph[unit] .iter() .map(|dep| { @@ -645,8 +659,13 @@ fn traverse_and_share( new_graph, unit_graph, &dep.unit, + false, dep.unit_for.is_for_host(), to_host, + // If we should compile the current unit, we should also compile + // its dependencies. And if not, we should compile compile time + // dependencies only. + skip_non_compile_time_deps, ); new_dep_unit.hash(&mut dep_hash); UnitDep { @@ -712,6 +731,7 @@ fn traverse_and_share( unit.dep_hash, unit.artifact, unit.artifact_target_for_features, + unit.skip_non_compile_time_dep, ); // We can now turn the deferred value into its actual final value. @@ -742,8 +762,11 @@ fn traverse_and_share( // Since `dep_hash` is now filled in, there's no need to specify the artifact target // for target-dependent feature resolution None, + skip_non_compile_time_deps, ); - assert!(memo.insert(unit.clone(), new_unit.clone()).is_none()); + if !unit_is_root || !compile_time_deps_only { + assert!(memo.insert(unit.clone(), new_unit.clone()).is_none()); + } new_graph.entry(new_unit.clone()).or_insert(new_deps); new_unit } @@ -904,6 +927,7 @@ fn override_rustc_crate_types( unit.dep_hash, unit.artifact, unit.artifact_target_for_features, + unit.skip_non_compile_time_dep, ) }; units[0] = match unit.target.kind() { diff --git a/src/cargo/ops/cargo_compile/unit_generator.rs b/src/cargo/ops/cargo_compile/unit_generator.rs index 29762c8f8..2e2398897 100644 --- a/src/cargo/ops/cargo_compile/unit_generator.rs +++ b/src/cargo/ops/cargo_compile/unit_generator.rs @@ -167,6 +167,7 @@ impl<'a> UnitGenerator<'a, '_> { /*dep_hash*/ 0, IsArtifact::No, None, + false, ) }) .collect()