feat(compile-time-deps): Implement non compile time deps filtering

This commit is contained in:
Shoyu Vanilla 2025-06-18 00:00:28 +09:00
parent d253d12122
commit 5b178a8d05
9 changed files with 95 additions and 42 deletions

View File

@ -50,6 +50,8 @@ pub struct BuildConfig {
pub timing_outputs: Vec<TimingOutput>,
/// 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<u32> {
@ -129,6 +131,7 @@ impl BuildConfig {
future_incompat_report: false,
timing_outputs: Vec::new(),
sbom,
compile_time_deps_only: false,
})
}

View File

@ -448,6 +448,10 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
bcx: &BuildContext<'a, 'gctx>,
) -> CargoResult<Arc<Vec<OutputFile>>> {
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)

View File

@ -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.

View File

@ -210,6 +210,7 @@ fn generate_roots(
/*dep_hash*/ 0,
IsArtifact::No,
None,
false,
));
}
}

View File

@ -112,6 +112,13 @@ pub struct UnitInner {
///
/// [`FeaturesFor::ArtifactDep`]: crate::core::resolver::features::FeaturesFor::ArtifactDep
pub artifact_target_for_features: Option<CompileTarget>,
/// 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<CompileTarget>,
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 }
}

View File

@ -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,

View File

@ -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<CrateType> {
self.kind().rustc_crate_types()

View File

@ -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 `<compatible-ver>` 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<CompileKind>,
compile_time_deps_only: bool,
) -> (Vec<Unit>, Vec<Unit>, 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<CompileKind>,
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() {

View File

@ -167,6 +167,7 @@ impl<'a> UnitGenerator<'a, '_> {
/*dep_hash*/ 0,
IsArtifact::No,
None,
false,
)
})
.collect()