Skip to content

Commit 683bda9

Browse files
committed
add unstable support for outputting file checksums for use in cargo
1 parent c1b336c commit 683bda9

File tree

12 files changed

+246
-27
lines changed

12 files changed

+246
-27
lines changed

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
@@ -31,12 +31,13 @@ use rustc_session::output::filename_for_input;
3131
use rustc_session::search_paths::PathKind;
3232
use rustc_session::{Limit, Session};
3333
use rustc_span::symbol::{sym, Symbol};
34-
use rustc_span::FileName;
34+
use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm};
3535
use rustc_target::spec::PanicStrategy;
3636
use rustc_trait_selection::traits;
3737

3838
use std::any::Any;
3939
use std::ffi::OsString;
40+
use std::fs::File;
4041
use std::io::{self, BufWriter, Write};
4142
use std::path::{Path, PathBuf};
4243
use std::sync::LazyLock;
@@ -417,15 +418,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
417418
let result: io::Result<()> = try {
418419
// Build a list of files used to compile the output and
419420
// write Makefile-compatible dependency rules
420-
let mut files: Vec<String> = sess
421+
let mut files: Vec<(String, u64, Option<SourceFileHash>)> = sess
421422
.source_map()
422423
.files()
423424
.iter()
424425
.filter(|fmap| fmap.is_real_file())
425426
.filter(|fmap| !fmap.is_imported())
426-
.map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string()))
427+
.map(|fmap| {
428+
(
429+
escape_dep_filename(&fmap.name.prefer_local().to_string()),
430+
fmap.source_len.0 as u64,
431+
fmap.checksum_hash,
432+
)
433+
})
427434
.collect();
428435

436+
let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm;
437+
429438
// Account for explicitly marked-to-track files
430439
// (e.g. accessed in proc macros).
431440
let file_depinfo = sess.psess.file_depinfo.borrow();
@@ -435,58 +444,115 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
435444
escape_dep_filename(&file.prefer_local().to_string())
436445
};
437446

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

445481
// We also need to track used PGO profile files
446482
if let Some(ref profile_instr) = sess.opts.cg.profile_use {
447-
files.push(normalize_path(profile_instr.as_path().to_path_buf()));
483+
files.extend(hash_iter_files(
484+
iter::once(normalize_path(profile_instr.as_path().to_path_buf())),
485+
checksum_hash_algo,
486+
));
448487
}
449488
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()));
489+
files.extend(hash_iter_files(
490+
iter::once(normalize_path(profile_sample.as_path().to_path_buf())),
491+
checksum_hash_algo,
492+
));
451493
}
452494

453495
// Debugger visualizer files
454496
for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) {
455-
files.push(normalize_path(debugger_visualizer.path.clone().unwrap()));
497+
files.extend(hash_iter_files(
498+
iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())),
499+
checksum_hash_algo,
500+
));
456501
}
457502

458503
if sess.binary_dep_depinfo() {
459504
if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend {
460505
if backend.contains('.') {
461506
// If the backend name contain a `.`, it is the path to an external dynamic
462507
// library. If not, it is not a path.
463-
files.push(backend.to_string());
508+
files.extend(hash_iter_files(
509+
iter::once(backend.to_string()),
510+
checksum_hash_algo,
511+
));
464512
}
465513
}
466514

467515
for &cnum in tcx.crates(()) {
468516
let source = tcx.used_crate_source(cnum);
469517
if let Some((path, _)) = &source.dylib {
470-
files.push(escape_dep_filename(&path.display().to_string()));
518+
files.extend(hash_iter_files(
519+
iter::once(escape_dep_filename(&path.display().to_string())),
520+
checksum_hash_algo,
521+
));
471522
}
472523
if let Some((path, _)) = &source.rlib {
473-
files.push(escape_dep_filename(&path.display().to_string()));
524+
files.extend(hash_iter_files(
525+
iter::once(escape_dep_filename(&path.display().to_string())),
526+
checksum_hash_algo,
527+
));
474528
}
475529
if let Some((path, _)) = &source.rmeta {
476-
files.push(escape_dep_filename(&path.display().to_string()));
530+
files.extend(hash_iter_files(
531+
iter::once(escape_dep_filename(&path.display().to_string())),
532+
checksum_hash_algo,
533+
));
477534
}
478535
}
479536
}
480537

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

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

@@ -510,6 +576,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
510576
}
511577
}
512578

579+
// If caller requested this information, add special comments about source file checksums.
580+
// These are not necessarily the same checksums as was used in the debug files.
581+
if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() {
582+
for (path, file_len, checksum_hash) in
583+
files.iter().filter_map(|(path, file_len, hash_algo)| {
584+
hash_algo.map(|hash_algo| (path, file_len, hash_algo))
585+
})
586+
{
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
@@ -40,10 +40,12 @@ where
4040
let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone());
4141
let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot);
4242
let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
43+
let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
4344
let sm_inputs = Some(SourceMapInputs {
4445
file_loader: Box::new(RealFileLoader) as _,
4546
path_mapping: sessopts.file_path_mapping(),
4647
hash_kind,
48+
checksum_hash_kind,
4749
});
4850

4951
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
@@ -1720,6 +1720,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17201720
let rustc_span::SourceFile {
17211721
mut name,
17221722
src_hash,
1723+
checksum_hash,
17231724
start_pos: original_start_pos,
17241725
source_len,
17251726
lines,
@@ -1771,6 +1772,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17711772
let local_version = sess.source_map().new_imported_source_file(
17721773
name,
17731774
src_hash,
1775+
checksum_hash,
17741776
stable_id,
17751777
source_len.to_u32(),
17761778
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
@@ -1217,6 +1217,10 @@ impl UnstableOptions {
12171217
}
12181218
})
12191219
}
1220+
1221+
pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
1222+
self.checksum_hash_algorithm
1223+
}
12201224
}
12211225

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

compiler/rustc_session/src/options.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ mod desc {
412412
pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
413413
pub const parse_symbol_mangling_version: &str =
414414
"one of: `legacy`, `v0` (RFC 2603), or `hashed`";
415-
pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
415+
pub const parse_src_file_hash: &str = "either `md5`, `sha1`, or `sha256`";
416416
pub const parse_relocation_model: &str =
417417
"one of supported relocation models (`rustc --print relocation-models`)";
418418
pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
@@ -1609,6 +1609,8 @@ options! {
16091609
"instrument control-flow architecture protection"),
16101610
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],
16111611
"show all expected values in check-cfg diagnostics (default: no)"),
1612+
checksum_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
1613+
"hash algorithm of source files used to check freshness in cargo (`md5`, `sha1`, or `sha256`)"),
16121614
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
16131615
"the backend to use"),
16141616
combine_cgu: bool = (false, parse_bool, [TRACKED],

0 commit comments

Comments
 (0)