diff --git a/Cargo.lock b/Cargo.lock index 48574a00e32..2789211013b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ "rand", "regex", "rusqlite", + "rustc-stable-hash", "rustfix", "same-file", "semver", @@ -2941,6 +2942,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-stable-hash" +version = "0.1.0" +source = "git+https://github.com/rust-lang/rustc-stable-hash.git?rev=cb8e141b08fb839606a5f79f9b56087cd54b764d#cb8e141b08fb839606a5f79f9b56087cd54b764d" + [[package]] name = "rustfix" version = "0.8.4" diff --git a/Cargo.toml b/Cargo.toml index ac4c3924efe..53915dcc768 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ pulldown-cmark = { version = "0.11.0", default-features = false, features = ["ht rand = "0.8.5" regex = "1.10.4" rusqlite = { version = "0.31.0", features = ["bundled"] } +rustc-stable-hash = { git = "https://github.com/rust-lang/rustc-stable-hash.git", rev = "cb8e141b08fb839606a5f79f9b56087cd54b764d" } rustfix = { version = "0.8.2", path = "crates/rustfix" } same-file = "1.0.6" security-framework = "2.10.0" @@ -182,6 +183,7 @@ pathdiff.workspace = true rand.workspace = true regex.workspace = true rusqlite.workspace = true +rustc-stable-hash.workspace = true rustfix.workspace = true same-file.workspace = true semver.workspace = true diff --git a/src/cargo/core/compiler/build_runner/compilation_files.rs b/src/cargo/core/compiler/build_runner/compilation_files.rs index 41ef89d6f0b..6056d145602 100644 --- a/src/cargo/core/compiler/build_runner/compilation_files.rs +++ b/src/cargo/core/compiler/build_runner/compilation_files.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/src/cargo/core/compiler/compile_kind.rs b/src/cargo/core/compiler/compile_kind.rs index 222732ddebc..937c353dcba 100644 --- a/src/cargo/core/compiler/compile_kind.rs +++ b/src/cargo/core/compiler/compile_kind.rs @@ -8,7 +8,7 @@ use anyhow::Context as _; use serde::Serialize; use std::collections::BTreeSet; use std::fs; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::path::Path; /// Indicator for how a unit is being compiled. diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index d03a0a5769c..59f7683a3ec 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -786,70 +786,93 @@ mod tests { // Otherwise please just leave a comment in your PR as to why the hash value is // changing and why the old value can't be easily preserved. // - // The hash value depends on endianness and bit-width, so we only run this test on - // little-endian 64-bit CPUs (such as x86-64 and ARM64) where it matches the - // well-known value. + // The hash value should be stable across platforms, and doesn't depend on + // endianness and bit-width. One caveat is that absolute paths is inherently + // different on Windows than on Unix-like platforms. Unless we omit or strip + // the prefix components (e.g. `C:`), there is not way to have a + // cross-platform stable hash for absolute paths. #[test] - #[cfg(all(target_endian = "little", target_pointer_width = "64"))] fn test_cratesio_hash() { let gctx = GlobalContext::default().unwrap(); let crates_io = SourceId::crates_io(&gctx).unwrap(); - assert_eq!(crate::util::hex::short_hash(&crates_io), "1ecc6299db9ec823"); + assert_eq!(crate::util::hex::short_hash(&crates_io), "83d63c3e13aca8cc"); } // See the comment in `test_cratesio_hash`. // // Only test on non-Windows as paths on Windows will get different hashes. #[test] - #[cfg(all(target_endian = "little", target_pointer_width = "64", not(windows)))] fn test_stable_hash() { - use std::hash::Hasher; + use crate::util::StableHasher; use std::path::Path; + #[cfg(not(windows))] + let ws_root = Path::new("/tmp/ws"); + #[cfg(windows)] + let ws_root = Path::new(r"C:\\tmp\ws"); + let gen_hash = |source_id: SourceId| { - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - source_id.stable_hash(Path::new("/tmp/ws"), &mut hasher); + let mut hasher = StableHasher::new(); + source_id.stable_hash(ws_root, &mut hasher); hasher.finish() }; let url = "https://my-crates.io".into_url().unwrap(); let source_id = SourceId::for_registry(&url).unwrap(); - assert_eq!(gen_hash(source_id), 18108075011063494626); - assert_eq!(crate::util::hex::short_hash(&source_id), "fb60813d6cb8df79"); + assert_eq!(gen_hash(source_id), 2056262832525457700); + assert_eq!(crate::util::hex::short_hash(&source_id), "24b984d12650891c"); let url = "https://your-crates.io".into_url().unwrap(); let source_id = SourceId::for_alt_registry(&url, "alt").unwrap(); - assert_eq!(gen_hash(source_id), 12862859764592646184); - assert_eq!(crate::util::hex::short_hash(&source_id), "09c10fd0cbd74bce"); + assert_eq!(gen_hash(source_id), 7851411715584162426); + assert_eq!(crate::util::hex::short_hash(&source_id), "7afabb545bd1f56c"); let url = "sparse+https://my-crates.io".into_url().unwrap(); let source_id = SourceId::for_registry(&url).unwrap(); - assert_eq!(gen_hash(source_id), 8763561830438022424); - assert_eq!(crate::util::hex::short_hash(&source_id), "d1ea0d96f6f759b5"); + assert_eq!(gen_hash(source_id), 15233380663065439616); + assert_eq!(crate::util::hex::short_hash(&source_id), "80ed51ce00d767d3"); let url = "sparse+https://your-crates.io".into_url().unwrap(); let source_id = SourceId::for_alt_registry(&url, "alt").unwrap(); - assert_eq!(gen_hash(source_id), 5159702466575482972); - assert_eq!(crate::util::hex::short_hash(&source_id), "135d23074253cb78"); + assert_eq!(gen_hash(source_id), 12749290624384351691); + assert_eq!(crate::util::hex::short_hash(&source_id), "cbbda5344694eeb0"); let url = "file:///tmp/ws/crate".into_url().unwrap(); let source_id = SourceId::for_git(&url, GitReference::DefaultBranch).unwrap(); - assert_eq!(gen_hash(source_id), 15332537265078583985); - assert_eq!(crate::util::hex::short_hash(&source_id), "73a808694abda756"); - - let path = Path::new("/tmp/ws/crate"); + assert_eq!(gen_hash(source_id), 3109465066469481245); + assert_eq!(crate::util::hex::short_hash(&source_id), "1d5b66d8000a272b"); + let path = &ws_root.join("crate"); let source_id = SourceId::for_local_registry(path).unwrap(); - assert_eq!(gen_hash(source_id), 18446533307730842837); - assert_eq!(crate::util::hex::short_hash(&source_id), "52a84cc73f6fd48b"); + #[cfg(not(windows))] + { + assert_eq!(gen_hash(source_id), 17171351456028149232); + assert_eq!(crate::util::hex::short_hash(&source_id), "f0c5f1e92be54cee"); + } + #[cfg(windows)] + { + assert_eq!(gen_hash(source_id), 10712195329887934127); + assert_eq!(crate::util::hex::short_hash(&source_id), "af96919ae55ca994"); + } let source_id = SourceId::for_path(path).unwrap(); - assert_eq!(gen_hash(source_id), 8764714075439899829); - assert_eq!(crate::util::hex::short_hash(&source_id), "e1ddd48578620fc1"); + assert_eq!(gen_hash(source_id), 13241112980875747369); + #[cfg(not(windows))] + assert_eq!(crate::util::hex::short_hash(&source_id), "e5ba2edec163e65a"); + #[cfg(windows)] + assert_eq!(crate::util::hex::short_hash(&source_id), "429dd6f2283a9b5c"); let source_id = SourceId::for_directory(path).unwrap(); - assert_eq!(gen_hash(source_id), 17459999773908528552); - assert_eq!(crate::util::hex::short_hash(&source_id), "6568fe2c2fab5bfe"); + #[cfg(not(windows))] + { + assert_eq!(gen_hash(source_id), 12461124588148212881); + assert_eq!(crate::util::hex::short_hash(&source_id), "91c47582caceeeac"); + } + #[cfg(windows)] + { + assert_eq!(gen_hash(source_id), 17000469607053345884); + assert_eq!(crate::util::hex::short_hash(&source_id), "5c443d0709cdedeb"); + } } #[test] diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index a09a95c5a32..ad125aa1b82 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -36,7 +36,7 @@ //! ["Cargo Target"]: https://doc.rust-lang.org/nightly/cargo/reference/cargo-targets.html use std::collections::{HashMap, HashSet}; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::sync::Arc; use crate::core::compiler::unit_dependencies::build_unit_dependencies; diff --git a/src/cargo/util/hasher.rs b/src/cargo/util/hasher.rs index 01e15ae2c04..60d37f8863c 100644 --- a/src/cargo/util/hasher.rs +++ b/src/cargo/util/hasher.rs @@ -1,23 +1,24 @@ -//! Implementation of a hasher that produces the same values across releases. +//! A hasher that produces the same values across releases and platforms. //! -//! The hasher should be fast and have a low chance of collisions (but is not -//! sufficient for cryptographic purposes). -#![allow(deprecated)] +//! This is a wrapper around [`rustc_stable_hash::StableHasher`]. -use std::hash::{Hasher, SipHasher}; - -pub struct StableHasher(SipHasher); +pub struct StableHasher(rustc_stable_hash::StableHasher); impl StableHasher { pub fn new() -> StableHasher { - StableHasher(SipHasher::new()) + StableHasher(rustc_stable_hash::StableHasher::new()) + } + + pub fn finish(self) -> u64 { + self.0.finalize().0 } } -impl Hasher for StableHasher { +impl std::hash::Hasher for StableHasher { fn finish(&self) -> u64 { - self.0.finish() + panic!("call StableHasher::finish instead"); } + fn write(&mut self, bytes: &[u8]) { self.0.write(bytes) } diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index 2b80136dc05..4e035b22dbd 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::HashMap; use std::env; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::path::{Path, PathBuf}; use std::sync::Mutex;