Skip to content

Commit

Permalink
fix(msrv): Put MSRV-aware resolver behind a config
Browse files Browse the repository at this point in the history
This is a part of rust-lang#13540 which is a party of rust-lang#9930.

The config is `resolver.something-like-precedence` with values:
- `something-like-maximum` (default)
- `something-like-rust-version`

This is punting on the actual config schema so we can implement
`package.resolver` and `edition = "2024"` support as we want the
MSRV-aware resolver available without `cargo_features`.
  • Loading branch information
epage committed Apr 17, 2024
1 parent 7af4bcf commit 259574b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 9 deletions.
48 changes: 42 additions & 6 deletions src/cargo/core/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
use crate::util::lints::check_implicit_features;
use crate::util::toml::{read_manifest, InheritableFields};
use crate::util::{context::ConfigRelativePath, Filesystem, GlobalContext, IntoUrl};
use crate::util::{
context::CargoResolverConfig, context::CargoResolverPrecedence, context::ConfigRelativePath,
Filesystem, GlobalContext, IntoUrl,
};
use cargo_util::paths;
use cargo_util::paths::normalize_path;
use cargo_util_schemas::manifest;
Expand Down Expand Up @@ -100,6 +103,7 @@ pub struct Workspace<'gctx> {

/// The resolver behavior specified with the `resolver` field.
resolve_behavior: ResolveBehavior,
resolve_honors_rust_version: bool,
honor_rust_version: Option<bool>,

/// Workspace-level custom metadata
Expand Down Expand Up @@ -207,7 +211,7 @@ impl<'gctx> Workspace<'gctx> {
.load_workspace_config()?
.and_then(|cfg| cfg.custom_metadata);
ws.find_members()?;
ws.set_resolve_behavior();
ws.set_resolve_behavior()?;
ws.validate()?;
Ok(ws)
}
Expand All @@ -230,6 +234,7 @@ impl<'gctx> Workspace<'gctx> {
loaded_packages: RefCell::new(HashMap::new()),
ignore_lock: false,
resolve_behavior: ResolveBehavior::V1,
resolve_honors_rust_version: false,
honor_rust_version: None,
custom_metadata: None,
}
Expand All @@ -248,7 +253,7 @@ impl<'gctx> Workspace<'gctx> {
.packages
.insert(root_path, MaybePackage::Virtual(manifest));
ws.find_members()?;
ws.set_resolve_behavior();
ws.set_resolve_behavior()?;
// TODO: validation does not work because it walks up the directory
// tree looking for the root which is a fake file that doesn't exist.
Ok(ws)
Expand Down Expand Up @@ -284,11 +289,11 @@ impl<'gctx> Workspace<'gctx> {
ws.members.push(ws.current_manifest.clone());
ws.member_ids.insert(id);
ws.default_members.push(ws.current_manifest.clone());
ws.set_resolve_behavior();
ws.set_resolve_behavior()?;
Ok(ws)
}

fn set_resolve_behavior(&mut self) {
fn set_resolve_behavior(&mut self) -> CargoResult<()> {
// - If resolver is specified in the workspace definition, use that.
// - If the root package specifies the resolver, use that.
// - If the root package specifies edition 2021, use v2.
Expand All @@ -299,7 +304,36 @@ impl<'gctx> Workspace<'gctx> {
.resolve_behavior()
.unwrap_or_else(|| p.manifest().edition().default_resolve_behavior()),
MaybePackage::Virtual(vm) => vm.resolve_behavior().unwrap_or(ResolveBehavior::V1),
};

match self.gctx().get::<CargoResolverConfig>("resolver") {
Ok(CargoResolverConfig {
something_like_precedence: Some(precedence),
}) => {
if self.gctx().cli_unstable().msrv_policy {
self.resolve_honors_rust_version =
precedence == CargoResolverPrecedence::SomethingLikeRustVersion;
} else {
self.gctx()
.shell()
.warn("ignoring `resolver` config table without `-Zmsrv-policy`")?;
}
}
Ok(CargoResolverConfig {
something_like_precedence: None,
}) => {}
Err(err) => {
if self.gctx().cli_unstable().msrv_policy {
return Err(err);
} else {
self.gctx()
.shell()
.warn("ignoring `resolver` config table without `-Zmsrv-policy`")?;
}
}
}

Ok(())
}

/// Returns the current package of this workspace.
Expand Down Expand Up @@ -616,7 +650,9 @@ impl<'gctx> Workspace<'gctx> {
}

pub fn resolve_honors_rust_version(&self) -> bool {
self.gctx().cli_unstable().msrv_policy && self.honor_rust_version.unwrap_or(true)
// Give CLI precedence
self.honor_rust_version
.unwrap_or(self.resolve_honors_rust_version)
}

pub fn custom_metadata(&self) -> Option<&toml::Value> {
Expand Down
13 changes: 13 additions & 0 deletions src/cargo/util/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2644,6 +2644,19 @@ impl BuildTargetConfig {
}
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoResolverConfig {
pub something_like_precedence: Option<CargoResolverPrecedence>,
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum CargoResolverPrecedence {
SomethingLikeMaximum,
SomethingLikeRustVersion,
}

#[derive(Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct TermConfig {
Expand Down
17 changes: 14 additions & 3 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,26 @@ This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/

### MSRV-aware resolver

By default, `-Zmsrv-policy` enables an MSRV-aware resolver.
`-Zmsrv-policy` allows access to an MSRV-aware resolver which can be enabled with:
- `resolver.something-like-precedence` config field

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.
If there is none set, your toolchain version will be used with the intent to pick up the version from rustup's `rust-toolchain.toml`, if present.

MSRV-incompatible dependencies can still be selected by:
#### `resolver.something-like-precedence`
* Type: string
* Default: "something-like-maximum"
* Environment: `CARGO_RESOLVER_SOMETHING_LIKE_PRECEDENCE`

Select which policy should be used when resolving dependencies.
The path to where all compiler output is placed. The default if not specified
is a directory named `target` located at the root of the workspace.

Can be overridden with
- `--ignore-rust-version` CLI option
- Setting the dependency's version requirement too high
- Specifying the version to `cargo update` with `--precise`
- Passing `--ignore-rust-version`

## precise-pre-release

Expand Down
2 changes: 2 additions & 0 deletions tests/testsuite/rust_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ fn resolve_unstable_config_on_stable() {
)
.with_stderr(
"\
[WARNING] ignoring `resolver` config table without `-Zmsrv-policy`
[UPDATING] `dummy-registry` index
[LOCKING] 3 packages to latest compatible versions
",
Expand All @@ -595,6 +596,7 @@ foo v0.0.1 ([CWD])
.env("CARGO_RESOLVER_SOMETHING_LIKE_PRECEDENCE", "non-existent")
.with_stderr(
"\
[WARNING] ignoring `resolver` config table without `-Zmsrv-policy`
[UPDATING] `dummy-registry` index
[LOCKING] 3 packages to latest compatible versions
",
Expand Down

0 comments on commit 259574b

Please sign in to comment.