Skip to content

Commit ecb9851

Browse files
committed
Auto merge of #12847 - epage:backport, r=weihanglo
[beta-1.74.0] Partial-version spec support Beta backports: - #12806 In order to make CI pass, the following PRs are also cherry-picked: - f0d3cdf from #12800
2 parents 22a976c + 01a543e commit ecb9851

File tree

6 files changed

+232
-24
lines changed

6 files changed

+232
-24
lines changed

src/cargo/core/package_id_spec.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,7 @@ impl PackageIdSpec {
176176
}
177177

178178
if let Some(ref v) = self.version {
179-
let req = v.exact_req();
180-
if !req.matches(package_id.version()) {
179+
if !v.matches(package_id.version()) {
181180
return false;
182181
}
183182
}
@@ -444,15 +443,50 @@ mod tests {
444443
fn matching() {
445444
let url = Url::parse("https://example.com").unwrap();
446445
let sid = SourceId::for_registry(&url).unwrap();
447-
let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
448-
let bar = PackageId::new("bar", "1.2.3", sid).unwrap();
449446

447+
let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
450448
assert!(PackageIdSpec::parse("foo").unwrap().matches(foo));
451-
assert!(!PackageIdSpec::parse("foo").unwrap().matches(bar));
449+
assert!(!PackageIdSpec::parse("bar").unwrap().matches(foo));
452450
assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(foo));
453451
assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(foo));
454452
assert!(PackageIdSpec::parse("[email protected]").unwrap().matches(foo));
455453
assert!(!PackageIdSpec::parse("[email protected]").unwrap().matches(foo));
456454
assert!(PackageIdSpec::parse("[email protected]").unwrap().matches(foo));
455+
456+
let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
457+
assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
458+
assert!(PackageIdSpec::parse("meta@1").unwrap().matches(meta));
459+
assert!(PackageIdSpec::parse("[email protected]").unwrap().matches(meta));
460+
assert!(PackageIdSpec::parse("[email protected]").unwrap().matches(meta));
461+
assert!(!PackageIdSpec::parse("[email protected]")
462+
.unwrap()
463+
.matches(meta));
464+
assert!(PackageIdSpec::parse("[email protected]+hello")
465+
.unwrap()
466+
.matches(meta));
467+
assert!(!PackageIdSpec::parse("[email protected]+bye")
468+
.unwrap()
469+
.matches(meta));
470+
471+
let pre = PackageId::new("pre", "1.2.3-alpha.0", sid).unwrap();
472+
assert!(PackageIdSpec::parse("pre").unwrap().matches(pre));
473+
assert!(!PackageIdSpec::parse("pre@1").unwrap().matches(pre));
474+
assert!(!PackageIdSpec::parse("[email protected]").unwrap().matches(pre));
475+
assert!(!PackageIdSpec::parse("[email protected]").unwrap().matches(pre));
476+
assert!(PackageIdSpec::parse("[email protected]")
477+
.unwrap()
478+
.matches(pre));
479+
assert!(!PackageIdSpec::parse("[email protected]")
480+
.unwrap()
481+
.matches(pre));
482+
assert!(!PackageIdSpec::parse("[email protected]")
483+
.unwrap()
484+
.matches(pre));
485+
assert!(!PackageIdSpec::parse("[email protected]+hello")
486+
.unwrap()
487+
.matches(pre));
488+
assert!(!PackageIdSpec::parse("[email protected]+hello")
489+
.unwrap()
490+
.matches(pre));
457491
}
458492
}

src/cargo/core/resolver/dep_cache.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,20 @@ impl<'a> RegistryQueryer<'a> {
173173
)));
174174
}
175175

176-
// The dependency should be hard-coded to have the same name and an
177-
// exact version requirement, so both of these assertions should
178-
// never fail.
179-
assert_eq!(s.version(), summary.version());
180-
assert_eq!(s.name(), summary.name());
176+
assert_eq!(
177+
s.name(),
178+
summary.name(),
179+
"dependency should be hard coded to have the same name"
180+
);
181+
if s.version() != summary.version() {
182+
return Poll::Ready(Err(anyhow::anyhow!(
183+
"replacement specification `{}` matched {} and tried to override it with {}\n\
184+
avoid matching unrelated packages by being more specific",
185+
spec,
186+
summary.version(),
187+
s.version(),
188+
)));
189+
}
181190

182191
let replace = if s.source_id() == summary.source_id() {
183192
debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);

src/cargo/util/semver_ext.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,25 @@ impl PartialVersion {
186186
}
187187
}
188188

189-
pub fn exact_req(&self) -> VersionReq {
190-
VersionReq {
191-
comparators: vec![Comparator {
192-
op: semver::Op::Exact,
193-
major: self.major,
194-
minor: self.minor,
195-
patch: self.patch,
196-
pre: self.pre.as_ref().cloned().unwrap_or_default(),
197-
}],
189+
/// Check if this matches a version, including build metadata
190+
///
191+
/// Build metadata does not affect version precedence but may be necessary for uniquely
192+
/// identifying a package.
193+
pub fn matches(&self, version: &Version) -> bool {
194+
if !version.pre.is_empty() && self.pre.is_none() {
195+
// Pre-release versions must be explicitly opted into, if for no other reason than to
196+
// give us room to figure out and define the semantics
197+
return false;
198198
}
199+
self.major == version.major
200+
&& self.minor.map(|f| f == version.minor).unwrap_or(true)
201+
&& self.patch.map(|f| f == version.patch).unwrap_or(true)
202+
&& self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
203+
&& self
204+
.build
205+
.as_ref()
206+
.map(|f| f == &version.build)
207+
.unwrap_or(true)
199208
}
200209
}
201210

tests/testsuite/doc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,7 +2004,7 @@ fn crate_versions() {
20042004
let output_path = p.root().join("target/doc/foo/index.html");
20052005
let output_documentation = fs::read_to_string(&output_path).unwrap();
20062006

2007-
assert!(output_documentation.contains("Version 1.2.4"));
2007+
assert!(output_documentation.contains("1.2.4"));
20082008
}
20092009

20102010
#[cargo_test]
@@ -2028,7 +2028,7 @@ fn crate_versions_flag_is_overridden() {
20282028
};
20292029
let asserts = |html: String| {
20302030
assert!(!html.contains("1.2.4"));
2031-
assert!(html.contains("Version 2.0.3"));
2031+
assert!(html.contains("2.0.3"));
20322032
};
20332033

20342034
p.cargo("doc")

tests/testsuite/replace.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,3 +1298,157 @@ fn override_plus_dep() {
12981298
.with_stderr_contains("error: cyclic package dependency: [..]")
12991299
.run();
13001300
}
1301+
1302+
#[cargo_test]
1303+
fn override_generic_matching_other_versions() {
1304+
Package::new("bar", "0.1.0+a").publish();
1305+
1306+
let bar = git::repo(&paths::root().join("override"))
1307+
.file("Cargo.toml", &basic_manifest("bar", "0.1.0"))
1308+
.file("src/lib.rs", "pub fn bar() {}")
1309+
.build();
1310+
1311+
let p = project()
1312+
.file(
1313+
"Cargo.toml",
1314+
&format!(
1315+
r#"
1316+
[package]
1317+
name = "foo"
1318+
version = "0.0.1"
1319+
authors = []
1320+
1321+
[dependencies]
1322+
bar = "0.1.0"
1323+
1324+
[replace]
1325+
"bar:0.1.0" = {{ git = '{}' }}
1326+
"#,
1327+
bar.url()
1328+
),
1329+
)
1330+
.file(
1331+
"src/lib.rs",
1332+
"extern crate bar; pub fn foo() { bar::bar(); }",
1333+
)
1334+
.build();
1335+
1336+
p.cargo("check")
1337+
.with_stderr(
1338+
"\
1339+
[UPDATING] `dummy-registry` index
1340+
[UPDATING] git repository `[..]`
1341+
[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..]/foo)`
1342+
1343+
Caused by:
1344+
replacement specification `https://github.com/rust-lang/crates.io-index#[email protected]` matched 0.1.0+a and tried to override it with 0.1.0
1345+
avoid matching unrelated packages by being more specific
1346+
",
1347+
)
1348+
.with_status(101)
1349+
.run();
1350+
}
1351+
1352+
#[cargo_test]
1353+
fn override_respects_spec_metadata() {
1354+
Package::new("bar", "0.1.0+a").publish();
1355+
1356+
let bar = git::repo(&paths::root().join("override"))
1357+
.file("Cargo.toml", &basic_manifest("bar", "0.1.0+a"))
1358+
.file("src/lib.rs", "pub fn bar() {}")
1359+
.build();
1360+
1361+
let p = project()
1362+
.file(
1363+
"Cargo.toml",
1364+
&format!(
1365+
r#"
1366+
[package]
1367+
name = "foo"
1368+
version = "0.0.1"
1369+
authors = []
1370+
1371+
[dependencies]
1372+
bar = "0.1.0"
1373+
1374+
[replace]
1375+
"bar:0.1.0+notTheBuild" = {{ git = '{}' }}
1376+
"#,
1377+
bar.url()
1378+
),
1379+
)
1380+
.file(
1381+
"src/lib.rs",
1382+
"extern crate bar; pub fn foo() { bar::bar(); }",
1383+
)
1384+
.build();
1385+
1386+
p.cargo("check")
1387+
.with_stderr(
1388+
"\
1389+
[UPDATING] `dummy-registry` index
1390+
[WARNING] package replacement is not used: https://github.com/rust-lang/crates.io-index#[email protected]+notTheBuild
1391+
[DOWNLOADING] crates ...
1392+
[DOWNLOADED] bar v0.1.0+a (registry `dummy-registry`)
1393+
[CHECKING] bar v0.1.0+a
1394+
[CHECKING] foo v0.0.1 ([..]/foo)
1395+
[..]
1396+
[..]
1397+
[..]
1398+
[..]
1399+
[..]
1400+
[..]
1401+
[..]
1402+
error: could not compile `foo` (lib) due to previous error
1403+
",
1404+
)
1405+
.with_status(101)
1406+
.run();
1407+
}
1408+
1409+
#[cargo_test]
1410+
fn override_spec_metadata_is_optional() {
1411+
Package::new("bar", "0.1.0+a").publish();
1412+
1413+
let bar = git::repo(&paths::root().join("override"))
1414+
.file("Cargo.toml", &basic_manifest("bar", "0.1.0+a"))
1415+
.file("src/lib.rs", "pub fn bar() {}")
1416+
.build();
1417+
1418+
let p = project()
1419+
.file(
1420+
"Cargo.toml",
1421+
&format!(
1422+
r#"
1423+
[package]
1424+
name = "foo"
1425+
version = "0.0.1"
1426+
authors = []
1427+
1428+
[dependencies]
1429+
bar = "0.1.0"
1430+
1431+
[replace]
1432+
"bar:0.1.0" = {{ git = '{}' }}
1433+
"#,
1434+
bar.url()
1435+
),
1436+
)
1437+
.file(
1438+
"src/lib.rs",
1439+
"extern crate bar; pub fn foo() { bar::bar(); }",
1440+
)
1441+
.build();
1442+
1443+
p.cargo("check")
1444+
.with_stderr(
1445+
"\
1446+
[UPDATING] `dummy-registry` index
1447+
[UPDATING] git repository `[..]`
1448+
[CHECKING] bar v0.1.0+a (file://[..])
1449+
[CHECKING] foo v0.0.1 ([CWD])
1450+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
1451+
",
1452+
)
1453+
.run();
1454+
}

tests/testsuite/rustdocflags.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,19 @@ fn whitespace() {
110110
.with_status(101)
111111
.run();
112112

113-
const SPACED_VERSION: &str = "a\nb\tc\u{00a0}d";
114113
p.cargo("doc")
115114
.env_remove("__CARGO_TEST_FORCE_ARGFILE") // Not applicable for argfile.
116115
.env(
117116
"RUSTDOCFLAGS",
118-
format!("--crate-version {}", SPACED_VERSION),
117+
"--crate-version 1111\n2222\t3333\u{00a0}4444",
119118
)
120119
.run();
121120

122121
let contents = p.read_file("target/doc/foo/index.html");
123-
assert!(contents.contains(SPACED_VERSION));
122+
assert!(contents.contains("1111"));
123+
assert!(contents.contains("2222"));
124+
assert!(contents.contains("3333"));
125+
assert!(contents.contains("4444"));
124126
}
125127

126128
#[cargo_test]

0 commit comments

Comments
 (0)