Skip to content

Commit 73b5b33

Browse files
committed
refactor(resolve): allow multiple resolved feature sets in workspace resolve
1 parent 403f1e1 commit 73b5b33

File tree

5 files changed

+224
-148
lines changed

5 files changed

+224
-148
lines changed

src/cargo/core/compiler/standard_lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub fn resolve_std<'gctx>(
9696
&features, /*all_features*/ false, /*uses_default_features*/ false,
9797
)?;
9898
let dry_run = false;
99-
let resolve = ops::resolve_ws_with_opts(
99+
let mut resolve = ops::resolve_ws_with_opts(
100100
&std_ws,
101101
target_data,
102102
&build_config.requested_kinds,
@@ -106,10 +106,15 @@ pub fn resolve_std<'gctx>(
106106
crate::core::resolver::features::ForceAllTargets::No,
107107
dry_run,
108108
)?;
109+
debug_assert_eq!(resolve.specs_and_features.len(), 1);
109110
Ok((
110111
resolve.pkg_set,
111112
resolve.targeted_resolve,
112-
resolve.resolved_features,
113+
resolve
114+
.specs_and_features
115+
.pop()
116+
.expect("resolve should have a single spec with resolved features")
117+
.resolved_features,
113118
))
114119
}
115120

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use crate::core::resolver::{HasDevUnits, Resolve};
5252
use crate::core::{PackageId, PackageSet, SourceId, TargetKind, Workspace};
5353
use crate::drop_println;
5454
use crate::ops;
55-
use crate::ops::resolve::WorkspaceResolve;
55+
use crate::ops::resolve::{SpecsAndResolvedFeatures, WorkspaceResolve};
5656
use crate::util::context::{GlobalContext, WarningHandling};
5757
use crate::util::interning::InternedString;
5858
use crate::util::{CargoResult, StableHasher};
@@ -284,7 +284,7 @@ pub fn create_bcx<'a, 'gctx>(
284284
mut pkg_set,
285285
workspace_resolve,
286286
targeted_resolve: resolve,
287-
resolved_features,
287+
specs_and_features,
288288
} = resolve;
289289

290290
let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
@@ -363,72 +363,91 @@ pub fn create_bcx<'a, 'gctx>(
363363
})
364364
.collect();
365365

366-
// Passing `build_config.requested_kinds` instead of
367-
// `explicit_host_kinds` here so that `generate_root_units` can do
368-
// its own special handling of `CompileKind::Host`. It will
369-
// internally replace the host kind by the `explicit_host_kind`
370-
// before setting as a unit.
371-
let generator = UnitGenerator {
372-
ws,
373-
packages: &to_builds,
374-
spec,
375-
target_data: &target_data,
376-
filter,
377-
requested_kinds: &build_config.requested_kinds,
378-
explicit_host_kind,
379-
intent: build_config.intent,
380-
resolve: &resolve,
381-
workspace_resolve: &workspace_resolve,
382-
resolved_features: &resolved_features,
383-
package_set: &pkg_set,
384-
profiles: &profiles,
385-
interner,
386-
has_dev_units,
387-
};
388-
let mut units = generator.generate_root_units()?;
366+
let mut units = Vec::new();
367+
let mut unit_graph = HashMap::new();
368+
let mut scrape_units = Vec::new();
389369

390-
if let Some(args) = target_rustc_crate_types {
391-
override_rustc_crate_types(&mut units, args, interner)?;
392-
}
370+
for SpecsAndResolvedFeatures {
371+
specs,
372+
resolved_features,
373+
} in &specs_and_features
374+
{
375+
// Passing `build_config.requested_kinds` instead of
376+
// `explicit_host_kinds` here so that `generate_root_units` can do
377+
// its own special handling of `CompileKind::Host`. It will
378+
// internally replace the host kind by the `explicit_host_kind`
379+
// before setting as a unit.
380+
let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
381+
let packages = to_builds
382+
.iter()
383+
.filter(|package| spec_names.contains(&package.name().as_str()))
384+
.cloned()
385+
.collect::<Vec<_>>();
386+
let generator = UnitGenerator {
387+
ws,
388+
packages: &packages,
389+
spec,
390+
target_data: &target_data,
391+
filter,
392+
requested_kinds: &build_config.requested_kinds,
393+
explicit_host_kind,
394+
intent: build_config.intent,
395+
resolve: &resolve,
396+
workspace_resolve: &workspace_resolve,
397+
resolved_features: &resolved_features,
398+
package_set: &pkg_set,
399+
profiles: &profiles,
400+
interner,
401+
has_dev_units,
402+
};
403+
let mut targeted_root_units = generator.generate_root_units()?;
393404

394-
let should_scrape = build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
395-
let mut scrape_units = if should_scrape {
396-
generator.generate_scrape_units(&units)?
397-
} else {
398-
Vec::new()
399-
};
405+
if let Some(args) = target_rustc_crate_types {
406+
override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
407+
}
408+
409+
let should_scrape =
410+
build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
411+
let targeted_scrape_units = if should_scrape {
412+
generator.generate_scrape_units(&targeted_root_units)?
413+
} else {
414+
Vec::new()
415+
};
416+
417+
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
418+
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
419+
standard_lib::generate_std_roots(
420+
&crates,
421+
&targeted_root_units,
422+
std_resolve,
423+
std_features,
424+
&explicit_host_kinds,
425+
&pkg_set,
426+
interner,
427+
&profiles,
428+
&target_data,
429+
)?
430+
} else {
431+
Default::default()
432+
};
400433

401-
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
402-
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
403-
standard_lib::generate_std_roots(
404-
&crates,
405-
&units,
406-
std_resolve,
407-
std_features,
408-
&explicit_host_kinds,
434+
unit_graph.extend(build_unit_dependencies(
435+
ws,
409436
&pkg_set,
410-
interner,
411-
&profiles,
437+
&resolve,
438+
&resolved_features,
439+
std_resolve_features.as_ref(),
440+
&targeted_root_units,
441+
&targeted_scrape_units,
442+
&std_roots,
443+
build_config.intent,
412444
&target_data,
413-
)?
414-
} else {
415-
Default::default()
416-
};
417-
418-
let mut unit_graph = build_unit_dependencies(
419-
ws,
420-
&pkg_set,
421-
&resolve,
422-
&resolved_features,
423-
std_resolve_features.as_ref(),
424-
&units,
425-
&scrape_units,
426-
&std_roots,
427-
build_config.intent,
428-
&target_data,
429-
&profiles,
430-
interner,
431-
)?;
445+
&profiles,
446+
interner,
447+
)?);
448+
units.extend(targeted_root_units);
449+
scrape_units.extend(targeted_scrape_units);
450+
}
432451

433452
// TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
434453
// what heuristics to use in that case.

src/cargo/ops/fix/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,15 @@ fn check_resolver_change<'gctx>(
588588
feature_opts,
589589
)?;
590590

591-
let diffs = v2_features.compare_legacy(&ws_resolve.resolved_features);
591+
if ws_resolve.specs_and_features.len() != 1 {
592+
bail!(r#"cannot fix edition when using `feature-unification = "package"`."#);
593+
}
594+
let resolved_features = &ws_resolve
595+
.specs_and_features
596+
.first()
597+
.expect("We've already checked that there is exactly one.")
598+
.resolved_features;
599+
let diffs = v2_features.compare_legacy(resolved_features);
592600
Ok((ws_resolve, diffs))
593601
};
594602
let (_, without_dev_diffs) = resolve_differences(HasDevUnits::No)?;

src/cargo/ops/resolve.rs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ use crate::util::CanonicalUrl;
8181
use anyhow::Context as _;
8282
use cargo_util::paths;
8383
use cargo_util_schemas::core::PartialVersion;
84+
use std::borrow::Cow;
8485
use std::collections::{HashMap, HashSet};
86+
use std::rc::Rc;
8587
use tracing::{debug, trace};
8688

8789
/// Filter for keep using Package ID from previous lockfile.
@@ -96,9 +98,18 @@ pub struct WorkspaceResolve<'gctx> {
9698
/// This may be `None` for things like `cargo install` and `-Zavoid-dev-deps`.
9799
/// This does not include `paths` overrides.
98100
pub workspace_resolve: Option<Resolve>,
99-
/// The narrowed resolve, with the specific features enabled, and only the
100-
/// given package specs requested.
101+
/// The narrowed resolve, with the specific features enabled.
101102
pub targeted_resolve: Resolve,
103+
/// Package specs requested for compilation along with specific features enabled. This usually
104+
/// has the length of one but there may be more specs with different features when using the
105+
/// `package` feature resolver.
106+
pub specs_and_features: Vec<SpecsAndResolvedFeatures>,
107+
}
108+
109+
/// Pair of package specs requested for compilation along with enabled features.
110+
pub struct SpecsAndResolvedFeatures {
111+
/// Packages that are supposed to be built.
112+
pub specs: Vec<PackageIdSpec>,
102113
/// The features activated per package.
103114
pub resolved_features: ResolvedFeatures,
104115
}
@@ -145,10 +156,20 @@ pub fn resolve_ws_with_opts<'gctx>(
145156
force_all_targets: ForceAllTargets,
146157
dry_run: bool,
147158
) -> CargoResult<WorkspaceResolve<'gctx>> {
148-
let specs = match ws.resolve_feature_unification() {
149-
FeatureUnification::Selected => specs,
150-
FeatureUnification::Workspace => &ops::Packages::All(Vec::new()).to_package_id_specs(ws)?,
159+
let feature_unification = ws.resolve_feature_unification();
160+
let individual_specs = match feature_unification {
161+
FeatureUnification::Selected => vec![specs.to_owned()],
162+
FeatureUnification::Workspace => {
163+
vec![ops::Packages::All(Vec::new()).to_package_id_specs(ws)?]
164+
}
151165
};
166+
let specs: Vec<_> = individual_specs
167+
.iter()
168+
.map(|specs| specs.iter())
169+
.flatten()
170+
.cloned()
171+
.collect();
172+
let specs = &specs[..];
152173
let mut registry = ws.package_registry()?;
153174
let (resolve, resolved_with_overrides) = if ws.ignore_lock() {
154175
let add_patches = true;
@@ -229,9 +250,9 @@ pub fn resolve_ws_with_opts<'gctx>(
229250

230251
let pkg_set = get_resolved_packages(&resolved_with_overrides, registry)?;
231252

232-
let member_ids = ws
233-
.members_with_features(specs, cli_features)?
234-
.into_iter()
253+
let members_with_features = ws.members_with_features(specs, cli_features)?;
254+
let member_ids = members_with_features
255+
.iter()
235256
.map(|(p, _fts)| p.package_id())
236257
.collect::<Vec<_>>();
237258
pkg_set.download_accessible(
@@ -243,33 +264,49 @@ pub fn resolve_ws_with_opts<'gctx>(
243264
force_all_targets,
244265
)?;
245266

246-
let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
247-
let resolved_features = FeatureResolver::resolve(
248-
ws,
249-
target_data,
250-
&resolved_with_overrides,
251-
&pkg_set,
252-
cli_features,
253-
specs,
254-
requested_targets,
255-
feature_opts,
256-
)?;
267+
let mut specs_and_features = Vec::new();
257268

258-
pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps(
259-
ws,
260-
&resolved_with_overrides,
261-
&member_ids,
262-
has_dev_units,
263-
requested_targets,
264-
target_data,
265-
force_all_targets,
266-
)?;
269+
for specs in individual_specs {
270+
let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
271+
272+
let narrowed_features = match feature_unification {
273+
FeatureUnification::Selected | FeatureUnification::Workspace => {
274+
Cow::Borrowed(cli_features)
275+
}
276+
};
277+
278+
let resolved_features = FeatureResolver::resolve(
279+
ws,
280+
target_data,
281+
&resolved_with_overrides,
282+
&pkg_set,
283+
&*narrowed_features,
284+
&specs,
285+
requested_targets,
286+
feature_opts,
287+
)?;
288+
289+
pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps(
290+
ws,
291+
&resolved_with_overrides,
292+
&member_ids,
293+
has_dev_units,
294+
requested_targets,
295+
target_data,
296+
force_all_targets,
297+
)?;
298+
299+
specs_and_features.push(SpecsAndResolvedFeatures {
300+
specs,
301+
resolved_features,
302+
});
303+
}
267304

268305
Ok(WorkspaceResolve {
269306
pkg_set,
270307
workspace_resolve: resolve,
271308
targeted_resolve: resolved_with_overrides,
272-
resolved_features,
309+
specs_and_features,
273310
})
274311
}
275312

0 commit comments

Comments
 (0)