summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-10-18 20:42:16 +0000
committerbors <bors@rust-lang.org>2023-10-18 20:42:16 +0000
commitecb9851afd3095e988daaa35a48bc7f3cb748e04 (patch)
treeb4f8e079358649d199b0c6c323b90f1df5fe8091
parent22a976c6fb1bef45abcf182b4c7def87ad8b4a4d (diff)
parent01a543efdd4d90e0a70064b3fe32e563a5aac6f3 (diff)
Auto merge of #12847 - epage:backport, r=weihanglo0.75.0
[beta-1.74.0] Partial-version spec support Beta backports: - #12806 In order to make CI pass, the following PRs are also cherry-picked: - f0d3cdf191d7283464cbfeb40bad947cc8ed477f from #12800
-rw-r--r--src/cargo/core/package_id_spec.rs44
-rw-r--r--src/cargo/core/resolver/dep_cache.rs19
-rw-r--r--src/cargo/util/semver_ext.rs27
-rw-r--r--tests/testsuite/doc.rs4
-rw-r--r--tests/testsuite/replace.rs154
-rw-r--r--tests/testsuite/rustdocflags.rs8
6 files changed, 232 insertions, 24 deletions
diff --git a/src/cargo/core/package_id_spec.rs b/src/cargo/core/package_id_spec.rs
index 37a6f5e7b..53d99b84b 100644
--- a/src/cargo/core/package_id_spec.rs
+++ b/src/cargo/core/package_id_spec.rs
@@ -176,8 +176,7 @@ impl PackageIdSpec {
}
if let Some(ref v) = self.version {
- let req = v.exact_req();
- if !req.matches(package_id.version()) {
+ if !v.matches(package_id.version()) {
return false;
}
}
@@ -444,15 +443,50 @@ mod tests {
fn matching() {
let url = Url::parse("https://example.com").unwrap();
let sid = SourceId::for_registry(&url).unwrap();
- let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
- let bar = PackageId::new("bar", "1.2.3", sid).unwrap();
+ let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
assert!(PackageIdSpec::parse("foo").unwrap().matches(foo));
- assert!(!PackageIdSpec::parse("foo").unwrap().matches(bar));
+ assert!(!PackageIdSpec::parse("bar").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(foo));
assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo@1.2.3").unwrap().matches(foo));
assert!(!PackageIdSpec::parse("foo@1.2.2").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo@1.2").unwrap().matches(foo));
+
+ let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
+ assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
+ assert!(PackageIdSpec::parse("meta@1").unwrap().matches(meta));
+ assert!(PackageIdSpec::parse("meta@1.2").unwrap().matches(meta));
+ assert!(PackageIdSpec::parse("meta@1.2.3").unwrap().matches(meta));
+ assert!(!PackageIdSpec::parse("meta@1.2.3-alpha.0")
+ .unwrap()
+ .matches(meta));
+ assert!(PackageIdSpec::parse("meta@1.2.3+hello")
+ .unwrap()
+ .matches(meta));
+ assert!(!PackageIdSpec::parse("meta@1.2.3+bye")
+ .unwrap()
+ .matches(meta));
+
+ let pre = PackageId::new("pre", "1.2.3-alpha.0", sid).unwrap();
+ assert!(PackageIdSpec::parse("pre").unwrap().matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1").unwrap().matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2").unwrap().matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2.3").unwrap().matches(pre));
+ assert!(PackageIdSpec::parse("pre@1.2.3-alpha.0")
+ .unwrap()
+ .matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2.3-alpha.1")
+ .unwrap()
+ .matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2.3-beta.0")
+ .unwrap()
+ .matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2.3+hello")
+ .unwrap()
+ .matches(pre));
+ assert!(!PackageIdSpec::parse("pre@1.2.3-alpha.0+hello")
+ .unwrap()
+ .matches(pre));
}
}
diff --git a/src/cargo/core/resolver/dep_cache.rs b/src/cargo/core/resolver/dep_cache.rs
index 7b6e0661f..9041c5b0f 100644
--- a/src/cargo/core/resolver/dep_cache.rs
+++ b/src/cargo/core/resolver/dep_cache.rs
@@ -173,11 +173,20 @@ impl<'a> RegistryQueryer<'a> {
)));
}
- // The dependency should be hard-coded to have the same name and an
- // exact version requirement, so both of these assertions should
- // never fail.
- assert_eq!(s.version(), summary.version());
- assert_eq!(s.name(), summary.name());
+ assert_eq!(
+ s.name(),
+ summary.name(),
+ "dependency should be hard coded to have the same name"
+ );
+ if s.version() != summary.version() {
+ return Poll::Ready(Err(anyhow::anyhow!(
+ "replacement specification `{}` matched {} and tried to override it with {}\n\
+ avoid matching unrelated packages by being more specific",
+ spec,
+ summary.version(),
+ s.version(),
+ )));
+ }
let replace = if s.source_id() == summary.source_id() {
debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
diff --git a/src/cargo/util/semver_ext.rs b/src/cargo/util/semver_ext.rs
index bee3b2da3..5839d85d2 100644
--- a/src/cargo/util/semver_ext.rs
+++ b/src/cargo/util/semver_ext.rs
@@ -186,16 +186,25 @@ impl PartialVersion {
}
}
- pub fn exact_req(&self) -> VersionReq {
- VersionReq {
- comparators: vec![Comparator {
- op: semver::Op::Exact,
- major: self.major,
- minor: self.minor,
- patch: self.patch,
- pre: self.pre.as_ref().cloned().unwrap_or_default(),
- }],
+ /// Check if this matches a version, including build metadata
+ ///
+ /// Build metadata does not affect version precedence but may be necessary for uniquely
+ /// identifying a package.
+ pub fn matches(&self, version: &Version) -> bool {
+ if !version.pre.is_empty() && self.pre.is_none() {
+ // Pre-release versions must be explicitly opted into, if for no other reason than to
+ // give us room to figure out and define the semantics
+ return false;
}
+ self.major == version.major
+ && self.minor.map(|f| f == version.minor).unwrap_or(true)
+ && self.patch.map(|f| f == version.patch).unwrap_or(true)
+ && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
+ && self
+ .build
+ .as_ref()
+ .map(|f| f == &version.build)
+ .unwrap_or(true)
}
}
diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs
index 481df8590..a16980912 100644
--- a/tests/testsuite/doc.rs
+++ b/tests/testsuite/doc.rs
@@ -2004,7 +2004,7 @@ fn crate_versions() {
let output_path = p.root().join("target/doc/foo/index.html");
let output_documentation = fs::read_to_string(&output_path).unwrap();
- assert!(output_documentation.contains("Version 1.2.4"));
+ assert!(output_documentation.contains("1.2.4"));
}
#[cargo_test]
@@ -2028,7 +2028,7 @@ fn crate_versions_flag_is_overridden() {
};
let asserts = |html: String| {
assert!(!html.contains("1.2.4"));
- assert!(html.contains("Version 2.0.3"));
+ assert!(html.contains("2.0.3"));
};
p.cargo("doc")
diff --git a/tests/testsuite/replace.rs b/tests/testsuite/replace.rs
index b583de7b7..b9de51d2f 100644
--- a/tests/testsuite/replace.rs
+++ b/tests/testsuite/replace.rs
@@ -1298,3 +1298,157 @@ fn override_plus_dep() {
.with_stderr_contains("error: cyclic package dependency: [..]")
.run();
}
+
+#[cargo_test]
+fn override_generic_matching_other_versions() {
+ Package::new("bar", "0.1.0+a").publish();
+
+ let bar = git::repo(&paths::root().join("override"))
+ .file("Cargo.toml", &basic_manifest("bar", "0.1.0"))
+ .file("src/lib.rs", "pub fn bar() {}")
+ .build();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1.0"
+
+ [replace]
+ "bar:0.1.0" = {{ git = '{}' }}
+ "#,
+ bar.url()
+ ),
+ )
+ .file(
+ "src/lib.rs",
+ "extern crate bar; pub fn foo() { bar::bar(); }",
+ )
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[UPDATING] git repository `[..]`
+[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..]/foo)`
+
+Caused by:
+ replacement specification `https://github.com/rust-lang/crates.io-index#bar@0.1.0` matched 0.1.0+a and tried to override it with 0.1.0
+ avoid matching unrelated packages by being more specific
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn override_respects_spec_metadata() {
+ Package::new("bar", "0.1.0+a").publish();
+
+ let bar = git::repo(&paths::root().join("override"))
+ .file("Cargo.toml", &basic_manifest("bar", "0.1.0+a"))
+ .file("src/lib.rs", "pub fn bar() {}")
+ .build();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1.0"
+
+ [replace]
+ "bar:0.1.0+notTheBuild" = {{ git = '{}' }}
+ "#,
+ bar.url()
+ ),
+ )
+ .file(
+ "src/lib.rs",
+ "extern crate bar; pub fn foo() { bar::bar(); }",
+ )
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[WARNING] package replacement is not used: https://github.com/rust-lang/crates.io-index#bar@0.1.0+notTheBuild
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v0.1.0+a (registry `dummy-registry`)
+[CHECKING] bar v0.1.0+a
+[CHECKING] foo v0.0.1 ([..]/foo)
+[..]
+[..]
+[..]
+[..]
+[..]
+[..]
+[..]
+error: could not compile `foo` (lib) due to previous error
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn override_spec_metadata_is_optional() {
+ Package::new("bar", "0.1.0+a").publish();
+
+ let bar = git::repo(&paths::root().join("override"))
+ .file("Cargo.toml", &basic_manifest("bar", "0.1.0+a"))
+ .file("src/lib.rs", "pub fn bar() {}")
+ .build();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1.0"
+
+ [replace]
+ "bar:0.1.0" = {{ git = '{}' }}
+ "#,
+ bar.url()
+ ),
+ )
+ .file(
+ "src/lib.rs",
+ "extern crate bar; pub fn foo() { bar::bar(); }",
+ )
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[UPDATING] git repository `[..]`
+[CHECKING] bar v0.1.0+a (file://[..])
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
diff --git a/tests/testsuite/rustdocflags.rs b/tests/testsuite/rustdocflags.rs
index 6992961ce..c37d5a826 100644
--- a/tests/testsuite/rustdocflags.rs
+++ b/tests/testsuite/rustdocflags.rs
@@ -110,17 +110,19 @@ fn whitespace() {
.with_status(101)
.run();
- const SPACED_VERSION: &str = "a\nb\tc\u{00a0}d";
p.cargo("doc")
.env_remove("__CARGO_TEST_FORCE_ARGFILE") // Not applicable for argfile.
.env(
"RUSTDOCFLAGS",
- format!("--crate-version {}", SPACED_VERSION),
+ "--crate-version 1111\n2222\t3333\u{00a0}4444",
)
.run();
let contents = p.read_file("target/doc/foo/index.html");
- assert!(contents.contains(SPACED_VERSION));
+ assert!(contents.contains("1111"));
+ assert!(contents.contains("2222"));
+ assert!(contents.contains("3333"));
+ assert!(contents.contains("4444"));
}
#[cargo_test]