Skip to content

Commit f6faf34

Browse files
authored
Unrolled build for rust-lang#126930
Rollup merge of rust-lang#126930 - Xaeroxe:file-checksum-hint, r=chenyukang 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 fd1f8aa + 58c5ac4 commit f6faf34

File tree

20 files changed

+337
-28
lines changed

20 files changed

+337
-28
lines changed

Cargo.lock

+26
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ dependencies = [
199199
"object 0.36.4",
200200
]
201201

202+
[[package]]
203+
name = "arrayref"
204+
version = "0.3.7"
205+
source = "registry+https://github.com/rust-lang/crates.io-index"
206+
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
207+
202208
[[package]]
203209
name = "arrayvec"
204210
version = "0.7.6"
@@ -262,6 +268,19 @@ version = "2.6.0"
262268
source = "registry+https://github.com/rust-lang/crates.io-index"
263269
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
264270

271+
[[package]]
272+
name = "blake3"
273+
version = "1.5.2"
274+
source = "registry+https://github.com/rust-lang/crates.io-index"
275+
checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92"
276+
dependencies = [
277+
"arrayref",
278+
"arrayvec",
279+
"cc",
280+
"cfg-if",
281+
"constant_time_eq",
282+
]
283+
265284
[[package]]
266285
name = "block-buffer"
267286
version = "0.10.4"
@@ -722,6 +741,12 @@ dependencies = [
722741
"windows-sys 0.52.0",
723742
]
724743

744+
[[package]]
745+
name = "constant_time_eq"
746+
version = "0.3.0"
747+
source = "registry+https://github.com/rust-lang/crates.io-index"
748+
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
749+
725750
[[package]]
726751
name = "core-foundation-sys"
727752
version = "0.8.7"
@@ -4378,6 +4403,7 @@ dependencies = [
43784403
name = "rustc_span"
43794404
version = "0.0.0"
43804405
dependencies = [
4406+
"blake3",
43814407
"derive-where",
43824408
"indexmap",
43834409
"itoa",

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi
630630
rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
631631
rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
632632
rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
633+
rustc_span::SourceFileHashAlgorithm::Blake3 => llvm::ChecksumKind::None,
633634
};
634635
let hash_value = hex_encode(source_file.src_hash.hash_bytes());
635636

compiler/rustc_interface/src/interface.rs

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

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

compiler/rustc_interface/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// tidy-alphabetical-start
22
#![feature(decl_macro)]
33
#![feature(file_buffered)]
4+
#![feature(iter_intersperse)]
45
#![feature(let_chains)]
56
#![feature(try_blocks)]
67
#![warn(unreachable_pub)]

compiler/rustc_interface/src/passes.rs

+92-14
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use rustc_session::cstore::Untracked;
3232
use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
3333
use rustc_session::search_paths::PathKind;
3434
use rustc_session::{Limit, Session};
35-
use rustc_span::FileName;
3635
use rustc_span::symbol::{Symbol, sym};
36+
use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm};
3737
use rustc_target::spec::PanicStrategy;
3838
use rustc_trait_selection::traits;
3939
use tracing::{info, instrument};
@@ -417,15 +417,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
417417
let result: io::Result<()> = try {
418418
// Build a list of files used to compile the output and
419419
// write Makefile-compatible dependency rules
420-
let mut files: Vec<String> = sess
420+
let mut files: Vec<(String, u64, Option<SourceFileHash>)> = sess
421421
.source_map()
422422
.files()
423423
.iter()
424424
.filter(|fmap| fmap.is_real_file())
425425
.filter(|fmap| !fmap.is_imported())
426-
.map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string()))
426+
.map(|fmap| {
427+
(
428+
escape_dep_filename(&fmap.name.prefer_local().to_string()),
429+
fmap.source_len.0 as u64,
430+
fmap.checksum_hash,
431+
)
432+
})
427433
.collect();
428434

435+
let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm;
436+
429437
// Account for explicitly marked-to-track files
430438
// (e.g. accessed in proc macros).
431439
let file_depinfo = sess.psess.file_depinfo.borrow();
@@ -437,56 +445,113 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
437445

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

445480
// We also need to track used PGO profile files
446481
if let Some(ref profile_instr) = sess.opts.cg.profile_use {
447-
files.push(normalize_path(profile_instr.as_path().to_path_buf()));
482+
files.extend(hash_iter_files(
483+
iter::once(normalize_path(profile_instr.as_path().to_path_buf())),
484+
checksum_hash_algo,
485+
));
448486
}
449487
if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use {
450-
files.push(normalize_path(profile_sample.as_path().to_path_buf()));
488+
files.extend(hash_iter_files(
489+
iter::once(normalize_path(profile_sample.as_path().to_path_buf())),
490+
checksum_hash_algo,
491+
));
451492
}
452493

453494
// Debugger visualizer files
454495
for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) {
455-
files.push(normalize_path(debugger_visualizer.path.clone().unwrap()));
496+
files.extend(hash_iter_files(
497+
iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())),
498+
checksum_hash_algo,
499+
));
456500
}
457501

458502
if sess.binary_dep_depinfo() {
459503
if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend {
460504
if backend.contains('.') {
461505
// If the backend name contain a `.`, it is the path to an external dynamic
462506
// library. If not, it is not a path.
463-
files.push(backend.to_string());
507+
files.extend(hash_iter_files(
508+
iter::once(backend.to_string()),
509+
checksum_hash_algo,
510+
));
464511
}
465512
}
466513

467514
for &cnum in tcx.crates(()) {
468515
let source = tcx.used_crate_source(cnum);
469516
if let Some((path, _)) = &source.dylib {
470-
files.push(escape_dep_filename(&path.display().to_string()));
517+
files.extend(hash_iter_files(
518+
iter::once(escape_dep_filename(&path.display().to_string())),
519+
checksum_hash_algo,
520+
));
471521
}
472522
if let Some((path, _)) = &source.rlib {
473-
files.push(escape_dep_filename(&path.display().to_string()));
523+
files.extend(hash_iter_files(
524+
iter::once(escape_dep_filename(&path.display().to_string())),
525+
checksum_hash_algo,
526+
));
474527
}
475528
if let Some((path, _)) = &source.rmeta {
476-
files.push(escape_dep_filename(&path.display().to_string()));
529+
files.extend(hash_iter_files(
530+
iter::once(escape_dep_filename(&path.display().to_string())),
531+
checksum_hash_algo,
532+
));
477533
}
478534
}
479535
}
480536

481537
let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
482538
for path in out_filenames {
483-
writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
539+
writeln!(
540+
file,
541+
"{}: {}\n",
542+
path.display(),
543+
files
544+
.iter()
545+
.map(|(path, _file_len, _checksum_hash_algo)| path.as_str())
546+
.intersperse(" ")
547+
.collect::<String>()
548+
)?;
484549
}
485550

486551
// Emit a fake target for each input file to the compilation. This
487552
// prevents `make` from spitting out an error if a file is later
488553
// deleted. For more info see #28735
489-
for path in files {
554+
for (path, _file_len, _checksum_hash_algo) in &files {
490555
writeln!(file, "{path}:")?;
491556
}
492557

@@ -510,6 +575,19 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
510575
}
511576
}
512577

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

compiler/rustc_interface/src/tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ where
4444
let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone());
4545
let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot);
4646
let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
47+
let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
4748
let sm_inputs = Some(SourceMapInputs {
4849
file_loader: Box::new(RealFileLoader) as _,
4950
path_mapping: sessopts.file_path_mapping(),
5051
hash_kind,
52+
checksum_hash_kind,
5153
});
5254

5355
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
@@ -1702,6 +1702,7 @@ impl<'a> CrateMetadataRef<'a> {
17021702
let rustc_span::SourceFile {
17031703
mut name,
17041704
src_hash,
1705+
checksum_hash,
17051706
start_pos: original_start_pos,
17061707
source_len,
17071708
lines,
@@ -1752,6 +1753,7 @@ impl<'a> CrateMetadataRef<'a> {
17521753
let local_version = sess.source_map().new_imported_source_file(
17531754
name,
17541755
src_hash,
1756+
checksum_hash,
17551757
stable_id,
17561758
source_len.to_u32(),
17571759
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
@@ -1242,6 +1242,10 @@ impl UnstableOptions {
12421242
}
12431243
})
12441244
}
1245+
1246+
pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
1247+
self.checksum_hash_algorithm
1248+
}
12451249
}
12461250

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

compiler/rustc_session/src/options.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,9 @@ mod desc {
418418
"one of: `legacy`, `v0` (RFC 2603), or `hashed`";
419419
pub(crate) const parse_opt_symbol_visibility: &str =
420420
"one of: `hidden`, `protected`, or `interposable`";
421-
pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`";
421+
pub(crate) const parse_cargo_src_file_hash: &str =
422+
"one of `blake3`, `md5`, `sha1`, or `sha256`";
423+
pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`";
422424
pub(crate) const parse_relocation_model: &str =
423425
"one of supported relocation models (`rustc --print relocation-models`)";
424426
pub(crate) const parse_code_model: &str =
@@ -1288,6 +1290,19 @@ mod parse {
12881290
true
12891291
}
12901292

1293+
pub(crate) fn parse_cargo_src_file_hash(
1294+
slot: &mut Option<SourceFileHashAlgorithm>,
1295+
v: Option<&str>,
1296+
) -> bool {
1297+
match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) {
1298+
Some(hash_kind) => {
1299+
*slot = Some(hash_kind);
1300+
}
1301+
_ => return false,
1302+
}
1303+
true
1304+
}
1305+
12911306
pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
12921307
match v {
12931308
Some(s) => {
@@ -1688,6 +1703,8 @@ options! {
16881703
"instrument control-flow architecture protection"),
16891704
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],
16901705
"show all expected values in check-cfg diagnostics (default: no)"),
1706+
checksum_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_cargo_src_file_hash, [TRACKED],
1707+
"hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"),
16911708
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
16921709
"the backend to use"),
16931710
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
derive-where = "1.2.7"
910
indexmap = { version = "2.0.0" }
1011
itoa = "1.0"

0 commit comments

Comments
 (0)