From de39bf4aac10b6b3a7378eb3b17f30ef1af9481a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Apr 2024 12:00:28 -0500 Subject: [PATCH] feat(resolver): Add v3 resolver for MSRV-aware resolving This is a part of #9930 and is important for changing the default with the new edition. --- src/cargo/core/resolver/features.rs | 4 +- src/cargo/core/resolver/types.rs | 4 + src/cargo/core/workspace.rs | 13 ++- src/cargo/util/toml/mod.rs | 15 +++- src/doc/src/reference/unstable.md | 1 + tests/testsuite/rust_version.rs | 127 ++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index b86b96a5dab8..fdb49c0bf9f5 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -196,7 +196,7 @@ impl FeatureOpts { } match ws.resolve_behavior() { ResolveBehavior::V1 => {} - ResolveBehavior::V2 => { + ResolveBehavior::V2 | ResolveBehavior::V3 => { enable(&vec!["all".to_string()]).unwrap(); } } @@ -214,7 +214,7 @@ impl FeatureOpts { pub fn new_behavior(behavior: ResolveBehavior, has_dev_units: HasDevUnits) -> FeatureOpts { match behavior { ResolveBehavior::V1 => FeatureOpts::default(), - ResolveBehavior::V2 => FeatureOpts { + ResolveBehavior::V2 | ResolveBehavior::V3 => FeatureOpts { decouple_host_deps: true, decouple_dev_deps: has_dev_units == HasDevUnits::No, ignore_inactive_targets: true, diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index a5be52ab136b..67fac19aa591 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -111,6 +111,8 @@ pub enum ResolveBehavior { V1, /// V2 adds the new feature resolver. V2, + /// V3 changes version preferences + V3, } impl ResolveBehavior { @@ -118,6 +120,7 @@ impl ResolveBehavior { match resolver { "1" => Ok(ResolveBehavior::V1), "2" => Ok(ResolveBehavior::V2), + "3" => Ok(ResolveBehavior::V3), s => anyhow::bail!( "`resolver` setting `{}` is not valid, valid options are \"1\" or \"2\"", s @@ -129,6 +132,7 @@ impl ResolveBehavior { match self { ResolveBehavior::V1 => "1", ResolveBehavior::V2 => "2", + ResolveBehavior::V3 => "3", } .to_owned() } diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index ce1276d33e65..862b07dd1111 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -306,6 +306,17 @@ impl<'gctx> Workspace<'gctx> { MaybePackage::Virtual(vm) => vm.resolve_behavior().unwrap_or(ResolveBehavior::V1), }; + match self.resolve_behavior() { + ResolveBehavior::V1 | ResolveBehavior::V2 => {} + ResolveBehavior::V3 => { + if self.resolve_behavior == ResolveBehavior::V3 { + if !self.gctx().cli_unstable().msrv_policy { + anyhow::bail!("`resolver=\"3\"` requires `-Zmsrv-policy`"); + } + self.resolve_honors_rust_version = true; + } + } + } match self.gctx().get::("resolver") { Ok(CargoResolverConfig { something_like_precedence: Some(precedence), @@ -869,7 +880,7 @@ impl<'gctx> Workspace<'gctx> { self.is_virtual() || match self.resolve_behavior() { ResolveBehavior::V1 => false, - ResolveBehavior::V2 => true, + ResolveBehavior::V2 | ResolveBehavior::V3 => true, } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index ce08e8c3021b..8400ccac04d9 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -265,6 +265,12 @@ fn resolve_toml( warnings: &mut Vec, _errors: &mut Vec, ) -> CargoResult { + if let Some(workspace) = &original_toml.workspace { + if workspace.resolver.as_deref() == Some("3") { + features.require(Feature::edition2024())?; + } + } + let mut resolved_toml = manifest::TomlManifest { cargo_features: original_toml.cargo_features.clone(), package: None, @@ -299,7 +305,8 @@ fn resolve_toml( }; if let Some(original_package) = original_toml.package() { - let resolved_package = resolve_package_toml(original_package, package_root, &inherit)?; + let resolved_package = + resolve_package_toml(original_package, features, package_root, &inherit)?; resolved_toml.package = Some(resolved_package); resolved_toml.dependencies = resolve_dependencies( @@ -399,6 +406,7 @@ fn resolve_toml( #[tracing::instrument(skip_all)] fn resolve_package_toml<'a>( original_package: &manifest::TomlPackage, + features: &Features, package_root: &Path, inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>, ) -> CargoResult> { @@ -526,6 +534,11 @@ fn resolve_package_toml<'a>( metadata: original_package.metadata.clone(), _invalid_cargo_features: Default::default(), }; + + if resolved_package.resolver.as_deref() == Some("3") { + features.require(Feature::edition2024())?; + } + Ok(Box::new(resolved_package)) } diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 1e4b1dc8674a..ed65448f8ced 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -338,6 +338,7 @@ This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/ `-Zmsrv-policy` allows access to an MSRV-aware resolver which can be enabled with: - `resolver.something-like-precedence` config field +- `workspace.resolver = "3"` / `package.resolver = "3"` The resolver will prefer dependencies with a `package.rust-version` that is the same or older than your project's MSRV. Your project's MSRV is determined by taking the lowest `package.rust-version` set among your workspace members. diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index 483b9ff7e99d..18101b976d72 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -613,6 +613,133 @@ foo v0.0.1 ([CWD]) .run(); } +#[cargo_test(nightly, reason = "edition2024 in rustc is unstable")] +fn resolve_v3() { + Package::new("only-newer", "1.6.0") + .rust_version("1.65.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); + Package::new("newer-and-older", "1.5.0") + .rust_version("1.55.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); + Package::new("newer-and-older", "1.6.0") + .rust_version("1.65.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["edition2024"] + + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + authors = [] + rust-version = "1.60.0" + resolver = "3" + + [dependencies] + only-newer = "1.0.0" + newer-and-older = "1.0.0" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + // v3 should resolve for MSRV + p.cargo("generate-lockfile") + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["edition2024", "msrv-policy"]) + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[LOCKING] 3 packages to latest Rust 1.60.0 compatible versions +[ADDING] newer-and-older v1.5.0 (latest: v1.6.0) +", + ) + .run(); + p.cargo("tree") + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["edition2024", "msrv-policy"]) + .with_stdout( + "\ +foo v0.0.1 ([CWD]) +├── newer-and-older v1.5.0 +└── only-newer v1.6.0 +", + ) + .run(); + + // `--ignore-rust-version` has precedence over v3 + p.cargo("generate-lockfile --ignore-rust-version") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[LOCKING] 3 packages to latest compatible versions +", + ) + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["msrv-policy"]) + .run(); + p.cargo("tree") + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["edition2024", "msrv-policy"]) + .with_stdout( + "\ +foo v0.0.1 ([CWD]) +├── newer-and-older v1.6.0 +└── only-newer v1.6.0 +", + ) + .run(); + + // config has precedence over v3 + p.cargo("generate-lockfile") + .env( + "CARGO_RESOLVER_SOMETHING_LIKE_PRECEDENCE", + "something-like-maximum", + ) + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[LOCKING] 3 packages to latest compatible versions +", + ) + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["msrv-policy"]) + .run(); + p.cargo("tree") + .arg("-Zmsrv-policy") + .masquerade_as_nightly_cargo(&["edition2024", "msrv-policy"]) + .with_stdout( + "\ +foo v0.0.1 ([CWD]) +├── newer-and-older v1.6.0 +└── only-newer v1.6.0 +", + ) + .run(); + + // unstable + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + the cargo feature `edition2024` requires a nightly version of Cargo, but this is the `stable` channel + See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. + See https://doc.rust-lang.org/cargo/reference/unstable.html#edition-2024 for more information about using this feature. +", + ) + .run(); +} + #[cargo_test] fn generate_lockfile_ignore_rust_version_is_unstable() { Package::new("bar", "1.5.0")