Auto merge of #13626 - linyihai:pre-update, r=weihanglo

Allow precise update to prerelease.

### What does this PR try to resolve?

This is a feature that attempts to support updates to pre-release versions via `cargo update --precise`.

when `precise-pre-release` used, the prerelase version will be taking consider as compatible version.  That said, we can update to any compatible pre-release version. The logic of checking the compatibility of pre-release versions is currently tentative and does not take many conditions into account, this part of the logic makes more sense when implemented in semver.

Use `-Zunstable-options`  instead of  `-Zprecise-pre-release`.

### How should we test and review this PR?

### Additional information
Part of https://github.com/rust-lang/cargo/issues/13290
This commit is contained in:
bors 2024-04-03 15:44:08 +00:00
commit 6774b8503c
11 changed files with 124 additions and 29 deletions

View File

@ -402,6 +402,14 @@ impl Dependency {
self.matches_id(sum.package_id()) self.matches_id(sum.package_id())
} }
pub fn matches_prerelease(&self, sum: &Summary) -> bool {
let id = sum.package_id();
self.inner.name == id.name()
&& (self.inner.only_match_name
|| (self.inner.req.matches_prerelease(id.version())
&& self.inner.source_id == id.source_id()))
}
/// Returns `true` if the package (`id`) can fulfill this dependency request. /// Returns `true` if the package (`id`) can fulfill this dependency request.
pub fn matches_ignoring_source(&self, id: PackageId) -> bool { pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
self.package_name() == id.name() && self.version_req().matches(id.version()) self.package_name() == id.name() && self.version_req().matches(id.version())

View File

@ -768,7 +768,6 @@ unstable_cli_options!(
next_lockfile_bump: bool, next_lockfile_bump: bool,
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"), no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"), panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
precise_pre_release: bool = ("Enable pre-release versions to be selected with `update --precise`"),
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"), profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
public_dependency: bool = ("Respect a dependency's `public` field in Cargo.toml to control public/private dependencies"), public_dependency: bool = ("Respect a dependency's `public` field in Cargo.toml to control public/private dependencies"),
publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"), publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
@ -1158,7 +1157,6 @@ impl CliUnstable {
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?, "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"public-dependency" => self.public_dependency = parse_empty(k, v)?, "public-dependency" => self.public_dependency = parse_empty(k, v)?,
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?, "profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
"precise-pre-release" => self.precise_pre_release = parse_empty(k, v)?,
"trim-paths" => self.trim_paths = parse_empty(k, v)?, "trim-paths" => self.trim_paths = parse_empty(k, v)?,
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?, "publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,

View File

@ -208,9 +208,9 @@ use crate::sources::source::QueryKind;
use crate::sources::source::Source; use crate::sources::source::Source;
use crate::sources::PathSource; use crate::sources::PathSource;
use crate::util::cache_lock::CacheLockMode; use crate::util::cache_lock::CacheLockMode;
use crate::util::hex;
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::network::PollExt; use crate::util::network::PollExt;
use crate::util::{hex, VersionExt};
use crate::util::{restricted_names, CargoResult, Filesystem, GlobalContext, LimitErrorReader}; use crate::util::{restricted_names, CargoResult, Filesystem, GlobalContext, LimitErrorReader};
/// The `.cargo-ok` file is used to track if the source is already unpacked. /// The `.cargo-ok` file is used to track if the source is already unpacked.
@ -752,7 +752,13 @@ impl<'gctx> Source for RegistrySource<'gctx> {
if let Some((_, requested)) = self if let Some((_, requested)) = self
.source_id .source_id
.precise_registry_version(dep.package_name().as_str()) .precise_registry_version(dep.package_name().as_str())
.filter(|(c, _)| req.matches(c)) .filter(|(c, to)| {
if to.is_prerelease() && self.gctx.cli_unstable().unstable_options {
req.matches_prerelease(c)
} else {
req.matches(c)
}
})
{ {
req.precise_to(&requested); req.precise_to(&requested);
} }
@ -790,7 +796,13 @@ impl<'gctx> Source for RegistrySource<'gctx> {
.index .index
.query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| { .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| {
let matched = match kind { let matched = match kind {
QueryKind::Exact => dep.matches(s.as_summary()), QueryKind::Exact => {
if req.is_precise() && self.gctx.cli_unstable().unstable_options {
dep.matches_prerelease(s.as_summary())
} else {
dep.matches(s.as_summary())
}
}
QueryKind::Alternatives => true, QueryKind::Alternatives => true,
QueryKind::Normalized => true, QueryKind::Normalized => true,
}; };

View File

@ -111,6 +111,19 @@ impl OptVersionReq {
} }
} }
/// Since Semver does not support prerelease versions,
/// the simplest implementation is taken here without comparing the prerelease section.
/// The logic here is temporary, we'll have to consider more boundary conditions later,
/// and we're not sure if this part of the functionality should be implemented in semver or cargo.
pub fn matches_prerelease(&self, version: &Version) -> bool {
if version.is_prerelease() {
let mut version = version.clone();
version.pre = semver::Prerelease::EMPTY;
return self.matches(&version);
}
self.matches(version)
}
pub fn matches(&self, version: &Version) -> bool { pub fn matches(&self, version: &Version) -> bool {
match self { match self {
OptVersionReq::Any => true, OptVersionReq::Any => true,

View File

@ -46,6 +46,8 @@ revision (such as a SHA hash or tag).
While not recommended, you can specify a yanked version of a package (nightly only). While not recommended, you can specify a yanked version of a package (nightly only).
When possible, try other non-yanked SemVer-compatible versions or seek help When possible, try other non-yanked SemVer-compatible versions or seek help
from the maintainers of the package. from the maintainers of the package.
A compatible `pre-release` version can also be specified even when the version requirement in `Cargo.toml` doesn't contain any pre-release identifer (nightly only).
{{/option}} {{/option}}
{{#option "`-w`" "`--workspace`" }} {{#option "`-w`" "`--workspace`" }}

View File

@ -40,6 +40,10 @@ OPTIONS
SemVer-compatible versions or seek help from the maintainers of the SemVer-compatible versions or seek help from the maintainers of the
package. package.
A compatible pre-release version can also be specified even when the
version requirement in Cargo.toml doesnt contain any pre-release
identifer (nightly only).
-w, --workspace -w, --workspace
Attempt to update only packages defined in the workspace. Other Attempt to update only packages defined in the workspace. Other
packages are updated only if they dont already exist in the packages are updated only if they dont already exist in the

View File

@ -42,7 +42,8 @@ the package to. If the package comes from a git repository, this can be a git
revision (such as a SHA hash or tag).</p> revision (such as a SHA hash or tag).</p>
<p>While not recommended, you can specify a yanked version of a package (nightly only). <p>While not recommended, you can specify a yanked version of a package (nightly only).
When possible, try other non-yanked SemVer-compatible versions or seek help When possible, try other non-yanked SemVer-compatible versions or seek help
from the maintainers of the package.</dd> from the maintainers of the package.</p>
<p>A compatible <code>pre-release</code> version can also be specified even when the version requirement in <code>Cargo.toml</code> doesnt contain any pre-release identifer (nightly only).</dd>
<dt class="option-term" id="option-cargo-update--w"><a class="option-anchor" href="#option-cargo-update--w"></a><code>-w</code></dt> <dt class="option-term" id="option-cargo-update--w"><a class="option-anchor" href="#option-cargo-update--w"></a><code>-w</code></dt>

View File

@ -347,7 +347,7 @@ Take for example this `Cargo.toml`.
my-dependency = "0.1.1" my-dependency = "0.1.1"
``` ```
It's possible to update `my-dependancy` to a pre-release with `update -Zprecise-pre-release -p my-dependency --precise 0.1.2-pre.0`. It's possible to update `my-dependancy` to a pre-release with `update -Zunstable-options my-dependency --precise 0.1.2-pre.0`.
This is because `0.1.2-pre.0` is considered compatible with `0.1.1`. This is because `0.1.2-pre.0` is considered compatible with `0.1.1`.
It would not be possible to upgrade to `0.2.0-pre.0` from `0.1.1` in the same way. It would not be possible to upgrade to `0.2.0-pre.0` from `0.1.1` in the same way.

View File

@ -43,6 +43,8 @@ revision (such as a SHA hash or tag).
While not recommended, you can specify a yanked version of a package (nightly only). While not recommended, you can specify a yanked version of a package (nightly only).
When possible, try other non\-yanked SemVer\-compatible versions or seek help When possible, try other non\-yanked SemVer\-compatible versions or seek help
from the maintainers of the package. from the maintainers of the package.
.sp
A compatible \fBpre\-release\fR version can also be specified even when the version requirement in \fBCargo.toml\fR doesn\[cq]t contain any pre\-release identifer (nightly only).
.RE .RE
.sp .sp
\fB\-w\fR, \fB\-w\fR,

View File

@ -1,4 +1,4 @@
<svg width="1230px" height="740px" xmlns="http://www.w3.org/2000/svg"> <svg width="1230px" height="722px" xmlns="http://www.w3.org/2000/svg">
<style> <style>
.fg { fill: #AAAAAA } .fg { fill: #AAAAAA }
.bg { background: #000000 } .bg { background: #000000 }
@ -69,35 +69,33 @@
</tspan> </tspan>
<tspan x="10px" y="460px"><tspan> </tspan><tspan class="fg-cyan bold">-Z panic-abort-tests </tspan><tspan> Enable support to run tests with -Cpanic=abort</tspan> <tspan x="10px" y="460px"><tspan> </tspan><tspan class="fg-cyan bold">-Z panic-abort-tests </tspan><tspan> Enable support to run tests with -Cpanic=abort</tspan>
</tspan> </tspan>
<tspan x="10px" y="478px"><tspan> </tspan><tspan class="fg-cyan bold">-Z precise-pre-release </tspan><tspan> Enable pre-release versions to be selected with `update --precise`</tspan> <tspan x="10px" y="478px"><tspan> </tspan><tspan class="fg-cyan bold">-Z profile-rustflags </tspan><tspan> Enable the `rustflags` option in profiles in .cargo/config.toml file</tspan>
</tspan> </tspan>
<tspan x="10px" y="496px"><tspan> </tspan><tspan class="fg-cyan bold">-Z profile-rustflags </tspan><tspan> Enable the `rustflags` option in profiles in .cargo/config.toml file</tspan> <tspan x="10px" y="496px"><tspan> </tspan><tspan class="fg-cyan bold">-Z public-dependency </tspan><tspan> Respect a dependency's `public` field in Cargo.toml to control public/private dependencies</tspan>
</tspan> </tspan>
<tspan x="10px" y="514px"><tspan> </tspan><tspan class="fg-cyan bold">-Z public-dependency </tspan><tspan> Respect a dependency's `public` field in Cargo.toml to control public/private dependencies</tspan> <tspan x="10px" y="514px"><tspan> </tspan><tspan class="fg-cyan bold">-Z publish-timeout </tspan><tspan> Enable the `publish.timeout` key in .cargo/config.toml file</tspan>
</tspan> </tspan>
<tspan x="10px" y="532px"><tspan> </tspan><tspan class="fg-cyan bold">-Z publish-timeout </tspan><tspan> Enable the `publish.timeout` key in .cargo/config.toml file</tspan> <tspan x="10px" y="532px"><tspan> </tspan><tspan class="fg-cyan bold">-Z rustdoc-map </tspan><tspan> Allow passing external documentation mappings to rustdoc</tspan>
</tspan> </tspan>
<tspan x="10px" y="550px"><tspan> </tspan><tspan class="fg-cyan bold">-Z rustdoc-map </tspan><tspan> Allow passing external documentation mappings to rustdoc</tspan> <tspan x="10px" y="550px"><tspan> </tspan><tspan class="fg-cyan bold">-Z rustdoc-scrape-examples</tspan><tspan> Allows Rustdoc to scrape code examples from reverse-dependencies</tspan>
</tspan> </tspan>
<tspan x="10px" y="568px"><tspan> </tspan><tspan class="fg-cyan bold">-Z rustdoc-scrape-examples</tspan><tspan> Allows Rustdoc to scrape code examples from reverse-dependencies</tspan> <tspan x="10px" y="568px"><tspan> </tspan><tspan class="fg-cyan bold">-Z script </tspan><tspan> Enable support for single-file, `.rs` packages</tspan>
</tspan> </tspan>
<tspan x="10px" y="586px"><tspan> </tspan><tspan class="fg-cyan bold">-Z script </tspan><tspan> Enable support for single-file, `.rs` packages</tspan> <tspan x="10px" y="586px"><tspan> </tspan><tspan class="fg-cyan bold">-Z target-applies-to-host </tspan><tspan> Enable the `target-applies-to-host` key in the .cargo/config.toml file</tspan>
</tspan> </tspan>
<tspan x="10px" y="604px"><tspan> </tspan><tspan class="fg-cyan bold">-Z target-applies-to-host </tspan><tspan> Enable the `target-applies-to-host` key in the .cargo/config.toml file</tspan> <tspan x="10px" y="604px"><tspan> </tspan><tspan class="fg-cyan bold">-Z trim-paths </tspan><tspan> Enable the `trim-paths` option in profiles</tspan>
</tspan> </tspan>
<tspan x="10px" y="622px"><tspan> </tspan><tspan class="fg-cyan bold">-Z trim-paths </tspan><tspan> Enable the `trim-paths` option in profiles</tspan> <tspan x="10px" y="622px"><tspan> </tspan><tspan class="fg-cyan bold">-Z unstable-options </tspan><tspan> Allow the usage of unstable options</tspan>
</tspan> </tspan>
<tspan x="10px" y="640px"><tspan> </tspan><tspan class="fg-cyan bold">-Z unstable-options </tspan><tspan> Allow the usage of unstable options</tspan> <tspan x="10px" y="640px">
</tspan> </tspan>
<tspan x="10px" y="658px"> <tspan x="10px" y="658px"><tspan>Run with `</tspan><tspan class="fg-cyan bold">cargo -Z</tspan><tspan> </tspan><tspan class="fg-cyan">[FLAG] [COMMAND]</tspan><tspan>`</tspan>
</tspan> </tspan>
<tspan x="10px" y="676px"><tspan>Run with `</tspan><tspan class="fg-cyan bold">cargo -Z</tspan><tspan> </tspan><tspan class="fg-cyan">[FLAG] [COMMAND]</tspan><tspan>`</tspan> <tspan x="10px" y="676px">
</tspan> </tspan>
<tspan x="10px" y="694px"> <tspan x="10px" y="694px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan>
</tspan> </tspan>
<tspan x="10px" y="712px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan> <tspan x="10px" y="712px">
</tspan>
<tspan x="10px" y="730px">
</tspan> </tspan>
</text> </text>

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -23,7 +23,7 @@ fn requires_nightly_cargo() {
.file("src/lib.rs", "") .file("src/lib.rs", "")
.build(); .build();
p.cargo("update -p my-dependency --precise 0.1.2-pre.0") p.cargo("update my-dependency --precise 0.1.2-pre.0")
.with_status(101) .with_status(101)
// This error is suffering from #12579 but still demonstrates that updating to // This error is suffering from #12579 but still demonstrates that updating to
// a pre-release does not work on stable // a pre-release does not work on stable
@ -41,20 +41,77 @@ perhaps a crate was updated and forgotten to be re-vendored?"#,
} }
#[cargo_test] #[cargo_test]
fn feature_exists() { fn update_pre_release() {
cargo_test_support::registry::init();
for version in ["0.1.1", "0.1.2-pre.0"] {
cargo_test_support::registry::Package::new("my-dependency", version).publish();
}
let p = project() let p = project()
.file( .file(
"Cargo.toml", "Cargo.toml",
r#" r#"
[package] [package]
name = "package" name = "package"
[dependencies]
my-dependency = "0.1.1"
"#, "#,
) )
.file("src/lib.rs", "") .file("src/lib.rs", "")
.build(); .build();
p.cargo("-Zprecise-pre-release update") p.cargo("update my-dependency --precise 0.1.2-pre.0 -Zunstable-options")
.masquerade_as_nightly_cargo(&["precise-pre-release"]) .masquerade_as_nightly_cargo(&["precise-pre-release"])
.with_stderr("") .with_stderr(
.run() r#"[UPDATING] `dummy-registry` index
[UPDATING] my-dependency v0.1.1 -> v0.1.2-pre.0
"#,
)
.run();
let lockfile = p.read_lockfile();
assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2-pre.0\""));
}
#[cargo_test]
fn update_pre_release_differ() {
cargo_test_support::registry::init();
for version in ["0.1.2", "0.1.2-pre.0", "0.1.2-pre.1"] {
cargo_test_support::registry::Package::new("my-dependency", version).publish();
}
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "package"
[dependencies]
my-dependency = "0.1.2"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("update -p my-dependency --precise 0.1.2-pre.0 -Zunstable-options")
.masquerade_as_nightly_cargo(&["precise-pre-release"])
.with_stderr(
r#"[UPDATING] `dummy-registry` index
[DOWNGRADING] my-dependency v0.1.2 -> v0.1.2-pre.0
"#,
)
.run();
p.cargo("update -p my-dependency --precise 0.1.2-pre.1 -Zunstable-options")
.masquerade_as_nightly_cargo(&["precise-pre-release"])
.with_stderr(
r#"[UPDATING] `dummy-registry` index
[UPDATING] my-dependency v0.1.2-pre.0 -> v0.1.2-pre.1
"#,
)
.run();
let lockfile = p.read_lockfile();
assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2-pre.1\""));
} }