Skip to content

Commit bf0a90d

Browse files
committed
Auto merge of #12772 - Eh2406:Locked-means-locked, r=epage
If there's a version in the lock file only use that exact version ### What does this PR try to resolve? Generally, cargo is of the opinion that semver metadata should be ignored. It's is relevant to dependency resolution as any other description of the version, just like the description field in cargo.toml. If your registry has two versions that only differing metadata you get the bugs you deserve. We implement functionality to make it easier for our users or for us to maintain. ~~So let's use `==` because it's less code to write and test.~~ We also believe that lock files should ensure reproducibility and protect against mutations from the registry. In this circumstance these two goals are in conflict, and this PR picks reproducibility. If the lock file tells us that there is a version called `1.0.0+bar` then we should not silently use `1.0.0+foo` even though they have the same version. This is one of the alternatives/follow-ups discussed in #11447. It was separated from #12749, to allow for separate discussion and because I was being too clever by half. ### How should we test and review this PR? All tests still pass except for the ones that were removed. The removed tests were only added to verify the on intuitive behavior of the older implementation in #9847.
2 parents 48a79c9 + 98766fb commit bf0a90d

File tree

5 files changed

+74
-50
lines changed

5 files changed

+74
-50
lines changed

src/cargo/core/registry.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ enum Kind {
116116
/// directive that we found in a lockfile, if present.
117117
pub struct LockedPatchDependency {
118118
/// The original `Dependency` directive, except "locked" so it's version
119-
/// requirement is `=foo` and its `SourceId` has a "precise" listed.
119+
/// requirement is Locked to `foo` and its `SourceId` has a "precise" listed.
120120
pub dependency: Dependency,
121121
/// The `PackageId` that was previously found in a lock file which
122122
/// `dependency` matches.

src/cargo/sources/registry/index.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,9 @@ impl<'cfg> RegistryIndex<'cfg> {
439439
/// checking the integrity of a downloaded package matching the checksum in
440440
/// the index file, aka [`IndexSummary`].
441441
pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> {
442-
let req = OptVersionReq::exact(pkg.version());
442+
let req = OptVersionReq::lock_to_exact(pkg.version());
443443
let summary = self.summaries(pkg.name(), &req, load)?;
444-
let summary = ready!(summary)
445-
.filter(|s| s.package_id().version() == pkg.version())
446-
.next();
444+
let summary = ready!(summary).next();
447445
Poll::Ready(Ok(summary
448446
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?
449447
.as_summary()
@@ -697,10 +695,8 @@ impl<'cfg> RegistryIndex<'cfg> {
697695
pkg: PackageId,
698696
load: &mut dyn RegistryData,
699697
) -> Poll<CargoResult<bool>> {
700-
let req = OptVersionReq::exact(pkg.version());
701-
let found = ready!(self.summaries(pkg.name(), &req, load))?
702-
.filter(|s| s.package_id().version() == pkg.version())
703-
.any(|s| s.is_yanked());
698+
let req = OptVersionReq::lock_to_exact(pkg.version());
699+
let found = ready!(self.summaries(pkg.name(), &req, load))?.any(|s| s.is_yanked());
704700
Poll::Ready(Ok(found))
705701
}
706702
}

src/cargo/util/semver_ext.rs

+17-39
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use semver::{Comparator, Op, Version, VersionReq};
22
use serde_untagged::UntaggedEnumVisitor;
3-
use std::cmp::Ordering;
43
use std::fmt::{self, Display};
54

65
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
@@ -44,6 +43,13 @@ impl OptVersionReq {
4443
OptVersionReq::Req(VersionReq::exact(version))
4544
}
4645

46+
// Since some registries have allowed crate versions to differ only by build metadata,
47+
// A query using OptVersionReq::exact return nondeterministic results.
48+
// So we `lock_to` the exact version were interested in.
49+
pub fn lock_to_exact(version: &Version) -> Self {
50+
OptVersionReq::Locked(version.clone(), VersionReq::exact(version))
51+
}
52+
4753
pub fn is_exact(&self) -> bool {
4854
match self {
4955
OptVersionReq::Any => false,
@@ -84,7 +90,16 @@ impl OptVersionReq {
8490
match self {
8591
OptVersionReq::Any => true,
8692
OptVersionReq::Req(req) => req.matches(version),
87-
OptVersionReq::Locked(v, _) => v.cmp_precedence(version) == Ordering::Equal,
93+
OptVersionReq::Locked(v, _) => {
94+
// Generally, cargo is of the opinion that semver metadata should be ignored.
95+
// If your registry has two versions that only differing metadata you get the bugs you deserve.
96+
// We also believe that lock files should ensure reproducibility
97+
// and protect against mutations from the registry.
98+
// In this circumstance these two goals are in conflict, and we pick reproducibility.
99+
// If the lock file tells us that there is a version called `1.0.0+bar` then
100+
// we should not silently use `1.0.0+foo` even though they have the same version.
101+
v == version
102+
}
88103
}
89104
}
90105
}
@@ -316,40 +331,3 @@ fn is_req(value: &str) -> bool {
316331
};
317332
"<>=^~".contains(first) || value.contains('*') || value.contains(',')
318333
}
319-
320-
#[cfg(test)]
321-
mod tests {
322-
use super::*;
323-
324-
#[test]
325-
fn locked_has_the_same_with_exact() {
326-
fn test_versions(target_ver: &str, vers: &[&str]) {
327-
let ver = Version::parse(target_ver).unwrap();
328-
let exact = OptVersionReq::exact(&ver);
329-
let mut locked = exact.clone();
330-
locked.lock_to(&ver);
331-
for v in vers {
332-
let v = Version::parse(v).unwrap();
333-
assert_eq!(exact.matches(&v), locked.matches(&v));
334-
}
335-
}
336-
337-
test_versions(
338-
"1.0.0",
339-
&["1.0.0", "1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"],
340-
);
341-
test_versions("0.9.0", &["0.9.0", "0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]);
342-
test_versions("0.0.2", &["0.0.2", "0.0.1", "0.0.3", "0.0.2-pre"]);
343-
test_versions(
344-
"0.1.0-beta2.a",
345-
&[
346-
"0.1.0-beta2.a",
347-
"0.9.1",
348-
"0.1.0",
349-
"0.1.1-beta2.a",
350-
"0.1.0-beta2",
351-
],
352-
);
353-
test_versions("0.1.0+meta", &["0.1.0", "0.1.0+meta", "0.1.0+any"]);
354-
}
355-
}

src/cargo/util/toml/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1274,8 +1274,7 @@ impl TomlManifest {
12741274
replacement.unused_keys(),
12751275
&mut cx.warnings,
12761276
);
1277-
dep.set_version_req(OptVersionReq::exact(&version))
1278-
.lock_version(&version);
1277+
dep.set_version_req(OptVersionReq::exact(&version));
12791278
replace.push((spec, dep));
12801279
}
12811280
Ok(replace)

tests/testsuite/registry.rs

+51
Original file line numberDiff line numberDiff line change
@@ -3597,6 +3597,57 @@ fn differ_only_by_metadata() {
35973597
[CHECKING] baz v0.0.1+b
35983598
[CHECKING] foo v0.0.1 ([CWD])
35993599
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
3600+
",
3601+
)
3602+
.run();
3603+
3604+
Package::new("baz", "0.0.1+d").publish();
3605+
3606+
p.cargo("clean").run();
3607+
p.cargo("check")
3608+
.with_stderr_contains("[CHECKING] baz v0.0.1+b")
3609+
.run();
3610+
}
3611+
3612+
#[cargo_test]
3613+
fn differ_only_by_metadata_with_lockfile() {
3614+
let p = project()
3615+
.file(
3616+
"Cargo.toml",
3617+
r#"
3618+
[package]
3619+
name = "foo"
3620+
version = "0.0.1"
3621+
authors = []
3622+
3623+
[dependencies]
3624+
baz = "=0.0.1"
3625+
"#,
3626+
)
3627+
.file("src/main.rs", "fn main() {}")
3628+
.build();
3629+
3630+
Package::new("baz", "0.0.1+a").publish();
3631+
Package::new("baz", "0.0.1+b").publish();
3632+
Package::new("baz", "0.0.1+c").publish();
3633+
3634+
p.cargo("update --package baz --precise 0.0.1+b")
3635+
.with_stderr(
3636+
"\
3637+
[UPDATING] [..] index
3638+
[..] baz v0.0.1+c -> v0.0.1+b
3639+
",
3640+
)
3641+
.run();
3642+
3643+
p.cargo("check")
3644+
.with_stderr(
3645+
"\
3646+
[DOWNLOADING] crates ...
3647+
[DOWNLOADED] [..] v0.0.1+b (registry `dummy-registry`)
3648+
[CHECKING] baz v0.0.1+b
3649+
[CHECKING] foo v0.0.1 ([CWD])
3650+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
36003651
",
36013652
)
36023653
.run();

0 commit comments

Comments
 (0)