@@ -31,12 +31,13 @@ use rustc_session::output::filename_for_input;
3131use rustc_session:: search_paths:: PathKind ;
3232use rustc_session:: { Limit , Session } ;
3333use rustc_span:: symbol:: { sym, Symbol } ;
34- use rustc_span:: FileName ;
34+ use rustc_span:: { FileName , SourceFileHash , SourceFileHashAlgorithm } ;
3535use rustc_target:: spec:: PanicStrategy ;
3636use rustc_trait_selection:: traits;
3737
3838use std:: any:: Any ;
3939use std:: ffi:: OsString ;
40+ use std:: fs:: File ;
4041use std:: io:: { self , BufWriter , Write } ;
4142use std:: path:: { Path , PathBuf } ;
4243use 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
0 commit comments