From fb569649eec1865077bbdb7a19ce40d44cb31f3f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 14 Nov 2023 16:55:50 -0600 Subject: [PATCH 001/184] chore: Add skeleton --- text/0000-msrv-resolver.md | 97 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 text/0000-msrv-resolver.md diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md new file mode 100644 index 00000000000..a2ab4c4c8a6 --- /dev/null +++ b/text/0000-msrv-resolver.md @@ -0,0 +1,97 @@ +- Feature Name: (fill me in with a unique ident, `my_awesome_feature`) +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +One paragraph explanation of the feature. + +# Motivation +[motivation]: #motivation + +Why are we doing this? What use cases does it support? What is the expected outcome? + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: + +- Introducing new named concepts. +- Explaining the feature largely in terms of examples. +- Explaining how Rust programmers should *think* about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. +- If applicable, provide sample error messages, deprecation warnings, or migration guidance. +- If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. +- Discuss how this impacts the ability to read, understand, and maintain Rust code. Code is read and modified far more often than written; will the proposed feature make code easier to maintain? + +For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This is the technical portion of the RFC. Explain the design in sufficient detail that: + +- Its interaction with other features is clear. +- It is reasonably clear how the feature would be implemented. +- Corner cases are dissected by example. + +The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. + +# Drawbacks +[drawbacks]: #drawbacks + +Why should we *not* do this? + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +- Why is this design the best in the space of possible designs? +- What other designs have been considered and what is the rationale for not choosing them? +- What is the impact of not doing this? +- If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? + +# Prior art +[prior-art]: #prior-art + +Discuss prior art, both the good and the bad, in relation to this proposal. +A few examples of what this can include are: + +- For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? +- For community proposals: Is this done by some other community and what were their experiences with it? +- For other teams: What lessons can we learn from what other communities have done here? +- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. + +This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. +If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. + +Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. +Please also take into consideration that rust sometimes intentionally diverges from common language features. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +# Future possibilities +[future-possibilities]: #future-possibilities + +Think about what the natural extension and evolution of your proposal would +be and how it would affect the language and project as a whole in a holistic +way. Try to use this section as a tool to more fully consider all possible +interactions with the project and language in your proposal. +Also consider how this all fits into the roadmap for the project +and of the relevant sub-team. + +This is also a good place to "dump ideas", if they are out of scope for the +RFC you are writing but otherwise related. + +If you have tried and cannot think of any future possibilities, +you may simply state that you cannot think of anything. + +Note that having something written down in the future-possibilities section +is not a reason to accept the current or a future RFC; such notes should be +in the section on motivation or rationale in this or subsequent RFCs. +The section merely provides additional information. From b41b34236f02708c1cf8f47df75b1795c7ee7326 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 14 Nov 2023 16:56:10 -0600 Subject: [PATCH 002/184] feat: Initial draft --- text/0000-msrv-resolver.md | 325 +++++++++++++++++++++++++++++++------ 1 file changed, 272 insertions(+), 53 deletions(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index a2ab4c4c8a6..07418408cd0 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -1,97 +1,316 @@ -- Feature Name: (fill me in with a unique ident, `my_awesome_feature`) -- Start Date: (fill me in with today's date, YYYY-MM-DD) +- Feature Name: `msrv-resolver` +- Start Date: 2023-11-14 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary [summary]: #summary -One paragraph explanation of the feature. +Provide a happy path for developers needing to work with older versions of Rust by +- Preferring MSRV (minimum-supported-rust-version) compatible dependencies when Cargo resolves dependencies +- Ensuring compatible version requirements when `cargo add` auto-selects a version + +Note: `cargo install` is intentionally left out for now to decouple discussions on how to handle the security ramifications. # Motivation [motivation]: #motivation -Why are we doing this? What use cases does it support? What is the expected outcome? +Let's step through a simple scenario where a developer can develop with the +latest Rust version but production uses an older version: +```console +$ cargo new msrv-resolver + Created binary (application) `msrv-resolver` package +$ cd msrv-resolver +$ # ... add `package.rust-version = "1.64.0"` to `Cargo.toml` +$ cargo add clap + Updating crates.io index + Adding clap v4.4.8 to dependencies. + Features: +... + Updating crates.io index +$ git commit -a -m "WIP" && git push +... +``` +After 30 minutes, CI fails. +The first step is to reproduce this locally +```console +$ rustup install 1.64.0 +... +$ cargo +1.64.0 check + Updating crates.io index + Fetch [===============> ] 67.08%, (28094/50225) resolving deltas +``` +After waiting several minutes, cursing being stuck on a version from before sparse registry support was added... +```console +$ cargo +1.64.0 check + Updating crates.io index + Downloaded clap v4.4.8 + Downloaded clap_builder v4.4.8 + Downloaded clap_lex v0.6.0 + Downloaded anstyle-parse v0.2.2 + Downloaded anstyle v1.0.4 + Downloaded anstream v0.6.4 + Downloaded 6 crates (289.3 KB) in 0.35s +error: package `clap_builder v4.4.8` cannot be built because it requires rustc 1.70.0 or newer, while the currently ac +tive rustc version is 1.64.0 +``` +Thankfully, crates.io now shows [supported Rust versions](https://crates.io/crates/clap_builder/versions), so I pick v4.3.24. +```console +$ cargo update -p clap_builder --precise 4.3.24 + Updating crates.io index +error: failed to select a version for the requirement `clap_builder = "=4.4.8"` +candidate versions found which didn't match: 4.3.24 +location searched: crates.io index +required by package `clap v4.4.8` + ... which satisfies dependency `clap = "^4.4.8"` (locked to 4.4.8) of package `msrv-resolver v0.1.0 (/home/epage/src/personal/dump/msrv-resolver)` +perhaps a crate was updated and forgotten to be re-vendored? +``` +After browsing on some forums, I edit my `Cargo.toml` to roll back to `clap = "4.3.24"` and try again +```console +$ cargo update -p clap --precise 4.3.24 + Updating crates.io index + Downgrading anstream v0.6.4 -> v0.3.2 + Downgrading anstyle-wincon v3.0.1 -> v1.0.2 + Adding bitflags v2.4.1 + Downgrading clap v4.4.8 -> v4.3.24 + Downgrading clap_builder v4.4.8 -> v4.3.24 + Downgrading clap_lex v0.6.0 -> v0.5.1 + Adding errno v0.3.6 + Adding hermit-abi v0.3.3 + Adding is-terminal v0.4.9 + Adding libc v0.2.150 + Adding linux-raw-sys v0.4.11 + Adding rustix v0.38.23 +$ cargo +1.64.0 check + Downloaded clap_builder v4.3.24 + Downloaded errno v0.3.6 + Downloaded clap_lex v0.5.1 + Downloaded bitflags v2.4.1 + Downloaded clap v4.3.24 + Downloaded rustix v0.38.23 + Downloaded libc v0.2.150 + Downloaded linux-raw-sys v0.4.11 + Downloaded 8 crates (2.8 MB) in 1.15s (largest was `linux-raw-sys` at 1.4 MB) +error: package `anstyle-parse v0.2.2` cannot be built because it requires rustc 1.70.0 or newer, while the currently a +ctive rustc version is 1.64.0 +``` +Again, consulting [crates.io](https://crates.io/crates/anstyle-parse/versions) +```console +$ cargo update -p anstyle-parse --precise 0.2.1 + Updating crates.io index + Downgrading anstyle-parse v0.2.2 -> v0.2.1 +$ cargo +1.64.0 check +error: package `clap_lex v0.5.1` cannot be built because it requires rustc 1.70.0 or newer, while the currently active + rustc version is 1.64.0 +``` +Again, consulting [crates.io](https://crates.io/crates/clap_lex/versions) +```console +$ cargo update -p clap_lex --precise 0.5.0 + Updating crates.io index + Downgrading clap_lex v0.5.1 -> v0.5.0 +$ cargo +1.64.0 check +error: package `anstyle v1.0.4` cannot be built because it requires rustc 1.70.0 or newer, while the currently active +rustc version is 1.64.0 +``` +Again, consulting [crates.io](https://crates.io/crates/anstyle/versions) +```console +cargo update -p anstyle --precise 1.0.2 + Updating crates.io index + Downgrading anstyle v1.0.4 -> v1.0.2 +$ cargo +1.64.0 check + Downloaded anstyle v1.0.2 + Downloaded 1 crate (14.0 KB) in 0.60s + Compiling rustix v0.38.23 + Checking bitflags v2.4.1 + Checking linux-raw-sys v0.4.11 + Checking utf8parse v0.2.1 + Checking anstyle v1.0.2 + Checking colorchoice v1.0.0 + Checking anstyle-query v1.0.0 + Checking clap_lex v0.5.0 + Checking strsim v0.10.0 + Checking anstyle-parse v0.2.1 + Checking is-terminal v0.4.9 + Checking anstream v0.3.2 + Checking clap_builder v4.3.24 + Checking clap v4.3.24 + Checking msrv-resolver v0.1.0 (/home/epage/src/personal/dump/msrv-resolver) + Finished dev [unoptimized + debuginfo] target(s) in 2.96s +``` +Success! Mixed with many tears and less hair. -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation +How wide spread is this? Take this with a grain of salt but based on crates.io user agents: + +| Common MSRVs | % Compatible Requests | +|--------------------:|:----------------------| +| N (`1.73.0`) | 47.432% | +| N-2 (`1.71.0`) | 74.003% | +| ~6 mo (`1.69.0`) | 93.272% | +| ~1 year (`1.65.0`) | 98.766% | +| Debian (`1.63.0`) | 99.106% | +| ~2 years (`1.56.0`) | 99.949% | + +*([source](https://rust-lang.zulipchat.com/#narrow/stream/318791-t-crates-io/topic/cargo.20version.20usage/near/401440149))* -Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: +People have tried to reduce the pain from MSRV with its own costs: +- Treating it as a breaking change: + - This leads to extra churn in the ecosystem when a fraction of users are likely going to benefit + - We have the precedence elsewhere in the Rust ecosystem for build and runtime system requirement changes not being breaking, like when rustc requires new glibc, AndroiNDK, etc +- Adding upper limits to version requirements: + - This fractures the ecosystem by making packages incompatible with each other and the Cargo team [discourages doing this](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#multiple-requirements) -- Introducing new named concepts. -- Explaining the feature largely in terms of examples. -- Explaining how Rust programmers should *think* about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. -- If applicable, provide sample error messages, deprecation warnings, or migration guidance. -- If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. -- Discuss how this impacts the ability to read, understand, and maintain Rust code. Code is read and modified far more often than written; will the proposed feature make code easier to maintain? +Another way the status quo exhibits pain on the ecosystem is long +arguments over what is the right policy for updating a minimum-supported Rust +version (MSRV), wearing on all parties. +For example: +- [libc](https://github.com/rust-lang/libs-team/issues/72) +- [time](https://github.com/time-rs/time/discussions/535) -For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. +Supporting older MSRVs means maintainers don't have access to all of the latest +resources for improving their project. +This indirectly affects users as it can slow maintainers down. +This can also directly affect users. +For example, +by clap updating its MSRV from 1.64.0 to 1.70.0, +it was able to drop the large [is-terminal](https://crates.io/crates/is-terminal) dependency, +[cutting the build time from 6s to 3s](https://github.com/rosetta-rs/argparse-rosetta-rs/commit/378cd2c30679afdf9b9843dbadea3e8951090809). + +The sooner we improve the status quo, the better, as it can take years for +these changes to percolate out to those exclusively developing with an older +Rust version (in contrast with the example above). + +In solving this, we need to keep in mind +- Users need to be aware when they are on old versions for evaluating security risk and when debugging issues +- We don't want to end up like other ecosystems where no one can use new features + because users are stuck on 3-20 year old versions of the language specification. + The compatibility story is fairly strong with Rust, helping us keep + compiler and dependency upgrades cheap. +- We also want to continue to support people whose workflow is to develop with + latest dependencies in a `Cargo.lock` and then verify MSRV with a carefully + crafted `Cargo.lock`. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This is the technical portion of the RFC. Explain the design in sufficient detail that: +## Cargo Resolver + +Cargo's resolver will be updated to *prefer* MSRV compatible versions over +incompatible versions when resolving versions. +Packages without `package.rust-version` will be treated as compatible. +This can be overridden with `--ignore-rust-version`. + +Implications +- If you use do `cargo update --precise `, it will work +- If you use `--ignore-rust-version` once, you don't need to specify it again to keep those dependencies +- If a dependency doesn't specify `package.rust-version` but its transitive dependencies specify an incompatible `package.rust-version`, + we won't backtrack to older versions of the dependency to find one with a MSRV-compatible transitive dependency. + +As there is no `workspace.rust-version`, +the resolver will pick the lowest version among workspace members. +This will be less optimal for workspaces with multiple MSRVs and dependencies unique to the higher-MSRV packages. +Users can workaround this by raising the version requirement or using `cargo update --precise`. + +The resolver will only do this for local packages and not for `cargo install`. + +## `cargo update` + +`cargo update` will inform users when an MSRV or semver incompatible version is available. +`cargo update -n` will also report this information so that users can check on the status of this at any time. + +**Note:** other operations that cause `Cargo.lock` entries to be changed (like +editing `Cargo.toml` and running `cargo check`) will not inform the user. + +## `cargo add` -- Its interaction with other features is clear. -- It is reasonably clear how the feature would be implemented. -- Corner cases are dissected by example. +`cargo add ` (no version) will pick a version requirement that is low +enough so that when it resolves, it will pick a dependency that is +MSRV-compatible. +`cargo add` will warn when it does this. -The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. +This behavior can be bypassed with `--ignore-rust-version` # Drawbacks [drawbacks]: #drawbacks -Why should we *not* do this? +This might further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -- Why is this design the best in the space of possible designs? -- What other designs have been considered and what is the rationale for not choosing them? -- What is the impact of not doing this? -- If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? +## Fallback to users rustc version -# Prior art -[prior-art]: #prior-art +If no `package.rust-version` is specified, we can fallback to `rustc --version`. + +As the dependency resolution is just a preference, this shouldn't cause churn. + +We already query `rustc` for feature resolution, so this hopefully won't impact performance. + +## Sort order when `package.rust-version` is unspecified + +We could give versions without `package.rust-version` a lower priority, acting +as if they are always too new. + +## Hard-error + +Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. +This means that we would also backtrack on transitive dependencies, trying alternative versions of direct dependencies, which would create an MSRV-compatible `Cargo.lock` in more cases. + +Nothing in this solution changes our ability to do this later. -Discuss prior art, both the good and the bad, in relation to this proposal. -A few examples of what this can include are: +However, blocking progress on this approach would greatly delay stabilization of this because of bad error messages. +This was supported in 1.74 and 1.75 nightlies under `-Zmsrv-policy` and the biggest problem was in error reporting. +The resolver acted as if the MSRV-incompatible versions don't exist so if there was no solution, the error message was confusing: +```console +$ cargo +nightly update -Z msrv-policy + Updating crates.io index +error: failed to select a version for the requirement `hashbrown = "^0.14"` +candidate versions found which didn't match: 0.14.2, 0.14.1, 0.14.0, ... +location searched: crates.io index +required by package `app v0.1.0 (/app)` +perhaps a crate was updated and forgotten to be re-vendored? +``` -- For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? -- For community proposals: Is this done by some other community and what were their experiences with it? -- For other teams: What lessons can we learn from what other communities have done here? -- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. +It would also be a breaking change to hard-error. +We'd need to provide a way for some people to opt-in while some people opt-out and remember that. +We could add a sticky flag to `Cargo.lock` though that could also be confusing. -This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. -If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. +This would also error or pick lower versions more than it needs to when a workspace contains multiple MSRVs. +We'd want to extend the resolver to treat Rust as yet another dependency and turn `package.rust-version` into dependencies on Rust. +This could cause a lot more backtracking which could negatively affect resolver performance for people with lower MSRVs. + +If no `package.rust-version` is specified, we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. + +# Prior art +[prior-art]: #prior-art -Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. -Please also take into consideration that rust sometimes intentionally diverges from common language features. # Unresolved questions [unresolved-questions]: #unresolved-questions -- What parts of the design do you expect to resolve through the RFC process before this gets merged? -- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? -- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? # Future possibilities [future-possibilities]: #future-possibilities -Think about what the natural extension and evolution of your proposal would -be and how it would affect the language and project as a whole in a holistic -way. Try to use this section as a tool to more fully consider all possible -interactions with the project and language in your proposal. -Also consider how this all fits into the roadmap for the project -and of the relevant sub-team. +## Integrate `cargo audit` + +If we [integrate `cargo audit`](https://github.com/rust-lang/cargo/issues/7678), +we can better help users on older dependencies identify security vulnerabilities. + +## "cargo upgrade" + +As we pull [`cargo upgrade` into cargo](https://github.com/rust-lang/cargo/issues/12425), +we'll want to make it respect MSRV as well + +## cargo install -This is also a good place to "dump ideas", if they are out of scope for the -RFC you are writing but otherwise related. +`cargo install` could auto-select a top-level package that is compatible with the version of rustc that will be used to build it. -If you have tried and cannot think of any future possibilities, -you may simply state that you cannot think of anything. +See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. -Note that having something written down in the future-possibilities section -is not a reason to accept the current or a future RFC; such notes should be -in the section on motivation or rationale in this or subsequent RFCs. -The section merely provides additional information. +**Note:** [rust-lang/cago#12798](https://github.com/rust-lang/cargo/pull/12798) +(slated to be released in 1.75) made it so `cargo install` will error upfront, +suggesting a version of the package to use and to pass `--locked` assuming the +bundled `Cargo.lock` has MSRV compatible dependencies. From 2ace3a01dce501bbce2b30faaf094fa6768ae22b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 14 Nov 2023 20:44:40 -0600 Subject: [PATCH 003/184] fix: Add missing connector statement --- text/0000-msrv-resolver.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 07418408cd0..1c985f8a442 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -174,6 +174,9 @@ For example, by clap updating its MSRV from 1.64.0 to 1.70.0, it was able to drop the large [is-terminal](https://crates.io/crates/is-terminal) dependency, [cutting the build time from 6s to 3s](https://github.com/rosetta-rs/argparse-rosetta-rs/commit/378cd2c30679afdf9b9843dbadea3e8951090809). +So if we can find a solution that allows maintainers to move forward, helping +users more the edge, while not impacting users on older rust version, would be +a big help. The sooner we improve the status quo, the better, as it can take years for these changes to percolate out to those exclusively developing with an older From 42b6853684ac308608b565c5c37c95375530c4f9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 08:38:16 -0600 Subject: [PATCH 004/184] fix: Clarify the stagnation drawback --- text/0000-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 1c985f8a442..0296f19a065 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -238,7 +238,8 @@ This behavior can be bypassed with `--ignore-rust-version` # Drawbacks [drawbacks]: #drawbacks -This might further entrench rust-version stagnation in the ecosystem. +While we hope this will give maintainers more freedom to upgrade their MSRV, +this could instead further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 993f52fa928785c63c81e0e47d0315b7be5a9bfc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 08:48:18 -0600 Subject: [PATCH 005/184] feat: Acknowledge CI incompatibility change --- text/0000-msrv-resolver.md | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 0296f19a065..408129f86fc 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -235,15 +235,53 @@ MSRV-compatible. This behavior can be bypassed with `--ignore-rust-version` +## Cargo config + +We'll add a `build.rust-version = ` field to `.cargo/config.toml` that will control whether `package.rust-version` is respected or not. +`--ignore-rust-version` can override this. + +This will let users effectively pass `--ignore-rust-version` to all commands, +without having to support it on every single one. + +We can also stabilize this earlier than the rest of this so we can use it in our +[Verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) +documentation so people will be more likely to prepared for this change. + # Drawbacks [drawbacks]: #drawbacks +Maintainers that commit their `Cargo.lock` and verify their latest dependencies +will need to set `CARGO_BUILD_RUST_VERSION=false` in their environment. +See Alternatives for more on this. + While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives +## Make this opt-in + +As proposed, CI that tries to verify against the latest dependencies will no longer do so. +Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RUST_VERSION=false`. + +If we consider this a major incompatibility, then it needs to be opted into. +As `cargo fix` can't migrate a user's CI, +this would be out of scope for migrating to with a new Edition. + +I would argue that the number of maintainers verifying latest dependencies is +relatively low and they are more likely to be "in the know", +making them less likely to be negatively affected by this. +Therefore, I propose we consider this a minor incompatibility + +If we do a slow roll out (opt-in then opt-out), the visibility for the switch +to opt-out will be a lot less than the initial announcement and we're more +likely to miss people compared to making switch over when this gets released. + +If we change behavior with a new edition (assuming we treat this as a minor incompatibility), +we get the fanfare needed but it requires waiting until people bump their MSRV, +making it so the people who need it the most are the those who will least benefit. + ## Fallback to users rustc version If no `package.rust-version` is specified, we can fallback to `rustc --version`. @@ -318,3 +356,9 @@ See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for (slated to be released in 1.75) made it so `cargo install` will error upfront, suggesting a version of the package to use and to pass `--locked` assuming the bundled `Cargo.lock` has MSRV compatible dependencies. + +## `build.rust-version = "."` + +We could allow people setting an effective rust-version within the config. +This would be useful for people who have a reason to not set `package.rust-version` +as well as to reproduce behavior with different Rust versions. From 404ec9a8d11fbe33deef0a451b286a8ceb311ed6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:27:40 -0600 Subject: [PATCH 006/184] feat: Acknowledge desire to set msrv more --- text/0000-msrv-resolver.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 408129f86fc..eac2cf96f85 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -336,6 +336,35 @@ If no `package.rust-version` is specified, we wouldn't want to fallback to the v # Future possibilities [future-possibilities]: #future-possibilities +## Encourage `package.rust-version` to be set more frequently + +The user experience for this is based on the extent and quality of the data. +Ensuring we have `package.rust-version` populated more often (while maintaining +quality of that data) is an important problem but does not have to be solved to +get value out of this RFC and can be handled separately. + +We could encourage people to set their MSRV by having `cargo new` default `package.rust-version` +We don't want to default `package.rust-version` in `cargo new`. +If people aren't committed to verifying it, +it is likely to go stale and will claim an MSRV much older than what is used in practice. +If we had the hard-error resolver mode and +[clippy warning people when using API items stabilized after their MSRV](https://github.com/rust-lang/rust-clippy/issues/6324), +this will at least annoy people into either being somewhat compatible or removing the field. + +When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish. +This will err on the side of a higher MSRV than necessry and the only way to +workaround it is to set `CARGO_BUILD_RUST_VERSION=false` which will then lose +all other protections. + +When missing, `cargo publish` could inject based on the rustup toolchain file. +This will err on the side of a higher MSRV than necessary as well. + +When missing, `cargo publish` could inject `package.rust-version` inferred from +`package.edition` and/or other `Cargo.toml` fields. +This will err on the side of too low of an MSRV. +While this might help with in this situation, +it would lock us in to inaccurate information which might limit what analysis we could do in the future. + ## Integrate `cargo audit` If we [integrate `cargo audit`](https://github.com/rust-lang/cargo/issues/7678), From b757cb7a516e8e9d83d4978b9bacaa27148a623b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:42:03 -0600 Subject: [PATCH 007/184] feat: Cover workspace.rust-version --- text/0000-msrv-resolver.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index eac2cf96f85..25c86cc63ed 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -260,6 +260,20 @@ this could instead further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives +## Add `workspace.rust-version` + +Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. + +This opens its own set of questions +- Do packages implicitly inherit this? +- What are the semantics if its unset? +- Would it be confusing to have this be set in mixed-MSRV workspaces? Would blocking it be incompatible with the semantics when unset? +- In mixed-MSRV workspaces, does it need to be the highest or lowest MSRV of your packages? + - For the resolver, it would need to be the lowest but there might be other use cases where it needs to be the highest + +The proposed solution does not block us from later going down this road but +allows us to move forward without having to figure out all of these details. + ## Make this opt-in As proposed, CI that tries to verify against the latest dependencies will no longer do so. From 2da8375d2cc8257a7293052b31c40df1b300f28f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:45:01 -0600 Subject: [PATCH 008/184] fix: Expand on implicit MSRV --- text/0000-msrv-resolver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 25c86cc63ed..87ff0827a9d 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -379,6 +379,10 @@ This will err on the side of too low of an MSRV. While this might help with in this situation, it would lock us in to inaccurate information which might limit what analysis we could do in the future. +We could add new fields to the Index's package summary to track this +information so it can inform our decisions without losing the intent of the +publisher. + ## Integrate `cargo audit` If we [integrate `cargo audit`](https://github.com/rust-lang/cargo/issues/7678), From 72f7bef01c2c44a9fe8c460930e4ffbe2e74bb41 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:52:09 -0600 Subject: [PATCH 009/184] fix: Be more explicit about rustc fallback --- text/0000-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 87ff0827a9d..effde19169e 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -298,7 +298,7 @@ making it so the people who need it the most are the those who will least benefi ## Fallback to users rustc version -If no `package.rust-version` is specified, we can fallback to `rustc --version`. +If no `package.rust-version` is specified, we can fallback to `rustc --version` for resolving dependencies. As the dependency resolution is just a preference, this shouldn't cause churn. From 44c28b4e481c0e018ce38db039101216f4480860 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:53:07 -0600 Subject: [PATCH 010/184] fix: Add another design consideration --- text/0000-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index effde19169e..978ed532e5a 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -191,6 +191,7 @@ In solving this, we need to keep in mind - We also want to continue to support people whose workflow is to develop with latest dependencies in a `Cargo.lock` and then verify MSRV with a carefully crafted `Cargo.lock`. +- A `Cargo.lock` should not resolve differently on upgrade. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From f663bf237c11fe1a3cd67d2db78a8a6b50a698e5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 Nov 2023 10:57:41 -0600 Subject: [PATCH 011/184] feat: Add 'rustc --version' fallback to proposal --- text/0000-msrv-resolver.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 978ed532e5a..c0217b30464 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -203,7 +203,8 @@ In solving this, we need to keep in mind Cargo's resolver will be updated to *prefer* MSRV compatible versions over incompatible versions when resolving versions. -Packages without `package.rust-version` will be treated as compatible. +Dependencies without `package.rust-version` will be treated as compatible. + This can be overridden with `--ignore-rust-version`. Implications @@ -217,6 +218,13 @@ the resolver will pick the lowest version among workspace members. This will be less optimal for workspaces with multiple MSRVs and dependencies unique to the higher-MSRV packages. Users can workaround this by raising the version requirement or using `cargo update --precise`. +If `package.rust-version` is unset among all workspace members, +we'll fallback to `rustc --version`, +ensuring a build that at least works for the current system. +As this is just a preference for resolving dependencies, rather than prescriptive, +this shouldn't cause churn. +We already call `rustc` for feature resolution, so hopefully this won't have a performance impact. + The resolver will only do this for local packages and not for `cargo install`. ## `cargo update` @@ -297,14 +305,6 @@ If we change behavior with a new edition (assuming we treat this as a minor inco we get the fanfare needed but it requires waiting until people bump their MSRV, making it so the people who need it the most are the those who will least benefit. -## Fallback to users rustc version - -If no `package.rust-version` is specified, we can fallback to `rustc --version` for resolving dependencies. - -As the dependency resolution is just a preference, this shouldn't cause churn. - -We already query `rustc` for feature resolution, so this hopefully won't impact performance. - ## Sort order when `package.rust-version` is unspecified We could give versions without `package.rust-version` a lower priority, acting @@ -338,7 +338,8 @@ This would also error or pick lower versions more than it needs to when a worksp We'd want to extend the resolver to treat Rust as yet another dependency and turn `package.rust-version` into dependencies on Rust. This could cause a lot more backtracking which could negatively affect resolver performance for people with lower MSRVs. -If no `package.rust-version` is specified, we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. +If no `package.rust-version` is specified, +we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. # Prior art [prior-art]: #prior-art From 2fc50a26ea8f97f2654bb3fc70b81c29b045d865 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 17 Nov 2023 15:44:20 -0600 Subject: [PATCH 012/184] fix: Small language clarifications --- text/0000-msrv-resolver.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index c0217b30464..835f29d086b 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -191,7 +191,7 @@ In solving this, we need to keep in mind - We also want to continue to support people whose workflow is to develop with latest dependencies in a `Cargo.lock` and then verify MSRV with a carefully crafted `Cargo.lock`. -- A `Cargo.lock` should not resolve differently on upgrade. +- A `Cargo.lock` should not resolve differently when upgrading Rust. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -205,7 +205,7 @@ Cargo's resolver will be updated to *prefer* MSRV compatible versions over incompatible versions when resolving versions. Dependencies without `package.rust-version` will be treated as compatible. -This can be overridden with `--ignore-rust-version`. +This can be overridden with `--ignore-rust-version` and config's `build.rust-version`. Implications - If you use do `cargo update --precise `, it will work @@ -309,6 +309,7 @@ making it so the people who need it the most are the those who will least benefi We could give versions without `package.rust-version` a lower priority, acting as if they are always too new. +I suspect that no matter which direction we go on this, we'll negatively impact someone. ## Hard-error From 01feadcba35664c660f5d7e7a7b0e3e0411963ed Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 13:50:27 -0600 Subject: [PATCH 013/184] fix: Minor cleanup --- text/0000-msrv-resolver.md | 47 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 835f29d086b..284b8bff1ec 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -175,12 +175,14 @@ by clap updating its MSRV from 1.64.0 to 1.70.0, it was able to drop the large [is-terminal](https://crates.io/crates/is-terminal) dependency, [cutting the build time from 6s to 3s](https://github.com/rosetta-rs/argparse-rosetta-rs/commit/378cd2c30679afdf9b9843dbadea3e8951090809). So if we can find a solution that allows maintainers to move forward, helping -users more the edge, while not impacting users on older rust version, would be +users more on the edge, while not impacting users on older rust version, would be a big help. The sooner we improve the status quo, the better, as it can take years for these changes to percolate out to those exclusively developing with an older Rust version (in contrast with the example above). +This delay can be reduced somewhat if a newer development version can be used +without upgrading the MSRV. In solving this, we need to keep in mind - Users need to be aware when they are on old versions for evaluating security risk and when debugging issues @@ -188,10 +190,8 @@ In solving this, we need to keep in mind because users are stuck on 3-20 year old versions of the language specification. The compatibility story is fairly strong with Rust, helping us keep compiler and dependency upgrades cheap. -- We also want to continue to support people whose workflow is to develop with - latest dependencies in a `Cargo.lock` and then verify MSRV with a carefully - crafted `Cargo.lock`. -- A `Cargo.lock` should not resolve differently when upgrading Rust. +- Some people keep their development and production MSRVs the same while others keep them separate, like with a `Cargo.msrv.lock` +- A `Cargo.lock` should not resolve differently when upgrading Rust without any other action. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -234,6 +234,7 @@ The resolver will only do this for local packages and not for `cargo install`. **Note:** other operations that cause `Cargo.lock` entries to be changed (like editing `Cargo.toml` and running `cargo check`) will not inform the user. +If they want to check the status of things, they can run `cargo update -n`. ## `cargo add` @@ -345,6 +346,9 @@ we wouldn't want to fallback to the version of rustc being used because that cou # Prior art [prior-art]: #prior-art +- Python: instead of tying packages to a particular tooling version, the community instead focuses on their equivalent of the [`rustversion` crate](https://crates.io/crates/rustversion) combined with tool-version-conditional dependencies that allow polyfills. + - We have [cfg_accessible](https://github.com/rust-lang/rust/issues/64797) as a first step though it has been stalled + - These don't have to be mutually exclusive solutions as conditional compilation offers flexibility at the cost of maintenance. Different maintainers might make different decisions in how much they leverage each # Unresolved questions [unresolved-questions]: #unresolved-questions @@ -353,38 +357,43 @@ we wouldn't want to fallback to the version of rustc being used because that cou # Future possibilities [future-possibilities]: #future-possibilities -## Encourage `package.rust-version` to be set more frequently +## Improve the experience with lack of `rust-version` The user experience for this is based on the extent and quality of the data. Ensuring we have `package.rust-version` populated more often (while maintaining quality of that data) is an important problem but does not have to be solved to get value out of this RFC and can be handled separately. -We could encourage people to set their MSRV by having `cargo new` default `package.rust-version` -We don't want to default `package.rust-version` in `cargo new`. -If people aren't committed to verifying it, +~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ +However, if people aren't committed to verifying it, it is likely to go stale and will claim an MSRV much older than what is used in practice. If we had the hard-error resolver mode and [clippy warning people when using API items stabilized after their MSRV](https://github.com/rust-lang/rust-clippy/issues/6324), this will at least annoy people into either being somewhat compatible or removing the field. -When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish. -This will err on the side of a higher MSRV than necessry and the only way to +~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ +However, this will err on the side of a higher MSRV than necessry and the only way to workaround it is to set `CARGO_BUILD_RUST_VERSION=false` which will then lose all other protections. -When missing, `cargo publish` could inject based on the rustup toolchain file. -This will err on the side of a higher MSRV than necessary as well. +~~When missing, `cargo publish` could inject based on the rustup toolchain file.~~ +However, this will err on the side of a higher MSRV than necessary as well. -When missing, `cargo publish` could inject `package.rust-version` inferred from -`package.edition` and/or other `Cargo.toml` fields. -This will err on the side of too low of an MSRV. +~~When missing, `cargo publish` could inject `package.rust-version` inferred from +`package.edition` and/or other `Cargo.toml` fields.~~ +However, this will err on the side of too low of an MSRV. While this might help with in this situation, it would lock us in to inaccurate information which might limit what analysis we could do in the future. -We could add new fields to the Index's package summary to track this -information so it can inform our decisions without losing the intent of the -publisher. +Alternatively, `cargo publish` / the registry could add new fields to the Index +to represent an inferred MSRV, the published version, etc +so it can inform our decisions without losing the intent of the publisher. + +On the resolver side, we could +- Assume the MSRV of the next published package with an MSRV set +- Sort no-MSRV versions by minimal versions, the lower the version the more likely it is to be compatible + - This runs into quality issues with version requirements that are likely too low for what the package actually needs + - For dependencies that never set their MSRV, this effectively switches us from maximal versions to minimal versions. ## Integrate `cargo audit` From 4547449e0ca2ef550af6e6fb95b73fec1d6d9c85 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 13:51:05 -0600 Subject: [PATCH 014/184] feat: Add Guide --- text/0000-msrv-resolver.md | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 284b8bff1ec..e33d121772b 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -196,6 +196,63 @@ In solving this, we need to keep in mind # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +## The `rust-version` field + +*(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* + +The `rust-version` field is an optional key that tells cargo what version of the +Rust language and compiler your package can be compiled with. If the currently +selected version of the Rust compiler is older than the stated version, cargo +will exit with an error, telling the user what version is required. +To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. + +The first version of Cargo that supports this field was released with Rust 1.56.0. +In older releases, the field will be ignored, and Cargo will display a warning. + +```toml +[package] +# ... +rust-version = "1.56" +``` + +The Rust version must be a bare version number with two or three components; it +cannot include semver operators or pre-release identifiers. Compiler pre-release +identifiers such as -nightly will be ignored while checking the Rust version. +The `rust-version` must be equal to or newer than the version that first +introduced the configured `edition`. + +The `rust-version` may be ignored using the `--ignore-rust-version` option. + +Setting the `rust-version` key in `[package]` will affect all targets/crates in +the package, including test suites, benchmarks, binaries, examples, etc. + +## Rust Version + +*(update to [Dependency Resolution's Other Constraints documentation](https://doc.rust-lang.org/cargo/reference/resolver.html))* + +When multiple versions of a dependency satisfy all version requirements, +cargo will prefer those with a compatible `package.rust-version` over those that +aren't compatible. +Some details may change over time though `cargo check && rustup update && cargo check` should not cause `Cargo.lock` to change. + +#### `build.resolver.precedence` + +*(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* + +* Type: string +* Default: "rust-version=package" +* Environment: `CARGO_BUILD_RESOLVER_PRECEDENCE` + +Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. + +* `maximum`: Prefer the highest compatible versions of dependencies +* `minimum`: Prefer the lowest versions of dependencies +* `rust-version=package`: Prefer dependencies where their `rust-version` is compatible with `package.rust-version` +* `rust-version=rustc`: Prefer dependencies where their `rust-version` is compatible with `rustc --version` +* `rust-version=[.[.]]`: Prefer dependencies where their `rust-version` is compatible with the specified version + +`rust-version=` values can be overridden with `--ignore-rust-version` which will fallback to `maximum`. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 6a704c922632435d76dc61345457e6792660f76c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:07:18 -0600 Subject: [PATCH 015/184] feat: Update proposal for new config --- text/0000-msrv-resolver.md | 68 ++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index e33d121772b..7e972e958d3 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -260,13 +260,16 @@ Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `carg Cargo's resolver will be updated to *prefer* MSRV compatible versions over incompatible versions when resolving versions. -Dependencies without `package.rust-version` will be treated as compatible. +Dependencies without `package.rust-version` will be preferred over those without an MSRV but less than those with one. +The exact details for how preferences are determined may change over time but, +since the currently resolved dependencies always get preference, +this shouldn't affect existing `Cargo.lock` files. -This can be overridden with `--ignore-rust-version` and config's `build.rust-version`. +This can be overridden with `--ignore-rust-version` and config's `build.resolver.precedence`. Implications - If you use do `cargo update --precise `, it will work -- If you use `--ignore-rust-version` once, you don't need to specify it again to keep those dependencies +- If you use `--ignore-rust-version` once, you don't need to specify it again to keep those dependencies though you might need it again on the next edit of `Cargo.toml` or `cargo update` run - If a dependency doesn't specify `package.rust-version` but its transitive dependencies specify an incompatible `package.rust-version`, we won't backtrack to older versions of the dependency to find one with a MSRV-compatible transitive dependency. @@ -304,21 +307,32 @@ This behavior can be bypassed with `--ignore-rust-version` ## Cargo config -We'll add a `build.rust-version = ` field to `.cargo/config.toml` that will control whether `package.rust-version` is respected or not. -`--ignore-rust-version` can override this. +We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` that will control the control pick the mechanisms for preferring one compatible version over another. +```toml +[build] +resolver.precedence = "rust-version=package" # Default +``` +with support values being: +- `maximum`: behavior today + - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) +- `minimum` (unstable): `-Zminimal-versions` + - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this +- `rust-version=` (assumes `maximum` is the fallback) + - `package`: what is defined in the package (default) + - `rustc`: the current running version + - Needed for "separate development / publish MSRV" workflow + - `[.[.]]` (future possibility): manually override the version used + +If a `rust-version=` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. This will let users effectively pass `--ignore-rust-version` to all commands, -without having to support it on every single one. - -We can also stabilize this earlier than the rest of this so we can use it in our -[Verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) -documentation so people will be more likely to prepared for this change. +without having to support the flag on every single command. # Drawbacks [drawbacks]: #drawbacks Maintainers that commit their `Cargo.lock` and verify their latest dependencies -will need to set `CARGO_BUILD_RUST_VERSION=false` in their environment. +will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version=rustc` in their environment. See Alternatives for more on this. While we hope this will give maintainers more freedom to upgrade their MSRV, @@ -327,6 +341,10 @@ this could instead further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives +Misc +- Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future +- Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. + ## Add `workspace.rust-version` Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. @@ -344,11 +362,11 @@ allows us to move forward without having to figure out all of these details. ## Make this opt-in As proposed, CI that tries to verify against the latest dependencies will no longer do so. -Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RUST_VERSION=false`. +Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. If we consider this a major incompatibility, then it needs to be opted into. As `cargo fix` can't migrate a user's CI, -this would be out of scope for migrating to with a new Edition. +this would be out of scope for migrating to this with a new Edition. I would argue that the number of maintainers verifying latest dependencies is relatively low and they are more likely to be "in the know", @@ -363,11 +381,19 @@ If we change behavior with a new edition (assuming we treat this as a minor inco we get the fanfare needed but it requires waiting until people bump their MSRV, making it so the people who need it the most are the those who will least benefit. -## Sort order when `package.rust-version` is unspecified +## Make `rust-version=rustc` the default + +This proposal elevates "shared development / publish rust-version" workflow over "separate development and publish rust-version" workflow. +We could instead do the opposite, picking `rust-version=rustc` as a "safe" default for assuming the development rust-version. +Users of the "shared development / publish rust-version" workflow could either set the config or use a `rust-toolchain.toml` file. -We could give versions without `package.rust-version` a lower priority, acting -as if they are always too new. -I suspect that no matter which direction we go on this, we'll negatively impact someone. +The reasons we didnn't go with this approach are +- The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. +- Split MSRV workflows are inherently more complex to support with more caveats of where they apply, making single MSRV workflows the path of least resistance for users. +- Without configuration, defaulting to single MSRV workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the split MSRV workflows. +- Single MSRV workflows catch too-new API problems sooner +- We want to encourage developing on the latest version of rustc/cargo to get all of the latest workflow improvements (e.g. error messages, sparse registry for cargo, etc), rather than lock people into the MSRV with `rust-toolchain.toml` + - The toolchain is another type of dependency so this might seem contradictory but we feel the value-add of a new toolchain outweighs the cost while the value add of new dependencies doesn't ## Hard-error @@ -410,6 +436,10 @@ we wouldn't want to fallback to the version of rustc being used because that cou # Unresolved questions [unresolved-questions]: #unresolved-questions +The config field is fairly rought + - The location (within `build`) needs more consideration + - The name isn't very clear + - The values are awkward # Future possibilities [future-possibilities]: #future-possibilities @@ -430,7 +460,7 @@ this will at least annoy people into either being somewhat compatible or removin ~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ However, this will err on the side of a higher MSRV than necessry and the only way to -workaround it is to set `CARGO_BUILD_RUST_VERSION=false` which will then lose +workaround it is to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. ~~When missing, `cargo publish` could inject based on the rustup toolchain file.~~ @@ -465,6 +495,8 @@ we'll want to make it respect MSRV as well ## cargo install `cargo install` could auto-select a top-level package that is compatible with the version of rustc that will be used to build it. +This could be controlled through a config field `install.resolver.precedence`, +mirroring `build.resolver.precedence`. See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. From f534b341c67f4c8e9e43b85d713395149bec6c8a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:07:50 -0600 Subject: [PATCH 016/184] fix: Typo --- text/0000-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 7e972e958d3..b21d597b571 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -459,7 +459,7 @@ If we had the hard-error resolver mode and this will at least annoy people into either being somewhat compatible or removing the field. ~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ -However, this will err on the side of a higher MSRV than necessry and the only way to +However, this will err on the side of a higher MSRV than necessary and the only way to workaround it is to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. From e794db98430a4b1df89ca5cfcc97c28fd6b36865 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:34:49 -0600 Subject: [PATCH 017/184] fix: Cover why we use config --- text/0000-msrv-resolver.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index b21d597b571..785a56702ed 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -395,6 +395,18 @@ The reasons we didnn't go with this approach are - We want to encourage developing on the latest version of rustc/cargo to get all of the latest workflow improvements (e.g. error messages, sparse registry for cargo, etc), rather than lock people into the MSRV with `rust-toolchain.toml` - The toolchain is another type of dependency so this might seem contradictory but we feel the value-add of a new toolchain outweighs the cost while the value add of new dependencies doesn't +## Configuring the resolver mode on the command-line or `Cargo.toml` + +The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). +However, there is a lot more to define for us to get there. Some routes that need further exploration include: +- If its a CLI flag, then its transient, and its unclear which modes should be transient now and in the future + - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change +- We could put this in `Cargo.toml` but that implies it unconditionally applies to everything + - But we want `cargo install` to use the latest dependencies so people get bug/security fixes + - This gets in the way of the split MSRV workflow + +By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. + ## Hard-error Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. @@ -417,7 +429,7 @@ perhaps a crate was updated and forgotten to be re-vendored? It would also be a breaking change to hard-error. We'd need to provide a way for some people to opt-in while some people opt-out and remember that. -We could add a sticky flag to `Cargo.lock` though that could also be confusing. +We could add a sticky flag to `Cargo.lock` though that could also be confusing, see "Configuring the resolver mode on the command-line or `Cargo.toml`". This would also error or pick lower versions more than it needs to when a workspace contains multiple MSRVs. We'd want to extend the resolver to treat Rust as yet another dependency and turn `package.rust-version` into dependencies on Rust. From 9a2b85ceacce9c063be805ba7dae0d024347e74f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:49:41 -0600 Subject: [PATCH 018/184] fix: Make explicit that feature-specific MSRVs can still work --- text/0000-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 785a56702ed..050bfe0a0f6 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -272,6 +272,7 @@ Implications - If you use `--ignore-rust-version` once, you don't need to specify it again to keep those dependencies though you might need it again on the next edit of `Cargo.toml` or `cargo update` run - If a dependency doesn't specify `package.rust-version` but its transitive dependencies specify an incompatible `package.rust-version`, we won't backtrack to older versions of the dependency to find one with a MSRV-compatible transitive dependency. +- A package with multiple MSRVs, depending on the features selected, can still do this as version requirements can still require versions newer than the MSRV and `Cargo.lock` can depend on those as well. As there is no `workspace.rust-version`, the resolver will pick the lowest version among workspace members. From 5a10cd67473c3b1704e3a8357209b550fcb11b64 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:49:59 -0600 Subject: [PATCH 019/184] fix: Mention the backtracking trade off --- text/0000-msrv-resolver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 050bfe0a0f6..92d83ad28bc 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -345,6 +345,10 @@ this could instead further entrench rust-version stagnation in the ecosystem. Misc - Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. +- The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. +- `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version=*` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. + - `cargo add` selecting rust-version-compatible minimum bounds helps + - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. ## Add `workspace.rust-version` From 260ef18df99410d541a0613cc7e3d3603546a14d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:50:09 -0600 Subject: [PATCH 020/184] fix: Mention rustup MSRV support --- text/0000-msrv-resolver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 92d83ad28bc..21528c13efe 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -527,3 +527,7 @@ bundled `Cargo.lock` has MSRV compatible dependencies. We could allow people setting an effective rust-version within the config. This would be useful for people who have a reason to not set `package.rust-version` as well as to reproduce behavior with different Rust versions. + +## rustup supporting `+msrv` + +See https://github.com/rust-lang/rustup/issues/1484#issuecomment-1494058857 From ac35d970f172edb73ede450c570b81c0582e85fa Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 28 Nov 2023 16:54:57 -0600 Subject: [PATCH 021/184] fix: Cover cargo-unrelated MSRV improvements --- text/0000-msrv-resolver.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 21528c13efe..e028289230f 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -531,3 +531,18 @@ as well as to reproduce behavior with different Rust versions. ## rustup supporting `+msrv` See https://github.com/rust-lang/rustup/issues/1484#issuecomment-1494058857 + +## Language-version lints + +We could make developing with the latest toolchain with old MSRVs easier if we provided lints. +Due to accuracy of information, this might start as a clippy lint, see +[#6324](https://github.com/rust-lang/rust-clippy/issues/6324). +This doesn't have to be perfect (covering all facets of the lanuage) to be useful in helping developers identify their change is MSRV incompatible as early as possible. + +If we allowed this to bypass caplints, +then you could more easily track when a dependency with an unspecified MSRV is incompatible. + +## Language-version awareness for rust-analyzer + +rust-analyzer could mark auto-complete options as being incompatible with the MSRV and +automatically bump the MSRV if selected, much like auto-adding a `use` statement. From ac9aef6dffc1f03034049b9485dd6de6fb50b042 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 09:31:25 -0600 Subject: [PATCH 022/184] fix: Mention Python's support window --- text/0000-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index e028289230f..5a6b7caf62a 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -449,6 +449,7 @@ we wouldn't want to fallback to the version of rustc being used because that cou - Python: instead of tying packages to a particular tooling version, the community instead focuses on their equivalent of the [`rustversion` crate](https://crates.io/crates/rustversion) combined with tool-version-conditional dependencies that allow polyfills. - We have [cfg_accessible](https://github.com/rust-lang/rust/issues/64797) as a first step though it has been stalled - These don't have to be mutually exclusive solutions as conditional compilation offers flexibility at the cost of maintenance. Different maintainers might make different decisions in how much they leverage each + - One big difference is Python continues to support previous releases which sets a standard within the community for "MSRV" policies. # Unresolved questions [unresolved-questions]: #unresolved-questions From 6d85ec97850a410c7c9d51767e506e3fc033f446 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 13:13:58 -0600 Subject: [PATCH 023/184] fix: Add PreRFC link --- text/0000-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-msrv-resolver.md b/text/0000-msrv-resolver.md index 5a6b7caf62a..8c808f568c4 100644 --- a/text/0000-msrv-resolver.md +++ b/text/0000-msrv-resolver.md @@ -1,5 +1,6 @@ - Feature Name: `msrv-resolver` - Start Date: 2023-11-14 +- Pre-RFC: [internals](https://internals.rust-lang.org/t/pre-rfc-msrv-aware-resolver/19871) - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) From 66c04c05827117cb7c6b758a283b181905a955f6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 13:14:56 -0600 Subject: [PATCH 024/184] fix: Add RFC number --- text/{0000-msrv-resolver.md => 3537-msrv-resolver.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{0000-msrv-resolver.md => 3537-msrv-resolver.md} (99%) diff --git a/text/0000-msrv-resolver.md b/text/3537-msrv-resolver.md similarity index 99% rename from text/0000-msrv-resolver.md rename to text/3537-msrv-resolver.md index 8c808f568c4..04f2a012b75 100644 --- a/text/0000-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1,7 +1,7 @@ - Feature Name: `msrv-resolver` - Start Date: 2023-11-14 - Pre-RFC: [internals](https://internals.rust-lang.org/t/pre-rfc-msrv-aware-resolver/19871) -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3537](https://github.com/rust-lang/rfcs/pull/3537) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary From 736060aa1447117f9610bacadb1339919f714e83 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 13:32:41 -0600 Subject: [PATCH 025/184] feat: Acknowledge a role in MSRV policy setting --- text/3537-msrv-resolver.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 04f2a012b75..ba3fc07afc5 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -548,3 +548,18 @@ then you could more easily track when a dependency with an unspecified MSRV is i rust-analyzer could mark auto-complete options as being incompatible with the MSRV and automatically bump the MSRV if selected, much like auto-adding a `use` statement. + +## Establish a policy on MSRV + +For us to say "your MSRV should be X" would likely be both premature and would have a lot of caveats for different use cases. + +With [rust-lang/cargo#13056](https://github.com/rust-lang/cargo/pull/13056), +we at least made it explicit that people should verify their MSRV. + +Ideally, we'd at least facilitate people in setting their MSRV. Some data that could help includes: +- A report of rust-versions used making requests to crates.io as determined by the user-agent +- A report of `package.rust-version` for the latest versions of packages on crates.io +- A report of `package.rust-version` for the recently downloaded versons of packages on crates.io + +Once people have more data to help them in picking an MSRV policy, +it would help to also document trade-offs on whether an MSRV policy should proactive or reactive on when to bump it. From 6de5f4d49dae09e0cd9518add5b64d549c49a6d2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:20:41 -0600 Subject: [PATCH 026/184] fix: Clarify wording Co-authored-by: Jacob Lifshay --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index ba3fc07afc5..8196471c8b4 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -156,7 +156,7 @@ How wide spread is this? Take this with a grain of salt but based on crates.io People have tried to reduce the pain from MSRV with its own costs: - Treating it as a breaking change: - This leads to extra churn in the ecosystem when a fraction of users are likely going to benefit - - We have the precedence elsewhere in the Rust ecosystem for build and runtime system requirement changes not being breaking, like when rustc requires new glibc, AndroiNDK, etc + - We have the precedence elsewhere in the Rust ecosystem for build and runtime system requirement changes not being breaking, like when rustc requires newer versions of glibc, Android NDK, etc. - Adding upper limits to version requirements: - This fractures the ecosystem by making packages incompatible with each other and the Cargo team [discourages doing this](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#multiple-requirements) From 76d2f98b9d271c193dd0cfcefdc141980d2e1a91 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:50:00 -0600 Subject: [PATCH 027/184] fix: Update future possibility for config change --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8196471c8b4..dff3b78598e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -524,7 +524,7 @@ See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for suggesting a version of the package to use and to pass `--locked` assuming the bundled `Cargo.lock` has MSRV compatible dependencies. -## `build.rust-version = "."` +## `build.resolver.precedence = "rust-version=[.[.]]"` We could allow people setting an effective rust-version within the config. This would be useful for people who have a reason to not set `package.rust-version` From c668698172f8fe0f6b2f55ec1a9cc791f61fbee1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:54:21 -0600 Subject: [PATCH 028/184] fix: Simplify config --- text/3537-msrv-resolver.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index dff3b78598e..c75c97f509f 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -241,18 +241,15 @@ Some details may change over time though `cargo check && rustup update && cargo *(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* * Type: string -* Default: "rust-version=package" +* Default: "rust-version" * Environment: `CARGO_BUILD_RESOLVER_PRECEDENCE` Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. * `maximum`: Prefer the highest compatible versions of dependencies -* `minimum`: Prefer the lowest versions of dependencies -* `rust-version=package`: Prefer dependencies where their `rust-version` is compatible with `package.rust-version` -* `rust-version=rustc`: Prefer dependencies where their `rust-version` is compatible with `rustc --version` -* `rust-version=[.[.]]`: Prefer dependencies where their `rust-version` is compatible with the specified version +* `rust-version`: Prefer dependencies where their `rust-version` is compatible with `package.rust-version` -`rust-version=` values can be overridden with `--ignore-rust-version` which will fallback to `maximum`. +`rust-version` can be overridden with `--ignore-rust-version` which will fallback to `maximum`. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -313,16 +310,17 @@ We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` that will ```toml [build] -resolver.precedence = "rust-version=package" # Default +resolver.precedence = "rust-version" # Default ``` with support values being: - `maximum`: behavior today - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) - `minimum` (unstable): `-Zminimal-versions` - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this +- `rust-version`: what is defined in the package (default) - `rust-version=` (assumes `maximum` is the fallback) - - `package`: what is defined in the package (default) - - `rustc`: the current running version + - `package`: long from of `rust-version` + - `rustc` (future possibility): the current running version - Needed for "separate development / publish MSRV" workflow - `[.[.]]` (future possibility): manually override the version used @@ -517,6 +515,11 @@ we'll want to make it respect MSRV as well This could be controlled through a config field `install.resolver.precedence`, mirroring `build.resolver.precedence`. +A smaller step towards this is we could stabilize `install.resolver.precedence += "rust-version=rustc"` without changing the default away from `maximum`, +allowing people to intentionally opt-in to rust-version compatible +installation. + See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. **Note:** [rust-lang/cago#12798](https://github.com/rust-lang/cargo/pull/12798) From 3015bd31b1e264a91db11000a2f65854bd25afca Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:54:28 -0600 Subject: [PATCH 029/184] fix: Typos --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c75c97f509f..c71c345a891 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -542,7 +542,7 @@ See https://github.com/rust-lang/rustup/issues/1484#issuecomment-1494058857 We could make developing with the latest toolchain with old MSRVs easier if we provided lints. Due to accuracy of information, this might start as a clippy lint, see [#6324](https://github.com/rust-lang/rust-clippy/issues/6324). -This doesn't have to be perfect (covering all facets of the lanuage) to be useful in helping developers identify their change is MSRV incompatible as early as possible. +This doesn't have to be perfect (covering all facets of the language) to be useful in helping developers identify their change is MSRV incompatible as early as possible. If we allowed this to bypass caplints, then you could more easily track when a dependency with an unspecified MSRV is incompatible. @@ -562,7 +562,7 @@ we at least made it explicit that people should verify their MSRV. Ideally, we'd at least facilitate people in setting their MSRV. Some data that could help includes: - A report of rust-versions used making requests to crates.io as determined by the user-agent - A report of `package.rust-version` for the latest versions of packages on crates.io -- A report of `package.rust-version` for the recently downloaded versons of packages on crates.io +- A report of `package.rust-version` for the recently downloaded versions of packages on crates.io Once people have more data to help them in picking an MSRV policy, it would help to also document trade-offs on whether an MSRV policy should proactive or reactive on when to bump it. From 2db39c0ba66782c02e7ba20fc1f7b688b7194b19 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 16:10:15 -0600 Subject: [PATCH 030/184] fix: Typo Co-authored-by: Josh Triplett --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c71c345a891..550ed558bac 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -319,7 +319,7 @@ with support values being: - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this - `rust-version`: what is defined in the package (default) - `rust-version=` (assumes `maximum` is the fallback) - - `package`: long from of `rust-version` + - `package`: long form of `rust-version` - `rustc` (future possibility): the current running version - Needed for "separate development / publish MSRV" workflow - `[.[.]]` (future possibility): manually override the version used From 5c55bd17d7529eace17cbdf55c1996b24dc23956 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 16:13:24 -0600 Subject: [PATCH 031/184] fix: Separate top-level selection from deps clearer --- text/3537-msrv-resolver.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 550ed558bac..789dc7f7e97 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -512,13 +512,15 @@ we'll want to make it respect MSRV as well ## cargo install `cargo install` could auto-select a top-level package that is compatible with the version of rustc that will be used to build it. -This could be controlled through a config field `install.resolver.precedence`, -mirroring `build.resolver.precedence`. -A smaller step towards this is we could stabilize `install.resolver.precedence -= "rust-version=rustc"` without changing the default away from `maximum`, -allowing people to intentionally opt-in to rust-version compatible -installation. +This could be controlled through a config field and +a smaller step towards this is we could stabilize the field +without changing the default away from `maximum`, +allowing people to intentionally opt-in to auto-selecting a compatible top-level paclage. + +Dependency resolution could be controlled through a config field `install.resolver.precedence`, +mirroring `build.resolver.precedence`. +The value add of this compared to `--locked` is unclear. See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. From 4fc3608c9b2c632efa9a51fc455340313a96cd4a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 16:27:16 -0600 Subject: [PATCH 032/184] fix: Extend future possibility with rust-version-policy field Co-authored-by: Josh Triplett --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 789dc7f7e97..9aabb30f4f1 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -493,6 +493,8 @@ Alternatively, `cargo publish` / the registry could add new fields to the Index to represent an inferred MSRV, the published version, etc so it can inform our decisions without losing the intent of the publisher. +We could help people keep their MSRV up to date, by letting them specify a policy (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. + On the resolver side, we could - Assume the MSRV of the next published package with an MSRV set - Sort no-MSRV versions by minimal versions, the lower the version the more likely it is to be compatible From 0228ae712d2226173905e3c45acced0455c368fd Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:39:43 -0600 Subject: [PATCH 033/184] fix: Typo Co-authored-by: Tobias Bieniek --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9aabb30f4f1..386f8b4c451 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -391,7 +391,7 @@ This proposal elevates "shared development / publish rust-version" workflow over We could instead do the opposite, picking `rust-version=rustc` as a "safe" default for assuming the development rust-version. Users of the "shared development / publish rust-version" workflow could either set the config or use a `rust-toolchain.toml` file. -The reasons we didnn't go with this approach are +The reasons we didn't go with this approach are - The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. - Split MSRV workflows are inherently more complex to support with more caveats of where they apply, making single MSRV workflows the path of least resistance for users. - Without configuration, defaulting to single MSRV workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the split MSRV workflows. From d418634835e433b09f6d7a85bed8ef682a74b7ac Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:39:59 -0600 Subject: [PATCH 034/184] fix: Typo Co-authored-by: Tobias Bieniek --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 386f8b4c451..24ae2a42b98 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -453,7 +453,7 @@ we wouldn't want to fallback to the version of rustc being used because that cou # Unresolved questions [unresolved-questions]: #unresolved-questions -The config field is fairly rought +The config field is fairly rough - The location (within `build`) needs more consideration - The name isn't very clear - The values are awkward From 8eaf4dd060e1d231ac90d232643587d01ac6633a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:47:53 -0600 Subject: [PATCH 035/184] fix: Actually make sense when discussing precedence without MSRV set --- text/3537-msrv-resolver.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 24ae2a42b98..bb87b88e49a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -258,10 +258,12 @@ Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `carg Cargo's resolver will be updated to *prefer* MSRV compatible versions over incompatible versions when resolving versions. -Dependencies without `package.rust-version` will be preferred over those without an MSRV but less than those with one. -The exact details for how preferences are determined may change over time but, -since the currently resolved dependencies always get preference, -this shouldn't affect existing `Cargo.lock` files. +Initially, dependencies without `package.rust-version` will be preferred over +MSRV-incompatible packages but less than those that are compatible. +The exact details for how preferences are determined may change over time, +particularly when no MSRV is specified, +but this shouldn't affect existing `Cargo.lock` files since the currently +resolved dependencies always get preference. This can be overridden with `--ignore-rust-version` and config's `build.resolver.precedence`. From e54af30ceab5df69cc9ea2c9cf1941d38b0b90f8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:49:19 -0600 Subject: [PATCH 036/184] fix: Call out rust-toolchain.toml --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index bb87b88e49a..83884b1c8b8 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -280,7 +280,7 @@ This will be less optimal for workspaces with multiple MSRVs and dependencies un Users can workaround this by raising the version requirement or using `cargo update --precise`. If `package.rust-version` is unset among all workspace members, -we'll fallback to `rustc --version`, +we'll fallback to `rustc --version` (implicitly picking up `rust-toolchain.toml` files), ensuring a build that at least works for the current system. As this is just a preference for resolving dependencies, rather than prescriptive, this shouldn't cause churn. From 5b6b3069fc1f9499a596934c6406e2b01fbfae14 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:51:53 -0600 Subject: [PATCH 037/184] fix: Update stale references for proposed config --- text/3537-msrv-resolver.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 83884b1c8b8..6f995776cb8 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -326,7 +326,7 @@ with support values being: - Needed for "separate development / publish MSRV" workflow - `[.[.]]` (future possibility): manually override the version used -If a `rust-version=` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. +If a `rust-version` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. This will let users effectively pass `--ignore-rust-version` to all commands, without having to support the flag on every single command. @@ -334,7 +334,7 @@ without having to support the flag on every single command. [drawbacks]: #drawbacks Maintainers that commit their `Cargo.lock` and verify their latest dependencies -will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version=rustc` in their environment. +will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` in their environment. See Alternatives for more on this. While we hope this will give maintainers more freedom to upgrade their MSRV, @@ -347,7 +347,7 @@ Misc - Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. -- `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version=*` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. +- `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. - `cargo add` selecting rust-version-compatible minimum bounds helps - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. From 413ee13ff3d9487fba4cf0057b4bfb29b64686a2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 07:52:42 -0600 Subject: [PATCH 038/184] fix: Call out use of 'cargo update' for CI --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 6f995776cb8..27e903ba8b0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -334,7 +334,7 @@ without having to support the flag on every single command. [drawbacks]: #drawbacks Maintainers that commit their `Cargo.lock` and verify their latest dependencies -will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` in their environment. +will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` in their environment or run `cargo update --ignore-rust-version`. See Alternatives for more on this. While we hope this will give maintainers more freedom to upgrade their MSRV, From 974f23366e68c7b452c01c42412f19376f2c9798 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 08:52:59 -0600 Subject: [PATCH 039/184] fix: Be consistent when naming MSRV workflows --- text/3537-msrv-resolver.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 27e903ba8b0..cb201984969 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -389,15 +389,15 @@ making it so the people who need it the most are the those who will least benefi ## Make `rust-version=rustc` the default -This proposal elevates "shared development / publish rust-version" workflow over "separate development and publish rust-version" workflow. +This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. We could instead do the opposite, picking `rust-version=rustc` as a "safe" default for assuming the development rust-version. Users of the "shared development / publish rust-version" workflow could either set the config or use a `rust-toolchain.toml` file. The reasons we didn't go with this approach are - The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. -- Split MSRV workflows are inherently more complex to support with more caveats of where they apply, making single MSRV workflows the path of least resistance for users. -- Without configuration, defaulting to single MSRV workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the split MSRV workflows. -- Single MSRV workflows catch too-new API problems sooner +- "Separate development / publish MSRV" workflows are inherently more complex to support with more caveats of where they apply, making "shared development / publish MSRV" workflows the path of least resistance for users. +- Without configuration, defaulting to "shared development / publish MSRV" workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the "separate development / publish MSRV" workflows. +- "Shared development / publish MSRV" workflows catch too-new API problems sooner - We want to encourage developing on the latest version of rustc/cargo to get all of the latest workflow improvements (e.g. error messages, sparse registry for cargo, etc), rather than lock people into the MSRV with `rust-toolchain.toml` - The toolchain is another type of dependency so this might seem contradictory but we feel the value-add of a new toolchain outweighs the cost while the value add of new dependencies doesn't @@ -409,7 +409,7 @@ However, there is a lot more to define for us to get there. Some routes that ne - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change - We could put this in `Cargo.toml` but that implies it unconditionally applies to everything - But we want `cargo install` to use the latest dependencies so people get bug/security fixes - - This gets in the way of the split MSRV workflow + - This gets in the way of the "separate development / publish MSRV" workflow By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. From b5d0cef10fd15d58c9888410a52952b0ca1c111c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 08:56:44 -0600 Subject: [PATCH 040/184] fix: Clarify and define workflow terms --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cb201984969..23bab3417d7 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -191,7 +191,8 @@ In solving this, we need to keep in mind because users are stuck on 3-20 year old versions of the language specification. The compatibility story is fairly strong with Rust, helping us keep compiler and dependency upgrades cheap. -- Some people keep their development and production MSRVs the same while others keep them separate, like with a `Cargo.msrv.lock` +- Some people keep their development and production MSRVs the same (henceforth referred to as "shared development / publish MSRV" workflow) + while others keep them separate, e.g. tracking two sets of dependencies via a `Cargo.msrv.lock` (henceforth referred to as "separate development / publish MSRV" workflow) - A `Cargo.lock` should not resolve differently when upgrading Rust without any other action. # Guide-level explanation From 4deef7fabd4bd00b38be7e9c0f1eccbc51e7aabd Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 09:14:44 -0600 Subject: [PATCH 041/184] fix: Use existing issue for tracking issue --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 23bab3417d7..b34486e5b69 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -2,7 +2,7 @@ - Start Date: 2023-11-14 - Pre-RFC: [internals](https://internals.rust-lang.org/t/pre-rfc-msrv-aware-resolver/19871) - RFC PR: [rust-lang/rfcs#3537](https://github.com/rust-lang/rfcs/pull/3537) -- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) +- Rust Issue: [rust-lang/cargo#9930](https://github.com/rust-lang/cargo/issues/9930) # Summary [summary]: #summary From 065c97f10b07955ea6009bf574013752c9205d76 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 5 Dec 2023 13:38:28 -0600 Subject: [PATCH 042/184] fix: Talk through more extensive reporting --- text/3537-msrv-resolver.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b34486e5b69..b54db963053 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -366,6 +366,23 @@ This opens its own set of questions The proposed solution does not block us from later going down this road but allows us to move forward without having to figure out all of these details. +## Reporting + +Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), +we can run the resolver twice on the original input, once for MSRV and once without. +We then do a depth-first diff of the trees, stopping and reporting on the first different node. +This would let us report on any command that changes the way the tree is resolved. +We'd likely want to limit the output to only the sub-tree that changed. + +We could either always do the second resolve or only do the second resolve if the resolver changed anything, +whichever is faster. + +Its unknown whether making the inputs available for multiple resolves would have a performance impact. + +While a no-change resolve is fast, if this negatively impacts it enough, we +could explore hashing the resolve inputs and storing that in the lockfile, +allowing us to detect if the inputs have changed and only resolving then. + ## Make this opt-in As proposed, CI that tries to verify against the latest dependencies will no longer do so. From 66ee8bf431e5d6fa8a003b3619973ae1b2e6f853 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 09:17:23 -0600 Subject: [PATCH 043/184] feat: Provide examples for why people are on old versions --- text/3537-msrv-resolver.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b54db963053..dddafb8c69a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -153,6 +153,24 @@ How wide spread is this? Take this with a grain of salt but based on crates.io *([source](https://rust-lang.zulipchat.com/#narrow/stream/318791-t-crates-io/topic/cargo.20version.20usage/near/401440149))* +So why are people on old versions? +- Not everyone is focused on Rust development and might only touch their Rust code once every couple of months, + making it a pain if they have to update every time +- While a distribution provides rust to build other packages in the distribution, + users might assume that is a version to use. +- Re-validation costs for updating core parts of the image for an embedded Linux developers can be high, keeping them on older versions +- Updates can be slow within tightly controlled environments (airgaps, paperwork, etc) +- Qualifying Rust toolchains takes time and money, see [Ferrocene](https://ferrous-systems.com/ferrocene/) + +We need to keep in mind though that the Rust Project only provides support (e.g +bug and security fixes) for the latest version and the burden for support for +older versions is on the vendor providing the older Rust toolchain. +Similarly, open source developers are not obligated to support older toolchains +and it is reasonable that the burden for these cases should be paid by those +who need it. +That said, we can still make the process less onerous for those who wish to +support users on older toolchains. + People have tried to reduce the pain from MSRV with its own costs: - Treating it as a breaking change: - This leads to extra churn in the ecosystem when a fraction of users are likely going to benefit From 1d507f1248a8c934d61229f0cc13d3d658a22019 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 13:42:17 -0600 Subject: [PATCH 044/184] fix: Capture matklad's feedback on testing latest deps --- text/3537-msrv-resolver.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index dddafb8c69a..ff35419b340 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -423,6 +423,17 @@ If we change behavior with a new edition (assuming we treat this as a minor inco we get the fanfare needed but it requires waiting until people bump their MSRV, making it so the people who need it the most are the those who will least benefit. +On the surface, encouraging people to primarily use maximal version resolution by +making this opt-in encourages more testing of the latest depednencies. +Before we [changed our guidance on lockfiles](https://github.com/rust-lang/cargo/pull/12382), +this was already limited as `bin`s should have a `Cargo.lock` file which is +infectious to their entire workspace. +This was also subject to the velocity of the project; +for passively maintain projects they can go a year without a CI run. +Now that we've changed our guidance on lockfiles, +we encourage people to verify their latest dependencies. +Assuming they are, this point becomes moot. + ## Make `rust-version=rustc` the default This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. @@ -437,6 +448,15 @@ The reasons we didn't go with this approach are - We want to encourage developing on the latest version of rustc/cargo to get all of the latest workflow improvements (e.g. error messages, sparse registry for cargo, etc), rather than lock people into the MSRV with `rust-toolchain.toml` - The toolchain is another type of dependency so this might seem contradictory but we feel the value-add of a new toolchain outweighs the cost while the value add of new dependencies doesn't +As for encouraging testing of the latest dependencies, +this falls somewhere between the opt-in and opt-out proposals for resoling to `package.rust-version`, +depending on the scenario. +If you don't check-in your `Cargo.lock`, +what developers will test with is anyone's guess. +As for CI, it will be dependent on which toolchain is used (at least `stable`). +If you do check-in your `Cargo.lock` as is suggested (but not prescribed), +then you are subject to whatever versions were compatible with the toolchain of each developer who caused a `Cargo.lock` change. + ## Configuring the resolver mode on the command-line or `Cargo.toml` The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). From 538167b4da46039aa6f1bd3f3a5b4695c5ae9d74 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 13:53:43 -0600 Subject: [PATCH 045/184] fix: Shift most of the CI discussion from Alt to Drawback --- text/3537-msrv-resolver.md | 39 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index ff35419b340..69f291ef9c6 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -352,9 +352,21 @@ without having to support the flag on every single command. # Drawbacks [drawbacks]: #drawbacks -Maintainers that commit their `Cargo.lock` and verify their latest dependencies -will need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` in their environment or run `cargo update --ignore-rust-version`. -See Alternatives for more on this. +As proposed, CI that tries to verify against the latest dependencies will no longer do so. +Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. +- If we consider this a major incompatibility, then it needs to be opted into. + As `cargo fix` can't migrate a user's CI, + this would be out of scope for migrating to this with a new Edition. +- I would argue that the number of maintainers verifying latest dependencies is + relatively low and they are more likely to be "in the know", + making them less likely to be negatively affected by this. + Therefore, I propose we consider this a minor incompatibility +- If we do a slow roll out (opt-in then opt-out), the visibility for the switch + to opt-out will be a lot less than the initial announcement and we're more + likely to miss people compared to making switch over when this gets released. +- If we change behavior with a new edition (assuming we treat this as a minor incompatibility), + we get the fanfare needed but it requires waiting until people bump their MSRV, + making it so the people who need it the most are the those who will least benefit. While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. @@ -403,25 +415,8 @@ allowing us to detect if the inputs have changed and only resolving then. ## Make this opt-in -As proposed, CI that tries to verify against the latest dependencies will no longer do so. -Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. - -If we consider this a major incompatibility, then it needs to be opted into. -As `cargo fix` can't migrate a user's CI, -this would be out of scope for migrating to this with a new Edition. - -I would argue that the number of maintainers verifying latest dependencies is -relatively low and they are more likely to be "in the know", -making them less likely to be negatively affected by this. -Therefore, I propose we consider this a minor incompatibility - -If we do a slow roll out (opt-in then opt-out), the visibility for the switch -to opt-out will be a lot less than the initial announcement and we're more -likely to miss people compared to making switch over when this gets released. - -If we change behavior with a new edition (assuming we treat this as a minor incompatibility), -we get the fanfare needed but it requires waiting until people bump their MSRV, -making it so the people who need it the most are the those who will least benefit. +By requiring `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` to get the proposed resolver behavior, +users don't need to update CI to verify the latest dependencies On the surface, encouraging people to primarily use maximal version resolution by making this opt-in encourages more testing of the latest depednencies. From 3419057d114203e6fe723fb94ecd0e4d5f1fc642 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 14:04:56 -0600 Subject: [PATCH 046/184] fix: Add another old-MSRV reason --- text/3537-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 69f291ef9c6..8a3fa41a639 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -161,6 +161,7 @@ So why are people on old versions? - Re-validation costs for updating core parts of the image for an embedded Linux developers can be high, keeping them on older versions - Updates can be slow within tightly controlled environments (airgaps, paperwork, etc) - Qualifying Rust toolchains takes time and money, see [Ferrocene](https://ferrous-systems.com/ferrocene/) +- Build on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) We need to keep in mind though that the Rust Project only provides support (e.g bug and security fixes) for the latest version and the burden for support for From f597ea39b45016fce23b504883c36f029cd256d7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 14:07:37 -0600 Subject: [PATCH 047/184] refactor: Make most pertinent alternatives more obvious --- text/3537-msrv-resolver.md | 88 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8a3fa41a639..492231a1ee5 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -383,38 +383,7 @@ Misc - `cargo add` selecting rust-version-compatible minimum bounds helps - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. -## Add `workspace.rust-version` - -Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. - -This opens its own set of questions -- Do packages implicitly inherit this? -- What are the semantics if its unset? -- Would it be confusing to have this be set in mixed-MSRV workspaces? Would blocking it be incompatible with the semantics when unset? -- In mixed-MSRV workspaces, does it need to be the highest or lowest MSRV of your packages? - - For the resolver, it would need to be the lowest but there might be other use cases where it needs to be the highest - -The proposed solution does not block us from later going down this road but -allows us to move forward without having to figure out all of these details. - -## Reporting - -Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), -we can run the resolver twice on the original input, once for MSRV and once without. -We then do a depth-first diff of the trees, stopping and reporting on the first different node. -This would let us report on any command that changes the way the tree is resolved. -We'd likely want to limit the output to only the sub-tree that changed. - -We could either always do the second resolve or only do the second resolve if the resolver changed anything, -whichever is faster. - -Its unknown whether making the inputs available for multiple resolves would have a performance impact. - -While a no-change resolve is fast, if this negatively impacts it enough, we -could explore hashing the resolve inputs and storing that in the lockfile, -allowing us to detect if the inputs have changed and only resolving then. - -## Make this opt-in +## Make this opt-in rather than opt-out By requiring `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` to get the proposed resolver behavior, users don't need to update CI to verify the latest dependencies @@ -453,18 +422,6 @@ As for CI, it will be dependent on which toolchain is used (at least `stable`). If you do check-in your `Cargo.lock` as is suggested (but not prescribed), then you are subject to whatever versions were compatible with the toolchain of each developer who caused a `Cargo.lock` change. -## Configuring the resolver mode on the command-line or `Cargo.toml` - -The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). -However, there is a lot more to define for us to get there. Some routes that need further exploration include: -- If its a CLI flag, then its transient, and its unclear which modes should be transient now and in the future - - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change -- We could put this in `Cargo.toml` but that implies it unconditionally applies to everything - - But we want `cargo install` to use the latest dependencies so people get bug/security fixes - - This gets in the way of the "separate development / publish MSRV" workflow - -By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. - ## Hard-error Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. @@ -496,6 +453,49 @@ This could cause a lot more backtracking which could negatively affect resolver If no `package.rust-version` is specified, we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. +## Reporting + +Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), +we can run the resolver twice on the original input, once for MSRV and once without. +We then do a depth-first diff of the trees, stopping and reporting on the first different node. +This would let us report on any command that changes the way the tree is resolved. +We'd likely want to limit the output to only the sub-tree that changed. + +We could either always do the second resolve or only do the second resolve if the resolver changed anything, +whichever is faster. + +Its unknown whether making the inputs available for multiple resolves would have a performance impact. + +While a no-change resolve is fast, if this negatively impacts it enough, we +could explore hashing the resolve inputs and storing that in the lockfile, +allowing us to detect if the inputs have changed and only resolving then. + +## Configuring the resolver mode on the command-line or `Cargo.toml` + +The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). +However, there is a lot more to define for us to get there. Some routes that need further exploration include: +- If its a CLI flag, then its transient, and its unclear which modes should be transient now and in the future + - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change +- We could put this in `Cargo.toml` but that implies it unconditionally applies to everything + - But we want `cargo install` to use the latest dependencies so people get bug/security fixes + - This gets in the way of the "separate development / publish MSRV" workflow + +By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. + +## Add `workspace.rust-version` + +Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. + +This opens its own set of questions +- Do packages implicitly inherit this? +- What are the semantics if its unset? +- Would it be confusing to have this be set in mixed-MSRV workspaces? Would blocking it be incompatible with the semantics when unset? +- In mixed-MSRV workspaces, does it need to be the highest or lowest MSRV of your packages? + - For the resolver, it would need to be the lowest but there might be other use cases where it needs to be the highest + +The proposed solution does not block us from later going down this road but +allows us to move forward without having to figure out all of these details. + # Prior art [prior-art]: #prior-art From 05d86e45b47f8aa66132cdbfae6828aaefacaade Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 14:20:33 -0600 Subject: [PATCH 048/184] fix: Try to clarify the multi-MSRV situations --- text/3537-msrv-resolver.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 492231a1ee5..78179c2b528 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -210,8 +210,12 @@ In solving this, we need to keep in mind because users are stuck on 3-20 year old versions of the language specification. The compatibility story is fairly strong with Rust, helping us keep compiler and dependency upgrades cheap. -- Some people keep their development and production MSRVs the same (henceforth referred to as "shared development / publish MSRV" workflow) - while others keep them separate, e.g. tracking two sets of dependencies via a `Cargo.msrv.lock` (henceforth referred to as "separate development / publish MSRV" workflow) +- The MSRV for a package depends on the context + - It can be dependent on which features are enabled + (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) + - Some projects might treat building the package from crates.io and from the repo the same + (henceforth referred to as "shared development / publish MSRV" workflow) + while others might have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish), henceforth referred to as "separate development / publish MSRV" - A `Cargo.lock` should not resolve differently when upgrading Rust without any other action. # Guide-level explanation From c60d85ec4f58ac217f705b236bf2d95513798e30 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 14:23:12 -0600 Subject: [PATCH 049/184] fix: Call out hard-error's problem with feature-dependent MSRV --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 78179c2b528..b4be1f7c131 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -457,6 +457,8 @@ This could cause a lot more backtracking which could negatively affect resolver If no `package.rust-version` is specified, we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. +Without further design work, this would be incompatible with feature-dependent MSRV and likely with the "separate development / publish MSRV" workflow. + ## Reporting Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), From 98db27a85fcdfcbce52fa858159f2d65edcc3c2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 15:00:03 -0600 Subject: [PATCH 050/184] fix: Cover rust-toolchain file --- text/3537-msrv-resolver.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b4be1f7c131..b29d2e376ff 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -409,6 +409,14 @@ This proposal elevates "shared development / publish rust-version" workflow over We could instead do the opposite, picking `rust-version=rustc` as a "safe" default for assuming the development rust-version. Users of the "shared development / publish rust-version" workflow could either set the config or use a `rust-toolchain.toml` file. +The downsides to "use `rust-toolchain.toml`" are +- Its environment config, and not project config, and is infectious in other situations without explicit action by a user who knows how to resolve it +- You lose out on new toolchain features like + improved clippy lints, + `cargo publish` waiting until publish is complete, + `Cargo.toml`s `[lints]`, + or this proposal once implemented. + The reasons we didn't go with this approach are - The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. - "Separate development / publish MSRV" workflows are inherently more complex to support with more caveats of where they apply, making "shared development / publish MSRV" workflows the path of least resistance for users. From fc0d66e0761898c68c55fc7274c990bba17e0f28 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 16:23:47 -0600 Subject: [PATCH 051/184] fix: Apply feedback to 'resolve with rustc version' --- text/3537-msrv-resolver.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b29d2e376ff..98bb9dfa3e0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -406,24 +406,25 @@ Assuming they are, this point becomes moot. ## Make `rust-version=rustc` the default This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. -We could instead do the opposite, picking `rust-version=rustc` as a "safe" default for assuming the development rust-version. -Users of the "shared development / publish rust-version" workflow could either set the config or use a `rust-toolchain.toml` file. - -The downsides to "use `rust-toolchain.toml`" are +We could instead do the opposite, adding support for `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` instead as a "safe" default for assuming the development rust-version. + +In terms of keeping this proposal minimal, this means we are likely to not include `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. +For people with the "shared development / publish rust-version" workflow, this would push them to using a `rust-toolchain.toml` file. +The downsides to using a `rust-toolchain.toml`" are: +- People are being "locked in" to unsupported versions of Rust + - **This does not align with us wanting to drive behavior we want because we are + pushing people to develop with unsupported toolchains.** - Its environment config, and not project config, and is infectious in other situations without explicit action by a user who knows how to resolve it - You lose out on new toolchain features like + improved error messages, improved clippy lints, + sparse registry support, `cargo publish` waiting until publish is complete, `Cargo.toml`s `[lints]`, or this proposal once implemented. - -The reasons we didn't go with this approach are -- The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. -- "Separate development / publish MSRV" workflows are inherently more complex to support with more caveats of where they apply, making "shared development / publish MSRV" workflows the path of least resistance for users. -- Without configuration, defaulting to "shared development / publish MSRV" workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the "separate development / publish MSRV" workflows. -- "Shared development / publish MSRV" workflows catch too-new API problems sooner -- We want to encourage developing on the latest version of rustc/cargo to get all of the latest workflow improvements (e.g. error messages, sparse registry for cargo, etc), rather than lock people into the MSRV with `rust-toolchain.toml` - - The toolchain is another type of dependency so this might seem contradictory but we feel the value-add of a new toolchain outweighs the cost while the value add of new dependencies doesn't + - While the toolchain is another type of dependency so this might seem + contradictory but we feel the value-add of a new toolchain outweighs the cost + while the value add of new dependencies doesn't As for encouraging testing of the latest dependencies, this falls somewhere between the opt-in and opt-out proposals for resoling to `package.rust-version`, @@ -434,6 +435,12 @@ As for CI, it will be dependent on which toolchain is used (at least `stable`). If you do check-in your `Cargo.lock` as is suggested (but not prescribed), then you are subject to whatever versions were compatible with the toolchain of each developer who caused a `Cargo.lock` change. +Like with `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` being the default, some other downsides are: +- The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. +- "Separate development / publish MSRV" workflows are inherently more complex to support with more caveats of where they apply, making "shared development / publish MSRV" workflows the path of least resistance for users. +- Without configuration, defaulting to "shared development / publish MSRV" workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the "separate development / publish MSRV" workflows. +- "Shared development / publish MSRV" workflows catch too-new API problems sooner + ## Hard-error Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. From 593d56d05bc0d9c1a9c488ec9a6d2041e908e743 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Dec 2023 16:46:04 -0600 Subject: [PATCH 052/184] feat: Define 'support' --- text/3537-msrv-resolver.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 98bb9dfa3e0..b4f7d5bad57 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -210,6 +210,12 @@ In solving this, we need to keep in mind because users are stuck on 3-20 year old versions of the language specification. The compatibility story is fairly strong with Rust, helping us keep compiler and dependency upgrades cheap. +- MSRV stands for "minimum **supported** rust version". + People have different definitions of "support", + ranging from "if it works for you" + (much like Rust's [Tier 3 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3)) + to "we validate it" + (much like Rust's [Tier 1 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-1)) - The MSRV for a package depends on the context - It can be dependent on which features are enabled (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) From 8f0c0cf36666361e4a1493751afbe06a23cd83ce Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 12 Dec 2023 17:04:42 -0600 Subject: [PATCH 053/184] feat: Add PHP prior art --- text/3537-msrv-resolver.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b4f7d5bad57..8677040c8e8 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -530,6 +530,12 @@ allows us to move forward without having to figure out all of these details. - We have [cfg_accessible](https://github.com/rust-lang/rust/issues/64797) as a first step though it has been stalled - These don't have to be mutually exclusive solutions as conditional compilation offers flexibility at the cost of maintenance. Different maintainers might make different decisions in how much they leverage each - One big difference is Python continues to support previous releases which sets a standard within the community for "MSRV" policies. +- [PHP Platform Packages](https://getcomposer.org/doc/01-basic-usage.md#platform-packages) is a more general mechanism than MSRV that allows declaring dependencies on external runtime requirements, like the interpreter version, interpreter extensions presence and version, or even whether the interpreter is 64-bit. + - Resolves to current system + - Can be overridden to so current system is always considered compatible + - Not tracked in their lockfile + - When run on an incompatible system, it will error and require running a command to re-resolve the dependencies for the current system + - One difference is that PHP is interpreted and that their lockfile must encompass not just development dependencies but deployment dependencies. This is in contrast to Rust which has development and deployment-build dependencies tracked with a lockfile while deployment uses OS-specific dependencies, like shared-object dependencies of ELF binaries which are not locked by their nature but instead developers rely on other technologies like docker or Nix (not even static linking can help as they that still leaves them subject to the kernel version in non-bare metal deployments). # Unresolved questions [unresolved-questions]: #unresolved-questions From 1514401d0ea7e49d507e751b9926a93795e75329 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 14 Dec 2023 13:58:27 -0600 Subject: [PATCH 054/184] fix: Clarify misc section --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8677040c8e8..c02c9282d92 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -385,7 +385,7 @@ this could instead further entrench rust-version stagnation in the ecosystem. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -Misc +Misc alternatives - Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. From b268e4540f36100ac2ed43b51e3e086c09b11df3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 14 Dec 2023 14:08:38 -0600 Subject: [PATCH 055/184] Be explicit about impact for verifying latest dependencies --- text/3537-msrv-resolver.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c02c9282d92..3e1c475f12c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -382,6 +382,14 @@ Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RE While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. +For projects with larger MSRVs than their dependencies, +this introduces another form of drift from the latest dependencies +(in addition to [lockfiles](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control)). +However, we already recommend people +[verify their latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies), +so the only scenario this degrades it further is when lockfiles are verified by always updating to the latest, like with RenovateBot, +and only in the sense that the user needs to know to explicitly take action to add another verification job to CI. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From ff544c41c8017c1b9c6f0856ef7355fd95d73e4b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 14 Dec 2023 14:46:29 -0600 Subject: [PATCH 056/184] fix: Emphasize, rather than yellow --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 3e1c475f12c..6ec698cc4f7 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -210,7 +210,7 @@ In solving this, we need to keep in mind because users are stuck on 3-20 year old versions of the language specification. The compatibility story is fairly strong with Rust, helping us keep compiler and dependency upgrades cheap. -- MSRV stands for "minimum **supported** rust version". +- MSRV stands for "minimum *supported* rust version". People have different definitions of "support", ranging from "if it works for you" (much like Rust's [Tier 3 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3)) From 447048d94c9936b1cfb65de217a57ae9c38bf5b3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 11 Dec 2023 20:08:41 -0600 Subject: [PATCH 057/184] feat: Expand on opt-in --- text/3537-msrv-resolver.md | 115 ++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 6ec698cc4f7..7c9abb9f40e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -403,11 +403,25 @@ Misc alternatives ## Make this opt-in rather than opt-out -By requiring `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` to get the proposed resolver behavior, -users don't need to update CI to verify the latest dependencies +This proposed solution elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. +We could instead do the opposite, carrying forward our existing behavior as the default (`CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`). +CI verifying MSRV and users of the "shared development / publish rust-version" workflow would need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. + +When building with old Rust versions, error messages could suggest re-resolving with `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. +The next corrective step (and suggestion from cargo) depends on what the user is doing and could be either +- `git checkout main -- Cargo.lock && cargo check` +- `cargo generate-lockfile` + +We should update the "incompatible rust-version" checks to be top-down, rather +than bottom up, +so users see the root of their problem, rather than the leaves. + +This has no impact on `cargo add`; it will still pick a version requirement that is MSRV-compatible. + +This avoids changing behavior for CI jobs that are trying to test the latest dependencies,. On the surface, encouraging people to primarily use maximal version resolution by -making this opt-in encourages more testing of the latest depednencies. +making this opt-in encourages more testing of the latest dependencies. Before we [changed our guidance on lockfiles](https://github.com/rust-lang/cargo/pull/12382), this was already limited as `bin`s should have a `Cargo.lock` file which is infectious to their entire workspace. @@ -417,6 +431,101 @@ Now that we've changed our guidance on lockfiles, we encourage people to verify their latest dependencies. Assuming they are, this point becomes moot. +A lot of this comes down to a mixture of usability and what behavior we want to drive. +When driving behavior, the intention is to help people find a known good path +without preventing them from doing things differently if they have the need +(cargo is intended to be [opinionated](https://doc.crates.io/contrib/design.html#design-principles)). +When done right, this is frictionless and people are understanding. +When done wrong, it is frustrating and people feel condescended towards. + +One area of particular concern for the Rust project is stagnation. +We want to encourage people to actively upgrade Rust and their dependencies. +- Upgrading comes at a cost that you can pay now incrementally or pay all at once when forced, like with a security vulnerability. That said, the community puts a strong emphasis on keeping the cost low. +- The longer the delay for using new features, implementing new ones has greater diminishing returns, discouraging progress generally. +- The more separated people are from nightly, the more costly it is for them to test out new features on their project, causing the Rust project to lose out on a valuable source of feedback. +- When people push their needs for old versions onto their dependencies, this shifts the cost from the exception case to the maintainer and all of the dependents. + +For **"MSRV is stable"**, they are unaffected by either opt-in or opt-out. + +The **"separate development / publish rust-version"** workflow is interesting. + +This aligns with the opt-in approach, making it implicitly endorsed. + +At this time, validating MSRV with this workflow is complicated, either +juggling two separate lockfiles or being compatible with and using the unstable +feature `-Zminimal-version`. +This suggests that those following this workflow, at least for now, are more +advanced users who are less likely to be impacted by needing to opt-in to a new +flag. +Of course, this only reflects on the transition cost and how surprising changing the behavior would be for existing Rust users and not on what is the right choice long term. + +If your building and testing becomes dependent on the capabilities of the newer toolchain for development, +your dependents can't patch in the git dependency to try out a fix or feature and +you can't validate your package's MSRV [without heroics](https://github.com/taiki-e/cargo-hack/issues/216). +In some cases, people have leveraged this workflow to intentionally not verify their MSRV, +treating their MSRV as [tier 3](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3). +Instead, we should be driving people towards patchable dependencies and verified MSRVs and away from intentionally unverifiable MSRVs. + +A potential side effect of less-verified MSRVs is that frees maintainers up to have larger MSRV policies than their dependents as they aren't being forced to update their MSRV to get a bug fix +(assuming it won't be backported to a patch release within a compatible MSRV). +However, longer MSRV policies encourage more stagnation +as falling behind in dependencies represents a risk when it comes to security vulnerabilities. +For when longer MSRVs are needed, maintainers should reserve MSRV bumps for when bumping their minor version, +giving them room to backport fixes. + +In addition, without support for more granular MSRV declarations, +I would suggest that the behavior we should drive is that `package.rust-version` applies generally, +including for local development. +In this way, MSRV is different than minimal version resolution, +despite rustc being yet another dependency. + +Application development is a potential specialization in this area. +There are already likely differences between development and production, including +- Loading of assets locally vs bundling +- `debug` vs `release` profiles +- Default target CPU vs specialized target CPU + +An application developer might be willing to say "I'll support old MSRVs for +Debian but my regular releases get all the latest bells and whistles" (from cargo dependencies only) and +decide to develop with the latest dependencies, while verifying an MSRV in CI. +For the subset of applications that are being installed via `cargo install`, +this runs into a problem. +The standing assumption is that `cargo install` does not reuse the associated `Cargo.lock` to ensure the latest bug and security fixes are used +(except when they are stuck behind breaking changes) +but that if something goes wrong, +`--locked` is available to use the `Cargo.lock` file to build with a known, good (i.e. verified) state +(e.g. [rust-lang/cargo#10891](https://github.com/rust-lang/cargo/issues/10891)). +Without more granular MSRV declarations, +users should be able to expect that the MSRV means that `Cargo.lock` is verified against the MSRV so that `cargo install --locked` can work for these users as well. + +For the **"shared development / publish rust-version"** workflow, +the user will discover this the first time they validate with their MSRV. +The error will help guide them to how to fix this (set a config and re-resolve) +This will most likely be checked in via a config file which will make this a fix-and-forget. + +There can be some mild frustration in the vein of "if it can figure it out, why doesn't it". +This is the type of "guiding" of a user that can put people off and borderlines on condescending. +This also shows it fails the principle of least surprise for people supporting old rustc's. +Users know cargo has the information and are surprised that it doesn't use it. + +By having this behavior be non-default, we are implicitly steering people away from this approach. + +A side benefit for those following the "shared development / publish rust-version" workflow +(which we are steering people away from) +is that they get feedback earlier about using APIs from dependencies too new for their MSRV +(though this does not replace validation in CI). +There are more ideal solutions, like stable use of `#[stable]` and telling rustc about the minimum possible version of a dependency. +The path and time table for that is unclear. +This does not justify prioritizing this workflow on its own but contributes to the whole picture +and is not a precedence for switching to minimal version resolution. + +As the opt-in is a one-and-done (and likely not by someone setting policy), +it is unlikely to discourage stagnation. +Whether its opt-in or opt-out, keeping users informed that they are behind on dependencies is much more likely to drive people to updating. +Opt-in for MSRV resolution is also putting focus on a lower area of cost/risk for stagnation +while nothing is being done for major version. +Improving things for major versions will likely improve things for MSRV. + ## Make `rust-version=rustc` the default This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. From ca225b99f39eff198d012c0e92354728277c8a65 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 14 Dec 2023 13:56:29 -0600 Subject: [PATCH 058/184] rustc --- text/3537-msrv-resolver.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 7c9abb9f40e..3547b7b6562 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -531,6 +531,8 @@ Improving things for major versions will likely improve things for MSRV. This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. We could instead do the opposite, adding support for `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` instead as a "safe" default for assuming the development rust-version. +This has no impact on `cargo add`; it will still pick a version requirement that is MSRV-compatible. + In terms of keeping this proposal minimal, this means we are likely to not include `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. For people with the "shared development / publish rust-version" workflow, this would push them to using a `rust-toolchain.toml` file. The downsides to using a `rust-toolchain.toml`" are: @@ -558,11 +560,10 @@ As for CI, it will be dependent on which toolchain is used (at least `stable`). If you do check-in your `Cargo.lock` as is suggested (but not prescribed), then you are subject to whatever versions were compatible with the toolchain of each developer who caused a `Cargo.lock` change. -Like with `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` being the default, some other downsides are: -- The user explicitly told us the MSRV for the project; we do not have the granularity for different MSRVs for different workflows (or `features`) and likely the complexity would not be worth it. -- "Separate development / publish MSRV" workflows are inherently more complex to support with more caveats of where they apply, making "shared development / publish MSRV" workflows the path of least resistance for users. -- Without configuration, defaulting to "shared development / publish MSRV" workflows will lead to the least number of errors from cargo as the resulting lockfile is compatible with the "separate development / publish MSRV" workflows. -- "Shared development / publish MSRV" workflows catch too-new API problems sooner +In the scenario where the `Cargo.lock` is not committed, every contributor will be using a different set of dependencies, +making supporting them through problems more difficult. + +As this encourages "shared development / publish rust-version" workflow, see the "opt-in" solution for the caveats of encouraging that workflow. ## Hard-error From 2381b9d962e221a5ffe9cc2e5864ab3dd2f85bfc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 12:26:49 -0600 Subject: [PATCH 059/184] fix: Clarify the fallback to rustc --version --- text/3537-msrv-resolver.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 3547b7b6562..b47dd1073d6 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -309,11 +309,14 @@ the resolver will pick the lowest version among workspace members. This will be less optimal for workspaces with multiple MSRVs and dependencies unique to the higher-MSRV packages. Users can workaround this by raising the version requirement or using `cargo update --precise`. -If `package.rust-version` is unset among all workspace members, -we'll fallback to `rustc --version` (implicitly picking up `rust-toolchain.toml` files), -ensuring a build that at least works for the current system. +When `rust-version` is unset, +we'll fallback to `rustc --version`. +This is primarily targeted at helping users with a +[`rust-toolchain.toml` file](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) +(to reduce duplication) +though this would also help users who happen to be on an old rustc, for whatever reason. As this is just a preference for resolving dependencies, rather than prescriptive, -this shouldn't cause churn. +this shouldn't cause churn of the `Cargo.lock` file. We already call `rustc` for feature resolution, so hopefully this won't have a performance impact. The resolver will only do this for local packages and not for `cargo install`. From c874e2b00cda99c3400911bc6c8efd5a2f3250f1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 12:28:21 -0600 Subject: [PATCH 060/184] fix: Clear up language on cargo config --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b47dd1073d6..fe63d509c2d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -341,7 +341,7 @@ This behavior can be bypassed with `--ignore-rust-version` ## Cargo config -We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` that will control the control pick the mechanisms for preferring one compatible version over another. +We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. ```toml [build] From f4cb627d028dc24f617ed90c58d534dc647b0333 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 12:29:39 -0600 Subject: [PATCH 061/184] fix: Clarify the scope of a drawback --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fe63d509c2d..e13b1404d6c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -366,7 +366,7 @@ without having to support the flag on every single command. # Drawbacks [drawbacks]: #drawbacks -As proposed, CI that tries to verify against the latest dependencies will no longer do so. +As proposed, CI that tries to verify against the latest dependencies will no longer do so when `rust-version` is set. Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. - If we consider this a major incompatibility, then it needs to be opted into. As `cargo fix` can't migrate a user's CI, From f2c1e86cdef34453f761f23c5ae2c691ddf78b47 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 13:52:52 -0600 Subject: [PATCH 062/184] fix: Soften unintentionally strong language on minor bumps --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index e13b1404d6c..ec92381acbd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -473,7 +473,7 @@ A potential side effect of less-verified MSRVs is that frees maintainers up to h (assuming it won't be backported to a patch release within a compatible MSRV). However, longer MSRV policies encourage more stagnation as falling behind in dependencies represents a risk when it comes to security vulnerabilities. -For when longer MSRVs are needed, maintainers should reserve MSRV bumps for when bumping their minor version, +Alternatively for when longer MSRVs are needed, maintainers could [reserve MSRV bumps for when bumping their minor version](https://doc.rust-lang.org/cargo/reference/semver.html#env-new-rust), giving them room to backport fixes. In addition, without support for more granular MSRV declarations, From 94b0bf3a986e959d501d0da0a3ebe8f38fdaad29 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 14:05:30 -0600 Subject: [PATCH 063/184] fix: Clarify why minor is suggested --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index ec92381acbd..972a8d2906c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -473,8 +473,8 @@ A potential side effect of less-verified MSRVs is that frees maintainers up to h (assuming it won't be backported to a patch release within a compatible MSRV). However, longer MSRV policies encourage more stagnation as falling behind in dependencies represents a risk when it comes to security vulnerabilities. -Alternatively for when longer MSRVs are needed, maintainers could [reserve MSRV bumps for when bumping their minor version](https://doc.rust-lang.org/cargo/reference/semver.html#env-new-rust), -giving them room to backport fixes. +Alternatively when supporting dependents with older MSRVs is needed, maintainers could reserve MSRV bumps for when bumping their minor version, +leaving room in their version numbers to release backported fixes. In addition, without support for more granular MSRV declarations, I would suggest that the behavior we should drive is that `package.rust-version` applies generally, From df3f2759c6c4d5bad556ebbe23bd10108fd62af1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 14:06:30 -0600 Subject: [PATCH 064/184] fix: Remove reason against tying to edition --- text/3537-msrv-resolver.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 972a8d2906c..e7b8834157d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -378,9 +378,6 @@ Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RE - If we do a slow roll out (opt-in then opt-out), the visibility for the switch to opt-out will be a lot less than the initial announcement and we're more likely to miss people compared to making switch over when this gets released. -- If we change behavior with a new edition (assuming we treat this as a minor incompatibility), - we get the fanfare needed but it requires waiting until people bump their MSRV, - making it so the people who need it the most are the those who will least benefit. While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. From 6f1e52aca7daed3a7e71f0864f136fd88693943c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 15 Dec 2023 15:23:49 -0600 Subject: [PATCH 065/184] fix: Clarify role of CI --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index e7b8834157d..4f8d460ba83 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -425,6 +425,8 @@ making this opt-in encourages more testing of the latest dependencies. Before we [changed our guidance on lockfiles](https://github.com/rust-lang/cargo/pull/12382), this was already limited as `bin`s should have a `Cargo.lock` file which is infectious to their entire workspace. +For local development, you are reusing the same `Cargo.lock` over time, adjusted only when a version requirement forces it, causing you to not get "maximal versions" at the time of a change. +So that leaves CI for seeing / verifying maximal versions. This was also subject to the velocity of the project; for passively maintain projects they can go a year without a CI run. Now that we've changed our guidance on lockfiles, From 718a15a8b1c720d9ba6797c66839b3c5f1d97fe4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Jan 2024 15:01:08 -0600 Subject: [PATCH 066/184] fix: Make a sentence a little less redundant --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 4f8d460ba83..a94d97ddc07 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -16,7 +16,7 @@ Note: `cargo install` is intentionally left out for now to decouple discussions # Motivation [motivation]: #motivation -Let's step through a simple scenario where a developer can develop with the +Let's step through a simple scenario where a user develops with the latest Rust version but production uses an older version: ```console $ cargo new msrv-resolver From 914d764ae9ad5c0e22a8d0a1d44cabee39d8fd01 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 6 Jan 2024 20:41:27 -0600 Subject: [PATCH 067/184] refactor(motivation): Re-center on common workflows --- text/3537-msrv-resolver.md | 182 ++++++++++++++++++++++++++----------- 1 file changed, 129 insertions(+), 53 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index a94d97ddc07..c5d50da7325 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -153,25 +153,6 @@ How wide spread is this? Take this with a grain of salt but based on crates.io *([source](https://rust-lang.zulipchat.com/#narrow/stream/318791-t-crates-io/topic/cargo.20version.20usage/near/401440149))* -So why are people on old versions? -- Not everyone is focused on Rust development and might only touch their Rust code once every couple of months, - making it a pain if they have to update every time -- While a distribution provides rust to build other packages in the distribution, - users might assume that is a version to use. -- Re-validation costs for updating core parts of the image for an embedded Linux developers can be high, keeping them on older versions -- Updates can be slow within tightly controlled environments (airgaps, paperwork, etc) -- Qualifying Rust toolchains takes time and money, see [Ferrocene](https://ferrous-systems.com/ferrocene/) -- Build on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) - -We need to keep in mind though that the Rust Project only provides support (e.g -bug and security fixes) for the latest version and the burden for support for -older versions is on the vendor providing the older Rust toolchain. -Similarly, open source developers are not obligated to support older toolchains -and it is reasonable that the burden for these cases should be paid by those -who need it. -That said, we can still make the process less onerous for those who wish to -support users on older toolchains. - People have tried to reduce the pain from MSRV with its own costs: - Treating it as a breaking change: - This leads to extra churn in the ecosystem when a fraction of users are likely going to benefit @@ -186,43 +167,138 @@ For example: - [libc](https://github.com/rust-lang/libs-team/issues/72) - [time](https://github.com/time-rs/time/discussions/535) -Supporting older MSRVs means maintainers don't have access to all of the latest -resources for improving their project. -This indirectly affects users as it can slow maintainers down. -This can also directly affect users. -For example, -by clap updating its MSRV from 1.64.0 to 1.70.0, -it was able to drop the large [is-terminal](https://crates.io/crates/is-terminal) dependency, -[cutting the build time from 6s to 3s](https://github.com/rosetta-rs/argparse-rosetta-rs/commit/378cd2c30679afdf9b9843dbadea3e8951090809). -So if we can find a solution that allows maintainers to move forward, helping -users more on the edge, while not impacting users on older rust version, would be -a big help. - The sooner we improve the status quo, the better, as it can take years for these changes to percolate out to those exclusively developing with an older Rust version (in contrast with the example above). -This delay can be reduced somewhat if a newer development version can be used -without upgrading the MSRV. - -In solving this, we need to keep in mind -- Users need to be aware when they are on old versions for evaluating security risk and when debugging issues -- We don't want to end up like other ecosystems where no one can use new features - because users are stuck on 3-20 year old versions of the language specification. - The compatibility story is fairly strong with Rust, helping us keep - compiler and dependency upgrades cheap. -- MSRV stands for "minimum *supported* rust version". - People have different definitions of "support", - ranging from "if it works for you" - (much like Rust's [Tier 3 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3)) - to "we validate it" - (much like Rust's [Tier 1 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-1)) -- The MSRV for a package depends on the context - - It can be dependent on which features are enabled - (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) - - Some projects might treat building the package from crates.io and from the repo the same - (henceforth referred to as "shared development / publish MSRV" workflow) - while others might have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish), henceforth referred to as "separate development / publish MSRV" -- A `Cargo.lock` should not resolve differently when upgrading Rust without any other action. +This delay can be reduced somewhat if a newer toolchain can be used for +development version without upgrading the MSRV. + +## Workflows + +In solving this, we need to keep in mind how people are using Cargo and how to prioritize when needs of different workflows conflict. +We will then look at the potential designs within the context of this framework. + +Some design criteria we can use for evaluating use cases: +- Low barrier to entry +- Encourage a standard of quality within the ecosystem +- Encourage progress and avoid stagnation + - Proactively upgrading means the total benefit to developers from investments made in Rust is higher + - Conversely, when most of the community is on old versions, it has a chilling effect on improvements + - This also means feedback can come more quickly, making it easier and cheaper to pivot with user needs +- The costs of “non-recommended” setups should be isolated to those that need them +- Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users +- Cargo must not make major breaking changes +- Every feature has a cost and we should balance the cost against the value we expect + - Features can further constrain what can be done in the future due to backwards compatibility + - Features increase maintenance burden + - The larger the user-facing surface, the less likely users will find the feature they need and instead use the quickest shortcut +- When not competing with the above, we should do the right thing for the user rather than disrupt their flow to telling them what they should instead do + +And keeping in mind +- The Rust project only supports the latest version + (e.g bug and security fixes) + and the burden for support for older versions is on the vendor providing the older Rust toolchain. +- A `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain. + +Some implications: +- "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version +- MSRV applies to all interactions with a project + (including registry dependency, git dependency, `cargo install`, contributor experience), + unless documented otherwise + - Some projects may document that enabling a feature will affect the MSRV (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) + - Some projects may have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish) +- The cost for maintaining support for older versions of Rust should be on the user of the old version and not on the maintainer or the users of the library + - Costs include lower developer productivity due to lack of access to features, + APIs that don't integrate with the latest features, + and slower build times due to pulling in extra code to make up for missing features + (e.g. clap dropping its dependency on + [is-terminal](https://crates.io/crates/is-terminal) in favor of + [`IsTerminal`](https://doc.rust-lang.org/std/io/trait.IsTerminal.html) + cut build time from [6s to 3s](https://github.com/rosetta-rs/argparse-rosetta-rs/commit/378cd2c30679afdf9b9843dbadea3e8951090809)) + +### Latest Rust with no MSRV + +A user runs `cargo new` and starts development. + +Priority 0: +- No MSRV is fine as pushing people to have an MSRV would either lead to + - an inaccurate reported MSRV from going stale which would lower the quality of the ecosystem + - raise the barrier to entry by requiring more process for packages and pushing the cost of old Rust versions on people who don't care + +Currently, we do not provide a way to help users know new versions are available to support them in being up-to-date. +MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. +Otherwise, they must actively run `rustup update` or follow Rust news. + +### Latest Rust as the MSRV + +A maintainer regularly updates their MSRV to latest. +They can choose to provide a level of support for old MSRVs by reserving MSRV +changes to minor version bumps, +giving them room to backport fixes. + +Priority 0: +- Low barrier to maintaining a high quality of support for their MSRV +- Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain +- By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading + +Currently, we do not help these users with keeping their MSRV up-to-date. +They can use other tools like +[RenovateBot](https://github.com/rust-lang/cargo/blob/87eb374d499100bc945dc0e50ae5194ae539b964/.github/renovate.json5#L12-L24) +though that causes extra churn in the repo. + +A package could offer a lower MSRV in an unofficial capacity or with a lower quality of support +but the requirement that dependents always pass `--ignore-rust-version` makes this disruptive. + +### Extended MSRV + +This could really be people exclusively running one version or that support a range a versions. + +So why are people on old versions? +- Not everyone is focused on Rust development and might only touch their Rust code once every couple of months, + making it a pain if they have to update every time. + - Think back to slow git index updates when you've stepped away and consider people who we'd be telling to run `rustup update` every time they touch Rust +- While a distribution provides rust to build other packages in the distribution, + users might assume that is a version to use, rather than getting Rust through `rustup` +- Re-validation costs for updating core parts of the image for an embedded Linux developers can be high, keeping them on older versions +- Updates can be slow within tightly controlled environments (airgaps, paperwork, etc) +- Qualifying Rust toolchains takes time and money, see [Ferrocene](https://ferrous-systems.com/ferrocene/) +- Build on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) +- Library and tool maintainers catering to the above use cases + +Depending on the reason they are working with an old version, +they might be developing the project with it or they might be using the latest toolchain. + +Priority 1: +- MSRV applies to all interactions to the project which also means that the level of "support" is consistent +- This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users +- For library and tool maintainers, they are absorbing costs from these less common use cases + - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases + +Currently, maintaining a working `Cargo.lock` is frustrating, as demonstrated earlier. +When developing with the latest toolchain, +delaying feedback to CI or an embedded image build process can be frustrating +(using too-new dependencies, using too-new Cargo or Rust features, etc). + +### Extended published MSRV w/ latest development MSRV + +This is where the published package for a project claims an extended MSRV but interactions within the repo require the latest toolchain. +The requirement on the latest MSRV could from the `Cargo.lock` containing dependencies with the latest MSRV or they could be using Cargo features that don't affect the published package. +In some cases, the advertised MSRV might be for a lower tier of support than what is supported for the latest version. +For instance, a project might intentionally skip testing against their MSRV because of known bugs that will fail the test suite. + +Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. + +Priority 2: +- The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem + - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't + - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work + - For contributors, they must be on an unspecified Rust toolchain version +- The caveats involved in this approach (see prior item) would lead to worse documentation which lowers the quality to users +- This still leads to stagnation despite being able to use the latest dependencies as they are limited in what they can use from them and they can't use features from the latest Rust toolchain +- These library and tool maintainers are absorbing costs from the less common use cases of their dependents + - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases + +Currently to verify their MSRV, they must either juggle two lockfiles, keeping them in sync, or use the unstable `-Zminimal-versions`. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 2a768c98eb5c2745b69c5cb2a627be494da4e5c6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 8 Jan 2024 07:26:09 -0600 Subject: [PATCH 068/184] fix(motivation): Call out role of rust-toolchain.toml file --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c5d50da7325..56f5017ad33 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -268,6 +268,8 @@ So why are people on old versions? Depending on the reason they are working with an old version, they might be developing the project with it or they might be using the latest toolchain. +For some of these use cases, they might controlling their "MSRV" via `rust-toolchain.toml`, rather than `package.rust-version`, as its their only supported Rust version. + Priority 1: - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users From 6addf938183cd5ead294a1c3c0dcd5d9ca8ef237 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 8 Jan 2024 07:27:06 -0600 Subject: [PATCH 069/184] fix(motivation): Cover verifying multiple versions --- text/3537-msrv-resolver.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 56f5017ad33..503a3958cfb 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -270,6 +270,12 @@ they might be developing the project with it or they might be using the latest t For some of these use cases, they might controlling their "MSRV" via `rust-toolchain.toml`, rather than `package.rust-version`, as its their only supported Rust version. +When multiple Rust versions are supported, like with library and tool maintainers, +they will need to verify at least their MSRV and latest. +Ideally, they also [verify their latest dependencies](https://doc.rust-lang.org/cargo/guide/continuous-integration.html#verifying-latest-dependencies) +though this is already a recommended practice when people follow the +[default choice to commit their lockfile](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control). + Priority 1: - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users From 4e7c0d00646022f592e96d09f11fc53179f401c3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 8 Jan 2024 07:28:27 -0600 Subject: [PATCH 070/184] fix(motivation): Don't have two workflows at same priority --- text/3537-msrv-resolver.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 503a3958cfb..235fd4b21de 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -236,7 +236,7 @@ They can choose to provide a level of support for old MSRVs by reserving MSRV changes to minor version bumps, giving them room to backport fixes. -Priority 0: +Priority 1: - Low barrier to maintaining a high quality of support for their MSRV - Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain - By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading @@ -276,7 +276,7 @@ Ideally, they also [verify their latest dependencies](https://doc.rust-lang.org/ though this is already a recommended practice when people follow the [default choice to commit their lockfile](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control). -Priority 1: +Priority 2: - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users - For library and tool maintainers, they are absorbing costs from these less common use cases @@ -296,7 +296,7 @@ For instance, a project might intentionally skip testing against their MSRV beca Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. -Priority 2: +Priority 3: - The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work From d8bcc36b99aa4e95c5f79c0a5f59e417c5115ab2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 10 Jan 2024 13:00:13 -0600 Subject: [PATCH 071/184] fix(motivation): Clarify the structure --- text/3537-msrv-resolver.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 235fd4b21de..e9f68d38ffd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -220,12 +220,14 @@ Some implications: A user runs `cargo new` and starts development. -Priority 0: +Priority 0 because: - No MSRV is fine as pushing people to have an MSRV would either lead to - an inaccurate reported MSRV from going stale which would lower the quality of the ecosystem - raise the barrier to entry by requiring more process for packages and pushing the cost of old Rust versions on people who don't care -Currently, we do not provide a way to help users know new versions are available to support them in being up-to-date. +Pain points: + +We do not provide a way to help users know new versions are available to support them in being up-to-date. MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. Otherwise, they must actively run `rustup update` or follow Rust news. @@ -236,12 +238,14 @@ They can choose to provide a level of support for old MSRVs by reserving MSRV changes to minor version bumps, giving them room to backport fixes. -Priority 1: +Priority 1 because: - Low barrier to maintaining a high quality of support for their MSRV - Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain - By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading -Currently, we do not help these users with keeping their MSRV up-to-date. +Pain points: + +We do not help these users with keeping their MSRV up-to-date. They can use other tools like [RenovateBot](https://github.com/rust-lang/cargo/blob/87eb374d499100bc945dc0e50ae5194ae539b964/.github/renovate.json5#L12-L24) though that causes extra churn in the repo. @@ -276,13 +280,15 @@ Ideally, they also [verify their latest dependencies](https://doc.rust-lang.org/ though this is already a recommended practice when people follow the [default choice to commit their lockfile](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control). -Priority 2: +Priority 2 because: - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users - For library and tool maintainers, they are absorbing costs from these less common use cases - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases -Currently, maintaining a working `Cargo.lock` is frustrating, as demonstrated earlier. +Pain points: + +Maintaining a working `Cargo.lock` is frustrating, as demonstrated earlier. When developing with the latest toolchain, delaying feedback to CI or an embedded image build process can be frustrating (using too-new dependencies, using too-new Cargo or Rust features, etc). @@ -296,7 +302,7 @@ For instance, a project might intentionally skip testing against their MSRV beca Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. -Priority 3: +Priority 3 because: - The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work @@ -306,7 +312,9 @@ Priority 3: - These library and tool maintainers are absorbing costs from the less common use cases of their dependents - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases -Currently to verify their MSRV, they must either juggle two lockfiles, keeping them in sync, or use the unstable `-Zminimal-versions`. +Pain points: + +To verify their MSRV, they must either juggle two lockfiles, keeping them in sync, or use the unstable `-Zminimal-versions`. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 6d15b97a02164132a920cb4f2e9a20ce10dc4448 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 10 Jan 2024 13:26:55 -0600 Subject: [PATCH 072/184] fix(motivation): Minor expansion on MSRV workarounds --- text/3537-msrv-resolver.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index e9f68d38ffd..42b57bc42d8 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -159,13 +159,15 @@ People have tried to reduce the pain from MSRV with its own costs: - We have the precedence elsewhere in the Rust ecosystem for build and runtime system requirement changes not being breaking, like when rustc requires newer versions of glibc, Android NDK, etc. - Adding upper limits to version requirements: - This fractures the ecosystem by making packages incompatible with each other and the Cargo team [discourages doing this](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#multiple-requirements) - -Another way the status quo exhibits pain on the ecosystem is long -arguments over what is the right policy for updating a minimum-supported Rust -version (MSRV), wearing on all parties. -For example: -- [libc](https://github.com/rust-lang/libs-team/issues/72) -- [time](https://github.com/time-rs/time/discussions/535) +- Avoiding dependencies, re-implementing it themselves at the cost of their time and the risk for bugs, especially if `unsafe` is involved +- Ensuring dependencies have a more inclusive MSRV policy then themselves + - This has lead to long arguments in the ecosystem over what is the right + policy for updating a minimum-supported Rust version (MSRV), + wearing on all + (e.g. + [libc](https://github.com/rust-lang/libs-team/issues/72) + [time](https://github.com/time-rs/time/discussions/535) + ) The sooner we improve the status quo, the better, as it can take years for these changes to percolate out to those exclusively developing with an older From a6bd027a7680701323341f26e28ce6735fa618e2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 10 Jan 2024 15:43:40 -0600 Subject: [PATCH 073/184] fix(motivation): Clarify and expand on workflows --- text/3537-msrv-resolver.md | 65 ++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 42b57bc42d8..f01bb48a06e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -185,7 +185,7 @@ Some design criteria we can use for evaluating use cases: - Encourage a standard of quality within the ecosystem - Encourage progress and avoid stagnation - Proactively upgrading means the total benefit to developers from investments made in Rust is higher - - Conversely, when most of the community is on old versions, it has a chilling effect on improvements + - Conversely, when most of the community is on old versions, it has a chilling effect on improving Rust - This also means feedback can come more quickly, making it easier and cheaper to pivot with user needs - The costs of “non-recommended” setups should be isolated to those that need them - Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users @@ -200,7 +200,9 @@ And keeping in mind - The Rust project only supports the latest version (e.g bug and security fixes) and the burden for support for older versions is on the vendor providing the older Rust toolchain. -- A `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain. +- A `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` + - e.g. If the maintainer does `cargo add foo && git commit && git push`, + then a contributor doing `git pull && cargo check` should have an unchanged `Cargo.lock` Some implications: - "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version @@ -209,7 +211,7 @@ Some implications: unless documented otherwise - Some projects may document that enabling a feature will affect the MSRV (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) - Some projects may have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish) -- The cost for maintaining support for older versions of Rust should be on the user of the old version and not on the maintainer or the users of the library +- We should focus the cost for maintaining support for older versions of Rust on the user of the old version and away from the maintainer or the other users of the library or tool - Costs include lower developer productivity due to lack of access to features, APIs that don't integrate with the latest features, and slower build times due to pulling in extra code to make up for missing features @@ -222,14 +224,16 @@ Some implications: A user runs `cargo new` and starts development. -Priority 0 because: -- No MSRV is fine as pushing people to have an MSRV would either lead to - - an inaccurate reported MSRV from going stale which would lower the quality of the ecosystem +A maintainer may also want to avoid constraining their dependents, for a variety of reasons, and leave MSRV support as a gray area. + +**Priority 0 because:** +- No MSRV is fine as pushing people to have an MSRV would lead to either + - an inaccurate reported MSRV from it going stale which would lower the quality of the ecosystem - raise the barrier to entry by requiring more process for packages and pushing the cost of old Rust versions on people who don't care -Pain points: +**Pain points:** -We do not provide a way to help users know new versions are available to support them in being up-to-date. +We do not provide a way to help users know new versions are available, to support the users in saying up-to-date. MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. Otherwise, they must actively run `rustup update` or follow Rust news. @@ -239,13 +243,16 @@ A maintainer regularly updates their MSRV to latest. They can choose to provide a level of support for old MSRVs by reserving MSRV changes to minor version bumps, giving them room to backport fixes. +Due to the pain points listed below, the target audience for this workflow is likely small, +likely pushing them to not specify their MSRV. -Priority 1 because: +**Priority 1 because:** - Low barrier to maintaining a high quality of support for their MSRV +- Being willing to advertising an MSRV, even if latest, improves the information available to developers, increasing the quality of the ecosystem - Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain - By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading -Pain points: +**Pain points (in addition to the prior workflow):** We do not help these users with keeping their MSRV up-to-date. They can use other tools like @@ -257,8 +264,7 @@ but the requirement that dependents always pass `--ignore-rust-version` makes th ### Extended MSRV -This could really be people exclusively running one version or that support a range a versions. - +This could be people exclusively running one version or that support a range a versions. So why are people on old versions? - Not everyone is focused on Rust development and might only touch their Rust code once every couple of months, making it a pain if they have to update every time. @@ -271,40 +277,50 @@ So why are people on old versions? - Build on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) - Library and tool maintainers catering to the above use cases +The MSRV may extend back only a couple releases or to a year+ and +they may choose to update on an as-need basis or keep to a strict cadence. + Depending on the reason they are working with an old version, they might be developing the project with it or they might be using the latest toolchain. -For some of these use cases, they might controlling their "MSRV" via `rust-toolchain.toml`, rather than `package.rust-version`, as its their only supported Rust version. +For some of these use cases, they might controlling their "MSRV" via `rust-toolchain.toml`, rather than `package.rust-version`, as its their only supported Rust version (e.g. an application with a vetted toolchain). When multiple Rust versions are supported, like with library and tool maintainers, they will need to verify at least their MSRV and latest. Ideally, they also [verify their latest dependencies](https://doc.rust-lang.org/cargo/guide/continuous-integration.html#verifying-latest-dependencies) though this is already a recommended practice when people follow the [default choice to commit their lockfile](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control). +The way they verify dependencies is restricted as they can't rely on always updating via Dependabot/RenovateBot as a way to verify them. +Maintainers likely only need to do a compilation check for MSRV as their regular CI runs ensure that the behavior (which is usually independent of rust version) is correct for the MSRV-compatible dependencies. -Priority 2 because: +**Priority 2 because:** - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users - For library and tool maintainers, they are absorbing costs from these less common use cases - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases -Pain points: +**Pain points:** Maintaining a working `Cargo.lock` is frustrating, as demonstrated earlier. + When developing with the latest toolchain, -delaying feedback to CI or an embedded image build process can be frustrating +feedback is delayed until CI or an embedded image build process which can be frustrating (using too-new dependencies, using too-new Cargo or Rust features, etc). ### Extended published MSRV w/ latest development MSRV This is where the published package for a project claims an extended MSRV but interactions within the repo require the latest toolchain. -The requirement on the latest MSRV could from the `Cargo.lock` containing dependencies with the latest MSRV or they could be using Cargo features that don't affect the published package. +The requirement on the latest MSRV could come from the `Cargo.lock` containing dependencies with the latest MSRV or they could be using Cargo features that don't affect the published package. In some cases, the advertised MSRV might be for a lower tier of support than what is supported for the latest version. For instance, a project might intentionally skip testing against their MSRV because of known bugs that will fail the test suite. +In some cases, the MSRV-incompatible dependencies might be restricted to `dev-dependencies`. +Though local development can't be performed with the MSRV, +the fact that the tests are verifying (on a newer MSRV) that the dependencies work gives a good amount of confidence that they will work on the MSRV so long as they compile. + Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. -Priority 3 because: +**Priority 3 because:** - The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work @@ -314,9 +330,18 @@ Priority 3 because: - These library and tool maintainers are absorbing costs from the less common use cases of their dependents - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases -Pain points: +**Pain points:** + +Like the prior workflow, when developing with the latest toolchain, +feedback is delayed until CI or an embedded image build process which can be frustrating +(using too-new dependencies, using too-new Cargo or Rust features, etc). + +When `Cargo.lock` is resolved for latest dependencies, independent of MSRV, +verifying the MSRV becomes difficult as they must either juggle two lockfiles, keeping them in sync, or use the unstable `-Zminimal-versions`. +The two lockfile approach also has all of the problems shown earlier in writing the lockfile. -To verify their MSRV, they must either juggle two lockfiles, keeping them in sync, or use the unstable `-Zminimal-versions`. +When only keeping MSRV-incompatible `dev-dependencies`, +one lockfile can be used but it can be difficult to edit the `Cargo.lock` to ensure you get new `dev-dependencies` without infecting other dependency types. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From f99ecac0863eabfcd52f92272d0bf5e9ff54cef1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 10 Jan 2024 16:49:19 -0600 Subject: [PATCH 074/184] fix(ref): Call out stabilization plan --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index f01bb48a06e..572a55dccd2 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -403,6 +403,8 @@ Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `carg # Reference-level explanation [reference-level-explanation]: #reference-level-explanation +We expect these changes to be beneficial enough on there own that they can be stabilized as each is completed. + ## Cargo Resolver Cargo's resolver will be updated to *prefer* MSRV compatible versions over From f1cb6bf547a43b5fc22f448f2f997abc90f0173a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 10 Jan 2024 16:59:17 -0600 Subject: [PATCH 075/184] feat(ref): Tie the solution to an Edition --- text/3537-msrv-resolver.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 572a55dccd2..9b2d01ace2e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -403,12 +403,15 @@ Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `carg # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -We expect these changes to be beneficial enough on there own that they can be stabilized as each is completed. +We expect these changes to be independent enough and beneficial on there own that they can be stabilized as each is completed. ## Cargo Resolver -Cargo's resolver will be updated to *prefer* MSRV compatible versions over -incompatible versions when resolving versions. +We will be adding a v3 resolver, specified through `workspace.resolver` / `package.resolver`. +This will become default with the next Edition. + +When `resolver = "3"` is set, Cargo's resolver will change to *prefer* MSRV compatible versions over +incompatible versions when resolving versions except for `cargo install`. Initially, dependencies without `package.rust-version` will be preferred over MSRV-incompatible packages but less than those that are compatible. The exact details for how preferences are determined may change over time, @@ -440,8 +443,6 @@ As this is just a preference for resolving dependencies, rather than prescriptiv this shouldn't cause churn of the `Cargo.lock` file. We already call `rustc` for feature resolution, so hopefully this won't have a performance impact. -The resolver will only do this for local packages and not for `cargo install`. - ## `cargo update` `cargo update` will inform users when an MSRV or semver incompatible version is available. @@ -487,18 +488,9 @@ without having to support the flag on every single command. # Drawbacks [drawbacks]: #drawbacks -As proposed, CI that tries to verify against the latest dependencies will no longer do so when `rust-version` is set. -Instead, they'll have to make a change to their CI, like setting `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. -- If we consider this a major incompatibility, then it needs to be opted into. - As `cargo fix` can't migrate a user's CI, - this would be out of scope for migrating to this with a new Edition. -- I would argue that the number of maintainers verifying latest dependencies is - relatively low and they are more likely to be "in the know", - making them less likely to be negatively affected by this. - Therefore, I propose we consider this a minor incompatibility -- If we do a slow roll out (opt-in then opt-out), the visibility for the switch - to opt-out will be a lot less than the initial announcement and we're more - likely to miss people compared to making switch over when this gets released. +Users upgrading to the next Edition (or changing to `resolver = '3"`), will have to manually update their CI to test the latest dependencies with `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. + +Workspaces have no `edition`, so its easy for users to not realize they need to set `resolver = "3"` or to update their `resolver = "2"` to `"3"`. While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. @@ -521,6 +513,13 @@ Misc alternatives - `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. - `cargo add` selecting rust-version-compatible minimum bounds helps - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. +- Instead of `resolver = "3"`, we could just change the default for everyone + - The number of maintainers verifying latest dependencies is likely + relatively low and they are more likely to be "in the know", + making them less likely to be negatively affected by this. + Therefore, we could probably get away with treating this as a minor incompatibility + - Either way, the big care about is there being attention drawn to the change. + We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) ## Make this opt-in rather than opt-out From 541580c77b70e15ebac300e1284d8b977fe14ae1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 11:09:40 -0600 Subject: [PATCH 076/184] feat(ref): Add perma-setting for '--ignore-rust-version' for builds --- text/3537-msrv-resolver.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9b2d01ace2e..51a69d8c6b0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -443,6 +443,13 @@ As this is just a preference for resolving dependencies, rather than prescriptiv this shouldn't cause churn of the `Cargo.lock` file. We already call `rustc` for feature resolution, so hopefully this won't have a performance impact. +## `cargo build` + +The MSRV-compatibility build check will be demoted from an error to a `deny`-by-default workspace +[diagnostic](https://github.com/rust-lang/cargo/issues/12235), +allowing users to intentionally use dependencies on an unsupported (or less supported) version of Rust +without requiring `--ignore-rust-version` on every invocation. + ## `cargo update` `cargo update` will inform users when an MSRV or semver incompatible version is available. @@ -520,6 +527,7 @@ Misc alternatives Therefore, we could probably get away with treating this as a minor incompatibility - Either way, the big care about is there being attention drawn to the change. We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) +- `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the package to a workspace-member for `[lint]` and dealing with unifying conflicting levels in `[lint]`. ## Make this opt-in rather than opt-out From ca32ebc052a8ad2ec88bee82cfa82de025d3b9c3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 11:23:23 -0600 Subject: [PATCH 077/184] feat(ref): Add '--update-rust-version' to help users 'progress' --- text/3537-msrv-resolver.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 51a69d8c6b0..7a5b07d01bf 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -459,6 +459,10 @@ without requiring `--ignore-rust-version` on every invocation. editing `Cargo.toml` and running `cargo check`) will not inform the user. If they want to check the status of things, they can run `cargo update -n`. +Users may pass +- `--ignore-rust-version` to pick the latest dependencies +- `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies + ## `cargo add` `cargo add ` (no version) will pick a version requirement that is low @@ -466,7 +470,9 @@ enough so that when it resolves, it will pick a dependency that is MSRV-compatible. `cargo add` will warn when it does this. -This behavior can be bypassed with `--ignore-rust-version` +Users may pass +- `--ignore-rust-version` to pick the latest dependencies +- `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies ## Cargo config @@ -528,6 +534,11 @@ Misc alternatives - Either way, the big care about is there being attention drawn to the change. We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) - `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the package to a workspace-member for `[lint]` and dealing with unifying conflicting levels in `[lint]`. +- `--ignore-rust-version` picks absolutely the latest dependencies to support (1) users on latest rustc and (2) users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows +- `--update-rust-version` picks `rustc --version`-compatible dependencies so users, no matter their `rustc` version, can easily walk the treadmill of updating their dependencies / MSRV + - There is little reason to select an MSRV higher than their Rust toolchain + - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain + - This comes at the cost of inconsistency with `--ignore-rust-version`. ## Make this opt-in rather than opt-out From e44a724394562813ff046ba35b02874e35e4c178 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 11:38:59 -0600 Subject: [PATCH 078/184] feat(ref): Add 'rust-version=auto' --- text/3537-msrv-resolver.md | 104 ++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 7a5b07d01bf..1dd1cc6de31 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -474,6 +474,13 @@ Users may pass - `--ignore-rust-version` to pick the latest dependencies - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies +## `cargo publish` + +We will add `"auto"` for `package.rust-version`. +On publish, `rustc --version` will replace `"auto"`. + +`cargo new` will include `package.rust-version = "auto"`. + ## Cargo config We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. @@ -540,7 +547,58 @@ Misc alternatives - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain - This comes at the cost of inconsistency with `--ignore-rust-version`. -## Make this opt-in rather than opt-out +## Ensuring the registry Index has `rust-version` without affecting quality + +The user experience for this is based on the extent and quality of the data. +Ensuring we have `package.rust-version` populated more often (while maintaining +quality of that data) is an important problem but does not have to be solved to +get value out of this RFC and can be handled separately. + +We chose an opt-in for populating `package.rust-version` based on `rustc --version`. +This will encourage a baseline of quality as are developing with that version and `cargo publish` will do a verification step, by default. +This will help seed the Index with more `package.rust-version` data for the resolver to work with. +The downside is that the `package.rust-version` will likely be higher than it absolutely needs. +However, considering our definition of "support" and that the user isn't bothering to set an MSRV themself, +aggressively updating is likely fine in this case. + +Alternatively... + +~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ +However, this will err on the side of a higher MSRV than necessary and the only way to +workaround it is to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` which will then lose +all other protections. +As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. +As things evolve, we could re-evaluate making `"auto"` the default. + +~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ +However, if people aren't committed to verifying it, +it is likely to go stale and will claim an MSRV much older than what is used in practice. +If we had the hard-error resolver mode and +[clippy warning people when using API items stabilized after their MSRV](https://github.com/rust-lang/rust-clippy/issues/6324), +this will at least annoy people into either being somewhat compatible or removing the field. + +~~When missing, `cargo publish` could inject `package.rust-version` inferred from +`package.edition` and/or other `Cargo.toml` fields.~~ +However, this will err on the side of too low of an MSRV. +While this might help with in this situation, +it would lock us in to inaccurate information which might limit what analysis we could do in the future. + +Alternatively, `cargo publish` / the registry could add new fields to the Index +to represent an inferred MSRV, the published version, etc +so it can inform our decisions without losing the intent of the publisher. + +We could help people keep their MSRV up to date, by letting them specify a policy (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. +This would also be an alternative to `--update-rust-version`. + +On the resolver side, we could +- Assume the MSRV of the next published package with an MSRV set +- Sort no-MSRV versions by minimal versions, the lower the version the more likely it is to be compatible + - This runs into quality issues with version requirements that are likely too low for what the package actually needs + - For dependencies that never set their MSRV, this effectively switches us from maximal versions to minimal versions. + +## Resolver behavior + +### Make this opt-in rather than opt-out This proposed solution elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. We could instead do the opposite, carrying forward our existing behavior as the default (`CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`). @@ -667,7 +725,7 @@ Opt-in for MSRV resolution is also putting focus on a lower area of cost/risk fo while nothing is being done for major version. Improving things for major versions will likely improve things for MSRV. -## Make `rust-version=rustc` the default +### Make `rust-version=rustc` the default This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. We could instead do the opposite, adding support for `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` instead as a "safe" default for assuming the development rust-version. @@ -706,7 +764,7 @@ making supporting them through problems more difficult. As this encourages "shared development / publish rust-version" workflow, see the "opt-in" solution for the caveats of encouraging that workflow. -## Hard-error +### Hard-error Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. This means that we would also backtrack on transitive dependencies, trying alternative versions of direct dependencies, which would create an MSRV-compatible `Cargo.lock` in more cases. @@ -807,46 +865,6 @@ The config field is fairly rough # Future possibilities [future-possibilities]: #future-possibilities -## Improve the experience with lack of `rust-version` - -The user experience for this is based on the extent and quality of the data. -Ensuring we have `package.rust-version` populated more often (while maintaining -quality of that data) is an important problem but does not have to be solved to -get value out of this RFC and can be handled separately. - -~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ -However, if people aren't committed to verifying it, -it is likely to go stale and will claim an MSRV much older than what is used in practice. -If we had the hard-error resolver mode and -[clippy warning people when using API items stabilized after their MSRV](https://github.com/rust-lang/rust-clippy/issues/6324), -this will at least annoy people into either being somewhat compatible or removing the field. - -~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ -However, this will err on the side of a higher MSRV than necessary and the only way to -workaround it is to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` which will then lose -all other protections. - -~~When missing, `cargo publish` could inject based on the rustup toolchain file.~~ -However, this will err on the side of a higher MSRV than necessary as well. - -~~When missing, `cargo publish` could inject `package.rust-version` inferred from -`package.edition` and/or other `Cargo.toml` fields.~~ -However, this will err on the side of too low of an MSRV. -While this might help with in this situation, -it would lock us in to inaccurate information which might limit what analysis we could do in the future. - -Alternatively, `cargo publish` / the registry could add new fields to the Index -to represent an inferred MSRV, the published version, etc -so it can inform our decisions without losing the intent of the publisher. - -We could help people keep their MSRV up to date, by letting them specify a policy (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. - -On the resolver side, we could -- Assume the MSRV of the next published package with an MSRV set -- Sort no-MSRV versions by minimal versions, the lower the version the more likely it is to be compatible - - This runs into quality issues with version requirements that are likely too low for what the package actually needs - - For dependencies that never set their MSRV, this effectively switches us from maximal versions to minimal versions. - ## Integrate `cargo audit` If we [integrate `cargo audit`](https://github.com/rust-lang/cargo/issues/7678), From 941b0a3d432b428987bc0b18512de04da077c6a6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 11:39:51 -0600 Subject: [PATCH 079/184] refactor(rationale): Re-order sections --- text/3537-msrv-resolver.md | 86 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 1dd1cc6de31..96409ad9900 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -596,6 +596,49 @@ On the resolver side, we could - This runs into quality issues with version requirements that are likely too low for what the package actually needs - For dependencies that never set their MSRV, this effectively switches us from maximal versions to minimal versions. +## Reporting + +Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), +we can run the resolver twice on the original input, once for MSRV and once without. +We then do a depth-first diff of the trees, stopping and reporting on the first different node. +This would let us report on any command that changes the way the tree is resolved. +We'd likely want to limit the output to only the sub-tree that changed. + +We could either always do the second resolve or only do the second resolve if the resolver changed anything, +whichever is faster. + +Its unknown whether making the inputs available for multiple resolves would have a performance impact. + +While a no-change resolve is fast, if this negatively impacts it enough, we +could explore hashing the resolve inputs and storing that in the lockfile, +allowing us to detect if the inputs have changed and only resolving then. + +## Configuring the resolver mode on the command-line or `Cargo.toml` + +The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). +However, there is a lot more to define for us to get there. Some routes that need further exploration include: +- If its a CLI flag, then its transient, and its unclear which modes should be transient now and in the future + - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change +- We could put this in `Cargo.toml` but that implies it unconditionally applies to everything + - But we want `cargo install` to use the latest dependencies so people get bug/security fixes + - This gets in the way of the "separate development / publish MSRV" workflow + +By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. + +## Add `workspace.rust-version` + +Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. + +This opens its own set of questions +- Do packages implicitly inherit this? +- What are the semantics if its unset? +- Would it be confusing to have this be set in mixed-MSRV workspaces? Would blocking it be incompatible with the semantics when unset? +- In mixed-MSRV workspaces, does it need to be the highest or lowest MSRV of your packages? + - For the resolver, it would need to be the lowest but there might be other use cases where it needs to be the highest + +The proposed solution does not block us from later going down this road but +allows us to move forward without having to figure out all of these details. + ## Resolver behavior ### Make this opt-in rather than opt-out @@ -797,49 +840,6 @@ we wouldn't want to fallback to the version of rustc being used because that cou Without further design work, this would be incompatible with feature-dependent MSRV and likely with the "separate development / publish MSRV" workflow. -## Reporting - -Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), -we can run the resolver twice on the original input, once for MSRV and once without. -We then do a depth-first diff of the trees, stopping and reporting on the first different node. -This would let us report on any command that changes the way the tree is resolved. -We'd likely want to limit the output to only the sub-tree that changed. - -We could either always do the second resolve or only do the second resolve if the resolver changed anything, -whichever is faster. - -Its unknown whether making the inputs available for multiple resolves would have a performance impact. - -While a no-change resolve is fast, if this negatively impacts it enough, we -could explore hashing the resolve inputs and storing that in the lockfile, -allowing us to detect if the inputs have changed and only resolving then. - -## Configuring the resolver mode on the command-line or `Cargo.toml` - -The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). -However, there is a lot more to define for us to get there. Some routes that need further exploration include: -- If its a CLI flag, then its transient, and its unclear which modes should be transient now and in the future - - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change -- We could put this in `Cargo.toml` but that implies it unconditionally applies to everything - - But we want `cargo install` to use the latest dependencies so people get bug/security fixes - - This gets in the way of the "separate development / publish MSRV" workflow - -By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. - -## Add `workspace.rust-version` - -Instead of using the lowest MSRV among workspace members, we could add `workspace.rust-version`. - -This opens its own set of questions -- Do packages implicitly inherit this? -- What are the semantics if its unset? -- Would it be confusing to have this be set in mixed-MSRV workspaces? Would blocking it be incompatible with the semantics when unset? -- In mixed-MSRV workspaces, does it need to be the highest or lowest MSRV of your packages? - - For the resolver, it would need to be the lowest but there might be other use cases where it needs to be the highest - -The proposed solution does not block us from later going down this road but -allows us to move forward without having to figure out all of these details. - # Prior art [prior-art]: #prior-art From 4e0a359062c4ff7d9623a6c51e2b032c4c2e19b4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 11:57:06 -0600 Subject: [PATCH 080/184] fix(unresolved): Add another alt for config field --- text/3537-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 96409ad9900..6484a3340df 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -861,6 +861,7 @@ The config field is fairly rough - The location (within `build`) needs more consideration - The name isn't very clear - The values are awkward + - Should we instead just have a `resolver.rust-version = true`? # Future possibilities [future-possibilities]: #future-possibilities From 2f709d60151f9f5bef8c6a196dfbe5f4208cc0be Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 12:24:15 -0600 Subject: [PATCH 081/184] fix(future): Clarify why cargo-audit --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 6484a3340df..51c8d218335 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -869,7 +869,8 @@ The config field is fairly rough ## Integrate `cargo audit` If we [integrate `cargo audit`](https://github.com/rust-lang/cargo/issues/7678), -we can better help users on older dependencies identify security vulnerabilities. +we can better help users on older dependencies identify security vulnerabilities, +reducing the risks associated with being on older versions. ## "cargo upgrade" From d52fdb3555f07acdf871ce725daa0a8e046555d9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 13:53:13 -0600 Subject: [PATCH 082/184] refactor(rationale): Re-work resolver discussion around workflows --- text/3537-msrv-resolver.md | 292 +++++++++++++------------------------ 1 file changed, 100 insertions(+), 192 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 51c8d218335..d722b0facf2 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -641,204 +641,112 @@ allows us to move forward without having to figure out all of these details. ## Resolver behavior +Affects of current solution on workflows (including non-resolver behavior): +- Latest Rust with no MSRV + - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) +- Latest Rust as the MSRV + - ✅ Packages can more easily keep their MSRV up-to-date with + - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV + - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic +- Extended MSRV + - ✅ `Cargo.lock` will Just Work +- Extended published MSRV w/ latest development MSRV + - ❌ Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml` + ### Make this opt-in rather than opt-out -This proposed solution elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. -We could instead do the opposite, carrying forward our existing behavior as the default (`CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`). -CI verifying MSRV and users of the "shared development / publish rust-version" workflow would need to set `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. - -When building with old Rust versions, error messages could suggest re-resolving with `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. -The next corrective step (and suggestion from cargo) depends on what the user is doing and could be either -- `git checkout main -- Cargo.lock && cargo check` -- `cargo generate-lockfile` - -We should update the "incompatible rust-version" checks to be top-down, rather -than bottom up, -so users see the root of their problem, rather than the leaves. - -This has no impact on `cargo add`; it will still pick a version requirement that is MSRV-compatible. - -This avoids changing behavior for CI jobs that are trying to test the latest dependencies,. - -On the surface, encouraging people to primarily use maximal version resolution by -making this opt-in encourages more testing of the latest dependencies. -Before we [changed our guidance on lockfiles](https://github.com/rust-lang/cargo/pull/12382), -this was already limited as `bin`s should have a `Cargo.lock` file which is -infectious to their entire workspace. -For local development, you are reusing the same `Cargo.lock` over time, adjusted only when a version requirement forces it, causing you to not get "maximal versions" at the time of a change. -So that leaves CI for seeing / verifying maximal versions. -This was also subject to the velocity of the project; -for passively maintain projects they can go a year without a CI run. -Now that we've changed our guidance on lockfiles, -we encourage people to verify their latest dependencies. -Assuming they are, this point becomes moot. - -A lot of this comes down to a mixture of usability and what behavior we want to drive. -When driving behavior, the intention is to help people find a known good path -without preventing them from doing things differently if they have the need -(cargo is intended to be [opinionated](https://doc.crates.io/contrib/design.html#design-principles)). -When done right, this is frictionless and people are understanding. -When done wrong, it is frustrating and people feel condescended towards. - -One area of particular concern for the Rust project is stagnation. -We want to encourage people to actively upgrade Rust and their dependencies. -- Upgrading comes at a cost that you can pay now incrementally or pay all at once when forced, like with a security vulnerability. That said, the community puts a strong emphasis on keeping the cost low. -- The longer the delay for using new features, implementing new ones has greater diminishing returns, discouraging progress generally. -- The more separated people are from nightly, the more costly it is for them to test out new features on their project, causing the Rust project to lose out on a valuable source of feedback. -- When people push their needs for old versions onto their dependencies, this shifts the cost from the exception case to the maintainer and all of the dependents. - -For **"MSRV is stable"**, they are unaffected by either opt-in or opt-out. - -The **"separate development / publish rust-version"** workflow is interesting. - -This aligns with the opt-in approach, making it implicitly endorsed. - -At this time, validating MSRV with this workflow is complicated, either -juggling two separate lockfiles or being compatible with and using the unstable -feature `-Zminimal-version`. -This suggests that those following this workflow, at least for now, are more -advanced users who are less likely to be impacted by needing to opt-in to a new -flag. -Of course, this only reflects on the transition cost and how surprising changing the behavior would be for existing Rust users and not on what is the right choice long term. - -If your building and testing becomes dependent on the capabilities of the newer toolchain for development, -your dependents can't patch in the git dependency to try out a fix or feature and -you can't validate your package's MSRV [without heroics](https://github.com/taiki-e/cargo-hack/issues/216). -In some cases, people have leveraged this workflow to intentionally not verify their MSRV, -treating their MSRV as [tier 3](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3). -Instead, we should be driving people towards patchable dependencies and verified MSRVs and away from intentionally unverifiable MSRVs. - -A potential side effect of less-verified MSRVs is that frees maintainers up to have larger MSRV policies than their dependents as they aren't being forced to update their MSRV to get a bug fix -(assuming it won't be backported to a patch release within a compatible MSRV). -However, longer MSRV policies encourage more stagnation -as falling behind in dependencies represents a risk when it comes to security vulnerabilities. -Alternatively when supporting dependents with older MSRVs is needed, maintainers could reserve MSRV bumps for when bumping their minor version, -leaving room in their version numbers to release backported fixes. - -In addition, without support for more granular MSRV declarations, -I would suggest that the behavior we should drive is that `package.rust-version` applies generally, -including for local development. -In this way, MSRV is different than minimal version resolution, -despite rustc being yet another dependency. - -Application development is a potential specialization in this area. -There are already likely differences between development and production, including -- Loading of assets locally vs bundling -- `debug` vs `release` profiles -- Default target CPU vs specialized target CPU - -An application developer might be willing to say "I'll support old MSRVs for -Debian but my regular releases get all the latest bells and whistles" (from cargo dependencies only) and -decide to develop with the latest dependencies, while verifying an MSRV in CI. -For the subset of applications that are being installed via `cargo install`, -this runs into a problem. -The standing assumption is that `cargo install` does not reuse the associated `Cargo.lock` to ensure the latest bug and security fixes are used -(except when they are stuck behind breaking changes) -but that if something goes wrong, -`--locked` is available to use the `Cargo.lock` file to build with a known, good (i.e. verified) state -(e.g. [rust-lang/cargo#10891](https://github.com/rust-lang/cargo/issues/10891)). -Without more granular MSRV declarations, -users should be able to expect that the MSRV means that `Cargo.lock` is verified against the MSRV so that `cargo install --locked` can work for these users as well. - -For the **"shared development / publish rust-version"** workflow, -the user will discover this the first time they validate with their MSRV. -The error will help guide them to how to fix this (set a config and re-resolve) -This will most likely be checked in via a config file which will make this a fix-and-forget. - -There can be some mild frustration in the vein of "if it can figure it out, why doesn't it". -This is the type of "guiding" of a user that can put people off and borderlines on condescending. -This also shows it fails the principle of least surprise for people supporting old rustc's. -Users know cargo has the information and are surprised that it doesn't use it. - -By having this behavior be non-default, we are implicitly steering people away from this approach. - -A side benefit for those following the "shared development / publish rust-version" workflow -(which we are steering people away from) -is that they get feedback earlier about using APIs from dependencies too new for their MSRV -(though this does not replace validation in CI). -There are more ideal solutions, like stable use of `#[stable]` and telling rustc about the minimum possible version of a dependency. -The path and time table for that is unclear. -This does not justify prioritizing this workflow on its own but contributes to the whole picture -and is not a precedence for switching to minimal version resolution. - -As the opt-in is a one-and-done (and likely not by someone setting policy), -it is unlikely to discourage stagnation. -Whether its opt-in or opt-out, keeping users informed that they are behind on dependencies is much more likely to drive people to updating. -Opt-in for MSRV resolution is also putting focus on a lower area of cost/risk for stagnation -while nothing is being done for major version. -Improving things for major versions will likely improve things for MSRV. - -### Make `rust-version=rustc` the default - -This proposal elevates "shared development / publish rust-version" workflow over "separate development / publish rust-version" workflow. -We could instead do the opposite, adding support for `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` instead as a "safe" default for assuming the development rust-version. - -This has no impact on `cargo add`; it will still pick a version requirement that is MSRV-compatible. - -In terms of keeping this proposal minimal, this means we are likely to not include `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. -For people with the "shared development / publish rust-version" workflow, this would push them to using a `rust-toolchain.toml` file. -The downsides to using a `rust-toolchain.toml`" are: -- People are being "locked in" to unsupported versions of Rust - - **This does not align with us wanting to drive behavior we want because we are - pushing people to develop with unsupported toolchains.** -- Its environment config, and not project config, and is infectious in other situations without explicit action by a user who knows how to resolve it -- You lose out on new toolchain features like - improved error messages, - improved clippy lints, - sparse registry support, - `cargo publish` waiting until publish is complete, - `Cargo.toml`s `[lints]`, - or this proposal once implemented. - - While the toolchain is another type of dependency so this might seem - contradictory but we feel the value-add of a new toolchain outweighs the cost - while the value add of new dependencies doesn't - -As for encouraging testing of the latest dependencies, -this falls somewhere between the opt-in and opt-out proposals for resoling to `package.rust-version`, -depending on the scenario. -If you don't check-in your `Cargo.lock`, -what developers will test with is anyone's guess. -As for CI, it will be dependent on which toolchain is used (at least `stable`). -If you do check-in your `Cargo.lock` as is suggested (but not prescribed), -then you are subject to whatever versions were compatible with the toolchain of each developer who caused a `Cargo.lock` change. - -In the scenario where the `Cargo.lock` is not committed, every contributor will be using a different set of dependencies, -making supporting them through problems more difficult. - -As this encourages "shared development / publish rust-version" workflow, see the "opt-in" solution for the caveats of encouraging that workflow. +Instead of adding `resolver = "3"`, we could keep the default resolver the same as today but allow opt-in to MSRV-aware resolver via `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. +- When building with old Rust versions, error messages could suggest re-resolving with `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. + The next corrective step (and suggestion from cargo) depends on what the user is doing and could be either + - `git checkout main -- Cargo.lock && cargo check` + - `cargo generate-lockfile` +- We should update the "incompatible rust-version" checks to be top-down, rather + than bottom up, + so users see the root of their problem, rather than the leaves. +- We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default + +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). + +Affects on workflows (including non-resolver behavior): +- Latest Rust with no MSRV + - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - 🟰 ~~Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`)~~ +- Latest Rust as the MSRV + - ✅ Packages can more easily keep their MSRV up-to-date with + - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ + - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic +- Extended MSRV + - ✅ Users be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` + - ❌ Users will be frustrated that the tool knew what they wanted and didn't do it +- Extended published MSRV w/ latest development MSRV + - 🟰 ~~Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml`~~ + - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features + +### Make `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` the default + +Instead of `resolver = "3"` changing the behavior to `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`, +it is changed to `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` where the resolver selects packages compatible with current toolchain, +matching the `cargo build` incompatible dependency error. +- We would still support `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users +- We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new defaul + +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). + +This is an auto-adapting variant where +- If they are on the latest toolchain, they get the current behavior +- If their toolchain matches their MSRV, they get an MSRV-aware resolver + +Affects on workflows (including non-resolver behavior): +- Latest Rust with no MSRV + - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) +- Latest Rust as the MSRV + - ✅ Packages can more easily keep their MSRV up-to-date with + - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ + - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic +- Extended MSRV + - ✅ Users be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` + - ❌ Users will be frustrated that the tool knew what they wanted and didn't do it + - ❌ This may encourage maintainers to develop using their MSRV, reducing the quality of their experience (not getting latest lints, not getting latest cargo features like "wait for publish", etc) +- Extended published MSRV w/ latest development MSRV + - ❌ Maintainers will have to opt-in to ensure they get the latest dependencies in a `.cargo/config.toml` ### Hard-error Instead of *preferring* MSRV-compatible dependencies, the resolver could hard error if only MSRV-incompatible versions are available. -This means that we would also backtrack on transitive dependencies, trying alternative versions of direct dependencies, which would create an MSRV-compatible `Cargo.lock` in more cases. - -Nothing in this solution changes our ability to do this later. - -However, blocking progress on this approach would greatly delay stabilization of this because of bad error messages. -This was supported in 1.74 and 1.75 nightlies under `-Zmsrv-policy` and the biggest problem was in error reporting. -The resolver acted as if the MSRV-incompatible versions don't exist so if there was no solution, the error message was confusing: -```console -$ cargo +nightly update -Z msrv-policy - Updating crates.io index -error: failed to select a version for the requirement `hashbrown = "^0.14"` -candidate versions found which didn't match: 0.14.2, 0.14.1, 0.14.0, ... -location searched: crates.io index -required by package `app v0.1.0 (/app)` -perhaps a crate was updated and forgotten to be re-vendored? -``` - -It would also be a breaking change to hard-error. -We'd need to provide a way for some people to opt-in while some people opt-out and remember that. -We could add a sticky flag to `Cargo.lock` though that could also be confusing, see "Configuring the resolver mode on the command-line or `Cargo.toml`". - -This would also error or pick lower versions more than it needs to when a workspace contains multiple MSRVs. -We'd want to extend the resolver to treat Rust as yet another dependency and turn `package.rust-version` into dependencies on Rust. -This could cause a lot more backtracking which could negatively affect resolver performance for people with lower MSRVs. - -If no `package.rust-version` is specified, -we wouldn't want to fallback to the version of rustc being used because that could cause `Cargo.lock` churn if contributors are on different Rust versions. - -Without further design work, this would be incompatible with feature-dependent MSRV and likely with the "separate development / publish MSRV" workflow. +- `--ignore-rust-version` would need to be "sticky" in the `Cargo.lock` to avoid the next run command from rolling back the `Cargo.lock` which might be confusing because it is "out of sight; out of mind". +- To avoid `Cargo.lock` churn, we can't fallback to `rustc --version` when `package.rust-version` is not present + +In addition to errors, differences from the "preference" solutions include: +- Increase the chance of an MSRV-compatible `Cargo.lock` because the resolver can backtrack on MSRV-incompatible transitive dependencies, trying alternative versions of direct dependencies +- When a workspace members have different MSRVs, dependencies exclusive to a higher MSRV package can use higher versions + +To get the error reporting to be of sufficient quality will require major work in a complex, high risk area of Cargo (the resolver). +This would block stabilization indefinitely. +We could adopt this approach in the future, if desired + +Affects on workflows (including non-resolver behavior): +- Latest Rust with no MSRV + - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ❌ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) +- Latest Rust as the MSRV + - ✅ Packages can more easily keep their MSRV up-to-date with + - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV + - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic +- Extended MSRV + - ✅ `Cargo.lock` will Just Work for `package.rust-version` + - ❌ Application developers using `rust-toolchain.toml` will have to duplicate that in `package.rust-version` and keep it in sync +- Extended published MSRV w/ latest development MSRV + - ❌ A design not been worked out to allow this workflow + - ❌ If this is done unconditionally, then the `Cargo.lock` will change on upgrade + - ❌ This is incompatible with per-`feature` MSRVs # Prior art [prior-art]: #prior-art From 8a6bc9bf9f251e706dbc8148c8b3a137686a5abf Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:02:30 -0600 Subject: [PATCH 083/184] fix(motivation): Better balance stagnation-avoidance --- text/3537-msrv-resolver.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index d722b0facf2..fb6a4755bea 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -187,6 +187,8 @@ Some design criteria we can use for evaluating use cases: - Proactively upgrading means the total benefit to developers from investments made in Rust is higher - Conversely, when most of the community is on old versions, it has a chilling effect on improving Rust - This also means feedback can come more quickly, making it easier and cheaper to pivot with user needs + - Spreading the cost of upgrades over time makes forced-upgrades (e.g. for a security vulnerability) less of an emergency + - Our commitment to compatibility helps keep the cost of upgrade low - The costs of “non-recommended” setups should be isolated to those that need them - Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users - Cargo must not make major breaking changes @@ -200,6 +202,7 @@ And keeping in mind - The Rust project only supports the latest version (e.g bug and security fixes) and the burden for support for older versions is on the vendor providing the older Rust toolchain. +- Even keeping upgrade costs low, there is still a re-validation cost that mission critical applications must pay - A `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` - e.g. If the maintainer does `cargo add foo && git commit && git push`, then a contributor doing `git pull && cargo check` should have an unchanged `Cargo.lock` From 12eb56fb4735ef1b627b973560bf4e6c7b4f9a03 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:03:31 -0600 Subject: [PATCH 084/184] fix(motivation): Soften language to not shame bugs --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fb6a4755bea..9c6d3867324 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -191,7 +191,7 @@ Some design criteria we can use for evaluating use cases: - Our commitment to compatibility helps keep the cost of upgrade low - The costs of “non-recommended” setups should be isolated to those that need them - Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users -- Cargo must not make major breaking changes +- Cargo should not make major breaking changes - Every feature has a cost and we should balance the cost against the value we expect - Features can further constrain what can be done in the future due to backwards compatibility - Features increase maintenance burden From 2504124744b70191998822e2891939c9d075eec0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:05:14 -0600 Subject: [PATCH 085/184] fix(motivation): Clarify that lockfile changes are restricted to dep versions --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9c6d3867324..f81ad2b19d2 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -203,9 +203,9 @@ And keeping in mind (e.g bug and security fixes) and the burden for support for older versions is on the vendor providing the older Rust toolchain. - Even keeping upgrade costs low, there is still a re-validation cost that mission critical applications must pay -- A `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` +- Dependencies in `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` - e.g. If the maintainer does `cargo add foo && git commit && git push`, - then a contributor doing `git pull && cargo check` should have an unchanged `Cargo.lock` + then a contributor doing `git pull && cargo check` should not have a different selection of dependencies. Some implications: - "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version From 2365d6f5b6eb4d85fb103924e48907dd7291f7b1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:05:47 -0600 Subject: [PATCH 086/184] fix(motivation): Grammar --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index f81ad2b19d2..1ce33931675 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -203,7 +203,7 @@ And keeping in mind (e.g bug and security fixes) and the burden for support for older versions is on the vendor providing the older Rust toolchain. - Even keeping upgrade costs low, there is still a re-validation cost that mission critical applications must pay -- Dependencies in `Cargo.lock` is expected to not change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` +- Dependencies in `Cargo.lock` are not expected to change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` - e.g. If the maintainer does `cargo add foo && git commit && git push`, then a contributor doing `git pull && cargo check` should not have a different selection of dependencies. From 5a8ed3d5ab8a6bf4499bc6415b4c70a949661b87 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:08:29 -0600 Subject: [PATCH 087/184] fix(motivation): Call out dependent impact on no-msrv --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 1ce33931675..381df99e9cd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -240,6 +240,8 @@ We do not provide a way to help users know new versions are available, to suppor MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. Otherwise, they must actively run `rustup update` or follow Rust news. +For dependents, this makes it harder to know what versions are "safe" to use. + ### Latest Rust as the MSRV A maintainer regularly updates their MSRV to latest. From 165d447d5ddd0b97ee81e33f055ae1afaba909d7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 11 Jan 2024 14:10:06 -0600 Subject: [PATCH 088/184] fix(motivation): Clarify toolchain problem with latest msrv --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 381df99e9cd..76f64cac258 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -259,7 +259,8 @@ likely pushing them to not specify their MSRV. **Pain points (in addition to the prior workflow):** -We do not help these users with keeping their MSRV up-to-date. +In addition to their toolchain version, we do not help these users with keeping +their MSRV up-to-date. They can use other tools like [RenovateBot](https://github.com/rust-lang/cargo/blob/87eb374d499100bc945dc0e50ae5194ae539b964/.github/renovate.json5#L12-L24) though that causes extra churn in the repo. From a98f54ad454ab4bad1c5e4f0ee312c699dd3b864 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 09:57:48 -0600 Subject: [PATCH 089/184] fix(motivation): Add list separator --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 76f64cac258..94339b09697 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -165,7 +165,7 @@ People have tried to reduce the pain from MSRV with its own costs: policy for updating a minimum-supported Rust version (MSRV), wearing on all (e.g. - [libc](https://github.com/rust-lang/libs-team/issues/72) + [libc](https://github.com/rust-lang/libs-team/issues/72), [time](https://github.com/time-rs/time/discussions/535) ) From c2b70e51d6cb0dd3ef4d7e77d95e97e876f09113 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:00:07 -0600 Subject: [PATCH 090/184] fix(motivation): Re-order principles to soften it --- text/3537-msrv-resolver.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 94339b09697..cc9702e91b0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -180,22 +180,22 @@ development version without upgrading the MSRV. In solving this, we need to keep in mind how people are using Cargo and how to prioritize when needs of different workflows conflict. We will then look at the potential designs within the context of this framework. -Some design criteria we can use for evaluating use cases: +Some design criteria we can use for evaluating workflows: +- Cargo should not make major breaking changes - Low barrier to entry +- The costs of “non-recommended” setups should be isolated to those that need them - Encourage a standard of quality within the ecosystem +- Every feature has a cost and we should balance the cost against the value we expect + - Features can further constrain what can be done in the future due to backwards compatibility + - Features increase maintenance burden + - The larger the user-facing surface, the less likely users will find the feature they need and instead use the quickest shortcut +- Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users - Encourage progress and avoid stagnation - Proactively upgrading means the total benefit to developers from investments made in Rust is higher - Conversely, when most of the community is on old versions, it has a chilling effect on improving Rust - This also means feedback can come more quickly, making it easier and cheaper to pivot with user needs - Spreading the cost of upgrades over time makes forced-upgrades (e.g. for a security vulnerability) less of an emergency - Our commitment to compatibility helps keep the cost of upgrade low -- The costs of “non-recommended” setups should be isolated to those that need them -- Being transparent makes debugging easier, helps in evaluating risks (including security), and builds confidence in users -- Cargo should not make major breaking changes -- Every feature has a cost and we should balance the cost against the value we expect - - Features can further constrain what can be done in the future due to backwards compatibility - - Features increase maintenance burden - - The larger the user-facing surface, the less likely users will find the feature they need and instead use the quickest shortcut - When not competing with the above, we should do the right thing for the user rather than disrupt their flow to telling them what they should instead do And keeping in mind From b28ffd2daccaa97f1293d9498211f6692ae7258c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:01:23 -0600 Subject: [PATCH 091/184] fix(motivation): Soften costs talk --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cc9702e91b0..1b4250f2083 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -183,7 +183,7 @@ We will then look at the potential designs within the context of this framework. Some design criteria we can use for evaluating workflows: - Cargo should not make major breaking changes - Low barrier to entry -- The costs of “non-recommended” setups should be isolated to those that need them +- The costs of “non-recommended” setups should focused on those that need them - Encourage a standard of quality within the ecosystem - Every feature has a cost and we should balance the cost against the value we expect - Features can further constrain what can be done in the future due to backwards compatibility From c5a7d63df3029d110c768d4596ea2c15827d4f59 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:03:33 -0600 Subject: [PATCH 092/184] fix(motivation): Bridge global MSRV to exceptions --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 1b4250f2083..d1fae83fb12 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -211,7 +211,7 @@ Some implications: - "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version - MSRV applies to all interactions with a project (including registry dependency, git dependency, `cargo install`, contributor experience), - unless documented otherwise + unless documented otherwise like - Some projects may document that enabling a feature will affect the MSRV (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) - Some projects may have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish) - We should focus the cost for maintaining support for older versions of Rust on the user of the old version and away from the maintainer or the other users of the library or tool From f390368d9e2941be05f7bb0caf642cb6073bd456 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:04:51 -0600 Subject: [PATCH 093/184] fix(motivation): Typo --- text/3537-msrv-resolver.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index d1fae83fb12..c81e40cfb65 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -236,7 +236,7 @@ A maintainer may also want to avoid constraining their dependents, for a variety **Pain points:** -We do not provide a way to help users know new versions are available, to support the users in saying up-to-date. +We do not provide a way to help users know new versions are available, to support the users in staying up-to-date. MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. Otherwise, they must actively run `rustup update` or follow Rust news. @@ -270,7 +270,7 @@ but the requirement that dependents always pass `--ignore-rust-version` makes th ### Extended MSRV -This could be people exclusively running one version or that support a range a versions. +This could be people exclusively running one version or that support a range of versions. So why are people on old versions? - Not everyone is focused on Rust development and might only touch their Rust code once every couple of months, making it a pain if they have to update every time. @@ -280,7 +280,7 @@ So why are people on old versions? - Re-validation costs for updating core parts of the image for an embedded Linux developers can be high, keeping them on older versions - Updates can be slow within tightly controlled environments (airgaps, paperwork, etc) - Qualifying Rust toolchains takes time and money, see [Ferrocene](https://ferrous-systems.com/ferrocene/) -- Build on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) +- Built on or for systems that are no longer supported by rustc (e.g. old glibc, AndroidNDK, etc) - Library and tool maintainers catering to the above use cases The MSRV may extend back only a couple releases or to a year+ and @@ -288,7 +288,6 @@ they may choose to update on an as-need basis or keep to a strict cadence. Depending on the reason they are working with an old version, they might be developing the project with it or they might be using the latest toolchain. - For some of these use cases, they might controlling their "MSRV" via `rust-toolchain.toml`, rather than `package.rust-version`, as its their only supported Rust version (e.g. an application with a vetted toolchain). When multiple Rust versions are supported, like with library and tool maintainers, From ba2acaed2ae649c7b6f4cf837efe7bb1233f51ba Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:14:34 -0600 Subject: [PATCH 094/184] fix(motivation): Clarify dev-dependencies workflow --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c81e40cfb65..43a8ba53522 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -321,7 +321,7 @@ For instance, a project might intentionally skip testing against their MSRV beca In some cases, the MSRV-incompatible dependencies might be restricted to `dev-dependencies`. Though local development can't be performed with the MSRV, -the fact that the tests are verifying (on a newer MSRV) that the dependencies work gives a good amount of confidence that they will work on the MSRV so long as they compile. +the fact that the tests are verifying (on a newer toolchain) that the build/normal dependencies work gives a good amount of confidence that they will work on the MSRV so long as they compile. Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. From 9b119ebbdf439858b70aa87ee911b668cbf9261a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:31:38 -0600 Subject: [PATCH 095/184] fix(ref): Soften language on cargo-update --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 43a8ba53522..705f2e02b06 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -461,7 +461,7 @@ without requiring `--ignore-rust-version` on every invocation. `cargo update -n` will also report this information so that users can check on the status of this at any time. **Note:** other operations that cause `Cargo.lock` entries to be changed (like -editing `Cargo.toml` and running `cargo check`) will not inform the user. +editing `Cargo.toml` and running `cargo check`) may not inform the user. If they want to check the status of things, they can run `cargo update -n`. Users may pass From eebb556211cabe59525d85e40f29b9d7fe2d15b6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:32:25 -0600 Subject: [PATCH 096/184] fix(ref): Clarify reasoning for behavior --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 705f2e02b06..4d20db42eda 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -465,7 +465,7 @@ editing `Cargo.toml` and running `cargo check`) may not inform the user. If they want to check the status of things, they can run `cargo update -n`. Users may pass -- `--ignore-rust-version` to pick the latest dependencies +- `--ignore-rust-version` to pick the latest dependencies, ignoring all `rust-version` fields (your own and from dependencies) - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies ## `cargo add` @@ -476,7 +476,7 @@ MSRV-compatible. `cargo add` will warn when it does this. Users may pass -- `--ignore-rust-version` to pick the latest dependencies +- `--ignore-rust-version` to pick the latest dependencies, ignoring all `rust-version` fields (your own and from dependencies) - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies ## `cargo publish` From 62a217fd9144e9b1fe084e1af64caa26fe38423c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:33:23 -0600 Subject: [PATCH 097/184] fix(ref): Soften cargo-add language --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 4d20db42eda..902dcd8bf06 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -471,7 +471,7 @@ Users may pass ## `cargo add` `cargo add ` (no version) will pick a version requirement that is low -enough so that when it resolves, it will pick a dependency that is +enough so that when it resolves, it can pick a dependency that is MSRV-compatible. `cargo add` will warn when it does this. From a886c92c4807826121c0e74546e3cf8844fe0771 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:34:25 -0600 Subject: [PATCH 098/184] fix(ref): Move config by resolver --- text/3537-msrv-resolver.md | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 902dcd8bf06..780683c0bbe 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -448,6 +448,30 @@ As this is just a preference for resolving dependencies, rather than prescriptiv this shouldn't cause churn of the `Cargo.lock` file. We already call `rustc` for feature resolution, so hopefully this won't have a performance impact. +## Cargo config + +We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. + +```toml +[build] +resolver.precedence = "rust-version" # Default +``` +with support values being: +- `maximum`: behavior today + - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) +- `minimum` (unstable): `-Zminimal-versions` + - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this +- `rust-version`: what is defined in the package (default) +- `rust-version=` (assumes `maximum` is the fallback) + - `package`: long form of `rust-version` + - `rustc` (future possibility): the current running version + - Needed for "separate development / publish MSRV" workflow + - `[.[.]]` (future possibility): manually override the version used + +If a `rust-version` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. +This will let users effectively pass `--ignore-rust-version` to all commands, +without having to support the flag on every single command. + ## `cargo build` The MSRV-compatibility build check will be demoted from an error to a `deny`-by-default workspace @@ -486,30 +510,6 @@ On publish, `rustc --version` will replace `"auto"`. `cargo new` will include `package.rust-version = "auto"`. -## Cargo config - -We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. - -```toml -[build] -resolver.precedence = "rust-version" # Default -``` -with support values being: -- `maximum`: behavior today - - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) -- `minimum` (unstable): `-Zminimal-versions` - - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this -- `rust-version`: what is defined in the package (default) -- `rust-version=` (assumes `maximum` is the fallback) - - `package`: long form of `rust-version` - - `rustc` (future possibility): the current running version - - Needed for "separate development / publish MSRV" workflow - - `[.[.]]` (future possibility): manually override the version used - -If a `rust-version` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. -This will let users effectively pass `--ignore-rust-version` to all commands, -without having to support the flag on every single command. - # Drawbacks [drawbacks]: #drawbacks From 330eee4d53b7afb1a24859bdba263632ad16c0c6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:36:24 -0600 Subject: [PATCH 099/184] fix(ref): Update config section --- text/3537-msrv-resolver.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 780683c0bbe..8be20bd02ba 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -454,19 +454,19 @@ We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which wil ```toml [build] -resolver.precedence = "rust-version" # Default +resolver.precedence = "rust-version" # Default with `v3` ``` -with support values being: -- `maximum`: behavior today +with potential values being: +- `maximum`: behavior today (default for v1 and v2 resolvers) - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) - `minimum` (unstable): `-Zminimal-versions` - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this -- `rust-version`: what is defined in the package (default) -- `rust-version=` (assumes `maximum` is the fallback) +- `rust-version`: what is defined in the package (default for v3 resolver) +- `rust-version=` (future possibility) - `package`: long form of `rust-version` - - `rustc` (future possibility): the current running version + - `rustc`: the current running version - Needed for "separate development / publish MSRV" workflow - - `[.[.]]` (future possibility): manually override the version used + - `[.[.]]`: manually override the version used If a `rust-version` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. This will let users effectively pass `--ignore-rust-version` to all commands, From 590e0850e6ca9be27df159637b9dfb53896cf846 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:39:58 -0600 Subject: [PATCH 100/184] fix(rationale): Add more to why aggressive MSRV is fine --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8be20bd02ba..43826ef87d0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -564,7 +564,7 @@ This will encourage a baseline of quality as are developing with that version an This will help seed the Index with more `package.rust-version` data for the resolver to work with. The downside is that the `package.rust-version` will likely be higher than it absolutely needs. However, considering our definition of "support" and that the user isn't bothering to set an MSRV themself, -aggressively updating is likely fine in this case. +aggressively updating is likely fine in this case, especially since we'll let dependents override the build failure for MSRV-incompatible packages. Alternatively... From e2128c449edf59754e101d535cda3f5b321eb6cc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:41:57 -0600 Subject: [PATCH 101/184] fix(rationale): Clean up grammar --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 43826ef87d0..52df643f822 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -595,7 +595,7 @@ so it can inform our decisions without losing the intent of the publisher. We could help people keep their MSRV up to date, by letting them specify a policy (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. This would also be an alternative to `--update-rust-version`. -On the resolver side, we could +When there still isn't an MSRV set, the resolver could - Assume the MSRV of the next published package with an MSRV set - Sort no-MSRV versions by minimal versions, the lower the version the more likely it is to be compatible - This runs into quality issues with version requirements that are likely too low for what the package actually needs From 8171e5fd4c9201b1daa56de361eb3876f9061779 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:44:48 -0600 Subject: [PATCH 102/184] fix(rationale): Clarify why config is still needed --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 52df643f822..82a12d0b885 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -626,7 +626,7 @@ However, there is a lot more to define for us to get there. Some routes that ne - We could make it sticky by tracking this in `Cargo.lock` but that becomes less obvious what resolver mode you are in and how to change - We could put this in `Cargo.toml` but that implies it unconditionally applies to everything - But we want `cargo install` to use the latest dependencies so people get bug/security fixes - - This gets in the way of the "separate development / publish MSRV" workflow + - This gets in the way of "Extended published MSRV w/ latest development MSRV" being able to change it in CI to verify MSRV and "Extended MSRV" being able to change it in CI to verify latest dependencies By relying on config we can have a stabilized solution sooner and we can work out more of the details as we better understand the relevant problems. From 037300bc1dc13013f79a35ab2e5f3003d8cf202d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 10:57:10 -0600 Subject: [PATCH 103/184] fix(rationale): Clean up alternative comparison --- text/3537-msrv-resolver.md | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 82a12d0b885..d552d8fb727 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -229,7 +229,7 @@ A user runs `cargo new` and starts development. A maintainer may also want to avoid constraining their dependents, for a variety of reasons, and leave MSRV support as a gray area. -**Priority 0 because:** +**Priority 1 because:** - No MSRV is fine as pushing people to have an MSRV would lead to either - an inaccurate reported MSRV from it going stale which would lower the quality of the ecosystem - raise the barrier to entry by requiring more process for packages and pushing the cost of old Rust versions on people who don't care @@ -251,7 +251,7 @@ giving them room to backport fixes. Due to the pain points listed below, the target audience for this workflow is likely small, likely pushing them to not specify their MSRV. -**Priority 1 because:** +**Priority 2 because:** - Low barrier to maintaining a high quality of support for their MSRV - Being willing to advertising an MSRV, even if latest, improves the information available to developers, increasing the quality of the ecosystem - Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain @@ -298,7 +298,7 @@ though this is already a recommended practice when people follow the The way they verify dependencies is restricted as they can't rely on always updating via Dependabot/RenovateBot as a way to verify them. Maintainers likely only need to do a compilation check for MSRV as their regular CI runs ensure that the behavior (which is usually independent of rust version) is correct for the MSRV-compatible dependencies. -**Priority 2 because:** +**Priority 3 because:** - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users - For library and tool maintainers, they are absorbing costs from these less common use cases @@ -325,7 +325,7 @@ the fact that the tests are verifying (on a newer toolchain) that the build/norm Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. -**Priority 3 because:** +**Priority 4 because:** - The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work @@ -647,18 +647,19 @@ allows us to move forward without having to figure out all of these details. ## Resolver behavior Affects of current solution on workflows (including non-resolver behavior): -- Latest Rust with no MSRV +1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) -- Latest Rust as the MSRV +2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic -- Extended MSRV +3. Extended MSRV - ✅ `Cargo.lock` will Just Work -- Extended published MSRV w/ latest development MSRV +4. Extended published MSRV w/ latest development MSRV - ❌ Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml` + - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features ### Make this opt-in rather than opt-out @@ -675,19 +676,19 @@ Instead of adding `resolver = "3"`, we could keep the default resolver the same This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). Affects on workflows (including non-resolver behavior): -- Latest Rust with no MSRV +1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - 🟰 ~~Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`)~~ -- Latest Rust as the MSRV +2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic -- Extended MSRV - - ✅ Users be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` +3. Extended MSRV + - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` - ❌ Users will be frustrated that the tool knew what they wanted and didn't do it -- Extended published MSRV w/ latest development MSRV +4. Extended published MSRV w/ latest development MSRV - 🟰 ~~Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml`~~ - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features @@ -706,21 +707,22 @@ This is an auto-adapting variant where - If their toolchain matches their MSRV, they get an MSRV-aware resolver Affects on workflows (including non-resolver behavior): -- Latest Rust with no MSRV +1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) -- Latest Rust as the MSRV +2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic -- Extended MSRV - - ✅ Users be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` +3. Extended MSRV + - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` - ❌ Users will be frustrated that the tool knew what they wanted and didn't do it - ❌ This may encourage maintainers to develop using their MSRV, reducing the quality of their experience (not getting latest lints, not getting latest cargo features like "wait for publish", etc) -- Extended published MSRV w/ latest development MSRV +4. Extended published MSRV w/ latest development MSRV - ❌ Maintainers will have to opt-in to ensure they get the latest dependencies in a `.cargo/config.toml` + - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features ### Hard-error @@ -737,18 +739,18 @@ This would block stabilization indefinitely. We could adopt this approach in the future, if desired Affects on workflows (including non-resolver behavior): -- Latest Rust with no MSRV +1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ❌ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) -- Latest Rust as the MSRV +2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic -- Extended MSRV +3. Extended MSRV - ✅ `Cargo.lock` will Just Work for `package.rust-version` - ❌ Application developers using `rust-toolchain.toml` will have to duplicate that in `package.rust-version` and keep it in sync -- Extended published MSRV w/ latest development MSRV +4. Extended published MSRV w/ latest development MSRV - ❌ A design not been worked out to allow this workflow - ❌ If this is done unconditionally, then the `Cargo.lock` will change on upgrade - ❌ This is incompatible with per-`feature` MSRVs From 7cf62af6ecf34a779346ad10e097ebb0cfe735da Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 11:04:26 -0600 Subject: [PATCH 104/184] fix(ref): Flatten the config --- text/3537-msrv-resolver.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index d552d8fb727..6b468056ae4 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -390,13 +390,13 @@ cargo will prefer those with a compatible `package.rust-version` over those that aren't compatible. Some details may change over time though `cargo check && rustup update && cargo check` should not cause `Cargo.lock` to change. -#### `build.resolver.precedence` +#### `resolver.precedence` *(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* * Type: string * Default: "rust-version" -* Environment: `CARGO_BUILD_RESOLVER_PRECEDENCE` +* Environment: `CARGO_RESOLVER_PRECEDENCE` Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. @@ -424,7 +424,7 @@ particularly when no MSRV is specified, but this shouldn't affect existing `Cargo.lock` files since the currently resolved dependencies always get preference. -This can be overridden with `--ignore-rust-version` and config's `build.resolver.precedence`. +This can be overridden with `--ignore-rust-version` and config's `resolver.precedence`. Implications - If you use do `cargo update --precise `, it will work @@ -450,7 +450,7 @@ We already call `rustc` for feature resolution, so hopefully this won't have a p ## Cargo config -We'll add a `build.resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. +We'll add a `resolver.precedence ` field to `.cargo/config.toml` which will control the package version prioritization policy. ```toml [build] @@ -513,7 +513,7 @@ On publish, `rustc --version` will replace `"auto"`. # Drawbacks [drawbacks]: #drawbacks -Users upgrading to the next Edition (or changing to `resolver = '3"`), will have to manually update their CI to test the latest dependencies with `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum`. +Users upgrading to the next Edition (or changing to `resolver = '3"`), will have to manually update their CI to test the latest dependencies with `CARGO_RESOLVER_PRECEDENCE=maximum`. Workspaces have no `edition`, so its easy for users to not realize they need to set `resolver = "3"` or to update their `resolver = "2"` to `"3"`. @@ -535,7 +535,7 @@ Misc alternatives - Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. -- `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. +- `CARGO_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. - `cargo add` selecting rust-version-compatible minimum bounds helps - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. - Instead of `resolver = "3"`, we could just change the default for everyone @@ -570,7 +570,7 @@ Alternatively... ~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ However, this will err on the side of a higher MSRV than necessary and the only way to -workaround it is to set `CARGO_BUILD_RESOLVER_PRECEDENCE=maximum` which will then lose +workaround it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. As things evolve, we could re-evaluate making `"auto"` the default. @@ -663,8 +663,8 @@ Affects of current solution on workflows (including non-resolver behavior): ### Make this opt-in rather than opt-out -Instead of adding `resolver = "3"`, we could keep the default resolver the same as today but allow opt-in to MSRV-aware resolver via `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. -- When building with old Rust versions, error messages could suggest re-resolving with `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`. +Instead of adding `resolver = "3"`, we could keep the default resolver the same as today but allow opt-in to MSRV-aware resolver via `CARGO_RESOLVER_PRECEDENCE=rust-version`. +- When building with old Rust versions, error messages could suggest re-resolving with `CARGO_RESOLVER_PRECEDENCE=rust-version`. The next corrective step (and suggestion from cargo) depends on what the user is doing and could be either - `git checkout main -- Cargo.lock && cargo check` - `cargo generate-lockfile` @@ -692,12 +692,12 @@ Affects on workflows (including non-resolver behavior): - 🟰 ~~Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml`~~ - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features -### Make `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` the default +### Make `CARGO_RESOLVER_PRECEDENCE=rustc` the default -Instead of `resolver = "3"` changing the behavior to `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version`, -it is changed to `CARGO_BUILD_RESOLVER_PRECEDENCE=rustc` where the resolver selects packages compatible with current toolchain, +Instead of `resolver = "3"` changing the behavior to `CARGO_RESOLVER_PRECEDENCE=rust-version`, +it is changed to `CARGO_RESOLVER_PRECEDENCE=rustc` where the resolver selects packages compatible with current toolchain, matching the `cargo build` incompatible dependency error. -- We would still support `CARGO_BUILD_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users +- We would still support `CARGO_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new defaul This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). @@ -773,7 +773,6 @@ Affects on workflows (including non-resolver behavior): [unresolved-questions]: #unresolved-questions The config field is fairly rough - - The location (within `build`) needs more consideration - The name isn't very clear - The values are awkward - Should we instead just have a `resolver.rust-version = true`? @@ -802,7 +801,7 @@ without changing the default away from `maximum`, allowing people to intentionally opt-in to auto-selecting a compatible top-level paclage. Dependency resolution could be controlled through a config field `install.resolver.precedence`, -mirroring `build.resolver.precedence`. +mirroring `resolver.precedence`. The value add of this compared to `--locked` is unclear. See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. @@ -812,7 +811,7 @@ See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for suggesting a version of the package to use and to pass `--locked` assuming the bundled `Cargo.lock` has MSRV compatible dependencies. -## `build.resolver.precedence = "rust-version=[.[.]]"` +## `resolver.precedence = "rust-version=[.[.]]"` We could allow people setting an effective rust-version within the config. This would be useful for people who have a reason to not set `package.rust-version` From d54dbb173f78067f4325c68aec15df20526f6c9c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 11:05:52 -0600 Subject: [PATCH 105/184] fix(future): 1.75 is now past tense --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 6b468056ae4..33dbd34b4c0 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -807,7 +807,7 @@ The value add of this compared to `--locked` is unclear. See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for more discussion. **Note:** [rust-lang/cago#12798](https://github.com/rust-lang/cargo/pull/12798) -(slated to be released in 1.75) made it so `cargo install` will error upfront, +(released in 1.75) made it so `cargo install` will error upfront, suggesting a version of the package to use and to pass `--locked` assuming the bundled `Cargo.lock` has MSRV compatible dependencies. From 1eca8649df49d997d1b24bf20863bd1f12af61c5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 12 Jan 2024 22:19:43 -0600 Subject: [PATCH 106/184] fix(motivation): Transfer use cases to priority description --- text/3537-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 33dbd34b4c0..45e207c3c2c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -299,6 +299,7 @@ The way they verify dependencies is restricted as they can't rely on always upda Maintainers likely only need to do a compilation check for MSRV as their regular CI runs ensure that the behavior (which is usually independent of rust version) is correct for the MSRV-compatible dependencies. **Priority 3 because:** +- Several use cases for this workflow have little alternative - MSRV applies to all interactions to the project which also means that the level of "support" is consistent - This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users - For library and tool maintainers, they are absorbing costs from these less common use cases From fdb539fd7741c4032f01fcb5f2b2025d00956fb3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Jan 2024 13:17:14 -0600 Subject: [PATCH 107/184] style(alt): Line wrap paragraph --- text/3537-msrv-resolver.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 45e207c3c2c..9db55345c4d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -593,7 +593,10 @@ Alternatively, `cargo publish` / the registry could add new fields to the Index to represent an inferred MSRV, the published version, etc so it can inform our decisions without losing the intent of the publisher. -We could help people keep their MSRV up to date, by letting them specify a policy (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. +We could help people keep their MSRV up to date, by letting them specify a policy +(e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); +then, every time the user runs `cargo update`, +we could automatically update their `rust-version` field as well. This would also be an alternative to `--update-rust-version`. When there still isn't an MSRV set, the resolver could From 4e269a5750a12c8dd51b9b8e169780cbbbec13b4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Jan 2024 13:25:52 -0600 Subject: [PATCH 108/184] feat(alt): Expand on rust-version-policy --- text/3537-msrv-resolver.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9db55345c4d..421afb7a668 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -597,7 +597,14 @@ We could help people keep their MSRV up to date, by letting them specify a polic (e.g. `rust-version-policy = "stable - 2"` or `rust-version-policy = "stable"`); then, every time the user runs `cargo update`, we could automatically update their `rust-version` field as well. -This would also be an alternative to `--update-rust-version`. +This would also be an alternative to `--update-rust-version` that can be further explored in the future if desired. +There are aspects of this that need to be worked out before going down this route +- Without gating this behind a flag, this will push people away from bumping their MSRV only on minor version bumps. +- Tying this to `cargo update` encourages other side effects by default (`--workspace` flag would be needed to do no other update) which pushes people to a more casual approach to MSRV updating, even if we have a flag +- We need to figure out what policies are appropriate and what syntax to use for them + - While a continuous sliding window (`N-M`) is most commonly used today, + it is unclear if that is the right policy to bake in compared to others like periodic updates (`*/M` in cron syntax) to be helping the "Extended MSRV" users along with everyone else. +- Is `stable` clear enough to mean "current version a time of `cargo update` with ratcheting semantics"? What name can work best? When there still isn't an MSRV set, the resolver could - Assume the MSRV of the next published package with an MSRV set From e87c4be398179059d914480eae7eb68d62902ca0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Jan 2024 10:53:53 -0600 Subject: [PATCH 109/184] feat(future): Include cargo-publish issue --- text/3537-msrv-resolver.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 421afb7a668..3e22790b088 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -822,6 +822,15 @@ See [rust-lang/cargo#10903](https://github.com/rust-lang/cargo/issues/10903) for suggesting a version of the package to use and to pass `--locked` assuming the bundled `Cargo.lock` has MSRV compatible dependencies. +## cargo publish + +If you publish a library using your MSRV and MSRV-incompatible dependencies exist, the publish verification step will fail. +You can workaround this by +- Upgrading +- Running with `--no-verify` + +See [rust-lang/cargo#13306](https://github.com/rust-lang/cargo/issues/13306). + ## `resolver.precedence = "rust-version=[.[.]]"` We could allow people setting an effective rust-version within the config. From 39908954d2f2baf73a0c3db12bf9dcd022676b80 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 09:26:25 -0600 Subject: [PATCH 110/184] fix: Typo Co-authored-by: Tobias Bieniek --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 3e22790b088..8d150ea9b84 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -461,7 +461,7 @@ with potential values being: - `maximum`: behavior today (default for v1 and v2 resolvers) - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) - `minimum` (unstable): `-Zminimal-versions` - - As this just just precedence, `-Zdirect-minimal-versions` doesn't fit into this + - As this is just precedence, `-Zdirect-minimal-versions` doesn't fit into this - `rust-version`: what is defined in the package (default for v3 resolver) - `rust-version=` (future possibility) - `package`: long form of `rust-version` From 9e76b7cfb3c13cce3edbf731f5ada47ab00b3b48 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 09:29:30 -0600 Subject: [PATCH 111/184] fix(ref): Be more specific on auto behavior --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8d150ea9b84..afefecb215c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -507,7 +507,8 @@ Users may pass ## `cargo publish` We will add `"auto"` for `package.rust-version`. -On publish, `rustc --version` will replace `"auto"`. +On `cargo publish`, `"auto"` in `Cargo.toml` will be replaced by `rustc --version`. +If `rustc --version` is a pre-release, it will be left as unspecified. `cargo new` will include `package.rust-version = "auto"`. From 351ec04c55bfcefa23ac05db7aed75010f82b779 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 15:54:38 -0600 Subject: [PATCH 112/184] feat(ref): Mirror new 'update' flags on 'generate-lockfile' --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index afefecb215c..f35019c711d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -493,6 +493,8 @@ Users may pass - `--ignore-rust-version` to pick the latest dependencies, ignoring all `rust-version` fields (your own and from dependencies) - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies +Those flags will also be added to `cargo generate-lockfile` + ## `cargo add` `cargo add ` (no version) will pick a version requirement that is low From 9b592cb795cfde6ac911dc32722a8346b77469a5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 16:21:01 -0600 Subject: [PATCH 113/184] fix(alt): Clarify reporting's tree change meaning --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index f35019c711d..cc7825c5767 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -620,7 +620,8 @@ When there still isn't an MSRV set, the resolver could Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), we can run the resolver twice on the original input, once for MSRV and once without. We then do a depth-first diff of the trees, stopping and reporting on the first different node. -This would let us report on any command that changes the way the tree is resolved. +This would let us report on any command that changes the way the tree is resolved +(from explicit changes with `cargo update` to `cargo build` syncing `Cargo.toml` changes to `Cargo.lock`). We'd likely want to limit the output to only the sub-tree that changed. We could either always do the second resolve or only do the second resolve if the resolver changed anything, From 31082685c97c342b5e06827a19d1e467137ee576 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 16:21:47 -0600 Subject: [PATCH 114/184] fix(alt): Cover Reporting for no Cargo.lock --- text/3537-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cc7825c5767..c8afb15fc74 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -623,6 +623,7 @@ We then do a depth-first diff of the trees, stopping and reporting on the first This would let us report on any command that changes the way the tree is resolved (from explicit changes with `cargo update` to `cargo build` syncing `Cargo.toml` changes to `Cargo.lock`). We'd likely want to limit the output to only the sub-tree that changed. +If there wasn't previously a `Cargo.lock`, this would mean everything. We could either always do the second resolve or only do the second resolve if the resolver changed anything, whichever is faster. From b1b8f2b60168067dc339c885b36076ba910e620d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 16:22:35 -0600 Subject: [PATCH 115/184] fix(unresolved): Call out reporting level for stabilization process --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c8afb15fc74..21047f604e6 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -793,6 +793,8 @@ The config field is fairly rough - The values are awkward - Should we instead just have a `resolver.rust-version = true`? +Whether we report stale dependencies only on `cargo update` or on every command. + # Future possibilities [future-possibilities]: #future-possibilities From 5efca852926b4bb60525e20fa444ed2cbbefa958 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 19:23:30 -0600 Subject: [PATCH 116/184] fix(ref): Be more explicit about auto behavior --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 21047f604e6..a28050b942d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -509,7 +509,7 @@ Users may pass ## `cargo publish` We will add `"auto"` for `package.rust-version`. -On `cargo publish`, `"auto"` in `Cargo.toml` will be replaced by `rustc --version`. +On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"auto"` replaced with `rustc --version`. If `rustc --version` is a pre-release, it will be left as unspecified. `cargo new` will include `package.rust-version = "auto"`. From 7f9355a73d60ff91a9e4a4d397475d0cb8da9a8f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Jan 2024 20:19:03 -0600 Subject: [PATCH 117/184] fix(guide): Clean up language in config docs --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index a28050b942d..621a05aaec9 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -401,8 +401,8 @@ Some details may change over time though `cargo check && rustup update && cargo Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. -* `maximum`: Prefer the highest compatible versions of dependencies -* `rust-version`: Prefer dependencies where their `rust-version` is compatible with `package.rust-version` +* `maximum`: prefer the highest compatible versions of dependencies +* `rust-version`: prefer dependencies where their `package.rust-version` is greater than or equal to your `package.rust-version` `rust-version` can be overridden with `--ignore-rust-version` which will fallback to `maximum`. From 8d083f85ddf4286cd4d2c09f930e3ed3417d3065 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 20 Jan 2024 11:38:30 -0600 Subject: [PATCH 118/184] fix(guide): Fix direction of comparison --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 621a05aaec9..c7c529f94f2 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -402,7 +402,7 @@ Some details may change over time though `cargo check && rustup update && cargo Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. * `maximum`: prefer the highest compatible versions of dependencies -* `rust-version`: prefer dependencies where their `package.rust-version` is greater than or equal to your `package.rust-version` +* `rust-version`: prefer dependencies where their `package.rust-version` is less than or equal to your `package.rust-version` `rust-version` can be overridden with `--ignore-rust-version` which will fallback to `maximum`. From 8753df11b6d713d8675b628c25f37dbaa3335fb9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 20 Jan 2024 11:39:37 -0600 Subject: [PATCH 119/184] fix(ref): Be explicit that old dependencies are preferred --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c7c529f94f2..2ac45fcb658 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -417,7 +417,7 @@ We will be adding a v3 resolver, specified through `workspace.resolver` / `packa This will become default with the next Edition. When `resolver = "3"` is set, Cargo's resolver will change to *prefer* MSRV compatible versions over -incompatible versions when resolving versions except for `cargo install`. +incompatible versions when resolving new dependencies, except for `cargo install`. Initially, dependencies without `package.rust-version` will be preferred over MSRV-incompatible packages but less than those that are compatible. The exact details for how preferences are determined may change over time, From 40e9374bda66f6e6716a5daf94a1e978a510ef1e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 20 Jan 2024 11:48:07 -0600 Subject: [PATCH 120/184] fix(summary): Call out the MSRV data help --- text/3537-msrv-resolver.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 2ac45fcb658..235e47ef4d5 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -10,6 +10,7 @@ Provide a happy path for developers needing to work with older versions of Rust by - Preferring MSRV (minimum-supported-rust-version) compatible dependencies when Cargo resolves dependencies - Ensuring compatible version requirements when `cargo add` auto-selects a version +- Smoothing out the path for setting and maintaining a verified MSRV so the above will be more likely to pick a working version. Note: `cargo install` is intentionally left out for now to decouple discussions on how to handle the security ramifications. From 1c66807d03e7a50743fbebd5f8332454cae546af Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 22 Jan 2024 16:30:00 -0600 Subject: [PATCH 121/184] fix(ref): Be more explicit on update behavior --- text/3537-msrv-resolver.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 235e47ef4d5..92047ccbcec 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -493,6 +493,9 @@ If they want to check the status of things, they can run `cargo update -n`. Users may pass - `--ignore-rust-version` to pick the latest dependencies, ignoring all `rust-version` fields (your own and from dependencies) - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies +- ` --precise ` to pick a specific version, independent of the `rust-version` field + +We expect the notice to inform users of these options for allowing them to upgrade. Those flags will also be added to `cargo generate-lockfile` From 5b360f9d490d58ad67fafbadf0c1c3610c1e6ea6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 12:52:56 -0600 Subject: [PATCH 122/184] feat(deferred): Defer resolving `auto`s name --- text/3537-msrv-resolver.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 92047ccbcec..67614a7907f 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -797,6 +797,19 @@ The config field is fairly rough - The values are awkward - Should we instead just have a `resolver.rust-version = true`? +`rust-version = "auto"`'s field name is unsettled and deciding on it is not blocking for stabilization. +Ideally, we make it clear that this this is not inferred from syntax, +that this is the currently running toolchain, +that we ignore pre-release toolchains, +and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these consistent. +Some options include: +- `auto` can imply "infer from syntactic minimum" +- `latest` can imply "latest globally (ie from rust-lang.org) +- `stable` can imply "latest globally (ie from rust-lang.org) +- `toolchain` might look weird? +- `local` implies a `remote` +- `current` is like `latest` but a little softer and might work + Whether we report stale dependencies only on `cargo update` or on every command. # Future possibilities From bb90a15c1d492e6a89b06a9f7769e3de0e52d4d2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 12:57:00 -0600 Subject: [PATCH 123/184] fix(deferred): Expand on config --- text/3537-msrv-resolver.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 67614a7907f..c77d49adda9 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -793,9 +793,11 @@ Affects on workflows (including non-resolver behavior): [unresolved-questions]: #unresolved-questions The config field is fairly rough - - The name isn't very clear - - The values are awkward - - Should we instead just have a `resolver.rust-version = true`? +- The name isn't very clear +- The values are awkward +- Should we instead just have a `resolver.rust-version = true`? + - If we later add "resolve to toolchain" version, this might be confusing. + - Maybe enumeration, like `resolver.rust-version = `? `rust-version = "auto"`'s field name is unsettled and deciding on it is not blocking for stabilization. Ideally, we make it clear that this this is not inferred from syntax, From ce36cf21bf7f679e173a32b82f65b343e8994c0a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 13:16:21 -0600 Subject: [PATCH 124/184] feat(rationale): Discuss --ignore-rust-version and compilation --- text/3537-msrv-resolver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c77d49adda9..7af420d289a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -555,6 +555,10 @@ Misc alternatives We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) - `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the package to a workspace-member for `[lint]` and dealing with unifying conflicting levels in `[lint]`. - `--ignore-rust-version` picks absolutely the latest dependencies to support (1) users on latest rustc and (2) users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows +- Compilation commands (e.g. `cargo check`) will take on two meanings for `--ignore-rust-version`, (1) `allow` the workspace diagnostic and (2) resolve changed dependencies to latest when syncing `Cargo.toml` to `Cargo.lock`. + - This expansion of scope is for consistency + - Being a flag to turn the `deny` into an `allow` is a high friction workflow that we expect users to not be too negatively impacted by this expansion. + - With the resolver config and the configurable lint, we also expect the flag on compilation commands to be diminished in value. Maybe in the future we could even deprecate it and/or hide it. - `--update-rust-version` picks `rustc --version`-compatible dependencies so users, no matter their `rustc` version, can easily walk the treadmill of updating their dependencies / MSRV - There is little reason to select an MSRV higher than their Rust toolchain - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain From c44a063a898e6f277a658602ac488611e2f35875 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 13:18:01 -0600 Subject: [PATCH 125/184] fix(alt): Typo --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 7af420d289a..5f0d34cba30 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -722,7 +722,7 @@ Instead of `resolver = "3"` changing the behavior to `CARGO_RESOLVER_PRECEDENCE= it is changed to `CARGO_RESOLVER_PRECEDENCE=rustc` where the resolver selects packages compatible with current toolchain, matching the `cargo build` incompatible dependency error. - We would still support `CARGO_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users -- We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new defaul +- We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). From ee1f87edb901d9f7209170e303b00098946c0ad8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 13:34:46 -0600 Subject: [PATCH 126/184] fix(guide): Split out documentation updates section --- text/3537-msrv-resolver.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 5f0d34cba30..29529701c88 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -353,7 +353,9 @@ one lockfile can be used but it can be difficult to edit the `Cargo.lock` to ens # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -## The `rust-version` field +## Example documentation updates + +### The `rust-version` field *(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* @@ -383,7 +385,7 @@ The `rust-version` may be ignored using the `--ignore-rust-version` option. Setting the `rust-version` key in `[package]` will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc. -## Rust Version +### Rust Version *(update to [Dependency Resolution's Other Constraints documentation](https://doc.rust-lang.org/cargo/reference/resolver.html))* @@ -392,7 +394,7 @@ cargo will prefer those with a compatible `package.rust-version` over those that aren't compatible. Some details may change over time though `cargo check && rustup update && cargo check` should not cause `Cargo.lock` to change. -#### `resolver.precedence` +##### `resolver.precedence` *(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* From 6b06734f6f30bae65734a9a995d6e7682446bd15 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 13:55:25 -0600 Subject: [PATCH 127/184] fix(future): Maybe caution about unspecified MSRVs? --- text/3537-msrv-resolver.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 29529701c88..3633f6ed6de 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -902,3 +902,10 @@ Ideally, we'd at least facilitate people in setting their MSRV. Some data that Once people have more data to help them in picking an MSRV policy, it would help to also document trade-offs on whether an MSRV policy should proactive or reactive on when to bump it. + +## Warn when adding dependencies with unspecified MSRVs + +When adding packages without an MSRV, +its not clear whether it will work with your project. +Knowing that they haven't declared support for your toolchain version could be important, +after we've made it easier to declare an MSRV. From 505224ad6682ead1c1dbb753c0974fbedc95de15 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 15:07:46 -0600 Subject: [PATCH 128/184] fix(ref): Nightly publish with auto should warn --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 3633f6ed6de..070124cfc10 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -516,7 +516,7 @@ Users may pass We will add `"auto"` for `package.rust-version`. On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"auto"` replaced with `rustc --version`. -If `rustc --version` is a pre-release, it will be left as unspecified. +If `rustc --version` is a pre-release, it will be left as unspecified with a warning. `cargo new` will include `package.rust-version = "auto"`. From 6c174404439d3d6ea9b8591422b1505087680e34 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 19:41:17 -0600 Subject: [PATCH 129/184] fix(ref): Improve compilation diagnostic --- text/3537-msrv-resolver.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 070124cfc10..44830d1af6c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -483,6 +483,14 @@ The MSRV-compatibility build check will be demoted from an error to a `deny`-by- allowing users to intentionally use dependencies on an unsupported (or less supported) version of Rust without requiring `--ignore-rust-version` on every invocation. +Ideally, we present all of the MSRV issues upfront to be resolved together. +At minimum, we should present a top-down message, rather than bottom up. + +If `package.rust-version` is unset or `"auto"`, the diagnostic should suggest setting it +to help raise awareness of `package.rust-version` being able to reduce future +resolution errors. +This would benefit from knowing the oldest MSRV. + ## `cargo update` `cargo update` will inform users when an MSRV or semver incompatible version is available. @@ -694,9 +702,6 @@ Instead of adding `resolver = "3"`, we could keep the default resolver the same The next corrective step (and suggestion from cargo) depends on what the user is doing and could be either - `git checkout main -- Cargo.lock && cargo check` - `cargo generate-lockfile` -- We should update the "incompatible rust-version" checks to be top-down, rather - than bottom up, - so users see the root of their problem, rather than the leaves. - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). From 0bc42a8541d9cacb541f028cafafc8c544911f3c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 20:19:53 -0600 Subject: [PATCH 130/184] fix(ref): Promote report on every command from alt to ref, but keep it deferred --- text/3537-msrv-resolver.md | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 44830d1af6c..ab058acc5e5 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -509,6 +509,30 @@ We expect the notice to inform users of these options for allowing them to upgra Those flags will also be added to `cargo generate-lockfile` +## Syncing `Cargo.toml` to `Cargo.lock` on any Cargo command + +In addition to the `cargo update` output to report when things are held back (both MSRV and semver), +We can attempt to have resolves to call out the new dependency versions that were held back from MSRV or semver. +Whether we do this and how much will be subject to factors like noisy output, performance, etc. + +Implementation ideas... + +We then do a depth-first diff of the trees, stopping and reporting on the first different node. +This would let us report on any command that changes the way the tree is resolved +(from explicit changes with `cargo update` to `cargo build` syncing `Cargo.toml` changes to `Cargo.lock`). +We'd likely want to limit the output to only the sub-tree that changed. +If there wasn't previously a `Cargo.lock`, this would mean everything. + +We could either always do the second resolve or only do the second resolve if the resolver changed anything, +whichever is faster. + +Its unknown whether making the inputs available for multiple resolves would have a performance impact. + +While a no-change resolve is fast, if this negatively impacts it enough, we +could explore hashing the resolve inputs and storing that in the lockfile, +allowing us to detect if the inputs have changed and only resolving then. + + ## `cargo add` `cargo add ` (no version) will pick a version requirement that is low @@ -633,25 +657,6 @@ When there still isn't an MSRV set, the resolver could - This runs into quality issues with version requirements that are likely too low for what the package actually needs - For dependencies that never set their MSRV, this effectively switches us from maximal versions to minimal versions. -## Reporting - -Alternative to or in addition to the `cargo update` output to report when things are held back (both MSRV and semver), -we can run the resolver twice on the original input, once for MSRV and once without. -We then do a depth-first diff of the trees, stopping and reporting on the first different node. -This would let us report on any command that changes the way the tree is resolved -(from explicit changes with `cargo update` to `cargo build` syncing `Cargo.toml` changes to `Cargo.lock`). -We'd likely want to limit the output to only the sub-tree that changed. -If there wasn't previously a `Cargo.lock`, this would mean everything. - -We could either always do the second resolve or only do the second resolve if the resolver changed anything, -whichever is faster. - -Its unknown whether making the inputs available for multiple resolves would have a performance impact. - -While a no-change resolve is fast, if this negatively impacts it enough, we -could explore hashing the resolve inputs and storing that in the lockfile, -allowing us to detect if the inputs have changed and only resolving then. - ## Configuring the resolver mode on the command-line or `Cargo.toml` The Cargo team is very interested in [moving project-specific config to manifests](https://github.com/rust-lang/cargo/issues/12738). @@ -824,6 +829,7 @@ Some options include: - `current` is like `latest` but a little softer and might work Whether we report stale dependencies only on `cargo update` or on every command. +See "Syncing `Cargo.toml` to `Cargo.lock` on any Cargo command". # Future possibilities [future-possibilities]: #future-possibilities From 242bc1911c3e3156430f6110bbe0b5df9008ca86 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jan 2024 20:26:06 -0600 Subject: [PATCH 131/184] feat(guide): Step through this RFC with different users/workflows --- text/3537-msrv-resolver.md | 282 +++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index ab058acc5e5..e13c5564ecd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -353,6 +353,288 @@ one lockfile can be used but it can be difficult to edit the `Cargo.lock` to ens # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +## Example workflows + +We'll step through several scenarios to highlight the changes in the user experience. + +### Latest Rust with MSRV + +I'm learning Rust and wanting to write my first application. +The book suggested I install using `rustup`. + +I've recently updated my toolchain +```console +$ rustup update +Downloading and install 1.92 +``` + +At some point, I start a project: +```console +$ cargo new foo +$ cat foo/Cargo.toml +``` +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "auto" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +``` +```console +$ cargo add clap -F derive +Adding clap 5.10.30 +``` +*(note: this user would traditionally be a "Latest Rust" user but `package.rust-version` automatically them moved to "Latest Rust with MSRV" without extra validation effort or risk of their MSRV going stale)* + +After some time, I get back to my project and decide to add completion support: +```console +$ cargo add clap_complete +Adding clap_complete 5.10.40 +warning: clap_complete 5.11.0 exists but requires Rust 1.93 while you are running 1.92. +To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup && cargo add clap_complete@5.11.0`. +To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap_complete@5.11.0` +``` +Wanting to be on the latest version, I run +```console +$ rustup update +Downloading and install 1.94 +$ cargo update +Updating clap v5.10.30 -> v5.11.0 +Updating clap_complete v5.10.40 -> v5.11.0 +``` + +**Alternate:** But what if I manually edited `Cargo.toml` instead of `cargo add`? +Here, we can shortcut some questions about version requirements because clap aligns on minor releases. +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "auto" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "5.10.30", features = ["derive"] } +clap_complete = "5.10" # <-- new +``` +And away I go: +```console +$ cargo check +Warning: adding clap_complete@5.10.40 because 5.11.0 requires Rust 1.93 while you are running 1.92. +To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup && cargo update`. +To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo update --ignore-rust-version` +``` +But I am in a hurry and don't want to disrupt my flow. +`clap_complete@5.10.40` is likely fine. +I am running `clap@5.10.30` and that has been working for me. +I might even run [`cargo deny`](https://crates.io/crates/cargo-deny) to see if there are known vulnerabilities. +So I continue development. + +Later I run: +```console +$ cargo update +Name Current Latest Note +============= ======= ====== ================== +clap 5.10.30 5.11.0 requires Rust 1.93 +clap_complete 5.10.40 5.11.0 requires Rust 1.93 +note: To use the latest depednencies, run `rustup && cargo update`. +To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` +$ rustup update +Downloading and install 1.94 +$ cargo update +Updating clap v5.10.30 -> v5.11.0 +Updating clap_complete v5.10.40 -> v5.11.0 +``` + +At this point, I want to publish +```console +$ cargo publish +... crates.io error about missing fields +$ $EDITOR `Cargo.toml` +$ cargo publish +Published foo 0.1.0 +``` +If I look on crates.io, the new 0.1.0 version shows up with a rust-version of 1.94 +without me having to manual update the field and +relying on the `cargo publish`s verify step to verify the correctness of that MSRV. + +### Extended "MSRV" with an application + +I am developing an application using a certified toolchain. +I specify this toolchain using a `rust-toolchain.toml` file. + +Rust 1.94 is the latest but my certified toolchain is 1.92. + +At some point, I start a project: +```console +$ cargo new foo +$ cat foo/Cargo.toml +``` +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "auto" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +``` +```console +$ cargo add clap -F derive +Adding clap 5.10.30 +warning: clap 5.11.0 exists but requires Rust 1.93 while you are running 1.92. +To use the clap@5.11.0 with a compatible Rust version, run `rustup && cargo add clap@5.10.0`. +To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap@5.10.0` +``` +At this point, I have a couple of options +1. I check and clap advertises that they "support" Rust 1.92 by cherry-picking fixes into 5.10 and I feel comfortable with that +2. I check `cargo deny` and don't see any vulnerabilities and that is good enough for me, knowing that the majority of my users are likely on newer versions +3. I decide that clap doesn't align with my interests and use something else + +Assuming (1) or (2) applies, I ignore the warning and move on. + +### Extended MSRV with an application targeting multiple Rust versions + +*(this is a re-imagining of the Motivation's example)* + +I'm building an application that is deployed to multiple embedded Linux targets. +Each target's image builder uses a different Rust toolchain version to avoid re-validating the image. + +I've recently updated my toolchain +```console +$ rustup update +Downloading and install 1.94 +``` + +At some point, I start a project: +```console +$ cargo new foo +$ cat foo/Cargo.toml +``` +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "auto" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +``` +```console +$ cargo add clap -F derive +Adding clap 5.11.0 +``` + +I send this to my image builder and I get this failure for one of my embedded targets: +``` +$ cargo build +error: clap 5.11.0 requires Rust 1.93.0 while you are running 1.92.0 + +note: downgrade to 5.10.30 for a version compatible with Rust 1.92.0 +note: set `package.rust-version = "1.92.0"` to ensure compatible versions are selected in the future +note: lint `cargo::incompatible-msrv` is denied by default + +``` +I make the prescribed changes: +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "1.92" # <-- was "auto" before I edited it + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "5.10.30", features = ["derive"] } # <-- downgraded +``` +And my image build works! + +After some time, I run: +```console +$ cargo update +Name Current Latest Note +============= ======= ====== ================== +clap 5.10.30 5.11.0 requires Rust 1.93 +clap_complete 5.10.40 5.11.0 requires Rust 1.93 +note: To use the latest depednencies, run `rustup && cargo update`. +To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` +``` +We've EOLed the last embedded target that supported 1.92 and so we can update our `package.rust-version`, +so we can update it and our dependencies: +```console +$ cargo update --update-rust-version +Updating clap 5.10.30 to 5.11.0 +Updating foo's rust-version from 1.92 to 1.93 +``` + +### Extended MSRV for a Library + +I'm developing a new library and am willing to take on some costs for supporting people on older toolchains. + +I've recently updated my toolchain +```console +$ rustup update +Downloading and install 1.94 +``` + +At some point, I start a project: +```console +$ cargo new foo --lib +``` +I've decided on an "N-2" MSRV policy: +```toml +[package] +name = "foo" +version = "0.1.0" +edition = "2024" +rust-version = "1.92" # <-- was "auto" before I edited it + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +``` +```console +$ cargo add clap -F derive +Adding clap 5.10.30 +warning: clap 5.11.0 exists but requires Rust 1.93 while `foo` has `package.rust-version = "1.92"` +To use clap@5.11.0 with a compatible package.rust-version, run `cargo add clap@5.11.0 --update-rust-version` +To force the use of clap@5.11.0 independent of your toolchain, run `cargo add clap@5.11.0` +``` +At this point, I have a couple of options +1. I check and clap advertises that they "support" Rust 1.92 by cherry-picking fixes into 5.10 and I feel comfortable with that +2. I check `cargo deny` and don't see any vulnerabilities and that is good enough for me, knowing that the majority of my users are likely on newer versions +3. I decide that clap doesn't align with my interests and use something else + +Assuming (1) or (2) applies, I ignore the warning and move on. + +After some time, I run: +```console +$ cargo update +Name Current Latest Note +============= ======= ====== ================== +clap 5.10.30 5.11.0 requires Rust 1.93 +clap_complete 5.10.40 5.11.0 requires Rust 1.93 +note: To use the latest depednencies, run `rustup && cargo update`. +To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` +``` +At this point, 1.95 is out, so I'm fine updating my MSRV and I run: +```console +$ cargo update --update-rust-version +Updating clap 5.10.30 to 5.11.0 +Updating foo's rust-version from 1.92 to 1.93 +``` +Instead, if a newer clap version was out needing 1.94 or 1.95, I would instead edit `Cargo.toml` myself. + ## Example documentation updates ### The `rust-version` field From 18a26839f7e8b5e7b9d306002d3d2f799c60a753 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 24 Jan 2024 09:15:40 -0600 Subject: [PATCH 132/184] fix(motivation): Tweak lockfile language --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index e13c5564ecd..78f3603ed90 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -206,7 +206,7 @@ And keeping in mind - Even keeping upgrade costs low, there is still a re-validation cost that mission critical applications must pay - Dependencies in `Cargo.lock` are not expected to change from contributors using different versions of the Rust toolchain without an explicit action like changing `Cargo.toml` or running `cargo update` - e.g. If the maintainer does `cargo add foo && git commit && git push`, - then a contributor doing `git pull && cargo check` should not have a different selection of dependencies. + then a contributor doing `git pull && cargo check` should not have a different selection of dependencies, independent of their toolchain versions (which might mean the second user sees an error about an incompatible package). Some implications: - "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version From 9e2641d15aed9951d88e9b778ae07907c7564f27 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 09:14:53 -0600 Subject: [PATCH 133/184] fix: Correct suggested rustup command --- text/3537-msrv-resolver.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 78f3603ed90..338db5e5d13 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -395,7 +395,7 @@ After some time, I get back to my project and decide to add completion support: $ cargo add clap_complete Adding clap_complete 5.10.40 warning: clap_complete 5.11.0 exists but requires Rust 1.93 while you are running 1.92. -To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup && cargo add clap_complete@5.11.0`. +To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup update && cargo add clap_complete@5.11.0`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap_complete@5.11.0` ``` Wanting to be on the latest version, I run @@ -426,7 +426,7 @@ And away I go: ```console $ cargo check Warning: adding clap_complete@5.10.40 because 5.11.0 requires Rust 1.93 while you are running 1.92. -To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup && cargo update`. +To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup update && cargo update`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo update --ignore-rust-version` ``` But I am in a hurry and don't want to disrupt my flow. @@ -442,7 +442,7 @@ Name Current Latest Note ============= ======= ====== ================== clap 5.10.30 5.11.0 requires Rust 1.93 clap_complete 5.10.40 5.11.0 requires Rust 1.93 -note: To use the latest depednencies, run `rustup && cargo update`. +note: To use the latest depednencies, run `rustup update && cargo update`. To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` $ rustup update Downloading and install 1.94 @@ -490,7 +490,7 @@ rust-version = "auto" $ cargo add clap -F derive Adding clap 5.10.30 warning: clap 5.11.0 exists but requires Rust 1.93 while you are running 1.92. -To use the clap@5.11.0 with a compatible Rust version, run `rustup && cargo add clap@5.10.0`. +To use the clap@5.11.0 with a compatible Rust version, run `rustup update && cargo add clap@5.10.0`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap@5.10.0` ``` At this point, I have a couple of options @@ -566,7 +566,7 @@ Name Current Latest Note ============= ======= ====== ================== clap 5.10.30 5.11.0 requires Rust 1.93 clap_complete 5.10.40 5.11.0 requires Rust 1.93 -note: To use the latest depednencies, run `rustup && cargo update`. +note: To use the latest depednencies, run `rustup update && cargo update`. To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` ``` We've EOLed the last embedded target that supported 1.92 and so we can update our `package.rust-version`, @@ -624,7 +624,7 @@ Name Current Latest Note ============= ======= ====== ================== clap 5.10.30 5.11.0 requires Rust 1.93 clap_complete 5.10.40 5.11.0 requires Rust 1.93 -note: To use the latest depednencies, run `rustup && cargo update`. +note: To use the latest depednencies, run `rustup update && cargo update`. To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` ``` At this point, 1.95 is out, so I'm fine updating my MSRV and I run: From c68b0726e3a1eddd65caddf762e7ca5691357463 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 09:15:27 -0600 Subject: [PATCH 134/184] fix: Remove extra word --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 338db5e5d13..b497486835c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -713,7 +713,7 @@ resolved dependencies always get preference. This can be overridden with `--ignore-rust-version` and config's `resolver.precedence`. Implications -- If you use do `cargo update --precise `, it will work +- If you use `cargo update --precise `, it will work - If you use `--ignore-rust-version` once, you don't need to specify it again to keep those dependencies though you might need it again on the next edit of `Cargo.toml` or `cargo update` run - If a dependency doesn't specify `package.rust-version` but its transitive dependencies specify an incompatible `package.rust-version`, we won't backtrack to older versions of the dependency to find one with a MSRV-compatible transitive dependency. From 6458c0cab042a8e1978a47251c6a82c1688dbc9e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 09:15:56 -0600 Subject: [PATCH 135/184] fix: Clarify -n is dry-run --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b497486835c..867f4764abc 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -776,7 +776,7 @@ This would benefit from knowing the oldest MSRV. ## `cargo update` `cargo update` will inform users when an MSRV or semver incompatible version is available. -`cargo update -n` will also report this information so that users can check on the status of this at any time. +`cargo update --dry-run` will also report this information so that users can check on the status of this at any time. **Note:** other operations that cause `Cargo.lock` entries to be changed (like editing `Cargo.toml` and running `cargo check`) may not inform the user. From 3ee143a834a7928b933db6ced42df38d210b9375 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 09:17:59 -0600 Subject: [PATCH 136/184] fix: Clarify 'we' in pain points --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 867f4764abc..cd6dd046121 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -237,7 +237,7 @@ A maintainer may also want to avoid constraining their dependents, for a variety **Pain points:** -We do not provide a way to help users know new versions are available, to support the users in staying up-to-date. +The Rust toolchain does not provide a way to help users know new dependency versions are available, to support the users in staying up-to-date. MSRV build errors from new dependency versions is one way to do it though not ideal as this disrupts the user. Otherwise, they must actively run `rustup update` or follow Rust news. @@ -260,7 +260,7 @@ likely pushing them to not specify their MSRV. **Pain points (in addition to the prior workflow):** -In addition to their toolchain version, we do not help these users with keeping +In addition to their toolchain version, the Rust toolchain does not help these users with keeping their MSRV up-to-date. They can use other tools like [RenovateBot](https://github.com/rust-lang/cargo/blob/87eb374d499100bc945dc0e50ae5194ae539b964/.github/renovate.json5#L12-L24) From 064fdac8d54363065c3d21e6da4df8b7cd173ab2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 10:46:21 -0600 Subject: [PATCH 137/184] fix(ref): Clarify transition for reporting stale dependencies --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cd6dd046121..cdc95b71d2d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -797,7 +797,7 @@ In addition to the `cargo update` output to report when things are held back (bo We can attempt to have resolves to call out the new dependency versions that were held back from MSRV or semver. Whether we do this and how much will be subject to factors like noisy output, performance, etc. -Implementation ideas... +Some approaches we can take for doing this include: We then do a depth-first diff of the trees, stopping and reporting on the first different node. This would let us report on any command that changes the way the tree is resolved From 7c30483a80db76adbaefc87bf1dc10324b13387b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 10:52:25 -0600 Subject: [PATCH 138/184] fix(guide): Include auto in documentation update --- text/3537-msrv-resolver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cdc95b71d2d..394a66267c3 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -662,6 +662,10 @@ identifiers such as -nightly will be ignored while checking the Rust version. The `rust-version` must be equal to or newer than the version that first introduced the configured `edition`. +The Rust version can also be `"auto"`. +This will act the same as if it was set to the version of your toolchain. +When publishing, `"auto"` will be replaced by the version of your toolchain in your published manifest. + The `rust-version` may be ignored using the `--ignore-rust-version` option. Setting the `rust-version` key in `[package]` will affect all targets/crates in From 30049fe017d4031e46be69a58002d2b9ae3edd2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 10:53:23 -0600 Subject: [PATCH 139/184] fix(motivation): Add status quo header --- text/3537-msrv-resolver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 394a66267c3..fe877046758 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -17,6 +17,8 @@ Note: `cargo install` is intentionally left out for now to decouple discussions # Motivation [motivation]: #motivation +## Status Quo + Let's step through a simple scenario where a user develops with the latest Rust version but production uses an older version: ```console From 7b84264017f44dadfa7fe2cdd65c5e3193dab94c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:03:33 -0600 Subject: [PATCH 140/184] fix(ref): Remove stale comment --- text/3537-msrv-resolver.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fe877046758..c4f665ca9f1 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -784,10 +784,6 @@ This would benefit from knowing the oldest MSRV. `cargo update` will inform users when an MSRV or semver incompatible version is available. `cargo update --dry-run` will also report this information so that users can check on the status of this at any time. -**Note:** other operations that cause `Cargo.lock` entries to be changed (like -editing `Cargo.toml` and running `cargo check`) may not inform the user. -If they want to check the status of things, they can run `cargo update -n`. - Users may pass - `--ignore-rust-version` to pick the latest dependencies, ignoring all `rust-version` fields (your own and from dependencies) - `--update-rust-version` to pick the `rustc --version`-compatible dependencies, updating your `package.rust-version` if needed to match the highest of your dependencies From 1244f87f60dcdab65060bbf790919a85924bd72f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:05:54 -0600 Subject: [PATCH 141/184] fix(ref): Clarify held-back reporting --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c4f665ca9f1..88957e87a76 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -796,7 +796,7 @@ Those flags will also be added to `cargo generate-lockfile` ## Syncing `Cargo.toml` to `Cargo.lock` on any Cargo command In addition to the `cargo update` output to report when things are held back (both MSRV and semver), -We can attempt to have resolves to call out the new dependency versions that were held back from MSRV or semver. +we will try having dependency resolves highlight newly selected dependency versions that were held back due to MSRV or semver. Whether we do this and how much will be subject to factors like noisy output, performance, etc. Some approaches we can take for doing this include: From aae359c1116fc2f8a93255f239b547cbeaf257e6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:08:34 -0600 Subject: [PATCH 142/184] fix(ref): Clarify rust-version statement --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 88957e87a76..0fa439ddb42 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -830,7 +830,7 @@ Users may pass ## `cargo publish` -We will add `"auto"` for `package.rust-version`. +`package.rust-version` will gain support for an `"auto"` value, in addition to partial versions. On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"auto"` replaced with `rustc --version`. If `rustc --version` is a pre-release, it will be left as unspecified with a warning. From 9b9390eadf2ea3eb1158a8009b8ed85a474e9cf6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:12:11 -0600 Subject: [PATCH 143/184] fix(drawback): Remind people about limtis of workspace.resolver warning --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 0fa439ddb42..7f9478cd60d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -841,7 +841,8 @@ If `rustc --version` is a pre-release, it will be left as unspecified with a war Users upgrading to the next Edition (or changing to `resolver = '3"`), will have to manually update their CI to test the latest dependencies with `CARGO_RESOLVER_PRECEDENCE=maximum`. -Workspaces have no `edition`, so its easy for users to not realize they need to set `resolver = "3"` or to update their `resolver = "2"` to `"3"`. +Workspaces have no `edition`, so its easy for users to not realize they need to set `resolver = "3"` or to update their `resolver = "2"` to `"3"` +(Cargo only warns on [virtual manifests without an explicit `workspace.resolver`](https://github.com/rust-lang/cargo/pull/10910)). While we hope this will give maintainers more freedom to upgrade their MSRV, this could instead further entrench rust-version stagnation in the ecosystem. From ec79f529e0d56bf712c40d86df5e1b5592c68cd4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:13:29 -0600 Subject: [PATCH 144/184] fix(rationale): Remove stale rationale --- text/3537-msrv-resolver.md | 1 - 1 file changed, 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 7f9478cd60d..6c3f315660a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -859,7 +859,6 @@ and only in the sense that the user needs to know to explicitly take action to a [rationale-and-alternatives]: #rationale-and-alternatives Misc alternatives -- Config was put under `build` to associate it with local development, as compared with `install` which could be supported in the future - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. - `CARGO_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. From 1dab41041b8fcdeb23af4541cc1740aa930d85c5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:16:33 -0600 Subject: [PATCH 145/184] fix(rationale): Clarify rust-version and minimal version interaction --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 6c3f315660a..bfcd80a0ca2 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -861,7 +861,7 @@ and only in the sense that the user needs to know to explicitly take action to a Misc alternatives - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. -- `CARGO_RESOLVER_PRECEDENCE=rust-version` assumes maximal resolution as generally minimal resolution will pick packages with compatible rust-versions as rust-version tends to (but doesn't always) increase over time. +- `CARGO_RESOLVER_PRECEDENCE=rust-version` implies maximal resolution among MSRV-compatible dependencies. Generally MSRV doesn't decrease over versions, so minimal resolution will likely pick packages with compatible rust-versions. - `cargo add` selecting rust-version-compatible minimum bounds helps - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. - Instead of `resolver = "3"`, we could just change the default for everyone From 43db9719cfae153b6159d367460d213de77c79c7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:17:00 -0600 Subject: [PATCH 146/184] fix(rationale): Clarify rust-version and minimal version interaction --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index bfcd80a0ca2..848913447c9 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -862,7 +862,7 @@ Misc alternatives - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. - `CARGO_RESOLVER_PRECEDENCE=rust-version` implies maximal resolution among MSRV-compatible dependencies. Generally MSRV doesn't decrease over versions, so minimal resolution will likely pick packages with compatible rust-versions. - - `cargo add` selecting rust-version-compatible minimum bounds helps + - `cargo add` helps by selecting rust-version-compatible minimum bounds - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. - Instead of `resolver = "3"`, we could just change the default for everyone - The number of maintainers verifying latest dependencies is likely From 5294aba05febd138b52de403111bc7203a379756 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:19:00 -0600 Subject: [PATCH 147/184] fix(rationale): Clarify workspace-level lint for incompatible MSRV --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 848913447c9..c9e76f2628a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -871,7 +871,7 @@ Misc alternatives Therefore, we could probably get away with treating this as a minor incompatibility - Either way, the big care about is there being attention drawn to the change. We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) -- `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the package to a workspace-member for `[lint]` and dealing with unifying conflicting levels in `[lint]`. +- `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the dependency to a workspace-member to select `[lint]` tables to respect and then dealing with unifying conflicting levels in between `[lint]` tables among members. - `--ignore-rust-version` picks absolutely the latest dependencies to support (1) users on latest rustc and (2) users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows - Compilation commands (e.g. `cargo check`) will take on two meanings for `--ignore-rust-version`, (1) `allow` the workspace diagnostic and (2) resolve changed dependencies to latest when syncing `Cargo.toml` to `Cargo.lock`. - This expansion of scope is for consistency From 382cb1d34396bf0df3cefe42d49199bbecde927c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:20:13 -0600 Subject: [PATCH 148/184] fix(rationale): Switch how we highlight different cases for ignore-rust-version --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c9e76f2628a..25ee8107e1c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -872,7 +872,7 @@ Misc alternatives - Either way, the big care about is there being attention drawn to the change. We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) - `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the dependency to a workspace-member to select `[lint]` tables to respect and then dealing with unifying conflicting levels in between `[lint]` tables among members. -- `--ignore-rust-version` picks absolutely the latest dependencies to support (1) users on latest rustc and (2) users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows +- `--ignore-rust-version` picks absolutely the latest dependencies to support both users on latest rustc and users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows - Compilation commands (e.g. `cargo check`) will take on two meanings for `--ignore-rust-version`, (1) `allow` the workspace diagnostic and (2) resolve changed dependencies to latest when syncing `Cargo.toml` to `Cargo.lock`. - This expansion of scope is for consistency - Being a flag to turn the `deny` into an `allow` is a high friction workflow that we expect users to not be too negatively impacted by this expansion. From 7542afcf431ea6ec14953ddafb69ad1b4d6095dc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:21:58 -0600 Subject: [PATCH 149/184] fix(rationale): Clarify sentence for update-rust-version --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 25ee8107e1c..bb242f6503d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -877,7 +877,7 @@ Misc alternatives - This expansion of scope is for consistency - Being a flag to turn the `deny` into an `allow` is a high friction workflow that we expect users to not be too negatively impacted by this expansion. - With the resolver config and the configurable lint, we also expect the flag on compilation commands to be diminished in value. Maybe in the future we could even deprecate it and/or hide it. -- `--update-rust-version` picks `rustc --version`-compatible dependencies so users, no matter their `rustc` version, can easily walk the treadmill of updating their dependencies / MSRV +- `--update-rust-version` picks `rustc --version`-compatible dependencies so users can easily walk the treadmill of updating their dependencies / MSRV , no matter their `rustc` version. - There is little reason to select an MSRV higher than their Rust toolchain - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain - This comes at the cost of inconsistency with `--ignore-rust-version`. From 1896b00152f1e32985d3061d46d505a80d5db9df Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 2 Feb 2024 11:41:59 -0600 Subject: [PATCH 150/184] fix(rationale): Clarify section on auto --- text/3537-msrv-resolver.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index bb242f6503d..78b28e1c862 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -889,24 +889,24 @@ Ensuring we have `package.rust-version` populated more often (while maintaining quality of that data) is an important problem but does not have to be solved to get value out of this RFC and can be handled separately. -We chose an opt-in for populating `package.rust-version` based on `rustc --version`. -This will encourage a baseline of quality as are developing with that version and `cargo publish` will do a verification step, by default. +We chose an opt-in for populating `package.rust-version` based on `rustc --version` (`"auto"`). +This will encourage a baseline of quality as users are developing with that version and `cargo publish` will do a verification step, by default. This will help seed the Index with more `package.rust-version` data for the resolver to work with. The downside is that the `package.rust-version` will likely be higher than it absolutely needs. However, considering our definition of "support" and that the user isn't bothering to set an MSRV themself, aggressively updating is likely fine in this case, especially since we'll let dependents override the build failure for MSRV-incompatible packages. -Alternatively... +Some alternative solutions include: -~~When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish.~~ -However, this will err on the side of a higher MSRV than necessary and the only way to +When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish. +**However**, this will err on the side of a higher MSRV than necessary and the only way to workaround it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. As things evolve, we could re-evaluate making `"auto"` the default. ~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ -However, if people aren't committed to verifying it, +**However**, if people aren't committed to verifying that was implicitly set, it is likely to go stale and will claim an MSRV much older than what is used in practice. If we had the hard-error resolver mode and [clippy warning people when using API items stabilized after their MSRV](https://github.com/rust-lang/rust-clippy/issues/6324), @@ -914,9 +914,11 @@ this will at least annoy people into either being somewhat compatible or removin ~~When missing, `cargo publish` could inject `package.rust-version` inferred from `package.edition` and/or other `Cargo.toml` fields.~~ -However, this will err on the side of too low of an MSRV. -While this might help with in this situation, -it would lock us in to inaccurate information which might limit what analysis we could do in the future. +**However**, this will err on the side of too low of an MSRV. +These fields have an incomplete picture. +While this helps ensure there is more data for the MSRV-aware resolver, +future analysis wouldn't be able to distinguish between inferred and explicit `package.rust-version`s. +We'd also need an explicit opt-out for those who intentionally don't want one set. Alternatively, `cargo publish` / the registry could add new fields to the Index to represent an inferred MSRV, the published version, etc From fda1f883c846be24157da583eb097312bc59d728 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Feb 2024 14:57:00 -0600 Subject: [PATCH 151/184] fix(guide): Collapse workflow step throughs --- text/3537-msrv-resolver.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 78b28e1c862..683efb034c1 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -19,6 +19,8 @@ Note: `cargo install` is intentionally left out for now to decouple discussions ## Status Quo +
Ensuring you have a `Cargo.lock` with dependencies compatible with your minimum-supported Rust version (MSRV) is an arduous task of running `cargo update --precise ` until it works. + Let's step through a simple scenario where a user develops with the latest Rust version but production uses an older version: ```console @@ -143,6 +145,8 @@ $ cargo +1.64.0 check ``` Success! Mixed with many tears and less hair. +
+ How wide spread is this? Take this with a grain of salt but based on crates.io user agents: | Common MSRVs | % Compatible Requests | @@ -364,6 +368,8 @@ We'll step through several scenarios to highlight the changes in the user experi I'm learning Rust and wanting to write my first application. The book suggested I install using `rustup`. +
Expand for step through of this workflow + I've recently updated my toolchain ```console $ rustup update @@ -465,6 +471,8 @@ If I look on crates.io, the new 0.1.0 version shows up with a rust-version of 1. without me having to manual update the field and relying on the `cargo publish`s verify step to verify the correctness of that MSRV. +
+ ### Extended "MSRV" with an application I am developing an application using a certified toolchain. @@ -472,6 +480,8 @@ I specify this toolchain using a `rust-toolchain.toml` file. Rust 1.94 is the latest but my certified toolchain is 1.92. +
Expand for step through of this workflow + At some point, I start a project: ```console $ cargo new foo @@ -502,6 +512,8 @@ At this point, I have a couple of options Assuming (1) or (2) applies, I ignore the warning and move on. +
+ ### Extended MSRV with an application targeting multiple Rust versions *(this is a re-imagining of the Motivation's example)* @@ -509,6 +521,8 @@ Assuming (1) or (2) applies, I ignore the warning and move on. I'm building an application that is deployed to multiple embedded Linux targets. Each target's image builder uses a different Rust toolchain version to avoid re-validating the image. +
Expand for step through of this workflow + I've recently updated my toolchain ```console $ rustup update @@ -579,10 +593,14 @@ Updating clap 5.10.30 to 5.11.0 Updating foo's rust-version from 1.92 to 1.93 ``` +
+ ### Extended MSRV for a Library I'm developing a new library and am willing to take on some costs for supporting people on older toolchains. +
Expand for step through of this workflow + I've recently updated my toolchain ```console $ rustup update @@ -637,6 +655,8 @@ Updating foo's rust-version from 1.92 to 1.93 ``` Instead, if a newer clap version was out needing 1.94 or 1.95, I would instead edit `Cargo.toml` myself. +
+ ## Example documentation updates ### The `rust-version` field From da29fceb795df2fea367822be400cb5707957c08 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Feb 2024 16:22:09 -0600 Subject: [PATCH 152/184] fix(ref): Switch nightly auto to unset on publish --- text/3537-msrv-resolver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 683efb034c1..5e69d0eea00 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -852,7 +852,7 @@ Users may pass `package.rust-version` will gain support for an `"auto"` value, in addition to partial versions. On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"auto"` replaced with `rustc --version`. -If `rustc --version` is a pre-release, it will be left as unspecified with a warning. +If `rustc --version` is a pre-release, publish will fail. `cargo new` will include `package.rust-version = "auto"`. @@ -901,6 +901,7 @@ Misc alternatives - There is little reason to select an MSRV higher than their Rust toolchain - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain - This comes at the cost of inconsistency with `--ignore-rust-version`. +- Nightly `cargo publish` with `auto` fails because there isn't a good value to use and this gives us flexibility to change it later (e.g. just leaving the `rust-version` as unset). ## Ensuring the registry Index has `rust-version` without affecting quality From 1a97aefcbc0da96809aa7f698766fd8ca2324416 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Feb 2024 16:29:24 -0600 Subject: [PATCH 153/184] refactor(guide): Move example workflows later --- text/3537-msrv-resolver.md | 120 ++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 5e69d0eea00..1044e663874 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -359,6 +359,66 @@ one lockfile can be used but it can be difficult to edit the `Cargo.lock` to ens # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +## Example documentation updates + +### The `rust-version` field + +*(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* + +The `rust-version` field is an optional key that tells cargo what version of the +Rust language and compiler your package can be compiled with. If the currently +selected version of the Rust compiler is older than the stated version, cargo +will exit with an error, telling the user what version is required. +To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. + +The first version of Cargo that supports this field was released with Rust 1.56.0. +In older releases, the field will be ignored, and Cargo will display a warning. + +```toml +[package] +# ... +rust-version = "1.56" +``` + +The Rust version must be a bare version number with two or three components; it +cannot include semver operators or pre-release identifiers. Compiler pre-release +identifiers such as -nightly will be ignored while checking the Rust version. +The `rust-version` must be equal to or newer than the version that first +introduced the configured `edition`. + +The Rust version can also be `"auto"`. +This will act the same as if it was set to the version of your toolchain. +When publishing, `"auto"` will be replaced by the version of your toolchain in your published manifest. + +The `rust-version` may be ignored using the `--ignore-rust-version` option. + +Setting the `rust-version` key in `[package]` will affect all targets/crates in +the package, including test suites, benchmarks, binaries, examples, etc. + +### Rust Version + +*(update to [Dependency Resolution's Other Constraints documentation](https://doc.rust-lang.org/cargo/reference/resolver.html))* + +When multiple versions of a dependency satisfy all version requirements, +cargo will prefer those with a compatible `package.rust-version` over those that +aren't compatible. +Some details may change over time though `cargo check && rustup update && cargo check` should not cause `Cargo.lock` to change. + +##### `resolver.precedence` + +*(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* + +* Type: string +* Default: "rust-version" +* Environment: `CARGO_RESOLVER_PRECEDENCE` + +Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. + +* `maximum`: prefer the highest compatible versions of dependencies +* `rust-version`: prefer dependencies where their `package.rust-version` is less than or equal to your `package.rust-version` + +`rust-version` can be overridden with `--ignore-rust-version` which will fallback to `maximum`. + ## Example workflows We'll step through several scenarios to highlight the changes in the user experience. @@ -657,66 +717,6 @@ Instead, if a newer clap version was out needing 1.94 or 1.95, I would instead e -## Example documentation updates - -### The `rust-version` field - -*(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* - -The `rust-version` field is an optional key that tells cargo what version of the -Rust language and compiler your package can be compiled with. If the currently -selected version of the Rust compiler is older than the stated version, cargo -will exit with an error, telling the user what version is required. -To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. - -The first version of Cargo that supports this field was released with Rust 1.56.0. -In older releases, the field will be ignored, and Cargo will display a warning. - -```toml -[package] -# ... -rust-version = "1.56" -``` - -The Rust version must be a bare version number with two or three components; it -cannot include semver operators or pre-release identifiers. Compiler pre-release -identifiers such as -nightly will be ignored while checking the Rust version. -The `rust-version` must be equal to or newer than the version that first -introduced the configured `edition`. - -The Rust version can also be `"auto"`. -This will act the same as if it was set to the version of your toolchain. -When publishing, `"auto"` will be replaced by the version of your toolchain in your published manifest. - -The `rust-version` may be ignored using the `--ignore-rust-version` option. - -Setting the `rust-version` key in `[package]` will affect all targets/crates in -the package, including test suites, benchmarks, binaries, examples, etc. - -### Rust Version - -*(update to [Dependency Resolution's Other Constraints documentation](https://doc.rust-lang.org/cargo/reference/resolver.html))* - -When multiple versions of a dependency satisfy all version requirements, -cargo will prefer those with a compatible `package.rust-version` over those that -aren't compatible. -Some details may change over time though `cargo check && rustup update && cargo check` should not cause `Cargo.lock` to change. - -##### `resolver.precedence` - -*(update to [Configuration](https://doc.rust-lang.org/cargo/reference/config.html))* - -* Type: string -* Default: "rust-version" -* Environment: `CARGO_RESOLVER_PRECEDENCE` - -Controls how `Cargo.lock` gets updated on changes to `Cargo.toml` and with `cargo update`. This does not affect `cargo install`. - -* `maximum`: prefer the highest compatible versions of dependencies -* `rust-version`: prefer dependencies where their `package.rust-version` is less than or equal to your `package.rust-version` - -`rust-version` can be overridden with `--ignore-rust-version` which will fallback to `maximum`. - # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 14dc48b10e7c4b9f4384843680f433a6cfce8c60 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Feb 2024 16:37:53 -0600 Subject: [PATCH 154/184] fix(guide): Clean up rust-version documentation --- text/3537-msrv-resolver.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 1044e663874..fdd974d134a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -371,30 +371,28 @@ selected version of the Rust compiler is older than the stated version, cargo will exit with an error, telling the user what version is required. To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. -The first version of Cargo that supports this field was released with Rust 1.56.0. -In older releases, the field will be ignored, and Cargo will display a warning. - ```toml [package] # ... rust-version = "1.56" ``` -The Rust version must be a bare version number with two or three components; it +The Rust version can be a bare version number with two or three components; it cannot include semver operators or pre-release identifiers. Compiler pre-release identifiers such as -nightly will be ignored while checking the Rust version. The `rust-version` must be equal to or newer than the version that first introduced the configured `edition`. The Rust version can also be `"auto"`. -This will act the same as if it was set to the version of your toolchain. -When publishing, `"auto"` will be replaced by the version of your toolchain in your published manifest. - -The `rust-version` may be ignored using the `--ignore-rust-version` option. +This will act the same as if it was set to the version of your Rust toolchain. +Your published manifest will have `"auto"` replaced with the version of your Rust toolchain. Setting the `rust-version` key in `[package]` will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc. +*Note: The first version of Cargo that supports this field was released with Rust 1.56.0. +In older releases, the field will be ignored, and Cargo will display a warning.* + ### Rust Version *(update to [Dependency Resolution's Other Constraints documentation](https://doc.rust-lang.org/cargo/reference/resolver.html))* From 392345407cdcd8fdafbe1fbcaabb5da738f327ae Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 8 Feb 2024 16:50:25 -0600 Subject: [PATCH 155/184] fix(guide): Add a summary --- text/3537-msrv-resolver.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fdd974d134a..98fa77655c7 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -359,6 +359,17 @@ one lockfile can be used but it can be difficult to edit the `Cargo.lock` to ens # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +We are introducing several new concepts +- A v3 resolver (`package.resolver`) that will prefer packages compatible with your `package.rust-version` over those that aren't + - If `package.rust-version` is unset, then your current Rust toolchain version will be used + - This resolver version will be the default for the next edition + - A `.cargo/config.toml` field will be added to disable this, e.g. for CI +- Cargo will ensure users are aware their dependencies are behind the latest in a unobtrusive way +- `cargo add` will select version requirements that can be met by a dependency with a compatible version +- A new value for `package.rust-version`, `"auto"`, which will advertise in your published package your current toolchain version as the minimum-supported Rust version + - `cargo new` will default to `package.rust-version = "auto"` +- A deny-by-default lint will replace the build error from a package having an incompatible Rust version, allowing users to opt-in to overriding it + ## Example documentation updates ### The `rust-version` field From 040a981523443aa2895250066f9f9d960965a64f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 14 Feb 2024 19:01:02 -0600 Subject: [PATCH 156/184] fix(motivation): Highlight which are pros and cons --- text/3537-msrv-resolver.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 98fa77655c7..0ec1273165d 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -237,7 +237,7 @@ A user runs `cargo new` and starts development. A maintainer may also want to avoid constraining their dependents, for a variety of reasons, and leave MSRV support as a gray area. **Priority 1 because:** -- No MSRV is fine as pushing people to have an MSRV would lead to either +- ✅ No MSRV is fine as pushing people to have an MSRV would lead to either - an inaccurate reported MSRV from it going stale which would lower the quality of the ecosystem - raise the barrier to entry by requiring more process for packages and pushing the cost of old Rust versions on people who don't care @@ -259,10 +259,10 @@ Due to the pain points listed below, the target audience for this workflow is li likely pushing them to not specify their MSRV. **Priority 2 because:** -- Low barrier to maintaining a high quality of support for their MSRV -- Being willing to advertising an MSRV, even if latest, improves the information available to developers, increasing the quality of the ecosystem -- Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain -- By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading +- ✅ Low barrier to maintaining a high quality of support for their MSRV +- ✅ Being willing to advertising an MSRV, even if latest, improves the information available to developers, increasing the quality of the ecosystem +- ✅ Costs for dealing with old Rust toolchains is shifted from the maintainer and the users on a supported toolchain to those on an unsupported toolchain +- ✅ By focusing new development on latest MSRV, this provides a carrot to encourage others to actively upgrading **Pain points (in addition to the prior workflow):** @@ -306,10 +306,10 @@ The way they verify dependencies is restricted as they can't rely on always upda Maintainers likely only need to do a compilation check for MSRV as their regular CI runs ensure that the behavior (which is usually independent of rust version) is correct for the MSRV-compatible dependencies. **Priority 3 because:** -- Several use cases for this workflow have little alternative -- MSRV applies to all interactions to the project which also means that the level of "support" is consistent -- This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users -- For library and tool maintainers, they are absorbing costs from these less common use cases +- ✅ Several use cases for this workflow have little alternative +- ✅ MSRV applies to all interactions to the project which also means that the level of "support" is consistent +- ❌ This implies stagnation and there are cases where people could more easily use newer toolchains, like Debian users, but that is less so the case for other users +- ❌ For library and tool maintainers, they are absorbing costs from these less common use cases - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases **Pain points:** @@ -334,13 +334,13 @@ the fact that the tests are verifying (on a newer toolchain) that the build/norm Compared to the above workflow, this is likely targeted at just library and tool maintainers as other use cases don't have access to the latest version or they are needing the repo to be compatible with their MSRV. **Priority 4 because:** -- The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem +- ❌ The MSRV has various carve outs, providing an inconsistent experience compared to other packages using other workflows and affecting the quality of the ecosystem - For workspaces with bins, `cargo install --locked` is expected to work with the MSRV but won't - If they use new Cargo features, then `[patch]`ing in a git source for the dependency won't work - For contributors, they must be on an unspecified Rust toolchain version -- The caveats involved in this approach (see prior item) would lead to worse documentation which lowers the quality to users -- This still leads to stagnation despite being able to use the latest dependencies as they are limited in what they can use from them and they can't use features from the latest Rust toolchain -- These library and tool maintainers are absorbing costs from the less common use cases of their dependents +- ❌ The caveats involved in this approach (see prior item) would lead to worse documentation which lowers the quality to users +- ❌ This still leads to stagnation despite being able to use the latest dependencies as they are limited in what they can use from them and they can't use features from the latest Rust toolchain +- ❌ These library and tool maintainers are absorbing costs from the less common use cases of their dependents - They could shift these costs to those that need old versions by switching to the "Latest MSRV" workflow by allowing their users to backport fixes to prior MSRV releases **Pain points:** From d08e04f3a74893ffa23f52d59bdf40c56146ac21 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 15 Feb 2024 08:12:53 -0600 Subject: [PATCH 157/184] fix(ref): Add missing context for resolve diffing --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 0ec1273165d..d3db7b558db 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -830,7 +830,7 @@ Whether we do this and how much will be subject to factors like noisy output, pe Some approaches we can take for doing this include: -We then do a depth-first diff of the trees, stopping and reporting on the first different node. +After resolving, we can do a depth-first diff of the trees, stopping and reporting on the first different node. This would let us report on any command that changes the way the tree is resolved (from explicit changes with `cargo update` to `cargo build` syncing `Cargo.toml` changes to `Cargo.lock`). We'd likely want to limit the output to only the sub-tree that changed. From 99776123952011fbf93ed4010e6460932de06505 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 15 Feb 2024 08:18:00 -0600 Subject: [PATCH 158/184] fix(ref): Specify rust-version fallback when on pre-release --- text/3537-msrv-resolver.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index d3db7b558db..4e2657a1af7 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -760,7 +760,7 @@ This will be less optimal for workspaces with multiple MSRVs and dependencies un Users can workaround this by raising the version requirement or using `cargo update --precise`. When `rust-version` is unset, -we'll fallback to `rustc --version`. +we'll fallback to `rustc --version` if its not a pre-release. This is primarily targeted at helping users with a [`rust-toolchain.toml` file](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) (to reduce duplication) @@ -1144,6 +1144,9 @@ Some options include: - `local` implies a `remote` - `current` is like `latest` but a little softer and might work +Resolving with an unset `package.rust-version` falls back to `rustc --version` only if its a non-pre-release. +Should we instead pick the previous stable release (e.g. nightly 1.77 would resolve for 1.76)? + Whether we report stale dependencies only on `cargo update` or on every command. See "Syncing `Cargo.toml` to `Cargo.lock` on any Cargo command". From cf4651b95382e83a891f8245f0c7b3f8db26bafc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 15 Feb 2024 08:23:10 -0600 Subject: [PATCH 159/184] fix(alt): Clarify why a CLI option isn't used --- text/3537-msrv-resolver.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 4e2657a1af7..5d13af1c360 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -790,8 +790,6 @@ with potential values being: - `[.[.]]`: manually override the version used If a `rust-version` value is used, we'd switch to `maximum` when `--ignore-rust-version` is set. -This will let users effectively pass `--ignore-rust-version` to all commands, -without having to support the flag on every single command. ## `cargo build` @@ -890,6 +888,9 @@ and only in the sense that the user needs to know to explicitly take action to a Misc alternatives - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. +- `CARGO_RESOLVER_PRECEDENCE` is used, rather than a CLI option + - This is unlikely to be used in one-off cases but across whole interactions which is better suited for config / env variables, rather than CLI options + - Minimize CLI clutter - `CARGO_RESOLVER_PRECEDENCE=rust-version` implies maximal resolution among MSRV-compatible dependencies. Generally MSRV doesn't decrease over versions, so minimal resolution will likely pick packages with compatible rust-versions. - `cargo add` helps by selecting rust-version-compatible minimum bounds - This bypasses a lot of complexity either from exploding the number of states we support or giving users control over the fallback by making the field an array of strategies. From 13fcb24c563846fa3a244f60493c77396a0e97c7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 15 Feb 2024 08:24:23 -0600 Subject: [PATCH 160/184] fix(alt): Further clarify CLI option --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 5d13af1c360..a1f7267e9cd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -888,7 +888,7 @@ and only in the sense that the user needs to know to explicitly take action to a Misc alternatives - Dependencies with unspecified `package.rust-version`: we could mark these as always-compatible or always-incompatible; there really isn't a right answer here. - The resolver doesn't support backtracking as that is extra complexity that we can always adopt later as we've reserved the right to make adjustments to what `cargo generate-lockfile` will produce over time. -- `CARGO_RESOLVER_PRECEDENCE` is used, rather than a CLI option +- `CARGO_RESOLVER_PRECEDENCE` is used, rather than a CLI option (e.g. ensuring every command has `--ignore-rust-version` or a `--rust-version `) - This is unlikely to be used in one-off cases but across whole interactions which is better suited for config / env variables, rather than CLI options - Minimize CLI clutter - `CARGO_RESOLVER_PRECEDENCE=rust-version` implies maximal resolution among MSRV-compatible dependencies. Generally MSRV doesn't decrease over versions, so minimal resolution will likely pick packages with compatible rust-versions. From ef57daa4b49fd1c731ecd51d8346cc4d25ac3b23 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 15 Feb 2024 15:16:24 -0600 Subject: [PATCH 161/184] fix: Space out code fences --- text/3537-msrv-resolver.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index a1f7267e9cd..43247439238 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -37,6 +37,7 @@ $ cargo add clap $ git commit -a -m "WIP" && git push ... ``` + After 30 minutes, CI fails. The first step is to reproduce this locally ```console @@ -46,6 +47,7 @@ $ cargo +1.64.0 check Updating crates.io index Fetch [===============> ] 67.08%, (28094/50225) resolving deltas ``` + After waiting several minutes, cursing being stuck on a version from before sparse registry support was added... ```console $ cargo +1.64.0 check @@ -60,6 +62,7 @@ $ cargo +1.64.0 check error: package `clap_builder v4.4.8` cannot be built because it requires rustc 1.70.0 or newer, while the currently ac tive rustc version is 1.64.0 ``` + Thankfully, crates.io now shows [supported Rust versions](https://crates.io/crates/clap_builder/versions), so I pick v4.3.24. ```console $ cargo update -p clap_builder --precise 4.3.24 @@ -71,6 +74,7 @@ required by package `clap v4.4.8` ... which satisfies dependency `clap = "^4.4.8"` (locked to 4.4.8) of package `msrv-resolver v0.1.0 (/home/epage/src/personal/dump/msrv-resolver)` perhaps a crate was updated and forgotten to be re-vendored? ``` + After browsing on some forums, I edit my `Cargo.toml` to roll back to `clap = "4.3.24"` and try again ```console $ cargo update -p clap --precise 4.3.24 @@ -100,6 +104,7 @@ $ cargo +1.64.0 check error: package `anstyle-parse v0.2.2` cannot be built because it requires rustc 1.70.0 or newer, while the currently a ctive rustc version is 1.64.0 ``` + Again, consulting [crates.io](https://crates.io/crates/anstyle-parse/versions) ```console $ cargo update -p anstyle-parse --precise 0.2.1 @@ -109,6 +114,7 @@ $ cargo +1.64.0 check error: package `clap_lex v0.5.1` cannot be built because it requires rustc 1.70.0 or newer, while the currently active rustc version is 1.64.0 ``` + Again, consulting [crates.io](https://crates.io/crates/clap_lex/versions) ```console $ cargo update -p clap_lex --precise 0.5.0 @@ -118,6 +124,7 @@ $ cargo +1.64.0 check error: package `anstyle v1.0.4` cannot be built because it requires rustc 1.70.0 or newer, while the currently active rustc version is 1.64.0 ``` + Again, consulting [crates.io](https://crates.io/crates/anstyle/versions) ```console cargo update -p anstyle --precise 1.0.2 @@ -143,6 +150,7 @@ $ cargo +1.64.0 check Checking msrv-resolver v0.1.0 (/home/epage/src/personal/dump/msrv-resolver) Finished dev [unoptimized + debuginfo] target(s) in 2.96s ``` + Success! Mixed with many tears and less hair. @@ -450,6 +458,7 @@ At some point, I start a project: $ cargo new foo $ cat foo/Cargo.toml ``` + ```toml [package] name = "foo" @@ -461,10 +470,12 @@ rust-version = "auto" [dependencies] ``` + ```console $ cargo add clap -F derive Adding clap 5.10.30 ``` + *(note: this user would traditionally be a "Latest Rust" user but `package.rust-version` automatically them moved to "Latest Rust with MSRV" without extra validation effort or risk of their MSRV going stale)* After some time, I get back to my project and decide to add completion support: @@ -475,6 +486,7 @@ warning: clap_complete 5.11.0 exists but requires Rust 1.93 while you are runnin To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup update && cargo add clap_complete@5.11.0`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap_complete@5.11.0` ``` + Wanting to be on the latest version, I run ```console $ rustup update @@ -499,6 +511,7 @@ rust-version = "auto" clap = { version = "5.10.30", features = ["derive"] } clap_complete = "5.10" # <-- new ``` + And away I go: ```console $ cargo check @@ -506,6 +519,7 @@ Warning: adding clap_complete@5.10.40 because 5.11.0 requires Rust 1.93 while yo To use the clap_complete@5.11.0 with a compatible Rust version, run `rustup update && cargo update`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo update --ignore-rust-version` ``` + But I am in a hurry and don't want to disrupt my flow. `clap_complete@5.10.40` is likely fine. I am running `clap@5.10.30` and that has been working for me. @@ -536,6 +550,7 @@ $ $EDITOR `Cargo.toml` $ cargo publish Published foo 0.1.0 ``` + If I look on crates.io, the new 0.1.0 version shows up with a rust-version of 1.94 without me having to manual update the field and relying on the `cargo publish`s verify step to verify the correctness of that MSRV. @@ -556,6 +571,7 @@ At some point, I start a project: $ cargo new foo $ cat foo/Cargo.toml ``` + ```toml [package] name = "foo" @@ -567,6 +583,7 @@ rust-version = "auto" [dependencies] ``` + ```console $ cargo add clap -F derive Adding clap 5.10.30 @@ -574,6 +591,7 @@ warning: clap 5.11.0 exists but requires Rust 1.93 while you are running 1.92. To use the clap@5.11.0 with a compatible Rust version, run `rustup update && cargo add clap@5.10.0`. To force the use of clap_complete@5.11.0 independent of your toolchain, run `cargo add clap@5.10.0` ``` + At this point, I have a couple of options 1. I check and clap advertises that they "support" Rust 1.92 by cherry-picking fixes into 5.10 and I feel comfortable with that 2. I check `cargo deny` and don't see any vulnerabilities and that is good enough for me, knowing that the majority of my users are likely on newer versions @@ -603,6 +621,7 @@ At some point, I start a project: $ cargo new foo $ cat foo/Cargo.toml ``` + ```toml [package] name = "foo" @@ -614,13 +633,14 @@ rust-version = "auto" [dependencies] ``` + ```console $ cargo add clap -F derive Adding clap 5.11.0 ``` I send this to my image builder and I get this failure for one of my embedded targets: -``` +```console $ cargo build error: clap 5.11.0 requires Rust 1.93.0 while you are running 1.92.0 @@ -629,6 +649,7 @@ note: set `package.rust-version = "1.92.0"` to ensure compatible versions are se note: lint `cargo::incompatible-msrv` is denied by default ``` + I make the prescribed changes: ```toml [package] @@ -642,6 +663,7 @@ rust-version = "1.92" # <-- was "auto" before I edited it [dependencies] clap = { version = "5.10.30", features = ["derive"] } # <-- downgraded ``` + And my image build works! After some time, I run: @@ -654,6 +676,7 @@ clap_complete 5.10.40 5.11.0 requires Rust 1.93 note: To use the latest depednencies, run `rustup update && cargo update`. To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` ``` + We've EOLed the last embedded target that supported 1.92 and so we can update our `package.rust-version`, so we can update it and our dependencies: ```console @@ -680,6 +703,7 @@ At some point, I start a project: ```console $ cargo new foo --lib ``` + I've decided on an "N-2" MSRV policy: ```toml [package] @@ -692,6 +716,7 @@ rust-version = "1.92" # <-- was "auto" before I edited it [dependencies] ``` + ```console $ cargo add clap -F derive Adding clap 5.10.30 @@ -699,6 +724,7 @@ warning: clap 5.11.0 exists but requires Rust 1.93 while `foo` has `package.rust To use clap@5.11.0 with a compatible package.rust-version, run `cargo add clap@5.11.0 --update-rust-version` To force the use of clap@5.11.0 independent of your toolchain, run `cargo add clap@5.11.0` ``` + At this point, I have a couple of options 1. I check and clap advertises that they "support" Rust 1.92 by cherry-picking fixes into 5.10 and I feel comfortable with that 2. I check `cargo deny` and don't see any vulnerabilities and that is good enough for me, knowing that the majority of my users are likely on newer versions @@ -716,12 +742,14 @@ clap_complete 5.10.40 5.11.0 requires Rust 1.93 note: To use the latest depednencies, run `rustup update && cargo update`. To force the use of the latest dependencies, independent of your toolchain, run `cargo update --ignore-rust-version` ``` + At this point, 1.95 is out, so I'm fine updating my MSRV and I run: ```console $ cargo update --update-rust-version Updating clap 5.10.30 to 5.11.0 Updating foo's rust-version from 1.92 to 1.93 ``` + Instead, if a newer clap version was out needing 1.94 or 1.95, I would instead edit `Cargo.toml` myself. @@ -777,6 +805,7 @@ We'll add a `resolver.precedence ` field to `.cargo/config.toml` which will cont [build] resolver.precedence = "rust-version" # Default with `v3` ``` + with potential values being: - `maximum`: behavior today (default for v1 and v2 resolvers) - Needed for [verifying latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies) From dce5d7c7a0dba7c644ba0bcc115fca3838ecd01d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 20 Feb 2024 08:41:10 -0600 Subject: [PATCH 162/184] fix(motivation): Include rust-version proliferation stats --- text/3537-msrv-resolver.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 43247439238..8b0b4691f5c 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -168,6 +168,11 @@ How wide spread is this? Take this with a grain of salt but based on crates.io *([source](https://rust-lang.zulipchat.com/#narrow/stream/318791-t-crates-io/topic/cargo.20version.20usage/near/401440149))* +This was aided by the presence of `package.rust-version`. +Of all packages (137,569), only 8,857 (6.4%) have that field set. +When limiting to the 61,758 "recently" published packages (an upload since the start of 2023), +only 8,550 (13.8%) have the field set. + People have tried to reduce the pain from MSRV with its own costs: - Treating it as a breaking change: - This leads to extra churn in the ecosystem when a fraction of users are likely going to benefit From abd9a5ece11ac3af3ca216b7434b0ceb0d50d01a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 22 Feb 2024 19:18:25 -0600 Subject: [PATCH 163/184] fix(motivation): Clarify scope of MSRV meaning --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8b0b4691f5c..b35841d0aef 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -229,8 +229,8 @@ And keeping in mind Some implications: - "Support" in MSRV implies the same quality and responsiveness to bug reports, regardless of Rust version -- MSRV applies to all interactions with a project - (including registry dependency, git dependency, `cargo install`, contributor experience), +- MSRV applies to all interactions with a project within the maintainers control + (including as a registry dependency, `cargo install --locked`, as a git dependency, contributor experience; excluding transitive dependencies, rust-analyzer, etc), unless documented otherwise like - Some projects may document that enabling a feature will affect the MSRV (e.g. [moka](https://docs.rs/moka/0.12.1/moka/#minimum-supported-rust-versions)) - Some projects may have a higher MSRV for building the repo (e.g. `Cargo.lock` with newer dependencies, reliance on cargo features that get stripped on publish) From 2ddb8476a3545448c400551754b15327521ad8c4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 22 Feb 2024 19:21:24 -0600 Subject: [PATCH 164/184] fix: Yeet the term 'auto' to avoid confusion --- text/3537-msrv-resolver.md | 62 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b35841d0aef..f7e4495c620 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -379,8 +379,8 @@ We are introducing several new concepts - A `.cargo/config.toml` field will be added to disable this, e.g. for CI - Cargo will ensure users are aware their dependencies are behind the latest in a unobtrusive way - `cargo add` will select version requirements that can be met by a dependency with a compatible version -- A new value for `package.rust-version`, `"auto"`, which will advertise in your published package your current toolchain version as the minimum-supported Rust version - - `cargo new` will default to `package.rust-version = "auto"` +- A new value for `package.rust-version`, `""`, which will advertise in your published package your current toolchain version as the minimum-supported Rust version + - `cargo new` will default to `package.rust-version = ""` - A deny-by-default lint will replace the build error from a package having an incompatible Rust version, allowing users to opt-in to overriding it ## Example documentation updates @@ -407,9 +407,9 @@ identifiers such as -nightly will be ignored while checking the Rust version. The `rust-version` must be equal to or newer than the version that first introduced the configured `edition`. -The Rust version can also be `"auto"`. +The Rust version can also be `""`. This will act the same as if it was set to the version of your Rust toolchain. -Your published manifest will have `"auto"` replaced with the version of your Rust toolchain. +Your published manifest will have `""` replaced with the version of your Rust toolchain. Setting the `rust-version` key in `[package]` will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc. @@ -469,7 +469,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "auto" +rust-version = "" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -508,7 +508,7 @@ Here, we can shortcut some questions about version requirements because clap ali name = "foo" version = "0.1.0" edition = "2024" -rust-version = "auto" +rust-version = "" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -582,7 +582,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "auto" +rust-version = "" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -632,7 +632,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "auto" +rust-version = "" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -661,7 +661,7 @@ I make the prescribed changes: name = "foo" version = "0.1.0" edition = "2024" -rust-version = "1.92" # <-- was "auto" before I edited it +rust-version = "1.92" # <-- was "" before I edited it # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -715,7 +715,7 @@ I've decided on an "N-2" MSRV policy: name = "foo" version = "0.1.0" edition = "2024" -rust-version = "1.92" # <-- was "auto" before I edited it +rust-version = "1.92" # <-- was "" before I edited it # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -835,7 +835,7 @@ without requiring `--ignore-rust-version` on every invocation. Ideally, we present all of the MSRV issues upfront to be resolved together. At minimum, we should present a top-down message, rather than bottom up. -If `package.rust-version` is unset or `"auto"`, the diagnostic should suggest setting it +If `package.rust-version` is unset or `""`, the diagnostic should suggest setting it to help raise awareness of `package.rust-version` being able to reduce future resolution errors. This would benefit from knowing the oldest MSRV. @@ -891,11 +891,11 @@ Users may pass ## `cargo publish` -`package.rust-version` will gain support for an `"auto"` value, in addition to partial versions. -On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"auto"` replaced with `rustc --version`. +`package.rust-version` will gain support for an `""` value, in addition to partial versions. +On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `""` replaced with `rustc --version`. If `rustc --version` is a pre-release, publish will fail. -`cargo new` will include `package.rust-version = "auto"`. +`cargo new` will include `package.rust-version = ""`. # Drawbacks [drawbacks]: #drawbacks @@ -945,7 +945,7 @@ Misc alternatives - There is little reason to select an MSRV higher than their Rust toolchain - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain - This comes at the cost of inconsistency with `--ignore-rust-version`. -- Nightly `cargo publish` with `auto` fails because there isn't a good value to use and this gives us flexibility to change it later (e.g. just leaving the `rust-version` as unset). +- Nightly `cargo publish` with `""` fails because there isn't a good value to use and this gives us flexibility to change it later (e.g. just leaving the `rust-version` as unset). ## Ensuring the registry Index has `rust-version` without affecting quality @@ -954,7 +954,7 @@ Ensuring we have `package.rust-version` populated more often (while maintaining quality of that data) is an important problem but does not have to be solved to get value out of this RFC and can be handled separately. -We chose an opt-in for populating `package.rust-version` based on `rustc --version` (`"auto"`). +We chose an opt-in for populating `package.rust-version` based on `rustc --version` (`""`). This will encourage a baseline of quality as users are developing with that version and `cargo publish` will do a verification step, by default. This will help seed the Index with more `package.rust-version` data for the resolver to work with. The downside is that the `package.rust-version` will likely be higher than it absolutely needs. @@ -968,7 +968,7 @@ When missing, `cargo publish` could inject `package.rust-version` using the vers workaround it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. -As things evolve, we could re-evaluate making `"auto"` the default. +As things evolve, we could re-evaluate making `""` the default. ~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ **However**, if people aren't committed to verifying that was implicitly set, @@ -1038,11 +1038,11 @@ allows us to move forward without having to figure out all of these details. Affects of current solution on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV @@ -1060,17 +1060,17 @@ Instead of adding `resolver = "3"`, we could keep the default resolver the same - `cargo generate-lockfile` - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default -This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = ""`, `cargo build` error to diagnostic). Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - 🟰 ~~Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`)~~ 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ❌ Without `cargo update --update-rust-version`, `""` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` @@ -1087,7 +1087,7 @@ matching the `cargo build` incompatible dependency error. - We would still support `CARGO_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default -This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "auto"`, `cargo build` error to diagnostic). +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = ""`, `cargo build` error to diagnostic). This is an auto-adapting variant where - If they are on the latest toolchain, they get the current behavior @@ -1095,13 +1095,13 @@ This is an auto-adapting variant where Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - - ❌ Without `cargo update --update-rust-version`, `"auto"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ❌ Without `cargo update --update-rust-version`, `""` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` @@ -1127,11 +1127,11 @@ We could adopt this approach in the future, if desired Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = "auto"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ❌ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = "auto"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV @@ -1166,13 +1166,13 @@ The config field is fairly rough - If we later add "resolve to toolchain" version, this might be confusing. - Maybe enumeration, like `resolver.rust-version = `? -`rust-version = "auto"`'s field name is unsettled and deciding on it is not blocking for stabilization. +`rust-version = ""`'s field name is unsettled and deciding on it is not blocking for stabilization. Ideally, we make it clear that this this is not inferred from syntax, that this is the currently running toolchain, that we ignore pre-release toolchains, and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these consistent. Some options include: -- `auto` can imply "infer from syntactic minimum" +- `""` can imply "infer from syntactic minimum" - `latest` can imply "latest globally (ie from rust-lang.org) - `stable` can imply "latest globally (ie from rust-lang.org) - `toolchain` might look weird? From 74651c9c44f6543ed5278365c043c835027cd354 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 22 Feb 2024 19:26:58 -0600 Subject: [PATCH 165/184] fix(motivation): Make sure we call out security explicitly --- text/3537-msrv-resolver.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index f7e4495c620..75ff3b93c31 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -204,7 +204,10 @@ Some design criteria we can use for evaluating workflows: - Cargo should not make major breaking changes - Low barrier to entry - The costs of “non-recommended” setups should focused on those that need them -- Encourage a standard of quality within the ecosystem +- Encourage a standard of quality within the ecosystem, including + - Assumption that things will work as advertised (e.g. our success with MSRV) + - A pleasant experience (e.g. meaningful error messages) + - Secure - Every feature has a cost and we should balance the cost against the value we expect - Features can further constrain what can be done in the future due to backwards compatibility - Features increase maintenance burden From a33ea4574d3aa264a650baddceb2b76c3c7a8f0d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 22 Feb 2024 19:41:34 -0600 Subject: [PATCH 166/184] fix(rationale): Include short-term benefit of polyfills --- text/3537-msrv-resolver.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 75ff3b93c31..91681d83257 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1054,6 +1054,11 @@ Affects of current solution on workflows (including non-resolver behavior): - ❌ Maintainers will have to opt-in to latest dependencies, in a `.cargo/config.toml` - ✅ Verifying MSRV will no longer require juggling `Cargo.lock` files or using unstable features +A short term benefit (hence why this is separate) is that an MSRV-aware resolver by default is that we can use it as a polyfill for +[`cfg(version)`](https://dev-doc.rust-lang.org/stable/unstable-book/language-features/cfg-version.html) +(which will likely need a lot of work in cargo after we finish stabilizing it for rustc). +A polyfill package can exist that has multiple maintained semver-compatible versions with different MSRVs with the older ones leveraging external libraries while the newer ones leverage the standard library. + ### Make this opt-in rather than opt-out Instead of adding `resolver = "3"`, we could keep the default resolver the same as today but allow opt-in to MSRV-aware resolver via `CARGO_RESOLVER_PRECEDENCE=rust-version`. From ff7d7ae67c7fc6a9d74fde8ff4e098a3a67c5b28 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 22 Feb 2024 19:46:36 -0600 Subject: [PATCH 167/184] fix: Don't confuse markdown parsers treating items like HTML --- text/3537-msrv-resolver.md | 62 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 91681d83257..a7280ef95ac 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -382,8 +382,8 @@ We are introducing several new concepts - A `.cargo/config.toml` field will be added to disable this, e.g. for CI - Cargo will ensure users are aware their dependencies are behind the latest in a unobtrusive way - `cargo add` will select version requirements that can be met by a dependency with a compatible version -- A new value for `package.rust-version`, `""`, which will advertise in your published package your current toolchain version as the minimum-supported Rust version - - `cargo new` will default to `package.rust-version = ""` +- A new value for `package.rust-version`, `"tbd-name-representing-currently-running-rust-toolchain"`, which will advertise in your published package your current toolchain version as the minimum-supported Rust version + - `cargo new` will default to `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` - A deny-by-default lint will replace the build error from a package having an incompatible Rust version, allowing users to opt-in to overriding it ## Example documentation updates @@ -410,9 +410,9 @@ identifiers such as -nightly will be ignored while checking the Rust version. The `rust-version` must be equal to or newer than the version that first introduced the configured `edition`. -The Rust version can also be `""`. +The Rust version can also be `"tbd-name-representing-currently-running-rust-toolchain"`. This will act the same as if it was set to the version of your Rust toolchain. -Your published manifest will have `""` replaced with the version of your Rust toolchain. +Your published manifest will have `"tbd-name-representing-currently-running-rust-toolchain"` replaced with the version of your Rust toolchain. Setting the `rust-version` key in `[package]` will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc. @@ -472,7 +472,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "" +rust-version = "tbd-name-representing-currently-running-rust-toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -511,7 +511,7 @@ Here, we can shortcut some questions about version requirements because clap ali name = "foo" version = "0.1.0" edition = "2024" -rust-version = "" +rust-version = "tbd-name-representing-currently-running-rust-toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -585,7 +585,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "" +rust-version = "tbd-name-representing-currently-running-rust-toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -635,7 +635,7 @@ $ cat foo/Cargo.toml name = "foo" version = "0.1.0" edition = "2024" -rust-version = "" +rust-version = "tbd-name-representing-currently-running-rust-toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -664,7 +664,7 @@ I make the prescribed changes: name = "foo" version = "0.1.0" edition = "2024" -rust-version = "1.92" # <-- was "" before I edited it +rust-version = "1.92" # <-- was "tbd-name-representing-currently-running-rust-toolchain" before I edited it # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -718,7 +718,7 @@ I've decided on an "N-2" MSRV policy: name = "foo" version = "0.1.0" edition = "2024" -rust-version = "1.92" # <-- was "" before I edited it +rust-version = "1.92" # <-- was "tbd-name-representing-currently-running-rust-toolchain" before I edited it # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -838,7 +838,7 @@ without requiring `--ignore-rust-version` on every invocation. Ideally, we present all of the MSRV issues upfront to be resolved together. At minimum, we should present a top-down message, rather than bottom up. -If `package.rust-version` is unset or `""`, the diagnostic should suggest setting it +If `package.rust-version` is unset or `"tbd-name-representing-currently-running-rust-toolchain"`, the diagnostic should suggest setting it to help raise awareness of `package.rust-version` being able to reduce future resolution errors. This would benefit from knowing the oldest MSRV. @@ -894,11 +894,11 @@ Users may pass ## `cargo publish` -`package.rust-version` will gain support for an `""` value, in addition to partial versions. -On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `""` replaced with `rustc --version`. +`package.rust-version` will gain support for an `"tbd-name-representing-currently-running-rust-toolchain"` value, in addition to partial versions. +On `cargo publish` / `cargo package`, the generated `*.crate`s `Cargo.toml` will have `"tbd-name-representing-currently-running-rust-toolchain"` replaced with `rustc --version`. If `rustc --version` is a pre-release, publish will fail. -`cargo new` will include `package.rust-version = ""`. +`cargo new` will include `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"`. # Drawbacks [drawbacks]: #drawbacks @@ -948,7 +948,7 @@ Misc alternatives - There is little reason to select an MSRV higher than their Rust toolchain - We should still be warning the user that new dependencies are available if they upgrade their Rust toolchain - This comes at the cost of inconsistency with `--ignore-rust-version`. -- Nightly `cargo publish` with `""` fails because there isn't a good value to use and this gives us flexibility to change it later (e.g. just leaving the `rust-version` as unset). +- Nightly `cargo publish` with `"tbd-name-representing-currently-running-rust-toolchain"` fails because there isn't a good value to use and this gives us flexibility to change it later (e.g. just leaving the `rust-version` as unset). ## Ensuring the registry Index has `rust-version` without affecting quality @@ -957,7 +957,7 @@ Ensuring we have `package.rust-version` populated more often (while maintaining quality of that data) is an important problem but does not have to be solved to get value out of this RFC and can be handled separately. -We chose an opt-in for populating `package.rust-version` based on `rustc --version` (`""`). +We chose an opt-in for populating `package.rust-version` based on `rustc --version` (`"tbd-name-representing-currently-running-rust-toolchain"`). This will encourage a baseline of quality as users are developing with that version and `cargo publish` will do a verification step, by default. This will help seed the Index with more `package.rust-version` data for the resolver to work with. The downside is that the `package.rust-version` will likely be higher than it absolutely needs. @@ -971,7 +971,7 @@ When missing, `cargo publish` could inject `package.rust-version` using the vers workaround it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. -As things evolve, we could re-evaluate making `""` the default. +As things evolve, we could re-evaluate making `"tbd-name-representing-currently-running-rust-toolchain"` the default. ~~We could encourage people to set their MSRV by having `cargo new` default `package.rust-version`.~~ **However**, if people aren't committed to verifying that was implicitly set, @@ -1041,11 +1041,11 @@ allows us to move forward without having to figure out all of these details. Affects of current solution on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV @@ -1068,17 +1068,17 @@ Instead of adding `resolver = "3"`, we could keep the default resolver the same - `cargo generate-lockfile` - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default -This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = ""`, `cargo build` error to diagnostic). +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"`, `cargo build` error to diagnostic). Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - 🟰 ~~Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`)~~ 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - - ❌ Without `cargo update --update-rust-version`, `""` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ❌ Without `cargo update --update-rust-version`, `"tbd-name-representing-currently-running-rust-toolchain"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` @@ -1095,7 +1095,7 @@ matching the `cargo build` incompatible dependency error. - We would still support `CARGO_RESOLVER_PRECEDENCE=rust-version` to help "Extended MSRV" users - We'd drop from this proposal `cargo update [--ignore-rust-version|--update-rust-version]` as they don't make sense with this new default -This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = ""`, `cargo build` error to diagnostic). +This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"`, `cargo build` error to diagnostic). This is an auto-adapting variant where - If they are on the latest toolchain, they get the current behavior @@ -1103,13 +1103,13 @@ This is an auto-adapting variant where Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - ~~`cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV~~ - - ❌ Without `cargo update --update-rust-version`, `""` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors + - ❌ Without `cargo update --update-rust-version`, `"tbd-name-representing-currently-running-rust-toolchain"` will be more of a default path, leading to more maintainers updating their MSRV more aggressively and waiting until minors - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV - ✅ Users will be able to opt-in to MSRV-compatible dependencies, in a `.cargo/config.toml` @@ -1135,11 +1135,11 @@ We could adopt this approach in the future, if desired Affects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - - ✅ `cargo new` setting `package.rust-version = ""` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost + - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ❌ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) 2. Latest Rust as the MSRV - ✅ Packages can more easily keep their MSRV up-to-date with - - `package.rust-version = ""` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) + - `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` (no policy around when it is changed) though this is dependent on your Rust toolchain being up-to-date (see "Latest Rust with no MSRV" for more) - `cargo update --update-rust-version` (e.g. when updating minor version) though this is dependent on what you dependencies are using for an MSRV - ✅ Packages can more easily offer unofficial support for an MSRV due to shifting the building with MSRV-incompatible dependencies from an error to a `deny` diagnostic 3. Extended MSRV @@ -1174,13 +1174,13 @@ The config field is fairly rough - If we later add "resolve to toolchain" version, this might be confusing. - Maybe enumeration, like `resolver.rust-version = `? -`rust-version = ""`'s field name is unsettled and deciding on it is not blocking for stabilization. +`rust-version = "tbd-name-representing-currently-running-rust-toolchain"`'s field name is unsettled and deciding on it is not blocking for stabilization. Ideally, we make it clear that this this is not inferred from syntax, that this is the currently running toolchain, that we ignore pre-release toolchains, and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these consistent. Some options include: -- `""` can imply "infer from syntactic minimum" +- `"tbd-name-representing-currently-running-rust-toolchain"` can imply "infer from syntactic minimum" - `latest` can imply "latest globally (ie from rust-lang.org) - `stable` can imply "latest globally (ie from rust-lang.org) - `toolchain` might look weird? From 895545c2d4dbbab7639b3677ccf32ae1f424ac97 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 23 Feb 2024 08:52:42 -0600 Subject: [PATCH 168/184] feat(future): Call out tracking of maintenance status --- text/3537-msrv-resolver.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index a7280ef95ac..504b8004d99 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1282,3 +1282,13 @@ When adding packages without an MSRV, its not clear whether it will work with your project. Knowing that they haven't declared support for your toolchain version could be important, after we've made it easier to declare an MSRV. + +## Track version maintenance status on crates.io + +If you `cargo add` a dependency and it says that a newer version is available but it supports a dramatically different MSRV than you, +it would be easy to assume there is a mismatch in expectations and you shouldn't use that dependency. +However, you may still be supported via an LTS but that information can only be captured in documentation which is not within the flow of the developer. + +If crates.io had a mutable package and package version metadata database, +maintainers could report the maintenance status of specific versions (or maybe encode their maintenance policy), +allowing cargo to report not just whether you are on latest, but whether you are supported. From 426757f28cce16a7454d1179abf3594fbfc36663 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 23 Feb 2024 16:16:31 -0600 Subject: [PATCH 169/184] fix(guide): Remove reference to MSRV is 'can be compiled' --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 504b8004d99..fc9153ccc36 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -393,7 +393,7 @@ We are introducing several new concepts *(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* The `rust-version` field is an optional key that tells cargo what version of the -Rust language and compiler your package can be compiled with. If the currently +Rust language and compiler your support compiling your package with. If the currently selected version of the Rust compiler is older than the stated version, cargo will exit with an error, telling the user what version is required. To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. From 9f22fbc17ab8cd1b76ed523961a3584aa5259a2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:19:08 -0600 Subject: [PATCH 170/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fc9153ccc36..8c8b85b5317 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -219,7 +219,7 @@ Some design criteria we can use for evaluating workflows: - This also means feedback can come more quickly, making it easier and cheaper to pivot with user needs - Spreading the cost of upgrades over time makes forced-upgrades (e.g. for a security vulnerability) less of an emergency - Our commitment to compatibility helps keep the cost of upgrade low -- When not competing with the above, we should do the right thing for the user rather than disrupt their flow to telling them what they should instead do +- When not competing with the above, we should do the right thing for the user rather than disrupt their flow to tell them what they should instead do And keeping in mind - The Rust project only supports the latest version From 03431f98f5381e26aa4a632633b1327cb0cf0eaa Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:19:25 -0600 Subject: [PATCH 171/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 8c8b85b5317..cf7ab6729a7 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -393,7 +393,7 @@ We are introducing several new concepts *(update to [manifest documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field))* The `rust-version` field is an optional key that tells cargo what version of the -Rust language and compiler your support compiling your package with. If the currently +Rust language and compiler you support compiling your package with. If the currently selected version of the Rust compiler is older than the stated version, cargo will exit with an error, telling the user what version is required. To support this, Cargo will prefer dependencies that are compatible with your `rust-version`. From 20c2bbfa7007fd78be0f28fb4069220061e587e2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:19:44 -0600 Subject: [PATCH 172/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index cf7ab6729a7..9acee845988 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -765,7 +765,7 @@ Instead, if a newer clap version was out needing 1.94 or 1.95, I would instead e # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -We expect these changes to be independent enough and beneficial on there own that they can be stabilized as each is completed. +We expect these changes to be independent enough and beneficial on their own that they can be stabilized as each is completed. ## Cargo Resolver From 53796589ec3bc61c838b7fa9658a45c112f3076f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:20:02 -0600 Subject: [PATCH 173/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9acee845988..757958dd122 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1101,7 +1101,7 @@ This is an auto-adapting variant where - If they are on the latest toolchain, they get the current behavior - If their toolchain matches their MSRV, they get an MSRV-aware resolver -Affects on workflows (including non-resolver behavior): +Effects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) From 551b73930dd4253d6201b7c0956fa42412c63965 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:21:55 -0600 Subject: [PATCH 174/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 757958dd122..985d745b304 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -968,7 +968,7 @@ Some alternative solutions include: When missing, `cargo publish` could inject `package.rust-version` using the version of rustc used during publish. **However**, this will err on the side of a higher MSRV than necessary and the only way to -workaround it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose +work around it is to set `CARGO_RESOLVER_PRECEDENCE=maximum` which will then lose all other protections. As we said, this is likely fine but then there will be no way to opt-out for the subset of maintainers who want to keep their support definition vague. As things evolve, we could re-evaluate making `"tbd-name-representing-currently-running-rust-toolchain"` the default. From 55ca765024f3d5f37b19c269704aa338b422b606 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:22:20 -0600 Subject: [PATCH 175/184] fix: Minor wording change Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 985d745b304..97c4f501342 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1178,7 +1178,7 @@ The config field is fairly rough Ideally, we make it clear that this this is not inferred from syntax, that this is the currently running toolchain, that we ignore pre-release toolchains, -and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these consistent. +and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these to be consistent. Some options include: - `"tbd-name-representing-currently-running-rust-toolchain"` can imply "infer from syntactic minimum" - `latest` can imply "latest globally (ie from rust-lang.org) From 15290937261f67d92b658aa2c2487ecb218873f6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:22:31 -0600 Subject: [PATCH 176/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 97c4f501342..c653762532a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1175,7 +1175,7 @@ The config field is fairly rough - Maybe enumeration, like `resolver.rust-version = `? `rust-version = "tbd-name-representing-currently-running-rust-toolchain"`'s field name is unsettled and deciding on it is not blocking for stabilization. -Ideally, we make it clear that this this is not inferred from syntax, +Ideally, we make it clear that this is not inferred from syntax, that this is the currently running toolchain, that we ignore pre-release toolchains, and the name works well for resolver config if we decide to add "resolve to toolchain version" and want these to be consistent. From d20e3ee6189c8fb0f3a37a6e6f476b79e3fe1ed3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:22:41 -0600 Subject: [PATCH 177/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c653762532a..f9b29c32e24 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1133,7 +1133,7 @@ To get the error reporting to be of sufficient quality will require major work i This would block stabilization indefinitely. We could adopt this approach in the future, if desired -Affects on workflows (including non-resolver behavior): +Effects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ❌ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) From 0de0ccd4d9dc33d8794a17c8f321f2e7907e5968 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:23:00 -0600 Subject: [PATCH 178/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index f9b29c32e24..b503e44a7da 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1127,7 +1127,7 @@ Instead of *preferring* MSRV-compatible dependencies, the resolver could hard er In addition to errors, differences from the "preference" solutions include: - Increase the chance of an MSRV-compatible `Cargo.lock` because the resolver can backtrack on MSRV-incompatible transitive dependencies, trying alternative versions of direct dependencies -- When a workspace members have different MSRVs, dependencies exclusive to a higher MSRV package can use higher versions +- When workspace members have different MSRVs, dependencies exclusive to a higher MSRV package can use higher versions To get the error reporting to be of sufficient quality will require major work in a complex, high risk area of Cargo (the resolver). This would block stabilization indefinitely. From 08015f37d33f9ecdf676f4787a3fa42c153a1f2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:23:15 -0600 Subject: [PATCH 179/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index b503e44a7da..9026746491e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1070,7 +1070,7 @@ Instead of adding `resolver = "3"`, we could keep the default resolver the same This has no impact on the other proposals (`cargo add` picking compatible versions, `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"`, `cargo build` error to diagnostic). -Affects on workflows (including non-resolver behavior): +Effects on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - 🟰 ~~Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`)~~ From fef356e8b4441375c4519fe7f79006e71da9dc4c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:23:27 -0600 Subject: [PATCH 180/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 9026746491e..7c536dd4b4e 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -1039,7 +1039,7 @@ allows us to move forward without having to figure out all of these details. ## Resolver behavior -Affects of current solution on workflows (including non-resolver behavior): +Effects of current solution on workflows (including non-resolver behavior): 1. Latest Rust with no MSRV - ✅ `cargo new` setting `package.rust-version = "tbd-name-representing-currently-running-rust-toolchain"` moves most users to "Latest Rust as the MSRV" with no extra maintenance cost - ✅ Dealing with incompatible dependencies will have a friendlier face because the hard build error after changing dependencies is changed to a notification during update suggesting they upgrade to get the new dependency because we fallback to `rustc --version` when `package.rust-version` is unset (as a side effect of us capturing `rust-toolchain.toml`) From 7c9f7fc7b869477731176e6106c72202503dacd0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:31:30 -0600 Subject: [PATCH 181/184] fix: Typo Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index 7c536dd4b4e..de808f821ad 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -939,7 +939,7 @@ Misc alternatives - Either way, the big care about is there being attention drawn to the change. We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) - `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the dependency to a workspace-member to select `[lint]` tables to respect and then dealing with unifying conflicting levels in between `[lint]` tables among members. -- `--ignore-rust-version` picks absolutely the latest dependencies to support both users on latest rustc and users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still want latest more up-to-date dependencies than their MSRV allows +- `--ignore-rust-version` picks absolutely the latest dependencies to support both users on latest rustc and users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still wanting latest more up-to-date dependencies than their MSRV allows - Compilation commands (e.g. `cargo check`) will take on two meanings for `--ignore-rust-version`, (1) `allow` the workspace diagnostic and (2) resolve changed dependencies to latest when syncing `Cargo.toml` to `Cargo.lock`. - This expansion of scope is for consistency - Being a flag to turn the `deny` into an `allow` is a high friction workflow that we expect users to not be too negatively impacted by this expansion. From a8a3c1e9833c9ae5512944b1e12df7bc3c873691 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:31:57 -0600 Subject: [PATCH 182/184] fix: Clarify language Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index de808f821ad..c3f2140a6dd 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -916,7 +916,7 @@ this introduces another form of drift from the latest dependencies (in addition to [lockfiles](https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control)). However, we already recommend people [verify their latest dependencies](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html#verifying-latest-dependencies), -so the only scenario this degrades it further is when lockfiles are verified by always updating to the latest, like with RenovateBot, +so the only scenario this further degrades is when lockfiles are verified by always updating to the latest, like with RenovateBot, and only in the sense that the user needs to know to explicitly take action to add another verification job to CI. # Rationale and alternatives From 16912fe87174591f9f429589c694d0fa6e7cf841 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 10:33:33 -0600 Subject: [PATCH 183/184] fix: Clarify language Co-authored-by: Kaur Kuut --- text/3537-msrv-resolver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index c3f2140a6dd..fdc7280b39a 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -936,8 +936,8 @@ Misc alternatives relatively low and they are more likely to be "in the know", making them less likely to be negatively affected by this. Therefore, we could probably get away with treating this as a minor incompatibility - - Either way, the big care about is there being attention drawn to the change. - We couldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) + - Either way, the concern is to ensure that the change receives attention. + We wouldn't want this to be like sparse registries where a setting exists and we change the default and people hardly notice (besides any improvements) - `cargo build` will treat incompatible MSRVs as a workspace-level lint, rather than a package level lint, to avoid the complexity of mapping the dependency to a workspace-member to select `[lint]` tables to respect and then dealing with unifying conflicting levels in between `[lint]` tables among members. - `--ignore-rust-version` picks absolutely the latest dependencies to support both users on latest rustc and users wanting "unsupported" dependencies, at the cost of users not on the latest rustc but still wanting latest more up-to-date dependencies than their MSRV allows - Compilation commands (e.g. `cargo check`) will take on two meanings for `--ignore-rust-version`, (1) `allow` the workspace diagnostic and (2) resolve changed dependencies to latest when syncing `Cargo.toml` to `Cargo.lock`. From b2947e3a7d6809982a3c1e0af4526916a3fef721 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 27 Feb 2024 11:12:48 -0600 Subject: [PATCH 184/184] feat(summary): Remind people of RFC fluidity --- text/3537-msrv-resolver.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/3537-msrv-resolver.md b/text/3537-msrv-resolver.md index fdc7280b39a..d11d9d49880 100644 --- a/text/3537-msrv-resolver.md +++ b/text/3537-msrv-resolver.md @@ -14,6 +14,18 @@ Provide a happy path for developers needing to work with older versions of Rust Note: `cargo install` is intentionally left out for now to decouple discussions on how to handle the security ramifications. +**Note:** Approval of this RFC does not mean everything is set in stone, like with all RFCs. +This RFC will be rolled out gradually as we stabilize each piece. +In particular, we expect to make the `cargo new` change last as it is dependent on the other changes to work well. +In evaluating stabilization, we take into account changes in the ecosystem and feedback from testing unstable features. +Based on that evaluation, we may make changes from what this RFC says. +Whether we make changes or not, stabilization will then require approval of the cargo team to merge +(explicit acknowledgement from all but 2 members with no concerns from any member) +followed by a 10 days Final Comment Period (FCP) for the remaining 2 team members and the wider community. +Cargo FCPs are now tracked in This Week in Rust to ensure the community is aware and can participate. +Even then, a change like `cargo new` can be reverted without an RFC, +likely only needing to follow the FCP process. + # Motivation [motivation]: #motivation