Skip to content

Commit cf53cc5

Browse files
committed
Auto merge of #14639 - epage:stabilize-msrv-config, r=weihanglo
feat: Stabilize MSRV-aware resolver config ### What does this PR try to resolve? This includes - `cargo generate-lockfile --ignore-rust-version` - `cargo update --ignore-rust-version` This does not include - `edition = "2024"` - `resolver = "3"` This is part of #9930 ### How should we test and review this PR? ### Additional information This is stacked on top of #14636. The commits for this PR start with the commit with a title that matches the PR title. [FCP](#14639 (comment))
2 parents 6f92aaa + 498d4df commit cf53cc5

File tree

18 files changed

+129
-243
lines changed

18 files changed

+129
-243
lines changed

src/bin/cargo/commands/generate_lockfile.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,13 @@ pub fn cli() -> Command {
88
.arg_silent_suggestion()
99
.arg_manifest_path()
1010
.arg_lockfile_path()
11-
.arg_ignore_rust_version_with_help(
12-
"Ignore `rust-version` specification in packages (unstable)",
13-
)
11+
.arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages")
1412
.after_help(color_print::cstr!(
1513
"Run `<cyan,bold>cargo help generate-lockfile</>` for more detailed information.\n"
1614
))
1715
}
1816

1917
pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
20-
if args.honor_rust_version().is_some() {
21-
gctx.cli_unstable().fail_if_stable_opt_custom_z(
22-
"--ignore-rust-version",
23-
9930,
24-
"msrv-policy",
25-
gctx.cli_unstable().msrv_policy,
26-
)?;
27-
}
2818
let ws = args.workspace(gctx)?;
2919
ops::generate_lockfile(&ws)?;
3020
Ok(())

src/bin/cargo/commands/update.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,13 @@ pub fn cli() -> Command {
5353
)
5454
.arg_manifest_path()
5555
.arg_lockfile_path()
56-
.arg_ignore_rust_version_with_help(
57-
"Ignore `rust-version` specification in packages (unstable)",
58-
)
56+
.arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages")
5957
.after_help(color_print::cstr!(
6058
"Run `<cyan,bold>cargo help update</>` for more detailed information.\n"
6159
))
6260
}
6361

6462
pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
65-
if args.honor_rust_version().is_some() {
66-
gctx.cli_unstable().fail_if_stable_opt_custom_z(
67-
"--ignore-rust-version",
68-
9930,
69-
"msrv-policy",
70-
gctx.cli_unstable().msrv_policy,
71-
)?;
72-
}
73-
7463
let mut ws = args.workspace(gctx)?;
7564

7665
if args.is_present_with_zero_values("package") {

src/cargo/core/workspace.rs

+6-25
Original file line numberDiff line numberDiff line change
@@ -306,31 +306,12 @@ impl<'gctx> Workspace<'gctx> {
306306
}
307307
}
308308
}
309-
match self.gctx().get::<CargoResolverConfig>("resolver") {
310-
Ok(CargoResolverConfig {
311-
incompatible_rust_versions: Some(incompatible_rust_versions),
312-
}) => {
313-
if self.gctx().cli_unstable().msrv_policy {
314-
self.resolve_honors_rust_version =
315-
incompatible_rust_versions == IncompatibleRustVersions::Fallback;
316-
} else {
317-
self.gctx()
318-
.shell()
319-
.warn("ignoring `resolver` config table without `-Zmsrv-policy`")?;
320-
}
321-
}
322-
Ok(CargoResolverConfig {
323-
incompatible_rust_versions: None,
324-
}) => {}
325-
Err(err) => {
326-
if self.gctx().cli_unstable().msrv_policy {
327-
return Err(err);
328-
} else {
329-
self.gctx()
330-
.shell()
331-
.warn("ignoring `resolver` config table without `-Zmsrv-policy`")?;
332-
}
333-
}
309+
if let CargoResolverConfig {
310+
incompatible_rust_versions: Some(incompatible_rust_versions),
311+
} = self.gctx().get::<CargoResolverConfig>("resolver")?
312+
{
313+
self.resolve_honors_rust_version =
314+
incompatible_rust_versions == IncompatibleRustVersions::Fallback;
334315
}
335316

336317
Ok(())

src/doc/src/reference/config.md

+27
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ rpath = false # Sets the rpath linking option.
143143
[profile.<name>.package.<name>] # Override profile for a package.
144144
# Same keys for a normal profile (minus `panic`, `lto`, and `rpath`).
145145

146+
[resolver]
147+
incompatible-rust-versions = "allow" # Specifies how resolver reacts to these
148+
146149
[registries.<name>] # registries other than crates.io
147150
index = "" # URL of the registry index
148151
token = "" # authentication token for the registry
@@ -972,6 +975,30 @@ See [rpath](profiles.md#rpath).
972975

973976
See [strip](profiles.md#strip).
974977

978+
### `[resolver]`
979+
980+
The `[resolver]` table overrides [dependency resolution behavior](resolver.md) for local development (e.g. excludes `cargo install`).
981+
982+
#### `resolver.incompatible-rust-versions`
983+
* Type: string
984+
* Default: `"allow"`
985+
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS`
986+
987+
When resolving which version of a dependency to use, select how versions with incompatible `package.rust-version`s are treated.
988+
Values include:
989+
- `allow`: treat `rust-version`-incompatible versions like any other version
990+
- `fallback`: only consider `rust-version`-incompatible versions if no other version matched
991+
992+
Can be overridden with
993+
- `--ignore-rust-version` CLI option
994+
- Setting the dependency's version requirement higher than any version with a compatible `rust-version`
995+
- Specifying the version to `cargo update` with `--precise`
996+
997+
See the [resolver](resolver.md#rust-version) chapter for more details.
998+
999+
> **MSRV:**
1000+
> - `allow` is supported on any version
1001+
> - `fallback` is respected as of 1.84
9751002
9761003
### `[registries]`
9771004

src/doc/src/reference/resolver.md

+85-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Key steps:
7373

7474
### Version numbers
7575

76-
Cargo prefers the highest version currently available.
76+
Generally, Cargo prefers the highest version currently available.
7777

7878
For example, if you had a package in the resolve graph with:
7979
```toml
@@ -83,6 +83,8 @@ bitflags = "*"
8383
If at the time the `Cargo.lock` file is generated, the greatest version of
8484
`bitflags` is `1.2.1`, then the package will use `1.2.1`.
8585

86+
For an example of a possible exception, see [Rust version](#rust-version).
87+
8688
### Version requirements
8789

8890
Package specify what versions they support, rejecting all others, through
@@ -201,6 +203,88 @@ ecosystem if you publish a SemVer-incompatible version of a popular library.
201203
[semver trick]: https://github.com/dtolnay/semver-trick
202204
[`downcast_ref`]: ../../std/any/trait.Any.html#method.downcast_ref
203205

206+
### Rust version
207+
208+
To support developing software with a minimum supported [Rust version],
209+
the resolver can take into account a dependency version's compatibility with your Rust version.
210+
This is controlled by the config field [`resolver.incompatible-rust-versions`].
211+
212+
With the `fallback` setting, the resolver will prefer packages with a Rust version that is
213+
equal to or greater than your own Rust version.
214+
For example, you are using Rust 1.85 to develop the following package:
215+
```toml
216+
[package]
217+
name = "my-cli"
218+
rust-version = "1.62"
219+
220+
[dependencies]
221+
clap = "4.0" # resolves to 4.0.32
222+
```
223+
The resolver would pick version 4.0.32 because it has a Rust version of 1.60.0.
224+
- 4.0.0 is not picked because it is a [lower version number](#version-numbers) despite it also having a Rust version of 1.60.0.
225+
- 4.5.20 is not picked because it is incompatible with `my-cli`'s Rust version of 1.62 despite having a much [higher version](#version-numbers) and it has a Rust version of 1.74.0 which is compatible with your 1.85 toolchain.
226+
227+
If a version requirement does not include a Rust version compatible dependency version,
228+
the resolver won't error but will instead pick a version, even if its potentially suboptimal.
229+
For example, you change the dependency on `clap`:
230+
```toml
231+
[package]
232+
name = "my-cli"
233+
rust-version = "1.62"
234+
235+
[dependencies]
236+
clap = "4.2" # resolves to 4.5.20
237+
```
238+
No version of `clap` matches that [version requirement](#version-requirements)
239+
that is compatible with Rust version 1.62.
240+
The resolver will then pick an incompatible version, like 4.5.20 despite it having a Rust version of 1.74.
241+
242+
When the resolver selects a dependency version of a package,
243+
it does not know all the workspace members that will eventually have a transitive dependency on that version
244+
and so it cannot take into account only the Rust versions relevant for that dependency.
245+
The resolver has heuristics to find a "good enough" solution when workspace members have different Rust versions.
246+
This applies even for packages in a workspace without a Rust version.
247+
248+
When a workspace has members with different Rust versions,
249+
the resolver may pick a lower dependency version than necessary.
250+
For example, you have the following workspace members:
251+
```toml
252+
[package]
253+
name = "a"
254+
rust-version = "1.62"
255+
256+
[package]
257+
name = "b"
258+
259+
[dependencies]
260+
clap = "4.2" # resolves to 4.5.20
261+
```
262+
Though package `b` does not have a Rust version and could use a higher version like 4.5.20,
263+
4.0.32 will be selected because of package `a`'s Rust version of 1.62.
264+
265+
Or the resolver may pick too high of a version.
266+
For example, you have the following workspace members:
267+
```toml
268+
[package]
269+
name = "a"
270+
rust-version = "1.62"
271+
272+
[dependencies]
273+
clap = "4.2" # resolves to 4.5.20
274+
275+
[package]
276+
name = "b"
277+
278+
[dependencies]
279+
clap = "4.5" # resolves to 4.5.20
280+
```
281+
Though each package has a version requirement for `clap` that would meet its own Rust version,
282+
because of [version unification](#version-numbers),
283+
the resolver will need to pick one version that works in both cases and that would be a version like 4.5.20.
284+
285+
[Rust version]: rust-version.md
286+
[`resolver.incompatible-rust-versions`]: config.md#resolverincompatible-rust-versions
287+
204288
### Features
205289

206290
For the purpose of generating `Cargo.lock`, the resolver builds the dependency

src/doc/src/reference/rust-version.md

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ benchmarks, etc.
2929
`cargo add` will auto-select the dependency's version requirement to be the latest version compatible with your `rust-version`.
3030
If that isn't the latest version, `cargo add` will inform users so they can make the choice on whether to keep it or update your `rust-version`.
3131

32+
The [resolver](resolver.md#rust-version) may take Rust version into account when picking dependencies.
33+
3234
Other tools may also take advantage of it, like `cargo clippy`'s
3335
[`incompatible_msrv` lint](https://rust-lang.github.io/rust-clippy/stable/index.html#/incompatible_msrv).
3436

@@ -131,6 +133,10 @@ potentially limiting access to features of the shared dependency for the workspa
131133
To allow users to patch a dependency on one of your workspace members,
132134
every package in the workspace would need to be loadable in the oldest Rust version supported by the workspace.
133135

136+
When using [`incompatible-rust-versions = "fallback"`](config.md#resolverincompatible-rust-versions),
137+
the Rust version of one package can affect dependency versions selected for another package with a different Rust version.
138+
See the [resolver](resolver.md#rust-version) chapter for more details.
139+
134140
### One or More Policies
135141

136142
One way to mitigate the downsides of supporting older Rust versions is to apply your policy to older major or minor versions of your package that you continue to support.

src/doc/src/reference/unstable.md

+1-25
Original file line numberDiff line numberDiff line change
@@ -351,31 +351,7 @@ This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/
351351

352352
### MSRV-aware resolver
353353

354-
`-Zmsrv-policy` allows access to an MSRV-aware resolver which can be enabled with:
355-
- `resolver.incompatible-rust-versions` config field
356-
- `workspace.resolver = "3"` / `package.resolver = "3"`
357-
- `package.edition = "2024"` (only in workspace root)
358-
359-
The resolver will prefer dependencies with a `package.rust-version` that is the same or older than your project's MSRV.
360-
As the resolver is unable to determine which workspace members will eventually
361-
depend on a package when it is being selected, we prioritize versions based on
362-
how many workspace member MSRVs they are compatible with.
363-
If there is no MSRV set then your toolchain version will be used, allowing it to pick up the toolchain version from pinned in rustup (e.g. `rust-toolchain.toml`).
364-
365-
#### `resolver.incompatible-rust-versions`
366-
* Type: string
367-
* Default: `"allow"`
368-
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS`
369-
370-
When resolving a version for a dependency, select how versions with incompatible `package.rust-version`s are treated.
371-
Values include:
372-
- `allow`: treat `rust-version`-incompatible versions like any other version
373-
- `fallback`: only consider `rust-version`-incompatible versions if no other version matched
374-
375-
Can be overridden with
376-
- `--ignore-rust-version` CLI option
377-
- Setting the dependency's version requirement higher than any version with a compatible `rust-version`
378-
- Specifying the version to `cargo update` with `--precise`
354+
This was stabilized in 1.83 in [#14639](https://github.com/rust-lang/cargo/pull/14639).
379355

380356
### Convert `incompatible_toolchain` error into a lint
381357

tests/testsuite/cargo_add/rust_version_ignore/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ fn case() {
2020
let cwd = &project_root;
2121

2222
snapbox::cmd::Command::cargo_ui()
23-
.arg("-Zmsrv-policy")
2423
.arg("add")
2524
.arg("--ignore-rust-version")
2625
.arg_line("rust-version-user")
2726
.current_dir(cwd)
2827
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
29-
.masquerade_as_nightly_cargo(&["msrv-policy"])
3028
.assert()
3129
.code(0)
3230
.stdout_eq(str![""])

tests/testsuite/cargo_add/rust_version_incompatible/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ fn case() {
2323
let cwd = &project_root;
2424

2525
snapbox::cmd::Command::cargo_ui()
26-
.arg("-Zmsrv-policy")
2726
.arg("add")
2827
.arg_line("rust-version-user")
2928
.current_dir(cwd)
3029
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
31-
.masquerade_as_nightly_cargo(&["msrv-policy"])
3230
.assert()
3331
.failure()
3432
.stdout_eq(str![""])

tests/testsuite/cargo_add/rust_version_latest/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ fn case() {
2020
let cwd = &project_root;
2121

2222
snapbox::cmd::Command::cargo_ui()
23-
.arg("-Zmsrv-policy")
2423
.arg("add")
2524
.arg_line("rust-version-user")
2625
.current_dir(cwd)
2726
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
28-
.masquerade_as_nightly_cargo(&["msrv-policy"])
2927
.assert()
3028
.success()
3129
.stdout_eq(str![""])

tests/testsuite/cargo_add/rust_version_older/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ fn case() {
2020
let cwd = &project_root;
2121

2222
snapbox::cmd::Command::cargo_ui()
23-
.arg("-Zmsrv-policy")
2423
.arg("add")
2524
.arg_line("rust-version-user")
2625
.current_dir(cwd)
2726
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
28-
.masquerade_as_nightly_cargo(&["msrv-policy"])
2927
.assert()
3028
.success()
3129
.stdout_eq(str![""])

tests/testsuite/cargo_add/rustc_ignore/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ fn case() {
2323
let cwd = &project_root;
2424

2525
snapbox::cmd::Command::cargo_ui()
26-
.arg("-Zmsrv-policy")
2726
.arg("add")
2827
.arg("--ignore-rust-version")
2928
.arg_line("rust-version-user")
3029
.current_dir(cwd)
3130
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
32-
.masquerade_as_nightly_cargo(&["msrv-policy"])
3331
.assert()
3432
.code(0)
3533
.stdout_eq(str![""])

tests/testsuite/cargo_add/rustc_incompatible/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ fn case() {
1717
let cwd = &project_root;
1818

1919
snapbox::cmd::Command::cargo_ui()
20-
.arg("-Zmsrv-policy")
2120
.arg("add")
2221
.arg_line("rust-version-user")
2322
.current_dir(cwd)
2423
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
25-
.masquerade_as_nightly_cargo(&["msrv-policy"])
2624
.assert()
2725
.failure()
2826
.stdout_eq(str![""])

tests/testsuite/cargo_add/rustc_latest/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ fn case() {
2323
let cwd = &project_root;
2424

2525
snapbox::cmd::Command::cargo_ui()
26-
.arg("-Zmsrv-policy")
2726
.arg("add")
2827
.arg_line("rust-version-user")
2928
.current_dir(cwd)
3029
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
31-
.masquerade_as_nightly_cargo(&["msrv-policy"])
3230
.assert()
3331
.success()
3432
.stdout_eq(str![""])

tests/testsuite/cargo_add/rustc_older/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ fn case() {
2323
let cwd = &project_root;
2424

2525
snapbox::cmd::Command::cargo_ui()
26-
.arg("-Zmsrv-policy")
2726
.arg("add")
2827
.arg_line("rust-version-user")
2928
.current_dir(cwd)
3029
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
31-
.masquerade_as_nightly_cargo(&["msrv-policy"])
3230
.assert()
3331
.success()
3432
.stdout_eq(str![""])

0 commit comments

Comments
 (0)