Skip to content

Commit 76f0d68

Browse files
committed
Consolidate -Zpackage-features member iteration logic.
1 parent 2c8b9fb commit 76f0d68

File tree

3 files changed

+105
-122
lines changed

3 files changed

+105
-122
lines changed

src/cargo/core/resolver/features.rs

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,6 @@ pub struct FeatureResolver<'a, 'cfg> {
182182
/// The platform to build for, requested by the user.
183183
requested_target: CompileKind,
184184
resolve: &'a Resolve,
185-
/// Packages to build, requested on the command-line.
186-
specs: &'a [PackageIdSpec],
187185
/// Options that change how the feature resolver operates.
188186
opts: FeatureOpts,
189187
/// Map of features activated for each package.
@@ -222,12 +220,11 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
222220
target_data,
223221
requested_target,
224222
resolve,
225-
specs,
226223
opts,
227224
activated_features: HashMap::new(),
228225
processed_deps: HashSet::new(),
229226
};
230-
r.do_resolve(requested_features)?;
227+
r.do_resolve(specs, requested_features)?;
231228
log::debug!("features={:#?}", r.activated_features);
232229
if r.opts.compare {
233230
r.compare();
@@ -240,44 +237,14 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
240237
}
241238

242239
/// Performs the process of resolving all features for the resolve graph.
243-
fn do_resolve(&mut self, requested_features: &RequestedFeatures) -> CargoResult<()> {
244-
if self.opts.package_features {
245-
let mut found = false;
246-
for member in self.ws.members() {
247-
let member_id = member.package_id();
248-
if self.specs.iter().any(|spec| spec.matches(member_id)) {
249-
found = true;
250-
self.activate_member(member_id, requested_features)?;
251-
}
252-
}
253-
if !found {
254-
// -p for a non-member. Just resolve all with defaults.
255-
let default = RequestedFeatures::new_all(false);
256-
for member in self.ws.members() {
257-
self.activate_member(member.package_id(), &default)?;
258-
}
259-
}
260-
} else {
261-
for member in self.ws.members() {
262-
let member_id = member.package_id();
263-
match self.ws.current_opt() {
264-
Some(current) if member_id == current.package_id() => {
265-
// The "current" member gets activated with the flags
266-
// from the command line.
267-
self.activate_member(member_id, requested_features)?;
268-
}
269-
_ => {
270-
// Ignore members that are not enabled on the command-line.
271-
if self.specs.iter().any(|spec| spec.matches(member_id)) {
272-
// -p for a workspace member that is not the
273-
// "current" one, don't use the local `--features`.
274-
let not_current_requested =
275-
RequestedFeatures::new_all(requested_features.all_features);
276-
self.activate_member(member_id, &not_current_requested)?;
277-
}
278-
}
279-
}
280-
}
240+
fn do_resolve(
241+
&mut self,
242+
specs: &[PackageIdSpec],
243+
requested_features: &RequestedFeatures,
244+
) -> CargoResult<()> {
245+
let member_features = self.ws.members_with_features(specs, requested_features)?;
246+
for (member, requested_features) in member_features {
247+
self.activate_member(member.package_id(), &requested_features)?;
281248
}
282249
Ok(())
283250
}

src/cargo/core/workspace.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use url::Url;
1010

1111
use crate::core::features::Features;
1212
use crate::core::registry::PackageRegistry;
13+
use crate::core::resolver::features::RequestedFeatures;
1314
use crate::core::{Dependency, PackageId, PackageIdSpec};
1415
use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
1516
use crate::ops;
@@ -833,6 +834,86 @@ impl<'cfg> Workspace<'cfg> {
833834
pub fn set_target_dir(&mut self, target_dir: Filesystem) {
834835
self.target_dir = Some(target_dir);
835836
}
837+
838+
/// Returns a Vec of `(&Package, RequestedFeatures)` tuples that
839+
/// represent the workspace members that were requested on the command-line.
840+
///
841+
/// `specs` may be empty, which indicates it should return all workspace
842+
/// members. In this case, `requested_features.all_features` must be
843+
/// `true`. This is used for generating `Cargo.lock`, which must include
844+
/// all members with all features enabled.
845+
pub fn members_with_features(
846+
&self,
847+
specs: &[PackageIdSpec],
848+
requested_features: &RequestedFeatures,
849+
) -> CargoResult<Vec<(&Package, RequestedFeatures)>> {
850+
assert!(
851+
!specs.is_empty() || requested_features.all_features,
852+
"no specs requires all_features"
853+
);
854+
if specs.is_empty() {
855+
// When resolving the entire workspace, resolve each member with
856+
// all features enabled.
857+
return Ok(self
858+
.members()
859+
.map(|m| (m, RequestedFeatures::new_all(true)))
860+
.collect());
861+
}
862+
if self.config().cli_unstable().package_features {
863+
if specs.len() > 1 && !requested_features.features.is_empty() {
864+
anyhow::bail!("cannot specify features for more than one package");
865+
}
866+
let members: Vec<(&Package, RequestedFeatures)> = self
867+
.members()
868+
.filter(|m| specs.iter().any(|spec| spec.matches(m.package_id())))
869+
.map(|m| (m, requested_features.clone()))
870+
.collect();
871+
if members.is_empty() {
872+
// `cargo build -p foo`, where `foo` is not a member.
873+
// Do not allow any command-line flags (defaults only).
874+
if !(requested_features.features.is_empty()
875+
&& !requested_features.all_features
876+
&& requested_features.uses_default_features)
877+
{
878+
anyhow::bail!("cannot specify features for packages outside of workspace");
879+
}
880+
// Add all members from the workspace so we can ensure `-p nonmember`
881+
// is in the resolve graph.
882+
return Ok(self
883+
.members()
884+
.map(|m| (m, RequestedFeatures::new_all(false)))
885+
.collect());
886+
}
887+
return Ok(members);
888+
} else {
889+
let ms = self.members().filter_map(|member| {
890+
let member_id = member.package_id();
891+
match self.current_opt() {
892+
// The features passed on the command-line only apply to
893+
// the "current" package (determined by the cwd).
894+
Some(current) if member_id == current.package_id() => {
895+
Some((member, requested_features.clone()))
896+
}
897+
_ => {
898+
// Ignore members that are not enabled on the command-line.
899+
if specs.iter().any(|spec| spec.matches(member_id)) {
900+
// -p for a workspace member that is not the
901+
// "current" one, don't use the local
902+
// `--features`, only allow `--all-features`.
903+
Some((
904+
member,
905+
RequestedFeatures::new_all(requested_features.all_features),
906+
))
907+
} else {
908+
// This member was not requested on the command-line, skip.
909+
None
910+
}
911+
}
912+
}
913+
});
914+
return Ok(ms.collect());
915+
}
916+
}
836917
}
837918

838919
impl<'cfg> Packages<'cfg> {

src/cargo/ops/resolve.rs

Lines changed: 15 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
1313
use crate::core::compiler::{CompileKind, RustcTargetData};
1414
use crate::core::registry::PackageRegistry;
15-
use crate::core::resolver::features::{FeatureResolver, RequestedFeatures, ResolvedFeatures};
15+
use crate::core::resolver::features::{FeatureResolver, ResolvedFeatures};
1616
use crate::core::resolver::{self, Resolve, ResolveOpts};
17+
use crate::core::summary::Summary;
1718
use crate::core::Feature;
1819
use crate::core::{PackageId, PackageIdSpec, PackageSet, Source, SourceId, Workspace};
1920
use crate::ops;
@@ -188,11 +189,6 @@ pub fn resolve_with_previous<'cfg>(
188189
specs: &[PackageIdSpec],
189190
register_patches: bool,
190191
) -> CargoResult<Resolve> {
191-
assert!(
192-
!specs.is_empty() || opts.features.all_features,
193-
"no specs requires all_features"
194-
);
195-
196192
// We only want one Cargo at a time resolving a crate graph since this can
197193
// involve a lot of frobbing of the global caches.
198194
let _lock = ws.config().acquire_package_cache_lock()?;
@@ -272,81 +268,20 @@ pub fn resolve_with_previous<'cfg>(
272268
registry.add_sources(Some(member.package_id().source_id()))?;
273269
}
274270

275-
let mut summaries = Vec::new();
276-
if ws.config().cli_unstable().package_features {
277-
let mut members = Vec::new();
278-
if specs.is_empty() {
279-
members.extend(ws.members());
280-
} else {
281-
if specs.len() > 1 && !opts.features.features.is_empty() {
282-
anyhow::bail!("cannot specify features for more than one package");
283-
}
284-
members.extend(
285-
ws.members()
286-
.filter(|m| specs.iter().any(|spec| spec.matches(m.package_id()))),
287-
);
288-
// Edge case: running `cargo build -p foo`, where `foo` is not a member
289-
// of current workspace. Add all packages from workspace to get `foo`
290-
// into the resolution graph.
291-
if members.is_empty() {
292-
if !(opts.features.features.is_empty()
293-
&& !opts.features.all_features
294-
&& opts.features.uses_default_features)
295-
{
296-
anyhow::bail!("cannot specify features for packages outside of workspace");
297-
}
298-
members.extend(ws.members());
299-
}
300-
}
301-
for member in members {
271+
let summaries: Vec<(Summary, ResolveOpts)> = ws
272+
.members_with_features(specs, &opts.features)?
273+
.into_iter()
274+
.map(|(member, features)| {
302275
let summary = registry.lock(member.summary().clone());
303-
summaries.push((summary, opts.clone()))
304-
}
305-
} else {
306-
for member in ws.members() {
307-
let summary_resolve_opts = if specs.is_empty() {
308-
// When resolving the entire workspace, resolve each member
309-
// with all features enabled.
310-
opts.clone()
311-
} else {
312-
// If we're not resolving everything though then we're constructing the
313-
// exact crate graph we're going to build. Here we don't necessarily
314-
// want to keep around all workspace crates as they may not all be
315-
// built/tested.
316-
//
317-
// Additionally, the `opts` specified represents command line
318-
// flags, which really only matters for the current package
319-
// (determined by the cwd). If other packages are specified (via
320-
// `-p`) then the command line flags like features don't apply to
321-
// them.
322-
//
323-
// As a result, if this `member` is the current member of the
324-
// workspace, then we use `opts` specified. Otherwise we use a
325-
// base `opts` with no features specified but using default features
326-
// for any other packages specified with `-p`.
327-
let member_id = member.package_id();
328-
match ws.current_opt() {
329-
Some(current) if member_id == current.package_id() => opts.clone(),
330-
_ => {
331-
if specs.iter().any(|spec| spec.matches(member_id)) {
332-
// -p for a workspace member that is not the
333-
// "current" one, don't use the local `--features`.
334-
ResolveOpts {
335-
dev_deps: opts.dev_deps,
336-
features: RequestedFeatures::new_all(opts.features.all_features),
337-
}
338-
} else {
339-
// This member was not requested on the command-line, skip.
340-
continue;
341-
}
342-
}
343-
}
344-
};
345-
346-
let summary = registry.lock(member.summary().clone());
347-
summaries.push((summary, summary_resolve_opts));
348-
}
349-
};
276+
(
277+
summary,
278+
ResolveOpts {
279+
dev_deps: opts.dev_deps,
280+
features,
281+
},
282+
)
283+
})
284+
.collect();
350285

351286
let root_replace = ws.root_replace();
352287

0 commit comments

Comments
 (0)