in tools/hakari/src/hakari.rs [830:988]
fn new(builder: &'b HakariBuilder<'g>) -> Self {
// This was just None or All for a bit under the theory that feature sets are additive only,
// but unfortunately we cannot exploit this property because it doesn't account for the fact
// that some dependencies might not be built *at all*, under certain feature combinations.
//
// That's also why we simulate builds with and without dev-only dependencies in all cases.
//
// For example, for:
//
// ```toml
// [dependencies]
// dep = { version = "1", optional = true }
//
// [dev-dependencies]
// dep = { version = "1", optional = true, features = ["dev-feature"] }
//
// [features]
// default = ["dep"]
// extra = ["dep/extra", "dep/dev-feature"]
// ```
//
// | feature set | include dev | dep status |
// | ----------- | ----------- | ------------------ |
// | none | no | not built |
// | none | yes | not built |
// | default | no | no features |
// | default | yes | dev-feature |
// | all | no | extra, dev-feature |
// | all | yes | extra, dev-feature |
//
// (And there's further complexity possible with transitive deps as well.)
let features_include_dev = [
(StandardFeatures::None, false),
(StandardFeatures::None, true),
(StandardFeatures::Default, false),
(StandardFeatures::Default, true),
(StandardFeatures::All, false),
(StandardFeatures::All, true),
];
// Features for the "always" platform spec.
let always_features = features_include_dev
.iter()
.map(|&(features, include_dev)| (None, PlatformSpec::Always, features, include_dev));
// Features for specified platforms.
let specified_features =
features_include_dev
.iter()
.flat_map(|&(features, include_dev)| {
builder
.platforms
.iter()
.enumerate()
.map(move |(idx, platform)| {
(
Some(idx),
PlatformSpec::Platform(platform.clone()),
features,
include_dev,
)
})
});
let platforms_features: Vec<_> = always_features.chain(specified_features).collect();
let workspace = builder.graph.workspace();
let excludes = builder.make_traversal_excludes();
let features_only = builder.make_features_only();
let excludes_ref = &excludes;
let features_only_ref = &features_only;
let computed_map: ComputedMap<'g> = platforms_features
.into_par_iter()
// The cargo_set computation in the inner iterator is the most expensive part of the
// process, so use flat_map instead of flat_map_iter.
.flat_map(|(idx, platform_spec, feature_filter, include_dev)| {
let mut cargo_options = CargoOptions::new();
cargo_options
.set_include_dev(include_dev)
.set_resolver(builder.resolver)
.set_platform(platform_spec)
.add_omitted_packages(excludes.iter());
workspace.par_iter().map(move |workspace_package| {
if excludes_ref.is_excluded(workspace_package.id()) {
// Skip this package since it was excluded during traversal.
return BTreeMap::new();
}
let initials = workspace_package
.to_package_set()
.to_feature_set(feature_filter);
let cargo_set =
CargoSet::new(initials, features_only_ref.clone(), &cargo_options)
.expect("cargo resolution should succeed");
let all_features = cargo_set.all_features();
let values = all_features.iter().flat_map(|&(build_platform, features)| {
features
.packages_with_features(DependencyDirection::Forward)
.filter_map(move |feature_list| {
let dep = feature_list.package();
if dep.in_workspace() {
// Only looking at third-party packages for hakari.
return None;
}
let features: BTreeSet<&'g str> =
feature_list.named_features().collect();
Some((
idx,
build_platform,
dep.id(),
features,
workspace_package,
feature_filter,
include_dev,
))
})
});
let mut map = ComputedMap::new();
for (
platform_idx,
build_platform,
package_id,
features,
package,
feature_filter,
include_dev,
) in values
{
// Accumulate the features and package for each key.
map.entry((platform_idx, package_id)).or_default().insert(
build_platform,
features,
package,
feature_filter,
include_dev,
);
}
map
})
})
.reduce(ComputedMap::new, |mut acc, map| {
// Accumulate across all threads.
for (k, v) in map {
acc.entry(k).or_default().merge(v);
}
acc
});
Self {
excludes,
computed_map,
}
}