in guppy/src/graph/cargo/build.rs [89:308]
fn build_set<'g>(
&self,
initials: FeatureSet<'g>,
features_only: FeatureSet<'g>,
intermediate_fn: impl FnOnce(FeatureQuery<'g>) -> CargoIntermediateSet<'g>,
) -> CargoSet<'g> {
// Prepare a package query for step 2.
let graph = *initials.graph();
// Note that currently, proc macros specified in initials are built on both the target and
// the host.
let mut host_ixs = Vec::new();
let target_ixs: Vec<_> = initials
.ixs_unordered()
.filter_map(|feature_ix| {
let metadata = graph.metadata_for_ix(feature_ix);
let package_ix = metadata.package_ix();
match self.opts.initials_platform {
InitialsPlatform::Host => {
// Always build on the host.
host_ixs.push(package_ix);
None
}
InitialsPlatform::Standard => {
// Proc macros on the host platform, everything else on the target platform.
if metadata.package().is_proc_macro() {
host_ixs.push(package_ix);
None
} else {
Some(package_ix)
}
}
InitialsPlatform::ProcMacrosOnTarget => {
// Proc macros on both the host and the target platforms, everything else
// on the target platform.
if metadata.package().is_proc_macro() {
host_ixs.push(package_ix);
}
Some(package_ix)
}
}
})
.collect();
let target_query = graph
.package_graph
.query_from_parts(SortedSet::new(target_ixs), DependencyDirection::Forward);
// 1. Build the intermediate set containing the features for any possible package that can
// be built, including features-only packages.
let initials_plus_features_only = initials.union(&features_only);
let intermediate_set = intermediate_fn(
initials_plus_features_only.to_feature_query(DependencyDirection::Forward),
);
let (target_set, host_set) = intermediate_set.target_host_sets();
// While doing traversal 2 below, record any packages discovered along build edges for use
// in host ixs, to prepare for step 3. This will also include proc-macros.
// This list will contain proc-macro edges out of target packages.
let mut proc_macro_edge_ixs = Vec::new();
// This list will contain build dep edges out of target packages.
let mut build_dep_edge_ixs = Vec::new();
let is_enabled = |feature_set: &FeatureSet<'_>,
link: &PackageLink<'_>,
kind: DependencyKind,
platform_spec: &PlatformSpec| {
let (from, to) = link.endpoints();
let req_status = link.req_for_kind(kind).status();
// Check the complete set to figure out whether we look at required_on or
// enabled_on.
let consider_optional = feature_set
.contains((from.id(), FeatureLabel::OptionalDependency(link.dep_name())))
.unwrap_or_else(|_| {
// If the feature ID isn't present, it means the dependency wasn't declared
// as optional. In that case the value doesn't matter.
debug_assert!(
req_status.optional_status().is_never(),
"for {} -> {}, dep '{}' not declared as optional",
from.name(),
to.name(),
link.dep_name()
);
false
});
if consider_optional {
req_status.enabled_on(platform_spec) != EnabledTernary::Disabled
} else {
req_status.required_on(platform_spec) != EnabledTernary::Disabled
}
};
// Record workspace + direct third-party deps in these sets.
let mut target_direct_deps =
FixedBitSet::with_capacity(graph.package_graph.package_count());
let mut host_direct_deps = FixedBitSet::with_capacity(graph.package_graph.package_count());
// 2. Figure out what packages will be included on the target platform, i.e. normal + dev
// (if requested).
let target_platform = &self.opts.target_platform;
let host_platform = &self.opts.host_platform;
let target_packages = target_query.resolve_with_fn(|query, link| {
let (from, to) = link.endpoints();
if from.in_workspace() {
// Mark initials in target_direct_deps.
target_direct_deps.visit(from.package_ix());
}
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.id()).expect("valid ID");
// Build dependencies are only considered if there's a build script.
let consider_build = from.has_build_script();
let mut follow_target =
is_enabled(target_set, &link, DependencyKind::Normal, target_platform)
|| (consider_dev
&& is_enabled(
target_set,
&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.is_proc_macro();
// Build dependencies are evaluated against the host platform.
let build_dep_redirect = consider_build
&& is_enabled(target_set, &link, DependencyKind::Build, host_platform);
// Finally, process what needs to be done.
if build_dep_redirect || proc_macro_redirect {
if from.in_workspace() {
// The 'to' node is either in the workspace or a direct dependency [a].
host_direct_deps.visit(to.package_ix());
}
host_ixs.push(to.package_ix());
}
if build_dep_redirect {
build_dep_edge_ixs.push(link.edge_ix());
}
if proc_macro_redirect {
proc_macro_edge_ixs.push(link.edge_ix());
follow_target = false;
}
if from.in_workspace() && follow_target {
// The 'to' node is either in the workspace or a direct dependency.
target_direct_deps.visit(to.package_ix());
}
follow_target
});
// 3. Figure out what packages will be included on the host platform.
let host_ixs = SortedSet::new(host_ixs);
let host_packages = graph
.package_graph
.query_from_parts(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;
}
// All relevant nodes in host_ixs have already been added to host_direct_deps at [a].
let consider_build = from.has_build_script();
// Only normal and build dependencies are considered, regardless of whether this is
// an initial. (Dev-dependencies of initials would have been considered in step 2).
let res = is_enabled(host_set, &link, DependencyKind::Normal, host_platform)
|| (consider_build
&& is_enabled(host_set, &link, DependencyKind::Build, host_platform));
if res {
if from.in_workspace() {
// The 'to' node is either in the workspace or a direct dependency.
host_direct_deps.visit(to.package_ix());
}
true
} else {
false
}
});
// Finally, the features are whatever packages were selected, intersected with whatever
// features were selected.
let target_features = target_packages
.to_feature_set(StandardFeatures::All)
.intersection(target_set);
let host_features = host_packages
.to_feature_set(StandardFeatures::All)
.intersection(host_set);
// Also construct the direct dep sets.
let target_direct_deps =
PackageSet::from_included(graph.package_graph(), target_direct_deps);
let host_direct_deps = PackageSet::from_included(graph.package_graph, host_direct_deps);
CargoSet {
initials,
features_only,
target_features,
host_features,
target_direct_deps,
host_direct_deps,
proc_macro_edge_ixs: SortedSet::new(proc_macro_edge_ixs),
build_dep_edge_ixs: SortedSet::new(build_dep_edge_ixs),
}
}