in guppy/src/graph/cargo/build.rs [337:437]
fn new_v2_intermediate<'g>(&self, query: FeatureQuery<'g>) -> CargoIntermediateSet<'g> {
let graph = *query.graph();
// Note that proc macros specified in initials take part in feature resolution
// for both target and host ixs. If they didn't, then the query would be partitioned into
// host and target ixs instead.
// https://github.com/rust-lang/cargo/issues/8312
let mut host_ixs: Vec<_> = query
.params
.initials()
.iter()
.filter_map(|feature_ix| {
let metadata = graph.metadata_for_ix(*feature_ix);
if self.opts.initials_platform == InitialsPlatform::Host
|| metadata.package().is_proc_macro()
{
// Proc macros are always unified on the host.
Some(metadata.feature_ix())
} else {
// Everything else is built on the target.
None
}
})
.collect();
let is_enabled =
|link: &ConditionalLink<'_>, kind: DependencyKind, platform_spec: &PlatformSpec| {
let platform_status = link.status_for_kind(kind);
platform_status.enabled_on(platform_spec) != EnabledTernary::Disabled
};
let target_query = if self.opts.initials_platform == InitialsPlatform::Host {
// Empty query on the target.
graph.query_from_parts(SortedSet::new(vec![]), DependencyDirection::Forward)
} else {
query
};
// Keep a copy of the target query for use in step 2.
let target_query_2 = target_query.clone();
// 1. Perform a feature query for the target.
let target_platform = &self.opts.target_platform;
let host_platform = &self.opts.host_platform;
let target = target_query.resolve_with_fn(|query, link| {
let (from, to) = link.endpoints();
if self.is_omitted(to.package_ix()) {
// Pretend that the omitted set doesn't exist.
return false;
}
let consider_dev =
self.opts.include_dev && query.starts_from(from.feature_id()).expect("valid ID");
// This resolver doesn't check for whether this package has a build script.
let mut follow_target = is_enabled(&link, DependencyKind::Normal, target_platform)
|| (consider_dev
&& is_enabled(&link, DependencyKind::Development, target_platform));
// Proc macros build on the host, so for normal/dev dependencies redirect it to the host
// instead.
let proc_macro_redirect = follow_target && to.package().is_proc_macro();
// Build dependencies are evaluated against the host platform.
let build_dep_redirect = is_enabled(&link, DependencyKind::Build, host_platform);
// Finally, process what needs to be done.
if build_dep_redirect || proc_macro_redirect {
host_ixs.push(to.feature_ix());
}
if proc_macro_redirect {
follow_target = false;
}
follow_target
});
// 2. Perform a feature query for the host.
let host = graph
.query_from_parts(SortedSet::new(host_ixs), DependencyDirection::Forward)
.resolve_with_fn(|_, link| {
let (from, to) = link.endpoints();
if self.is_omitted(to.package_ix()) {
// Pretend that the omitted set doesn't exist.
return false;
}
// During feature resolution, the v2 resolver doesn't check for whether this package
// has a build script. It also unifies dev dependencies of initials, even on the
// host platform.
let consider_dev = self.opts.include_dev
&& target_query_2
.starts_from(from.feature_id())
.expect("valid ID");
is_enabled(&link, DependencyKind::Normal, host_platform)
|| is_enabled(&link, DependencyKind::Build, host_platform)
|| (consider_dev
&& is_enabled(&link, DependencyKind::Development, host_platform))
});
CargoIntermediateSet::TargetHost { target, host }
}