Skip to content

Commit 38309ba

Browse files
committed
add unstable support for outputting file checksums for use in cargo
1 parent 3de0a7c commit 38309ba

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
@@ -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

+3-1
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ 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_src_file_hash: &str = "either `md5`, `sha1`, or `sha256`";
417417
pub const parse_relocation_model: &str =
418418
"one of supported relocation models (`rustc --print relocation-models`)";
419419
pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
@@ -1647,6 +1647,8 @@ options! {
16471647
"instrument control-flow architecture protection"),
16481648
check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED],
16491649
"show all expected values in check-cfg diagnostics (default: no)"),
1650+
checksum_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
1651+
"hash algorithm of source files used to check freshness in cargo (`md5`, `sha1`, or `sha256`)"),
16501652
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
16511653
"the backend to use"),
16521654
combine_cgu: bool = (false, parse_bool, [TRACKED],

0 commit comments

Comments
 (0)