Skip to content

Commit 9387a30

Browse files
committed
Fix cargo install with a semver metadata version.
1 parent e51522a commit 9387a30

File tree

2 files changed

+88
-5
lines changed

2 files changed

+88
-5
lines changed

src/cargo/core/package_id.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,22 @@ struct PackageIdInner {
3131
source_id: SourceId,
3232
}
3333

34-
// Custom equality that uses full equality of SourceId, rather than its custom equality.
34+
// Custom equality that uses full equality of SourceId, rather than its custom equality,
35+
// and Version, which usually ignores `build` metadata.
36+
//
37+
// The `build` part of the version is usually ignored (like a "comment").
38+
// However, there are some cases where it is important. The download path from
39+
// a registry includes the build metadata, and Cargo uses PackageIds for
40+
// creating download paths. Including it here prevents the PackageId interner
41+
// from getting poisoned with PackageIds where that build metadata is missing.
3542
impl PartialEq for PackageIdInner {
3643
fn eq(&self, other: &Self) -> bool {
3744
self.name == other.name
38-
&& self.version == other.version
45+
&& self.version.major == other.version.major
46+
&& self.version.minor == other.version.minor
47+
&& self.version.patch == other.version.patch
48+
&& self.version.pre == other.version.pre
49+
&& self.version.build == other.version.build
3950
&& self.source_id.full_eq(other.source_id)
4051
}
4152
}
@@ -44,7 +55,11 @@ impl PartialEq for PackageIdInner {
4455
impl Hash for PackageIdInner {
4556
fn hash<S: hash::Hasher>(&self, into: &mut S) {
4657
self.name.hash(into);
47-
self.version.hash(into);
58+
self.version.major.hash(into);
59+
self.version.minor.hash(into);
60+
self.version.patch.hash(into);
61+
self.version.pre.hash(into);
62+
self.version.build.hash(into);
4863
self.source_id.full_hash(into);
4964
}
5065
}
@@ -97,6 +112,8 @@ impl PartialEq for PackageId {
97112
if ptr::eq(self.inner, other.inner) {
98113
return true;
99114
}
115+
// This is here so that PackageId uses SourceId's and Version's idea
116+
// of equality. PackageIdInner uses a more exact notion of equality.
100117
self.inner.name == other.inner.name
101118
&& self.inner.version == other.inner.version
102119
&& self.inner.source_id == other.inner.source_id
@@ -105,6 +122,9 @@ impl PartialEq for PackageId {
105122

106123
impl Hash for PackageId {
107124
fn hash<S: hash::Hasher>(&self, state: &mut S) {
125+
// This is here (instead of derived) so that PackageId uses SourceId's
126+
// and Version's idea of equality. PackageIdInner uses a more exact
127+
// notion of hashing.
108128
self.inner.name.hash(state);
109129
self.inner.version.hash(state);
110130
self.inner.source_id.hash(state);
@@ -166,6 +186,12 @@ impl PackageId {
166186
}
167187
}
168188

189+
/// Returns a value that implements a "stable" hashable value.
190+
///
191+
/// Stable hashing removes the path prefix of the workspace from path
192+
/// packages. This helps with reproducible builds, since this hash is part
193+
/// of the symbol metadata, and we don't want the absolute path where the
194+
/// build is performed to affect the binary output.
169195
pub fn stable_hash(self, workspace: &Path) -> PackageIdStableHash<'_> {
170196
PackageIdStableHash(self, workspace)
171197
}

tests/testsuite/install.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ use std::io::prelude::*;
55

66
use cargo_test_support::cross_compile;
77
use cargo_test_support::git;
8-
use cargo_test_support::registry::{registry_path, registry_url, Package};
8+
use cargo_test_support::registry::{self, registry_path, registry_url, Package};
99
use cargo_test_support::{
1010
basic_manifest, cargo_process, no_such_file_err_msg, project, symlink_supported, t,
1111
};
1212

1313
use cargo_test_support::install::{
1414
assert_has_installed_exe, assert_has_not_installed_exe, cargo_home,
1515
};
16-
use cargo_test_support::paths;
16+
use cargo_test_support::paths::{self, CargoPathExt};
1717
use std::env;
1818
use std::path::PathBuf;
1919

@@ -1739,3 +1739,60 @@ fn locked_install_without_published_lockfile() {
17391739
.with_stderr_contains("[WARNING] no Cargo.lock file published in foo v0.1.0")
17401740
.run();
17411741
}
1742+
1743+
#[cargo_test]
1744+
fn install_semver_metadata() {
1745+
// Check trying to install a package that uses semver metadata.
1746+
// This uses alt registry because the bug this is exercising doesn't
1747+
// trigger with a replaced source.
1748+
registry::alt_init();
1749+
Package::new("foo", "1.0.0+abc")
1750+
.alternative(true)
1751+
.file("src/main.rs", "fn main() {}")
1752+
.publish();
1753+
1754+
cargo_process("install foo --registry alternative --version 1.0.0+abc").run();
1755+
cargo_process("install foo --registry alternative")
1756+
.with_stderr("\
1757+
[UPDATING] `[ROOT]/alternative-registry` index
1758+
[IGNORED] package `foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)` is already installed, use --force to override
1759+
[WARNING] be sure to add [..]
1760+
")
1761+
.run();
1762+
// "Updating" is not displayed here due to the --version fast-path.
1763+
cargo_process("install foo --registry alternative --version 1.0.0+abc")
1764+
.with_stderr("\
1765+
[IGNORED] package `foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)` is already installed, use --force to override
1766+
[WARNING] be sure to add [..]
1767+
")
1768+
.run();
1769+
cargo_process("install foo --registry alternative --version 1.0.0 --force")
1770+
.with_stderr(
1771+
"\
1772+
[UPDATING] `[ROOT]/alternative-registry` index
1773+
[INSTALLING] foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)
1774+
[COMPILING] foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)
1775+
[FINISHED] [..]
1776+
[REPLACING] [ROOT]/home/.cargo/bin/foo[EXE]
1777+
[REPLACED] package [..]
1778+
[WARNING] be sure to add [..]
1779+
",
1780+
)
1781+
.run();
1782+
// Check that from a fresh cache will work without metadata, too.
1783+
paths::home().join(".cargo/registry").rm_rf();
1784+
paths::home().join(".cargo/bin").rm_rf();
1785+
cargo_process("install foo --registry alternative --version 1.0.0")
1786+
.with_stderr("\
1787+
[UPDATING] `[ROOT]/alternative-registry` index
1788+
[DOWNLOADING] crates ...
1789+
[DOWNLOADED] foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)
1790+
[INSTALLING] foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)
1791+
[COMPILING] foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)
1792+
[FINISHED] [..]
1793+
[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE]
1794+
[INSTALLED] package `foo v1.0.0+abc (registry `[ROOT]/alternative-registry`)` (executable `foo[EXE]`)
1795+
[WARNING] be sure to add [..]
1796+
")
1797+
.run();
1798+
}

0 commit comments

Comments
 (0)