Skip to content

Commit

Permalink
docs(ref): Document MSRV-aware resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Oct 4, 2024
1 parent 0498e84 commit 498d4df
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 26 deletions.
27 changes: 27 additions & 0 deletions src/doc/src/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ rpath = false # Sets the rpath linking option.
[profile.<name>.package.<name>] # Override profile for a package.
# Same keys for a normal profile (minus `panic`, `lto`, and `rpath`).

[resolver]
incompatible-rust-versions = "allow" # Specifies how resolver reacts to these

[registries.<name>] # registries other than crates.io
index = "" # URL of the registry index
token = "" # authentication token for the registry
Expand Down Expand Up @@ -972,6 +975,30 @@ See [rpath](profiles.md#rpath).

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

### `[resolver]`

The `[resolver]` table overrides [dependency resolution behavior](resolver.md) for local development (e.g. excludes `cargo install`).

#### `resolver.incompatible-rust-versions`
* Type: string
* Default: `"allow"`
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS`

When resolving which version of a dependency to use, select how versions with incompatible `package.rust-version`s are treated.
Values include:
- `allow`: treat `rust-version`-incompatible versions like any other version
- `fallback`: only consider `rust-version`-incompatible versions if no other version matched

Can be overridden with
- `--ignore-rust-version` CLI option
- Setting the dependency's version requirement higher than any version with a compatible `rust-version`
- Specifying the version to `cargo update` with `--precise`

See the [resolver](resolver.md#rust-version) chapter for more details.

> **MSRV:**
> - `allow` is supported on any version
> - `fallback` is respected as of 1.84
### `[registries]`

Expand Down
86 changes: 85 additions & 1 deletion src/doc/src/reference/resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Key steps:

### Version numbers

Cargo prefers the highest version currently available.
Generally, Cargo prefers the highest version currently available.

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

For an example of a possible exception, see [Rust version](#rust-version).

### Version requirements

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

### Rust version

To support developing software with a minimum supported [Rust version],
the resolver can take into account a dependency version's compatibility with your Rust version.
This is controlled by the config field [`resolver.incompatible-rust-versions`].

With the `fallback` setting, the resolver will prefer packages with a Rust version that is
equal to or greater than your own Rust version.
For example, you are using Rust 1.85 to develop the following package:
```toml
[package]
name = "my-cli"
rust-version = "1.62"

[dependencies]
clap = "4.0" # resolves to 4.0.32
```
The resolver would pick version 4.0.32 because it has a Rust version of 1.60.0.
- 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.
- 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.

If a version requirement does not include a Rust version compatible dependency version,
the resolver won't error but will instead pick a version, even if its potentially suboptimal.
For example, you change the dependency on `clap`:
```toml
[package]
name = "my-cli"
rust-version = "1.62"

[dependencies]
clap = "4.2" # resolves to 4.5.20
```
No version of `clap` matches that [version requirement](#version-requirements)
that is compatible with Rust version 1.62.
The resolver will then pick an incompatible version, like 4.5.20 despite it having a Rust version of 1.74.

When the resolver selects a dependency version of a package,
it does not know all the workspace members that will eventually have a transitive dependency on that version
and so it cannot take into account only the Rust versions relevant for that dependency.
The resolver has heuristics to find a "good enough" solution when workspace members have different Rust versions.
This applies even for packages in a workspace without a Rust version.

When a workspace has members with different Rust versions,
the resolver may pick a lower dependency version than necessary.
For example, you have the following workspace members:
```toml
[package]
name = "a"
rust-version = "1.62"

[package]
name = "b"

[dependencies]
clap = "4.2" # resolves to 4.5.20
```
Though package `b` does not have a Rust version and could use a higher version like 4.5.20,
4.0.32 will be selected because of package `a`'s Rust version of 1.62.

Or the resolver may pick too high of a version.
For example, you have the following workspace members:
```toml
[package]
name = "a"
rust-version = "1.62"

[dependencies]
clap = "4.2" # resolves to 4.5.20

[package]
name = "b"

[dependencies]
clap = "4.5" # resolves to 4.5.20
```
Though each package has a version requirement for `clap` that would meet its own Rust version,
because of [version unification](#version-numbers),
the resolver will need to pick one version that works in both cases and that would be a version like 4.5.20.

[Rust version]: rust-version.md
[`resolver.incompatible-rust-versions`]: config.md#resolverincompatible-rust-versions

### Features

For the purpose of generating `Cargo.lock`, the resolver builds the dependency
Expand Down
6 changes: 6 additions & 0 deletions src/doc/src/reference/rust-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ benchmarks, etc.
`cargo add` will auto-select the dependency's version requirement to be the latest version compatible with your `rust-version`.
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`.

The [resolver](resolver.md#rust-version) may take Rust version into account when picking dependencies.

Other tools may also take advantage of it, like `cargo clippy`'s
[`incompatible_msrv` lint](https://rust-lang.github.io/rust-clippy/stable/index.html#/incompatible_msrv).

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

When using [`incompatible-rust-versions = "fallback"`](config.md#resolverincompatible-rust-versions),
the Rust version of one package can affect dependency versions selected for another package with a different Rust version.
See the [resolver](resolver.md#rust-version) chapter for more details.

### One or More Policies

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.
Expand Down
26 changes: 1 addition & 25 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,31 +351,7 @@ This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/

### MSRV-aware resolver

`-Zmsrv-policy` allows access to an MSRV-aware resolver which can be enabled with:
- `resolver.incompatible-rust-versions` config field
- `workspace.resolver = "3"` / `package.resolver = "3"`
- `package.edition = "2024"` (only in workspace root)

The resolver will prefer dependencies with a `package.rust-version` that is the same or older than your project's MSRV.
As the resolver is unable to determine which workspace members will eventually
depend on a package when it is being selected, we prioritize versions based on
how many workspace member MSRVs they are compatible with.
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`).

#### `resolver.incompatible-rust-versions`
* Type: string
* Default: `"allow"`
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS`

When resolving a version for a dependency, select how versions with incompatible `package.rust-version`s are treated.
Values include:
- `allow`: treat `rust-version`-incompatible versions like any other version
- `fallback`: only consider `rust-version`-incompatible versions if no other version matched

Can be overridden with
- `--ignore-rust-version` CLI option
- Setting the dependency's version requirement higher than any version with a compatible `rust-version`
- Specifying the version to `cargo update` with `--precise`
This was stabilized in 1.83 in [#14639](https://github.com/rust-lang/cargo/pull/14639).

### Convert `incompatible_toolchain` error into a lint

Expand Down

0 comments on commit 498d4df

Please sign in to comment.