Skip to content

Commit 963bfe4

Browse files
committed
Auto merge of #8799 - ehuss:new-namespaced, r=alexcrichton
New namespaced features implementation. This is a new implementation for namespaced features (#5565). See the `unstable.md` docs for a description of the new behavior. This is intended to fix several issues with the existing design, and to make it backwards compatible so that it does not require an opt-in flag. This also includes tangentially-related changes to the new feature resolver. The changes are: * `crate_name/feat_name` syntax will now always enable the feature `crate_name`, even if it is an inactive optional dependency (such as a dependency for another platform). The intent here is to have a certain amount of consistency whereby "features" are always activated, but individual crates will still not be activated. * `--all-features` will now enable features for inactive optional dependencies. This is more consistent with `--features foo` enabling the `foo` feature, even when the `foo` dep is not activated. I'm still very conflicted on how that should work, but I think it is better from a simplicity/consistency perspective. I still think it may be confusing if someone has a `cfg(some_dep)` in their code, and `some_dep` isn't built, it will error. The intent is that `cfg(accessible(some_dep))` will be the preferred method in the future, or using platform `cfg` expression like `cfg(windows)` to match whatever is in Cargo.toml. Closes #8044 Closes #8046 Closes #8047 Closes #8316 ## Questions - For various reasons, I changed the way dependency conflict errors are collected. One slightly negative consequence is that it will raise an error for the first problem it detects (like a "missing feature"). Previously it would collect a list of all missing features and display all of them in the error message. Let me know if I should retain the old behavior. I think it will make the code more complicated and brittle, but it shouldn't be too hard (essentially `Requirements` will need to collect a list of errors, and then `resolve_features` would need to check if the list is non-empty, and then aggregate the errors). - Should `cargo metadata` show the implicit features in the "features" table? Currently it does not, and I think that is probably best (it mirrors what is in `Cargo.toml`), but I could potentially see an argument to show how cargo sees the implicit features.
2 parents 31cf507 + f4ac82a commit 963bfe4

34 files changed

+1902
-968
lines changed

crates/cargo-test-support/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ enum MatchKind {
12181218

12191219
/// Compares a line with an expected pattern.
12201220
/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
1221-
/// (similar to `.*` in a regex).
1221+
/// (similar to `.*` in a regex). It is non-greedy.
12221222
/// - Use `[EXE]` to optionally add `.exe` on Windows (empty string on other
12231223
/// platforms).
12241224
/// - There is a wide range of macros (such as `[COMPILING]` or `[WARNING]`)

crates/resolver-tests/src/lib.rs

+4-26
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,7 @@ pub fn resolve_with_config_raw(
170170
list: registry,
171171
used: HashSet::new(),
172172
};
173-
let summary = Summary::new(
174-
pkg_id("root"),
175-
deps,
176-
&BTreeMap::<String, Vec<String>>::new(),
177-
None::<&String>,
178-
false,
179-
)
180-
.unwrap();
173+
let summary = Summary::new(pkg_id("root"), deps, &BTreeMap::new(), None::<&String>).unwrap();
181174
let opts = ResolveOpts::everything();
182175
let start = Instant::now();
183176
let resolve = resolver::resolve(
@@ -571,14 +564,7 @@ pub fn pkg_dep<T: ToPkgId>(name: T, dep: Vec<Dependency>) -> Summary {
571564
} else {
572565
None
573566
};
574-
Summary::new(
575-
name.to_pkgid(),
576-
dep,
577-
&BTreeMap::<String, Vec<String>>::new(),
578-
link,
579-
false,
580-
)
581-
.unwrap()
567+
Summary::new(name.to_pkgid(), dep, &BTreeMap::new(), link).unwrap()
582568
}
583569

584570
pub fn pkg_id(name: &str) -> PackageId {
@@ -599,14 +585,7 @@ pub fn pkg_loc(name: &str, loc: &str) -> Summary {
599585
} else {
600586
None
601587
};
602-
Summary::new(
603-
pkg_id_loc(name, loc),
604-
Vec::new(),
605-
&BTreeMap::<String, Vec<String>>::new(),
606-
link,
607-
false,
608-
)
609-
.unwrap()
588+
Summary::new(pkg_id_loc(name, loc), Vec::new(), &BTreeMap::new(), link).unwrap()
610589
}
611590

612591
pub fn remove_dep(sum: &Summary, ind: usize) -> Summary {
@@ -616,9 +595,8 @@ pub fn remove_dep(sum: &Summary, ind: usize) -> Summary {
616595
Summary::new(
617596
sum.package_id(),
618597
deps,
619-
&BTreeMap::<String, Vec<String>>::new(),
598+
&BTreeMap::new(),
620599
sum.links().map(|a| a.as_str()),
621-
sum.namespaced_features(),
622600
)
623601
.unwrap()
624602
}

crates/resolver-tests/tests/resolve.rs

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ proptest! {
228228
}
229229

230230
#[test]
231+
#[should_panic(expected = "pub dep")] // The error handling is not yet implemented.
231232
fn pub_fail() {
232233
let input = vec![
233234
pkg!(("a", "0.0.4")),

src/bin/cargo/cli.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ pub fn main(config: &mut Config) -> CliResult {
3535
"
3636
Available unstable (nightly-only) flags:
3737
38-
-Z avoid-dev-deps -- Avoid installing dev-dependencies if possible
39-
-Z minimal-versions -- Install minimal dependency versions instead of maximum
40-
-Z no-index-update -- Do not update the registry, avoids a network request for benchmarking
41-
-Z unstable-options -- Allow the usage of unstable options
42-
-Z timings -- Display concurrency information
43-
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
44-
-Z terminal-width -- Provide a terminal width to rustc for error truncation
38+
-Z avoid-dev-deps -- Avoid installing dev-dependencies if possible
39+
-Z minimal-versions -- Install minimal dependency versions instead of maximum
40+
-Z no-index-update -- Do not update the registry, avoids a network request for benchmarking
41+
-Z unstable-options -- Allow the usage of unstable options
42+
-Z timings -- Display concurrency information
43+
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
44+
-Z terminal-width -- Provide a terminal width to rustc for error truncation
45+
-Z namespaced-features -- Allow features with `dep:` prefix
4546
4647
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
4748
);

src/bin/cargo/commands/read_manifest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ Deprecated, use `cargo metadata --no-deps` instead.\
1515

1616
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
1717
let ws = args.workspace(config)?;
18-
config.shell().print_json(&ws.current()?);
18+
config.shell().print_json(&ws.current()?.serialized(config));
1919
Ok(())
2020
}

src/cargo/core/compiler/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ pub fn extern_args(
10341034
if unit
10351035
.pkg
10361036
.manifest()
1037-
.features()
1037+
.unstable_features()
10381038
.require(Feature::public_dependency())
10391039
.is_ok()
10401040
&& !dep.public

src/cargo/core/compiler/unit_dependencies.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,16 @@ impl<'a, 'cfg> State<'a, 'cfg> {
713713
features.activated_features(pkg_id, features_for)
714714
}
715715

716+
fn is_dep_activated(
717+
&self,
718+
pkg_id: PackageId,
719+
features_for: FeaturesFor,
720+
dep_name: InternedString,
721+
) -> bool {
722+
self.features()
723+
.is_dep_activated(pkg_id, features_for, dep_name)
724+
}
725+
716726
fn get(&self, id: PackageId) -> &'a Package {
717727
self.package_set
718728
.get_one(id)
@@ -738,9 +748,7 @@ impl<'a, 'cfg> State<'a, 'cfg> {
738748
// did not enable it, don't include it.
739749
if dep.is_optional() {
740750
let features_for = unit_for.map_to_features_for();
741-
742-
let feats = self.activated_features(pkg_id, features_for);
743-
if !feats.contains(&dep.name_in_toml()) {
751+
if !self.is_dep_activated(pkg_id, features_for, dep.name_in_toml()) {
744752
return false;
745753
}
746754
}

src/cargo/core/features.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
//! use core::{Feature, Features};
2626
//!
2727
//! let feature = Feature::launch_into_space();
28-
//! package.manifest().features().require(feature).chain_err(|| {
28+
//! package.manifest().unstable_features().require(feature).chain_err(|| {
2929
//! "launching Cargo into space right now is unstable and may result in \
3030
//! unintended damage to your codebase, use with caution"
3131
//! })?;
@@ -197,9 +197,6 @@ features! {
197197
// Overriding profiles for dependencies.
198198
[stable] profile_overrides: bool,
199199

200-
// Separating the namespaces for features and dependencies
201-
[unstable] namespaced_features: bool,
202-
203200
// "default-run" manifest option,
204201
[stable] default_run: bool,
205202

@@ -360,6 +357,7 @@ pub struct CliUnstable {
360357
pub multitarget: bool,
361358
pub rustdoc_map: bool,
362359
pub terminal_width: Option<Option<usize>>,
360+
pub namespaced_features: bool,
363361
}
364362

365363
fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
@@ -465,6 +463,7 @@ impl CliUnstable {
465463
"multitarget" => self.multitarget = parse_empty(k, v)?,
466464
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
467465
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
466+
"namespaced-features" => self.namespaced_features = parse_empty(k, v)?,
468467
_ => bail!("unknown `-Z` flag specified: {}", k),
469468
}
470469

src/cargo/core/manifest.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub struct Manifest {
4242
patch: HashMap<Url, Vec<Dependency>>,
4343
workspace: WorkspaceConfig,
4444
original: Rc<TomlManifest>,
45-
features: Features,
45+
unstable_features: Features,
4646
edition: Edition,
4747
im_a_teapot: Option<bool>,
4848
default_run: Option<String>,
@@ -371,7 +371,7 @@ impl Manifest {
371371
replace: Vec<(PackageIdSpec, Dependency)>,
372372
patch: HashMap<Url, Vec<Dependency>>,
373373
workspace: WorkspaceConfig,
374-
features: Features,
374+
unstable_features: Features,
375375
edition: Edition,
376376
im_a_teapot: Option<bool>,
377377
default_run: Option<String>,
@@ -393,7 +393,7 @@ impl Manifest {
393393
replace,
394394
patch,
395395
workspace,
396-
features,
396+
unstable_features,
397397
edition,
398398
original,
399399
im_a_teapot,
@@ -467,8 +467,9 @@ impl Manifest {
467467
&self.workspace
468468
}
469469

470-
pub fn features(&self) -> &Features {
471-
&self.features
470+
/// Unstable, nightly features that are enabled in this manifest.
471+
pub fn unstable_features(&self) -> &Features {
472+
&self.unstable_features
472473
}
473474

474475
/// The style of resolver behavior to use, declared with the `resolver` field.
@@ -487,7 +488,7 @@ impl Manifest {
487488

488489
pub fn feature_gate(&self) -> CargoResult<()> {
489490
if self.im_a_teapot.is_some() {
490-
self.features
491+
self.unstable_features
491492
.require(Feature::test_dummy_unstable())
492493
.chain_err(|| {
493494
anyhow::format_err!(
@@ -578,7 +579,7 @@ impl VirtualManifest {
578579
&self.warnings
579580
}
580581

581-
pub fn features(&self) -> &Features {
582+
pub fn unstable_features(&self) -> &Features {
582583
&self.features
583584
}
584585

0 commit comments

Comments
 (0)