Skip to content

Commit 85854b1

Browse files
committed
Refactor feature handling, and improve error messages.
1 parent cc70d60 commit 85854b1

29 files changed

+759
-351
lines changed

src/bin/cargo/commands/metadata.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4444
};
4545

4646
let options = OutputMetadataOptions {
47-
features: values(args, "features"),
48-
all_features: args.is_present("all-features"),
49-
no_default_features: args.is_present("no-default-features"),
47+
cli_features: args.cli_features()?,
5048
no_deps: args.is_present("no-deps"),
5149
filter_platforms: args._values_of("filter-platform"),
5250
version,

src/bin/cargo/commands/package.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4545
allow_dirty: args.is_present("allow-dirty"),
4646
targets: args.targets(),
4747
jobs: args.jobs()?,
48-
features: args._values_of("features"),
49-
all_features: args.is_present("all-features"),
50-
no_default_features: args.is_present("no-default-features"),
48+
cli_features: args.cli_features()?,
5149
},
5250
)?;
5351
Ok(())

src/bin/cargo/commands/publish.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4545
jobs: args.jobs()?,
4646
dry_run: args.is_present("dry-run"),
4747
registry,
48-
features: args._values_of("features"),
49-
all_features: args.is_present("all-features"),
50-
no_default_features: args.is_present("no-default-features"),
48+
cli_features: args.cli_features()?,
5149
},
5250
)?;
5351
Ok(())

src/bin/cargo/commands/tree.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,7 @@ subtree of the package given to -p.\n\
190190
let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
191191
.map_err(|e| anyhow::anyhow!("{}", e))?;
192192
let opts = tree::TreeOptions {
193-
features: values(args, "features"),
194-
all_features: args.is_present("all-features"),
195-
no_default_features: args.is_present("no-default-features"),
193+
cli_features: args.cli_features()?,
196194
packages,
197195
target,
198196
edge_kinds,

src/cargo/core/compiler/standard_lib.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use crate::core::compiler::UnitInterner;
44
use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
55
use crate::core::profiles::{Profiles, UnitFor};
6-
use crate::core::resolver::features::{FeaturesFor, RequestedFeatures, ResolvedFeatures};
7-
use crate::core::resolver::{HasDevUnits, ResolveOpts};
6+
use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
7+
use crate::core::resolver::HasDevUnits;
88
use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace};
99
use crate::ops::{self, Packages};
1010
use crate::util::errors::CargoResult;
@@ -107,18 +107,14 @@ pub fn resolve_std<'cfg>(
107107
"default".to_string(),
108108
],
109109
};
110-
// dev_deps setting shouldn't really matter here.
111-
let opts = ResolveOpts::new(
112-
/*dev_deps*/ false,
113-
RequestedFeatures::from_command_line(
114-
&features, /*all_features*/ false, /*uses_default_features*/ false,
115-
),
116-
);
110+
let cli_features = CliFeatures::from_command_line(
111+
&features, /*all_features*/ false, /*uses_default_features*/ false,
112+
)?;
117113
let resolve = ops::resolve_ws_with_opts(
118114
&std_ws,
119115
target_data,
120116
requested_targets,
121-
&opts,
117+
&cli_features,
122118
&specs,
123119
HasDevUnits::No,
124120
crate::core::resolver::features::ForceAllTargets::No,

src/cargo/core/resolver/context.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::dep_cache::RegistryQueryer;
22
use super::errors::ActivateResult;
33
use super::types::{ConflictMap, ConflictReason, FeaturesSet, ResolveOpts};
4+
use super::RequestedFeatures;
45
use crate::core::{Dependency, PackageId, SourceId, Summary};
56
use crate::util::interning::InternedString;
67
use crate::util::Graph;
@@ -160,23 +161,32 @@ impl Context {
160161
}
161162
}
162163
debug!("checking if {} is already activated", summary.package_id());
163-
if opts.features.all_features {
164-
return Ok(false);
165-
}
166-
167-
let has_default_feature = summary.features().contains_key("default");
168-
Ok(match self.resolve_features.get(&id) {
169-
Some(prev) => {
170-
opts.features.features.is_subset(prev)
171-
&& (!opts.features.uses_default_features
172-
|| prev.contains("default")
173-
|| !has_default_feature)
174-
}
175-
None => {
176-
opts.features.features.is_empty()
177-
&& (!opts.features.uses_default_features || !has_default_feature)
164+
match &opts.features {
165+
// This returns `false` for CliFeatures just for simplicity. It
166+
// would take a bit of work to compare since they are not in the
167+
// same format as DepFeatures (and that may be expensive
168+
// performance-wise). Also, it should only occur once for a root
169+
// package. The only drawback is that it may re-activate a root
170+
// package again, which should only affect performance, but that
171+
// should be rare. Cycles should still be detected since those
172+
// will have `DepFeatures` edges.
173+
RequestedFeatures::CliFeatures(_) => return Ok(false),
174+
RequestedFeatures::DepFeatures {
175+
features,
176+
uses_default_features,
177+
} => {
178+
let has_default_feature = summary.features().contains_key("default");
179+
Ok(match self.resolve_features.get(&id) {
180+
Some(prev) => {
181+
features.is_subset(prev)
182+
&& (!uses_default_features
183+
|| prev.contains("default")
184+
|| !has_default_feature)
185+
}
186+
None => features.is_empty() && (!uses_default_features || !has_default_feature),
187+
})
178188
}
179-
})
189+
}
180190
}
181191

182192
/// If the package is active returns the `ContextAge` when it was added

src/cargo/core/resolver/dep_cache.rs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
use crate::core::resolver::context::Context;
1313
use crate::core::resolver::errors::describe_path;
1414
use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
15-
use crate::core::resolver::{ActivateError, ActivateResult, ResolveOpts};
15+
use crate::core::resolver::{
16+
ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts,
17+
};
1618
use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
1719
use crate::util::errors::{CargoResult, CargoResultExt};
1820
use crate::util::interning::InternedString;
@@ -281,15 +283,6 @@ pub fn resolve_features<'b>(
281283
.unwrap_or(&default_dep)
282284
.clone();
283285
base.extend(dep.features().iter());
284-
for feature in base.iter() {
285-
if feature.contains('/') {
286-
return Err(anyhow::format_err!(
287-
"feature names may not contain slashes: `{}`",
288-
feature
289-
)
290-
.into());
291-
}
292-
}
293286
ret.push((dep.clone(), Rc::new(base)));
294287
}
295288

@@ -317,30 +310,46 @@ fn build_requirements<'a, 'b: 'a>(
317310
) -> ActivateResult<Requirements<'a>> {
318311
let mut reqs = Requirements::new(s);
319312

320-
if opts.features.all_features {
321-
for key in s.features().keys() {
322-
if let Err(e) = reqs.require_feature(*key) {
313+
let handle_default = |uses_default_features, reqs: &mut Requirements<'_>| {
314+
if uses_default_features && s.features().contains_key("default") {
315+
if let Err(e) = reqs.require_feature(InternedString::new("default")) {
323316
return Err(e.into_activate_error(parent, s));
324317
}
325318
}
326-
} else {
327-
for &f in opts.features.features.iter() {
328-
let fv = FeatureValue::new(f);
329-
if fv.has_dep_prefix() {
330-
return Err(ActivateError::Fatal(anyhow::format_err!(
331-
"feature value `{}` is not allowed to use explicit `dep:` syntax",
332-
fv
333-
)));
334-
}
335-
if let Err(e) = reqs.require_value(&fv) {
336-
return Err(e.into_activate_error(parent, s));
319+
Ok(())
320+
};
321+
322+
match &opts.features {
323+
RequestedFeatures::CliFeatures(CliFeatures {
324+
features,
325+
all_features,
326+
uses_default_features,
327+
}) => {
328+
if *all_features {
329+
for key in s.features().keys() {
330+
if let Err(e) = reqs.require_feature(*key) {
331+
return Err(e.into_activate_error(parent, s));
332+
}
333+
}
334+
} else {
335+
for fv in features.iter() {
336+
if let Err(e) = reqs.require_value(&fv) {
337+
return Err(e.into_activate_error(parent, s));
338+
}
339+
}
340+
handle_default(*uses_default_features, &mut reqs)?;
337341
}
338342
}
339-
}
340-
341-
if opts.features.uses_default_features && s.features().contains_key("default") {
342-
if let Err(e) = reqs.require_feature(InternedString::new("default")) {
343-
return Err(e.into_activate_error(parent, s));
343+
RequestedFeatures::DepFeatures {
344+
features,
345+
uses_default_features,
346+
} => {
347+
for feature in features.iter() {
348+
if let Err(e) = reqs.require_feature(*feature) {
349+
return Err(e.into_activate_error(parent, s));
350+
}
351+
}
352+
handle_default(*uses_default_features, &mut reqs)?;
344353
}
345354
}
346355

0 commit comments

Comments
 (0)