mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
refactor(resolve): allow multiple resolved feature sets in workspace resolve
This commit is contained in:
parent
403f1e12f6
commit
73b5b33e1d
@ -96,7 +96,7 @@ pub fn resolve_std<'gctx>(
|
||||
&features, /*all_features*/ false, /*uses_default_features*/ false,
|
||||
)?;
|
||||
let dry_run = false;
|
||||
let resolve = ops::resolve_ws_with_opts(
|
||||
let mut resolve = ops::resolve_ws_with_opts(
|
||||
&std_ws,
|
||||
target_data,
|
||||
&build_config.requested_kinds,
|
||||
@ -106,10 +106,15 @@ pub fn resolve_std<'gctx>(
|
||||
crate::core::resolver::features::ForceAllTargets::No,
|
||||
dry_run,
|
||||
)?;
|
||||
debug_assert_eq!(resolve.specs_and_features.len(), 1);
|
||||
Ok((
|
||||
resolve.pkg_set,
|
||||
resolve.targeted_resolve,
|
||||
resolve.resolved_features,
|
||||
resolve
|
||||
.specs_and_features
|
||||
.pop()
|
||||
.expect("resolve should have a single spec with resolved features")
|
||||
.resolved_features,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ use crate::core::resolver::{HasDevUnits, Resolve};
|
||||
use crate::core::{PackageId, PackageSet, SourceId, TargetKind, Workspace};
|
||||
use crate::drop_println;
|
||||
use crate::ops;
|
||||
use crate::ops::resolve::WorkspaceResolve;
|
||||
use crate::ops::resolve::{SpecsAndResolvedFeatures, WorkspaceResolve};
|
||||
use crate::util::context::{GlobalContext, WarningHandling};
|
||||
use crate::util::interning::InternedString;
|
||||
use crate::util::{CargoResult, StableHasher};
|
||||
@ -284,7 +284,7 @@ pub fn create_bcx<'a, 'gctx>(
|
||||
mut pkg_set,
|
||||
workspace_resolve,
|
||||
targeted_resolve: resolve,
|
||||
resolved_features,
|
||||
specs_and_features,
|
||||
} = resolve;
|
||||
|
||||
let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
|
||||
@ -363,72 +363,91 @@ pub fn create_bcx<'a, 'gctx>(
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Passing `build_config.requested_kinds` instead of
|
||||
// `explicit_host_kinds` here so that `generate_root_units` can do
|
||||
// its own special handling of `CompileKind::Host`. It will
|
||||
// internally replace the host kind by the `explicit_host_kind`
|
||||
// before setting as a unit.
|
||||
let generator = UnitGenerator {
|
||||
ws,
|
||||
packages: &to_builds,
|
||||
spec,
|
||||
target_data: &target_data,
|
||||
filter,
|
||||
requested_kinds: &build_config.requested_kinds,
|
||||
explicit_host_kind,
|
||||
intent: build_config.intent,
|
||||
resolve: &resolve,
|
||||
workspace_resolve: &workspace_resolve,
|
||||
resolved_features: &resolved_features,
|
||||
package_set: &pkg_set,
|
||||
profiles: &profiles,
|
||||
interner,
|
||||
has_dev_units,
|
||||
};
|
||||
let mut units = generator.generate_root_units()?;
|
||||
let mut units = Vec::new();
|
||||
let mut unit_graph = HashMap::new();
|
||||
let mut scrape_units = Vec::new();
|
||||
|
||||
if let Some(args) = target_rustc_crate_types {
|
||||
override_rustc_crate_types(&mut units, args, interner)?;
|
||||
}
|
||||
|
||||
let should_scrape = build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
|
||||
let mut scrape_units = if should_scrape {
|
||||
generator.generate_scrape_units(&units)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
|
||||
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
|
||||
standard_lib::generate_std_roots(
|
||||
&crates,
|
||||
&units,
|
||||
std_resolve,
|
||||
std_features,
|
||||
&explicit_host_kinds,
|
||||
&pkg_set,
|
||||
for SpecsAndResolvedFeatures {
|
||||
specs,
|
||||
resolved_features,
|
||||
} in &specs_and_features
|
||||
{
|
||||
// Passing `build_config.requested_kinds` instead of
|
||||
// `explicit_host_kinds` here so that `generate_root_units` can do
|
||||
// its own special handling of `CompileKind::Host`. It will
|
||||
// internally replace the host kind by the `explicit_host_kind`
|
||||
// before setting as a unit.
|
||||
let spec_names = specs.iter().map(|spec| spec.name()).collect::<Vec<_>>();
|
||||
let packages = to_builds
|
||||
.iter()
|
||||
.filter(|package| spec_names.contains(&package.name().as_str()))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let generator = UnitGenerator {
|
||||
ws,
|
||||
packages: &packages,
|
||||
spec,
|
||||
target_data: &target_data,
|
||||
filter,
|
||||
requested_kinds: &build_config.requested_kinds,
|
||||
explicit_host_kind,
|
||||
intent: build_config.intent,
|
||||
resolve: &resolve,
|
||||
workspace_resolve: &workspace_resolve,
|
||||
resolved_features: &resolved_features,
|
||||
package_set: &pkg_set,
|
||||
profiles: &profiles,
|
||||
interner,
|
||||
&profiles,
|
||||
&target_data,
|
||||
)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
has_dev_units,
|
||||
};
|
||||
let mut targeted_root_units = generator.generate_root_units()?;
|
||||
|
||||
let mut unit_graph = build_unit_dependencies(
|
||||
ws,
|
||||
&pkg_set,
|
||||
&resolve,
|
||||
&resolved_features,
|
||||
std_resolve_features.as_ref(),
|
||||
&units,
|
||||
&scrape_units,
|
||||
&std_roots,
|
||||
build_config.intent,
|
||||
&target_data,
|
||||
&profiles,
|
||||
interner,
|
||||
)?;
|
||||
if let Some(args) = target_rustc_crate_types {
|
||||
override_rustc_crate_types(&mut targeted_root_units, args, interner)?;
|
||||
}
|
||||
|
||||
let should_scrape =
|
||||
build_config.intent.is_doc() && gctx.cli_unstable().rustdoc_scrape_examples;
|
||||
let targeted_scrape_units = if should_scrape {
|
||||
generator.generate_scrape_units(&targeted_root_units)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
|
||||
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
|
||||
standard_lib::generate_std_roots(
|
||||
&crates,
|
||||
&targeted_root_units,
|
||||
std_resolve,
|
||||
std_features,
|
||||
&explicit_host_kinds,
|
||||
&pkg_set,
|
||||
interner,
|
||||
&profiles,
|
||||
&target_data,
|
||||
)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
unit_graph.extend(build_unit_dependencies(
|
||||
ws,
|
||||
&pkg_set,
|
||||
&resolve,
|
||||
&resolved_features,
|
||||
std_resolve_features.as_ref(),
|
||||
&targeted_root_units,
|
||||
&targeted_scrape_units,
|
||||
&std_roots,
|
||||
build_config.intent,
|
||||
&target_data,
|
||||
&profiles,
|
||||
interner,
|
||||
)?);
|
||||
units.extend(targeted_root_units);
|
||||
scrape_units.extend(targeted_scrape_units);
|
||||
}
|
||||
|
||||
// TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
|
||||
// what heuristics to use in that case.
|
||||
|
@ -588,7 +588,15 @@ fn check_resolver_change<'gctx>(
|
||||
feature_opts,
|
||||
)?;
|
||||
|
||||
let diffs = v2_features.compare_legacy(&ws_resolve.resolved_features);
|
||||
if ws_resolve.specs_and_features.len() != 1 {
|
||||
bail!(r#"cannot fix edition when using `feature-unification = "package"`."#);
|
||||
}
|
||||
let resolved_features = &ws_resolve
|
||||
.specs_and_features
|
||||
.first()
|
||||
.expect("We've already checked that there is exactly one.")
|
||||
.resolved_features;
|
||||
let diffs = v2_features.compare_legacy(resolved_features);
|
||||
Ok((ws_resolve, diffs))
|
||||
};
|
||||
let (_, without_dev_diffs) = resolve_differences(HasDevUnits::No)?;
|
||||
|
@ -81,7 +81,9 @@ use crate::util::CanonicalUrl;
|
||||
use anyhow::Context as _;
|
||||
use cargo_util::paths;
|
||||
use cargo_util_schemas::core::PartialVersion;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
/// Filter for keep using Package ID from previous lockfile.
|
||||
@ -96,9 +98,18 @@ pub struct WorkspaceResolve<'gctx> {
|
||||
/// This may be `None` for things like `cargo install` and `-Zavoid-dev-deps`.
|
||||
/// This does not include `paths` overrides.
|
||||
pub workspace_resolve: Option<Resolve>,
|
||||
/// The narrowed resolve, with the specific features enabled, and only the
|
||||
/// given package specs requested.
|
||||
/// The narrowed resolve, with the specific features enabled.
|
||||
pub targeted_resolve: Resolve,
|
||||
/// Package specs requested for compilation along with specific features enabled. This usually
|
||||
/// has the length of one but there may be more specs with different features when using the
|
||||
/// `package` feature resolver.
|
||||
pub specs_and_features: Vec<SpecsAndResolvedFeatures>,
|
||||
}
|
||||
|
||||
/// Pair of package specs requested for compilation along with enabled features.
|
||||
pub struct SpecsAndResolvedFeatures {
|
||||
/// Packages that are supposed to be built.
|
||||
pub specs: Vec<PackageIdSpec>,
|
||||
/// The features activated per package.
|
||||
pub resolved_features: ResolvedFeatures,
|
||||
}
|
||||
@ -145,10 +156,20 @@ pub fn resolve_ws_with_opts<'gctx>(
|
||||
force_all_targets: ForceAllTargets,
|
||||
dry_run: bool,
|
||||
) -> CargoResult<WorkspaceResolve<'gctx>> {
|
||||
let specs = match ws.resolve_feature_unification() {
|
||||
FeatureUnification::Selected => specs,
|
||||
FeatureUnification::Workspace => &ops::Packages::All(Vec::new()).to_package_id_specs(ws)?,
|
||||
let feature_unification = ws.resolve_feature_unification();
|
||||
let individual_specs = match feature_unification {
|
||||
FeatureUnification::Selected => vec![specs.to_owned()],
|
||||
FeatureUnification::Workspace => {
|
||||
vec![ops::Packages::All(Vec::new()).to_package_id_specs(ws)?]
|
||||
}
|
||||
};
|
||||
let specs: Vec<_> = individual_specs
|
||||
.iter()
|
||||
.map(|specs| specs.iter())
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect();
|
||||
let specs = &specs[..];
|
||||
let mut registry = ws.package_registry()?;
|
||||
let (resolve, resolved_with_overrides) = if ws.ignore_lock() {
|
||||
let add_patches = true;
|
||||
@ -229,9 +250,9 @@ pub fn resolve_ws_with_opts<'gctx>(
|
||||
|
||||
let pkg_set = get_resolved_packages(&resolved_with_overrides, registry)?;
|
||||
|
||||
let member_ids = ws
|
||||
.members_with_features(specs, cli_features)?
|
||||
.into_iter()
|
||||
let members_with_features = ws.members_with_features(specs, cli_features)?;
|
||||
let member_ids = members_with_features
|
||||
.iter()
|
||||
.map(|(p, _fts)| p.package_id())
|
||||
.collect::<Vec<_>>();
|
||||
pkg_set.download_accessible(
|
||||
@ -243,33 +264,49 @@ pub fn resolve_ws_with_opts<'gctx>(
|
||||
force_all_targets,
|
||||
)?;
|
||||
|
||||
let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
|
||||
let resolved_features = FeatureResolver::resolve(
|
||||
ws,
|
||||
target_data,
|
||||
&resolved_with_overrides,
|
||||
&pkg_set,
|
||||
cli_features,
|
||||
specs,
|
||||
requested_targets,
|
||||
feature_opts,
|
||||
)?;
|
||||
let mut specs_and_features = Vec::new();
|
||||
|
||||
pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps(
|
||||
ws,
|
||||
&resolved_with_overrides,
|
||||
&member_ids,
|
||||
has_dev_units,
|
||||
requested_targets,
|
||||
target_data,
|
||||
force_all_targets,
|
||||
)?;
|
||||
for specs in individual_specs {
|
||||
let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
|
||||
|
||||
let narrowed_features = match feature_unification {
|
||||
FeatureUnification::Selected | FeatureUnification::Workspace => {
|
||||
Cow::Borrowed(cli_features)
|
||||
}
|
||||
};
|
||||
|
||||
let resolved_features = FeatureResolver::resolve(
|
||||
ws,
|
||||
target_data,
|
||||
&resolved_with_overrides,
|
||||
&pkg_set,
|
||||
&*narrowed_features,
|
||||
&specs,
|
||||
requested_targets,
|
||||
feature_opts,
|
||||
)?;
|
||||
|
||||
pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps(
|
||||
ws,
|
||||
&resolved_with_overrides,
|
||||
&member_ids,
|
||||
has_dev_units,
|
||||
requested_targets,
|
||||
target_data,
|
||||
force_all_targets,
|
||||
)?;
|
||||
|
||||
specs_and_features.push(SpecsAndResolvedFeatures {
|
||||
specs,
|
||||
resolved_features,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(WorkspaceResolve {
|
||||
pkg_set,
|
||||
workspace_resolve: resolve,
|
||||
targeted_resolve: resolved_with_overrides,
|
||||
resolved_features,
|
||||
specs_and_features,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ use crate::core::compiler::{CompileKind, RustcTargetData};
|
||||
use crate::core::dependency::DepKind;
|
||||
use crate::core::resolver::{features::CliFeatures, ForceAllTargets, HasDevUnits};
|
||||
use crate::core::{Package, PackageId, PackageIdSpec, PackageIdSpecQuery, Workspace};
|
||||
use crate::ops::resolve::SpecsAndResolvedFeatures;
|
||||
use crate::ops::{self, Packages};
|
||||
use crate::util::CargoResult;
|
||||
use crate::{drop_print, drop_println};
|
||||
@ -179,61 +180,67 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
|
||||
.map(|pkg| (pkg.package_id(), pkg))
|
||||
.collect();
|
||||
|
||||
let mut graph = graph::build(
|
||||
ws,
|
||||
&ws_resolve.targeted_resolve,
|
||||
&ws_resolve.resolved_features,
|
||||
&specs,
|
||||
&opts.cli_features,
|
||||
&target_data,
|
||||
&requested_kinds,
|
||||
package_map,
|
||||
opts,
|
||||
)?;
|
||||
|
||||
let root_specs = if opts.invert.is_empty() {
|
||||
specs
|
||||
} else {
|
||||
opts.invert
|
||||
.iter()
|
||||
.map(|p| PackageIdSpec::parse(p))
|
||||
.collect::<Result<Vec<PackageIdSpec>, _>>()?
|
||||
};
|
||||
let root_ids = ws_resolve.targeted_resolve.specs_to_ids(&root_specs)?;
|
||||
let root_indexes = graph.indexes_from_ids(&root_ids);
|
||||
|
||||
let root_indexes = if opts.duplicates {
|
||||
// `-d -p foo` will only show duplicates within foo's subtree
|
||||
graph = graph.from_reachable(root_indexes.as_slice());
|
||||
graph.find_duplicates()
|
||||
} else {
|
||||
root_indexes
|
||||
};
|
||||
|
||||
if !opts.invert.is_empty() || opts.duplicates {
|
||||
graph.invert();
|
||||
}
|
||||
|
||||
// Packages to prune.
|
||||
let pkgs_to_prune = opts
|
||||
.pkgs_to_prune
|
||||
.iter()
|
||||
.map(|p| PackageIdSpec::parse(p).map_err(Into::into))
|
||||
.map(|r| {
|
||||
// Provide an error message if pkgid is not within the resolved
|
||||
// dependencies graph.
|
||||
r.and_then(|spec| spec.query(ws_resolve.targeted_resolve.iter()).and(Ok(spec)))
|
||||
})
|
||||
.collect::<CargoResult<Vec<PackageIdSpec>>>()?;
|
||||
|
||||
if root_indexes.len() == 0 {
|
||||
ws.gctx().shell().warn(
|
||||
"nothing to print.\n\n\
|
||||
To find dependencies that require specific target platforms, \
|
||||
try to use option `--target all` first, and then narrow your search scope accordingly.",
|
||||
for SpecsAndResolvedFeatures {
|
||||
specs,
|
||||
resolved_features,
|
||||
} in ws_resolve.specs_and_features
|
||||
{
|
||||
let mut graph = graph::build(
|
||||
ws,
|
||||
&ws_resolve.targeted_resolve,
|
||||
&resolved_features,
|
||||
&specs,
|
||||
&opts.cli_features,
|
||||
&target_data,
|
||||
&requested_kinds,
|
||||
package_map.clone(),
|
||||
opts,
|
||||
)?;
|
||||
} else {
|
||||
print(ws, opts, root_indexes, &pkgs_to_prune, &graph)?;
|
||||
|
||||
let root_specs = if opts.invert.is_empty() {
|
||||
specs
|
||||
} else {
|
||||
opts.invert
|
||||
.iter()
|
||||
.map(|p| PackageIdSpec::parse(p))
|
||||
.collect::<Result<Vec<PackageIdSpec>, _>>()?
|
||||
};
|
||||
let root_ids = ws_resolve.targeted_resolve.specs_to_ids(&root_specs)?;
|
||||
let root_indexes = graph.indexes_from_ids(&root_ids);
|
||||
|
||||
let root_indexes = if opts.duplicates {
|
||||
// `-d -p foo` will only show duplicates within foo's subtree
|
||||
graph = graph.from_reachable(root_indexes.as_slice());
|
||||
graph.find_duplicates()
|
||||
} else {
|
||||
root_indexes
|
||||
};
|
||||
|
||||
if !opts.invert.is_empty() || opts.duplicates {
|
||||
graph.invert();
|
||||
}
|
||||
|
||||
// Packages to prune.
|
||||
let pkgs_to_prune = opts
|
||||
.pkgs_to_prune
|
||||
.iter()
|
||||
.map(|p| PackageIdSpec::parse(p).map_err(Into::into))
|
||||
.map(|r| {
|
||||
// Provide an error message if pkgid is not within the resolved
|
||||
// dependencies graph.
|
||||
r.and_then(|spec| spec.query(ws_resolve.targeted_resolve.iter()).and(Ok(spec)))
|
||||
})
|
||||
.collect::<CargoResult<Vec<PackageIdSpec>>>()?;
|
||||
|
||||
if root_indexes.len() == 0 {
|
||||
ws.gctx().shell().warn(
|
||||
"nothing to print.\n\n\
|
||||
To find dependencies that require specific target platforms, \
|
||||
try to use option `--target all` first, and then narrow your search scope accordingly.",
|
||||
)?;
|
||||
} else {
|
||||
print(ws, opts, root_indexes, &pkgs_to_prune, &graph)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user