Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c5ac80a

Browse files
committedJul 17, 2024·
Auto merge of rust-lang#126930 - Xaeroxe:file-checksum-hint, r=<try>
Add unstable support for outputting file checksums for use in cargo Adds an unstable option that appends file checksums and expected lengths to the end of the dep-info file such that `cargo` can read and use these values as an alternative to file mtimes. This PR powers the changes made in this cargo PR rust-lang/cargo#14137 Here's the tracking issue for the cargo feature rust-lang/cargo#14136.
2 parents 3de0a7c + d80d73e commit c5ac80a

File tree

16 files changed

+340
-46
lines changed

16 files changed

+340
-46
lines changed
 

‎Cargo.lock

+26
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ dependencies = [
237237
"object 0.35.0",
238238
]
239239

240+
[[package]]
241+
name = "arrayref"
242+
version = "0.3.7"
243+
source = "registry+https://github.com/rust-lang/crates.io-index"
244+
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
245+
240246
[[package]]
241247
name = "arrayvec"
242248
version = "0.7.4"
@@ -356,6 +362,19 @@ version = "2.5.0"
356362
source = "registry+https://github.com/rust-lang/crates.io-index"
357363
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
358364

365+
[[package]]
366+
name = "blake3"
367+
version = "1.5.2"
368+
source = "registry+https://github.com/rust-lang/crates.io-index"
369+
checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92"
370+
dependencies = [
371+
"arrayref",
372+
"arrayvec",
373+
"cc",
374+
"cfg-if",
375+
"constant_time_eq",
376+
]
377+
359378
[[package]]
360379
name = "block-buffer"
361380
version = "0.10.4"
@@ -842,6 +861,12 @@ dependencies = [
842861
"windows-sys 0.52.0",
843862
]
844863

864+
[[package]]
865+
name = "constant_time_eq"
866+
version = "0.3.0"
867+
source = "registry+https://github.com/rust-lang/crates.io-index"
868+
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
869+
845870
[[package]]
846871
name = "core"
847872
version = "0.0.0"
@@ -4754,6 +4779,7 @@ dependencies = [
47544779
name = "rustc_span"
47554780
version = "0.0.0"
47564781
dependencies = [
4782+
"blake3",
47574783
"derivative",
47584784
"indexmap",
47594785
"itoa",

‎compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) ->
636636
rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
637637
rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
638638
rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
639+
rustc_span::SourceFileHashAlgorithm::Blake3 => llvm::ChecksumKind::None,
639640
};
640641
let hash_value = hex_encode(source_file.src_hash.hash_bytes());
641642

‎compiler/rustc_interface/src/interface.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -387,12 +387,13 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
387387
let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
388388
let path_mapping = config.opts.file_path_mapping();
389389
let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target);
390+
let checksum_hash_kind = config.opts.unstable_opts.checksum_hash_algorithm();
390391

391392
util::run_in_thread_pool_with_globals(
392393
&early_dcx,
393394
config.opts.edition,
394395
config.opts.unstable_opts.threads,
395-
SourceMapInputs { file_loader, path_mapping, hash_kind },
396+
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind },
396397
|current_gcx| {
397398
// The previous `early_dcx` can't be reused here because it doesn't
398399
// impl `Send`. Creating a new one is fine.

‎compiler/rustc_interface/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// tidy-alphabetical-start
22
#![feature(decl_macro)]
3+
#![feature(iter_intersperse)]
34
#![feature(let_chains)]
45
#![feature(thread_spawn_unchecked)]
56
#![feature(try_blocks)]

‎compiler/rustc_interface/src/passes.rs

+92-14
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ use rustc_session::output::{collect_crate_types, find_crate_name};
3232
use rustc_session::search_paths::PathKind;
3333
use rustc_session::{Limit, Session};
3434
use rustc_span::symbol::{sym, Symbol};
35-
use rustc_span::FileName;
35+
use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm};
3636
use rustc_target::spec::PanicStrategy;
3737
use rustc_trait_selection::traits;
3838

3939
use std::any::Any;
4040
use std::ffi::OsString;
41+
use std::fs::File;
4142
use std::io::{self, BufWriter, Write};
4243
use std::path::{Path, PathBuf};
4344
use std::sync::{Arc, LazyLock};
@@ -420,15 +421,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
420421
let result: io::Result<()> = try {
421422
// Build a list of files used to compile the output and
422423
// write Makefile-compatible dependency rules
423-
let mut files: Vec<String> = sess
424+
let mut files: Vec<(String, u64, Option<SourceFileHash>)> = sess
424425
.source_map()
425426
.files()
426427
.iter()
427428
.filter(|fmap| fmap.is_real_file())
428429
.filter(|fmap| !fmap.is_imported())
429-
.map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string()))
430+
.map(|fmap| {
431+
(
432+
escape_dep_filename(&fmap.name.prefer_local().to_string()),
433+
fmap.source_len.0 as u64,
434+
fmap.checksum_hash,
435+
)
436+
})
430437
.collect();
431438

439+
let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm;
440+
432441
// Account for explicitly marked-to-track files
433442
// (e.g. accessed in proc macros).
434443
let file_depinfo = sess.psess.file_depinfo.borrow();
@@ -438,58 +447,115 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
438447
escape_dep_filename(&file.prefer_local().to_string())
439448
};
440449

450+
fn hash_iter_files<P: AsRef<Path>>(
451+
it: impl Iterator<Item = P>,
452+
checksum_hash_algo: Option<SourceFileHashAlgorithm>,
453+
) -> impl Iterator<Item = (P, u64, Option<SourceFileHash>)> {
454+
it.map(move |path| {
455+
match checksum_hash_algo.and_then(|algo| {
456+
File::open(path.as_ref())
457+
.and_then(|mut file| {
458+
SourceFileHash::new(algo, &mut file).map(|h| (file, h))
459+
})
460+
.and_then(|(file, h)| file.metadata().map(|m| (m.len(), h)))
461+
.map_err(|e| {
462+
tracing::error!(
463+
"failed to compute checksum, omitting it from dep-info {} {e}",
464+
path.as_ref().display()
465+
)
466+
})
467+
.ok()
468+
}) {
469+
Some((file_len, checksum)) => (path, file_len, Some(checksum)),
470+
None => (path, 0, None),
471+
}
472+
})
473+
}
474+
441475
// The entries will be used to declare dependencies beween files in a
442476
// Makefile-like output, so the iteration order does not matter.
443477
#[allow(rustc::potential_query_instability)]
444-
let extra_tracked_files =
445-
file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str())));
478+
let extra_tracked_files = hash_iter_files(
479+
file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))),
480+
checksum_hash_algo,
481+
);
446482
files.extend(extra_tracked_files);
447483

448484
// We also need to track used PGO profile files
449485
if let Some(ref profile_instr) = sess.opts.cg.profile_use {
450-
files.push(normalize_path(profile_instr.as_path().to_path_buf()));
486+
files.extend(hash_iter_files(
487+
iter::once(normalize_path(profile_instr.as_path().to_path_buf())),
488+
checksum_hash_algo,
489+
));
451490
}
452491
if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use {
453-
files.push(normalize_path(profile_sample.as_path().to_path_buf()));
492+
files.extend(hash_iter_files(
493+
iter::once(normalize_path(profile_sample.as_path().to_path_buf())),
494+
checksum_hash_algo,
495+
));
454496
}
455497

456498
// Debugger visualizer files
457499
for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) {
458-
files.push(normalize_path(debugger_visualizer.path.clone().unwrap()));
500+
files.extend(hash_iter_files(
501+
iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())),
502+
checksum_hash_algo,
503+
));
459504
}
460505

461506
if sess.binary_dep_depinfo() {
462507
if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend {
463508
if backend.contains('.') {
464509
// If the backend name contain a `.`, it is the path to an external dynamic
465510
// library. If not, it is not a path.
466-
files.push(backend.to_string());
511+
files.extend(hash_iter_files(
512+
iter::once(backend.to_string()),
513+
checksum_hash_algo,
514+
));
467515
}
468516
}
469517

470518
for &cnum in tcx.crates(()) {
471519
let source = tcx.used_crate_source(cnum);
472520
if let Some((path, _)) = &source.dylib {
473-
files.push(escape_dep_filename(&path.display().to_string()));
521+
files.extend(hash_iter_files(
522+
iter::once(escape_dep_filename(&path.display().to_string())),
523+
checksum_hash_algo,
524+
));
474525
}
475526
if let Some((path, _)) = &source.rlib {
476-
files.push(escape_dep_filename(&path.display().to_string()));
527+
files.extend(hash_iter_files(
528+
iter::once(escape_dep_filename(&path.display().to_string())),
529+
checksum_hash_algo,
530+
));
477531
}
478532
if let Some((path, _)) = &source.rmeta {
479-
files.push(escape_dep_filename(&path.display().to_string()));
533+
files.extend(hash_iter_files(
534+
iter::once(escape_dep_filename(&path.display().to_string())),
535+
checksum_hash_algo,
536+
));
480537
}
481538
}
482539
}
483540

484541
let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
485542
for path in out_filenames {
486-
writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
543+
writeln!(
544+
file,
545+
"{}: {}\n",
546+
path.display(),
547+
files
548+
.iter()
549+
.map(|(path, _file_len, _checksum_hash_algo)| path.as_str())
550+
.intersperse(" ")
551+
.collect::<String>()
552+
)?;
487553
}
488554

489555
// Emit a fake target for each input file to the compilation. This
490556
// prevents `make` from spitting out an error if a file is later
491557
// deleted. For more info see #28735
492-
for path in files {
558+
for (path, _file_len, _checksum_hash_algo) in &files {
493559
writeln!(file, "{path}:")?;
494560
}
495561

@@ -513,6 +579,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
513579
}
514580
}
515581

582+
// If caller requested this information, add special comments about source file checksums.
583+
// These are not necessarily the same checksums as was used in the debug files.
584+
if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() {
585+
for (path, file_len, checksum_hash) in
586+
files.iter().filter_map(|(path, file_len, hash_algo)| {
587+
hash_algo.map(|hash_algo| (path, file_len, hash_algo))
588+
})
589+
{
590+
writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?;
591+
}
592+
}
593+
516594
Ok(())
517595
};
518596

‎compiler/rustc_interface/src/tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ where
4848
let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone());
4949
let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot);
5050
let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
51+
let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
5152
let sm_inputs = Some(SourceMapInputs {
5253
file_loader: Box::new(RealFileLoader) as _,
5354
path_mapping: sessopts.file_path_mapping(),
5455
hash_kind,
56+
checksum_hash_kind,
5557
});
5658

5759
rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || {

‎compiler/rustc_metadata/src/rmeta/decoder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1723,6 +1723,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17231723
let rustc_span::SourceFile {
17241724
mut name,
17251725
src_hash,
1726+
checksum_hash,
17261727
start_pos: original_start_pos,
17271728
source_len,
17281729
lines,
@@ -1774,6 +1775,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17741775
let local_version = sess.source_map().new_imported_source_file(
17751776
name,
17761777
src_hash,
1778+
checksum_hash,
17771779
stable_id,
17781780
source_len.to_u32(),
17791781
self.cnum,

‎compiler/rustc_query_system/src/ich/impls_syntax.rs

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
6868
// Do not hash the source as it is not encoded
6969
src: _,
7070
ref src_hash,
71+
// Already includes src_hash, this is redundant
72+
checksum_hash: _,
7173
external_src: _,
7274
start_pos: _,
7375
source_len: _,

‎compiler/rustc_session/src/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,10 @@ impl UnstableOptions {
12191219
}
12201220
})
12211221
}
1222+
1223+
pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
1224+
self.checksum_hash_algorithm
1225+
}
12221226
}
12231227

12241228
// The type of entry function, so users can have their own entry functions

‎compiler/rustc_session/src/options.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ mod desc {
413413
pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
414414
pub const parse_symbol_mangling_version: &str =
415415
"one of: `legacy`, `v0` (RFC 2603), or `hashed`";
416-
pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
416+
pub const parse_cargo_src_file_hash: &str = "one of `sha256`";
417+
pub const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`";
417418
pub const parse_relocation_model: &str =
418419
"one of supported relocation models (`rustc --print relocation-models`)";
419420
pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
@@ -1261,6 +1262,23 @@ mod parse {
12611262
true
12621263
}
12631264

1265+
pub(crate) fn parse_cargo_src_file_hash(
1266+
slot: &mut Option<SourceFileHashAlgorithm>,
1267+
v: Option<&str>,
1268+
) -> bool {
1269+
match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) {
1270+
Some(hash_kind) => {
1271+
if hash_kind.supported_in_cargo() {
1272+
*slot = Some(hash_kind);
1273+
} else {
1274+
return false;
1275+
}
1276+
}
1277+
_ => return false,
1278+
}
1279+
true
1280+
}
1281+
12641282
pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
12651283
match v {
12661284
Some(s) => {
@@ -1647,6 +1665,8 @@ options! {
16471665
"instrument control-flow architecture protection"),
16481666
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],
16491667
"show all expected values in check-cfg diagnostics (default: no)"),
1668+
checksum_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_cargo_src_file_hash, [TRACKED],
1669+
"hash algorithm of source files used to check freshness in cargo (`sha256`)"),
16501670
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
16511671
"the backend to use"),
16521672
combine_cgu: bool = (false, parse_bool, [TRACKED],

‎compiler/rustc_span/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
blake3 = "1.5.2"
89
derivative = "2.2.0"
910
indexmap = { version = "2.0.0" }
1011
itoa = "1.0"

‎compiler/rustc_span/src/lib.rs

+139-7
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc};
7676

7777
use std::borrow::Cow;
7878
use std::cmp::{self, Ordering};
79+
use std::fmt::Display;
7980
use std::hash::Hash;
81+
use std::io::{self, Read};
8082
use std::ops::{Add, Range, Sub};
8183
use std::path::{Path, PathBuf};
8284
use std::str::FromStr;
@@ -1456,6 +1458,27 @@ pub enum SourceFileHashAlgorithm {
14561458
Md5,
14571459
Sha1,
14581460
Sha256,
1461+
Blake3,
1462+
}
1463+
1464+
impl SourceFileHashAlgorithm {
1465+
pub fn supported_in_cargo(&self) -> bool {
1466+
match self {
1467+
Self::Md5 | Self::Sha1 => false,
1468+
Self::Sha256 | Self::Blake3 => true,
1469+
}
1470+
}
1471+
}
1472+
1473+
impl Display for SourceFileHashAlgorithm {
1474+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1475+
f.write_str(match self {
1476+
Self::Md5 => "md5",
1477+
Self::Sha1 => "sha1",
1478+
Self::Sha256 => "sha256",
1479+
Self::Blake3 => "blake3",
1480+
})
1481+
}
14591482
}
14601483

14611484
impl FromStr for SourceFileHashAlgorithm {
@@ -1466,25 +1489,36 @@ impl FromStr for SourceFileHashAlgorithm {
14661489
"md5" => Ok(SourceFileHashAlgorithm::Md5),
14671490
"sha1" => Ok(SourceFileHashAlgorithm::Sha1),
14681491
"sha256" => Ok(SourceFileHashAlgorithm::Sha256),
1492+
"blake3" => Ok(SourceFileHashAlgorithm::Blake3),
14691493
_ => Err(()),
14701494
}
14711495
}
14721496
}
14731497

1474-
/// The hash of the on-disk source file used for debug info.
1498+
/// The hash of the on-disk source file used for debug info and cargo freshness checks.
14751499
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
14761500
#[derive(HashStable_Generic, Encodable, Decodable)]
14771501
pub struct SourceFileHash {
14781502
pub kind: SourceFileHashAlgorithm,
14791503
value: [u8; 32],
14801504
}
14811505

1506+
impl Display for SourceFileHash {
1507+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1508+
write!(f, "{}=", self.kind)?;
1509+
for byte in self.value[0..self.hash_len()].into_iter() {
1510+
write!(f, "{byte:02x}")?;
1511+
}
1512+
Ok(())
1513+
}
1514+
}
1515+
14821516
impl SourceFileHash {
1483-
pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash {
1517+
pub fn new_in_memory(kind: SourceFileHashAlgorithm, src: impl AsRef<[u8]>) -> SourceFileHash {
14841518
let mut hash = SourceFileHash { kind, value: Default::default() };
14851519
let len = hash.hash_len();
14861520
let value = &mut hash.value[..len];
1487-
let data = src.as_bytes();
1521+
let data = src.as_ref();
14881522
match kind {
14891523
SourceFileHashAlgorithm::Md5 => {
14901524
value.copy_from_slice(&Md5::digest(data));
@@ -1495,13 +1529,94 @@ impl SourceFileHash {
14951529
SourceFileHashAlgorithm::Sha256 => {
14961530
value.copy_from_slice(&Sha256::digest(data));
14971531
}
1498-
}
1532+
SourceFileHashAlgorithm::Blake3 => value.copy_from_slice(blake3::hash(data).as_bytes()),
1533+
};
14991534
hash
15001535
}
15011536

1537+
pub fn new(kind: SourceFileHashAlgorithm, src: impl Read) -> Result<SourceFileHash, io::Error> {
1538+
let mut hash = SourceFileHash { kind, value: Default::default() };
1539+
let len = hash.hash_len();
1540+
let value = &mut hash.value[..len];
1541+
// Buffer size is the recommended amount to fully leverage SIMD instructions on AVX-512 as per
1542+
// blake3 documentation.
1543+
let mut buf = vec![0; 16 * 1024];
1544+
1545+
fn digest<T>(
1546+
mut hasher: T,
1547+
mut update: impl FnMut(&mut T, &[u8]),
1548+
finish: impl FnOnce(T, &mut [u8]),
1549+
mut src: impl Read,
1550+
buf: &mut [u8],
1551+
value: &mut [u8],
1552+
) -> Result<(), io::Error> {
1553+
loop {
1554+
let bytes_read = src.read(buf)?;
1555+
if bytes_read == 0 {
1556+
break;
1557+
}
1558+
update(&mut hasher, &buf[0..bytes_read]);
1559+
}
1560+
finish(hasher, value);
1561+
Ok(())
1562+
}
1563+
1564+
match kind {
1565+
SourceFileHashAlgorithm::Sha256 => {
1566+
digest(
1567+
Sha256::new(),
1568+
|h, b| {
1569+
h.update(b);
1570+
},
1571+
|h, out| out.copy_from_slice(&h.finalize()),
1572+
src,
1573+
&mut buf,
1574+
value,
1575+
)?;
1576+
}
1577+
SourceFileHashAlgorithm::Sha1 => {
1578+
digest(
1579+
Sha1::new(),
1580+
|h, b| {
1581+
h.update(b);
1582+
},
1583+
|h, out| out.copy_from_slice(&h.finalize()),
1584+
src,
1585+
&mut buf,
1586+
value,
1587+
)?;
1588+
}
1589+
SourceFileHashAlgorithm::Md5 => {
1590+
digest(
1591+
Md5::new(),
1592+
|h, b| {
1593+
h.update(b);
1594+
},
1595+
|h, out| out.copy_from_slice(&h.finalize()),
1596+
src,
1597+
&mut buf,
1598+
value,
1599+
)?;
1600+
}
1601+
SourceFileHashAlgorithm::Blake3 => {
1602+
digest(
1603+
blake3::Hasher::new(),
1604+
|h, b| {
1605+
h.update(b);
1606+
},
1607+
|h, out| out.copy_from_slice(h.finalize().as_bytes()),
1608+
src,
1609+
&mut buf,
1610+
value,
1611+
)?;
1612+
}
1613+
}
1614+
Ok(hash)
1615+
}
1616+
15021617
/// Check if the stored hash matches the hash of the string.
15031618
pub fn matches(&self, src: &str) -> bool {
1504-
Self::new(self.kind, src) == *self
1619+
Self::new_in_memory(self.kind, src.as_bytes()) == *self
15051620
}
15061621

15071622
/// The bytes of the hash.
@@ -1514,7 +1629,7 @@ impl SourceFileHash {
15141629
match self.kind {
15151630
SourceFileHashAlgorithm::Md5 => 16,
15161631
SourceFileHashAlgorithm::Sha1 => 20,
1517-
SourceFileHashAlgorithm::Sha256 => 32,
1632+
SourceFileHashAlgorithm::Sha256 | SourceFileHashAlgorithm::Blake3 => 32,
15181633
}
15191634
}
15201635
}
@@ -1570,6 +1685,10 @@ pub struct SourceFile {
15701685
pub src: Option<Lrc<String>>,
15711686
/// The source code's hash.
15721687
pub src_hash: SourceFileHash,
1688+
/// Used to enable cargo to use checksums to check if a crate is fresh rather
1689+
/// than mtimes. This might be the same as `src_hash`, and if the requested algorithm
1690+
/// is identical we won't compute it twice.
1691+
pub checksum_hash: Option<SourceFileHash>,
15731692
/// The external source code (used for external crates, which will have a `None`
15741693
/// value as `self.src`.
15751694
pub external_src: FreezeLock<ExternalSource>,
@@ -1599,6 +1718,7 @@ impl Clone for SourceFile {
15991718
name: self.name.clone(),
16001719
src: self.src.clone(),
16011720
src_hash: self.src_hash,
1721+
checksum_hash: self.checksum_hash,
16021722
external_src: self.external_src.clone(),
16031723
start_pos: self.start_pos,
16041724
source_len: self.source_len,
@@ -1616,6 +1736,7 @@ impl<S: SpanEncoder> Encodable<S> for SourceFile {
16161736
fn encode(&self, s: &mut S) {
16171737
self.name.encode(s);
16181738
self.src_hash.encode(s);
1739+
self.checksum_hash.encode(s);
16191740
// Do not encode `start_pos` as it's global state for this session.
16201741
self.source_len.encode(s);
16211742

@@ -1690,6 +1811,7 @@ impl<D: SpanDecoder> Decodable<D> for SourceFile {
16901811
fn decode(d: &mut D) -> SourceFile {
16911812
let name: FileName = Decodable::decode(d);
16921813
let src_hash: SourceFileHash = Decodable::decode(d);
1814+
let checksum_hash: Option<SourceFileHash> = Decodable::decode(d);
16931815
let source_len: RelativeBytePos = Decodable::decode(d);
16941816
let lines = {
16951817
let num_lines: u32 = Decodable::decode(d);
@@ -1716,6 +1838,7 @@ impl<D: SpanDecoder> Decodable<D> for SourceFile {
17161838
source_len,
17171839
src: None,
17181840
src_hash,
1841+
checksum_hash,
17191842
// Unused - the metadata decoder will construct
17201843
// a new SourceFile, filling in `external_src` properly
17211844
external_src: FreezeLock::frozen(ExternalSource::Unneeded),
@@ -1800,9 +1923,17 @@ impl SourceFile {
18001923
name: FileName,
18011924
mut src: String,
18021925
hash_kind: SourceFileHashAlgorithm,
1926+
checksum_hash_kind: Option<SourceFileHashAlgorithm>,
18031927
) -> Result<Self, OffsetOverflowError> {
18041928
// Compute the file hash before any normalization.
1805-
let src_hash = SourceFileHash::new(hash_kind, &src);
1929+
let src_hash = SourceFileHash::new_in_memory(hash_kind, src.as_bytes());
1930+
let checksum_hash = checksum_hash_kind.map(|checksum_hash_kind| {
1931+
if checksum_hash_kind == hash_kind {
1932+
src_hash
1933+
} else {
1934+
SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes())
1935+
}
1936+
});
18061937
let normalized_pos = normalize_src(&mut src);
18071938

18081939
let stable_id = StableSourceFileId::from_filename_in_current_crate(&name);
@@ -1816,6 +1947,7 @@ impl SourceFile {
18161947
name,
18171948
src: Some(Lrc::new(src)),
18181949
src_hash,
1950+
checksum_hash,
18191951
external_src: FreezeLock::frozen(ExternalSource::Unneeded),
18201952
start_pos: BytePos::from_u32(0),
18211953
source_len: RelativeBytePos::from_u32(source_len),

‎compiler/rustc_span/src/source_map.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ pub struct SourceMapInputs {
174174
pub file_loader: Box<dyn FileLoader + Send + Sync>,
175175
pub path_mapping: FilePathMapping,
176176
pub hash_kind: SourceFileHashAlgorithm,
177+
pub checksum_hash_kind: Option<SourceFileHashAlgorithm>,
177178
}
178179

179180
pub struct SourceMap {
@@ -186,6 +187,12 @@ pub struct SourceMap {
186187

187188
/// The algorithm used for hashing the contents of each source file.
188189
hash_kind: SourceFileHashAlgorithm,
190+
191+
/// Similar to `hash_kind`, however this algorithm is used for checksums to determine if a crate is fresh.
192+
/// `cargo` is the primary user of these.
193+
///
194+
/// If this is equal to `hash_kind` then the checksum won't be computed twice.
195+
checksum_hash_kind: Option<SourceFileHashAlgorithm>,
189196
}
190197

191198
impl SourceMap {
@@ -194,17 +201,19 @@ impl SourceMap {
194201
file_loader: Box::new(RealFileLoader),
195202
path_mapping,
196203
hash_kind: SourceFileHashAlgorithm::Md5,
204+
checksum_hash_kind: None,
197205
})
198206
}
199207

200208
pub fn with_inputs(
201-
SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs,
209+
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs,
202210
) -> SourceMap {
203211
SourceMap {
204212
files: Default::default(),
205213
file_loader: IntoDynSyncSend(file_loader),
206214
path_mapping,
207215
hash_kind,
216+
checksum_hash_kind,
208217
}
209218
}
210219

@@ -306,7 +315,8 @@ impl SourceMap {
306315
match self.source_file_by_stable_id(stable_id) {
307316
Some(lrc_sf) => Ok(lrc_sf),
308317
None => {
309-
let source_file = SourceFile::new(filename, src, self.hash_kind)?;
318+
let source_file =
319+
SourceFile::new(filename, src, self.hash_kind, self.checksum_hash_kind)?;
310320

311321
// Let's make sure the file_id we generated above actually matches
312322
// the ID we generate for the SourceFile we just created.
@@ -325,6 +335,7 @@ impl SourceMap {
325335
&self,
326336
filename: FileName,
327337
src_hash: SourceFileHash,
338+
checksum_hash: Option<SourceFileHash>,
328339
stable_id: StableSourceFileId,
329340
source_len: u32,
330341
cnum: CrateNum,
@@ -340,6 +351,7 @@ impl SourceMap {
340351
name: filename,
341352
src: None,
342353
src_hash,
354+
checksum_hash,
343355
external_src: FreezeLock::new(ExternalSource::Foreign {
344356
kind: ExternalSourceKind::AbsentOk,
345357
metadata_index,

‎compiler/rustc_span/src/source_map/tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ fn t10() {
229229
let SourceFile {
230230
name,
231231
src_hash,
232+
checksum_hash,
232233
source_len,
233234
lines,
234235
multibyte_chars,
@@ -241,6 +242,7 @@ fn t10() {
241242
let imported_src_file = sm.new_imported_source_file(
242243
name,
243244
src_hash,
245+
checksum_hash,
244246
stable_id,
245247
source_len.to_u32(),
246248
CrateNum::ZERO,

‎compiler/rustc_span/src/tests.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ use super::*;
33
#[test]
44
fn test_lookup_line() {
55
let source = "abcdefghijklm\nabcdefghij\n...".to_owned();
6-
let mut sf =
7-
SourceFile::new(FileName::Anon(Hash64::ZERO), source, SourceFileHashAlgorithm::Sha256)
8-
.unwrap();
6+
let mut sf = SourceFile::new(
7+
FileName::Anon(Hash64::ZERO),
8+
source,
9+
SourceFileHashAlgorithm::Sha256,
10+
Some(SourceFileHashAlgorithm::Sha256),
11+
)
12+
.unwrap();
913
sf.start_pos = BytePos(3);
1014
assert_eq!(sf.lines(), &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)]);
1115

‎src/tools/tidy/src/deps.rs

+24-18
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,28 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>)
8282
#[rustfmt::skip]
8383
const EXCEPTIONS: ExceptionList = &[
8484
// tidy-alphabetical-start
85-
("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
86-
("colored", "MPL-2.0"), // rustfmt
87-
("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
88-
("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
89-
("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target. FIXME: this dependency violates the documentation comment above.
90-
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
91-
("mdbook", "MPL-2.0"), // mdbook
92-
("option-ext", "MPL-2.0"), // cargo-miri (via `directories`)
93-
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
85+
("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
86+
("arrayref", "BSD-2-Clause"), // rustc
87+
("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc
88+
("colored", "MPL-2.0"), // rustfmt
89+
("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc
90+
("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
91+
("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
92+
("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target. FIXME: this dependency violates the documentation comment above.
93+
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
94+
("mdbook", "MPL-2.0"), // mdbook
95+
("option-ext", "MPL-2.0"), // cargo-miri (via `directories`)
96+
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
9497
("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
95-
("self_cell", "Apache-2.0"), // rustc (fluent translations)
96-
("snap", "BSD-3-Clause"), // rustc
97-
("wasm-encoder", "Apache-2.0 WITH LLVM-exception"), // rustc
98-
("wasm-metadata", "Apache-2.0 WITH LLVM-exception"), // rustc
99-
("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
100-
("wast", "Apache-2.0 WITH LLVM-exception"), // rustc
101-
("wat", "Apache-2.0 WITH LLVM-exception"), // rustc
102-
("wit-component", "Apache-2.0 WITH LLVM-exception"), // rustc
103-
("wit-parser", "Apache-2.0 WITH LLVM-exception"), // rustc
98+
("self_cell", "Apache-2.0"), // rustc (fluent translations)
99+
("snap", "BSD-3-Clause"), // rustc
100+
("wasm-encoder", "Apache-2.0 WITH LLVM-exception"), // rustc
101+
("wasm-metadata", "Apache-2.0 WITH LLVM-exception"), // rustc
102+
("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
103+
("wast", "Apache-2.0 WITH LLVM-exception"), // rustc
104+
("wat", "Apache-2.0 WITH LLVM-exception"), // rustc
105+
("wit-component", "Apache-2.0 WITH LLVM-exception"), // rustc
106+
("wit-parser", "Apache-2.0 WITH LLVM-exception"), // rustc
104107
// tidy-alphabetical-end
105108
];
106109

@@ -237,15 +240,18 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
237240
"annotate-snippets",
238241
"anstyle",
239242
"ar_archive_writer",
243+
"arrayref",
240244
"arrayvec",
241245
"autocfg",
242246
"bitflags",
247+
"blake3",
243248
"block-buffer",
244249
"byteorder", // via ruzstd in object in thorin-dwp
245250
"cc",
246251
"cfg-if",
247252
"cfg_aliases",
248253
"compiler_builtins",
254+
"constant_time_eq",
249255
"cpufeatures",
250256
"crc32fast",
251257
"crossbeam-channel",

0 commit comments

Comments
 (0)
Please sign in to comment.