diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d6c25cf524a5c..a0bbd2d41fdf2 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -140,7 +140,6 @@ fn emit_module( bytecode: None, assembly: None, llvm_ir: None, - links_from_incr_cache: Vec::new(), }) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4883eb1087be3..970ecc0e44531 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -463,23 +463,33 @@ fn thin_lto( info!("thin LTO data created"); - let (key_map_path, prev_key_map, curr_key_map) = if let Some(ref incr_comp_session_dir) = - cgcx.incr_comp_session_dir + let new_key_map_path = cgcx + .new_incr_comp_session_dir + .as_ref() + .map(|dir| dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME)); + + let (prev_key_map, curr_key_map) = if let Some(ref old_incr_comp_session_dir) = + cgcx.old_incr_comp_session_dir { - let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); + let old_path = old_incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); + // If the previous file was deleted, or we get an IO error // reading the file, then we'll just use `None` as the // prev_key_map, which will force the code to be recompiled. - let prev = - if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None }; + let prev = if old_path.exists() { + ThinLTOKeysMap::load_from_file(&old_path).ok() + } else { + None + }; let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names); - (Some(path), prev, curr) + + (prev, curr) } else { // If we don't compile incrementally, we don't need to load the // import data from LLVM. assert!(green_modules.is_empty()); let curr = ThinLTOKeysMap::default(); - (None, None, curr) + (None, curr) }; info!("thin LTO cache key map loaded"); info!("prev_key_map: {:#?}", prev_key_map); @@ -500,7 +510,8 @@ fn thin_lto( if let (Some(prev_key_map), true) = (prev_key_map.as_ref(), green_modules.contains_key(module_name)) { - assert!(cgcx.incr_comp_session_dir.is_some()); + assert!(cgcx.old_incr_comp_session_dir.is_some()); + assert!(cgcx.new_incr_comp_session_dir.is_some()); // If a module exists in both the current and the previous session, // and has the same LTO cache key in both sessions, then we can re-use it @@ -508,7 +519,6 @@ fn thin_lto( let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); - assert!(cgcx.incr_comp_session_dir.is_some()); continue; } } @@ -518,8 +528,8 @@ fn thin_lto( } // Save the current ThinLTO import information for the next compilation - // session, overwriting the previous serialized data (if any). - if let Some(path) = key_map_path + // session. + if let Some(path) = new_key_map_path && let Err(err) = curr_key_map.save_to_file(&path) { write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 2eaceb68a67a2..d994155e8fe59 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -16,7 +16,9 @@ use rustc_errors::{ }; use rustc_fs_util::link_or_copy; use rustc_hir::find_attr; -use rustc_incremental::{copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess}; +use rustc_incremental::{ + copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess, in_old_incr_comp_dir_sess, +}; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; @@ -354,9 +356,12 @@ pub struct CodegenContext { /// Directory into which should the LLVM optimization remarks be written. /// If `None`, they will be written to stderr. pub remark_dir: Option, + /// The previous incremental compilation session directory, or None if we + /// are not compiling incrementally or there is no previous session. + pub old_incr_comp_session_dir: Option, /// The incremental compilation session directory, or None if we are not /// compiling incrementally - pub incr_comp_session_dir: Option, + pub new_incr_comp_session_dir: Option, /// `Some(limit)` if the codegen should be run in parallel. /// /// Depends on [`WriteBackendMethods::supports_parallel()`] and `--jobs-backend`. @@ -503,7 +508,6 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( incr_comp_session.unwrap(), &module.name, files.as_slice(), - &module.links_from_incr_cache, ); work_products.insert(id, product); } @@ -845,7 +849,7 @@ fn execute_optimize_work_item( // save our module to disk first. let bitcode = if cgcx.module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); - cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) + cgcx.new_incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) } else { None }; @@ -892,11 +896,9 @@ fn execute_copy_from_cache_work_item( let dcx = DiagCtxt::new(Box::new(shared_emitter)); let dcx = dcx.handle(); - let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); - - let mut links_from_incr_cache = Vec::new(); + let incr_comp_session_dir = cgcx.old_incr_comp_session_dir.as_ref().unwrap(); - let mut load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { + let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { let source_file_in_incr_comp_dir = incr_comp_session_dir.join(saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", @@ -905,10 +907,7 @@ fn execute_copy_from_cache_work_item( output_path.display() ); match link_or_copy(&source_file_in_incr_comp_dir, &output_path) { - Ok(_) => { - links_from_incr_cache.push(source_file_in_incr_comp_dir); - Some(output_path) - } + Ok(_) => Some(output_path), Err(error) => { dcx.emit_err(diagnostics::CopyPathBuf { source_file: source_file_in_incr_comp_dir, @@ -931,7 +930,7 @@ fn execute_copy_from_cache_work_item( load_from_incr_comp_dir(dwarf_obj_out, saved_dwarf_object_file) }); - let mut load_from_incr_cache = |perform, output_type: OutputType| { + let load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name); @@ -959,7 +958,6 @@ fn execute_copy_from_cache_work_item( } CompiledModule { - links_from_incr_cache, kind: ModuleKind::Regular, name: module.name, object, @@ -1301,10 +1299,14 @@ fn start_executing_work( time_trace: sess.opts.unstable_opts.llvm_time_trace, remark: sess.opts.cg.remark.clone(), remark_dir, - incr_comp_session_dir: tcx + old_incr_comp_session_dir: tcx + .incr_comp_session + .as_ref() + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.clone()), + new_incr_comp_session_dir: tcx .incr_comp_session .as_ref() - .map(|incr_comp_session| incr_comp_session.session_directory.clone()), + .map(|incr_comp_session| incr_comp_session.new_session_directory.clone()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, @@ -2278,7 +2280,22 @@ pub(crate) fn submit_pre_lto_module_to_llvm( module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); + let old_bitcode_path = + in_old_incr_comp_dir_sess(tcx.incr_comp_session.unwrap(), &filename).unwrap(); let bitcode_path = in_incr_comp_dir_sess(tcx.incr_comp_session.unwrap(), &filename); + + match link_or_copy(&old_bitcode_path, &bitcode_path) { + Ok(_) => {} + Err(error) => { + tcx.sess.dcx().emit_err(diagnostics::CopyPathBuf { + source_file: old_bitcode_path, + output_path: bitcode_path, + error, + }); + return; + } + } + // Schedule the module to be loaded drop( coordinator diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9a42debe1dd97..54736b4550681 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -115,7 +115,6 @@ impl ModuleCodegen { bytecode, assembly, llvm_ir, - links_from_incr_cache: Vec::new(), } } } @@ -130,7 +129,6 @@ pub struct CompiledModule { pub bytecode: Option, pub assembly: Option, // --emit=asm pub llvm_ir: Option, // --emit=llvm-ir, llvm-bc is in bytecode - pub links_from_incr_cache: Vec, } impl CompiledModule { diff --git a/compiler/rustc_incremental/src/diagnostics.rs b/compiler/rustc_incremental/src/diagnostics.rs index 6e291b7ea3abb..b9ac4662dcbdc 100644 --- a/compiler/rustc_incremental/src/diagnostics.rs +++ b/compiler/rustc_incremental/src/diagnostics.rs @@ -169,21 +169,6 @@ pub(crate) struct DeleteLock<'a> { pub err: std::io::Error, } -#[derive(Diagnostic)] -#[diag( - "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" -)] -pub(crate) struct HardLinkFailed<'a> { - pub path: &'a Path, -} - -#[derive(Diagnostic)] -#[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] -pub(crate) struct DeletePartial<'a> { - pub path: &'a Path, - pub err: std::io::Error, -} - #[derive(Diagnostic)] #[diag("did not finalize incremental compilation session directory `{$path}`: {$err}")] #[help("the next build will not be able to reuse work from this compilation")] @@ -266,13 +251,6 @@ pub(crate) struct CopyWorkProductToCache<'a> { pub err: std::io::Error, } -#[derive(Diagnostic)] -#[diag("file-system error deleting outdated file `{$path}`: {$err}")] -pub(crate) struct DeleteWorkProduct<'a> { - pub path: &'a Path, - pub err: std::io::Error, -} - #[derive(Diagnostic)] #[diag( "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant" diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 83646cb086d8d..048fbe39b103a 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -11,7 +11,7 @@ mod persist; pub use persist::{ copy_cgu_workproduct_to_incr_comp_cache_dir, finalize_session_directory, in_incr_comp_dir_sess, - load_query_result_cache, save_work_product_index, setup_dep_graph, + in_old_incr_comp_dir_sess, load_query_result_cache, save_work_product_index, setup_dep_graph, }; use rustc_middle::util::Providers; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index de543ef0c53bc..6e035f1b0c0cc 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -110,11 +110,11 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use rand::{RngCore, rng}; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; -use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; +use rustc_fs_util::try_canonicalize; use rustc_middle::bug; use rustc_session::{IncrCompSession, Session, StableCrateId}; use rustc_span::Symbol; @@ -137,6 +137,11 @@ const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; // case-sensitive (as opposed to base64, for example). const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE; +/// Returns the path to a previous session's dependency graph. +pub(crate) fn old_dep_graph_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, DEP_GRAPH_FILENAME) +} + /// Returns the path to a session's dependency graph. pub(crate) fn dep_graph_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, DEP_GRAPH_FILENAME) @@ -150,10 +155,19 @@ pub(crate) fn staging_dep_graph_path(incr_comp_session: &IncrCompSession) -> Pat in_incr_comp_dir_sess(incr_comp_session, STAGING_DEP_GRAPH_FILENAME) } +pub(crate) fn old_work_products_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, WORK_PRODUCTS_FILENAME) +} + pub(crate) fn work_products_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, WORK_PRODUCTS_FILENAME) } +/// Returns the path to a previous session's query cache. +pub(crate) fn old_query_cache_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, QUERY_CACHE_FILENAME) +} + /// Returns the path to a session's query cache. pub(crate) fn query_cache_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, QUERY_CACHE_FILENAME) @@ -181,10 +195,19 @@ fn lock_file_path(session_dir: &Path) -> PathBuf { crate_dir.join(&directory_name[0..dash_indices[2]]).with_extension(&LOCK_FILE_EXT[1..]) } +/// Returns the path for a given filename within the incremental compilation directory +/// in the previous session. +pub fn in_old_incr_comp_dir_sess( + incr_comp_session: &IncrCompSession, + file_name: &str, +) -> Option { + incr_comp_session.old_session_directory.as_ref().map(|dir| dir.join(file_name)) +} + /// Returns the path for a given filename within the incremental compilation directory /// in the current session. pub fn in_incr_comp_dir_sess(incr_comp_session: &IncrCompSession, file_name: &str) -> PathBuf { - incr_comp_session.session_directory.join(file_name) + incr_comp_session.new_session_directory.join(file_name) } /// Allocates the private session directory. @@ -229,65 +252,40 @@ pub(crate) fn prepare_session_directory( } }; - let mut source_directories_already_tried = FxHashSet::default(); - - loop { - // Generate a session directory of the form: - // - // {incr-comp-dir}/{crate-name-and-disambiguator}/s-{timestamp}-{random}-working - let session_dir = generate_session_dir_path(&crate_dir); - debug!("session-dir: {}", session_dir.display()); - - // Lock the new session directory. If this fails, return an - // error without retrying - let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir); - - // Now that we have the lock, we can actually create the session - // directory - create_dir(sess, &session_dir, "session"); - - // Find a suitable source directory to copy from. Ignore those that we - // have already tried before. - let source_directory = find_source_directory(&crate_dir, &source_directories_already_tried); - - let Some(source_directory) = source_directory else { - // There's nowhere to copy from, we're done - debug!( - "no source directory found. Continuing with empty session \ - directory." - ); - - return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; - }; - - debug!("attempting to copy data from source: {}", source_directory.display()); - - // Try copying over all files from the source directory - if let Ok(allows_links) = copy_files(sess, &session_dir, &source_directory) { - debug!("successfully copied data from: {}", source_directory.display()); - - if !allows_links { - sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir }); - } - - return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; + // Generate a session directory of the form: + // + // {incr-comp-dir}/{crate-name-and-disambiguator}/s-{timestamp}-{random}-working + let session_dir = generate_session_dir_path(&crate_dir); + debug!("session-dir: {}", session_dir.display()); + + // Lock the new session directory. If this fails, return an + // error without retrying + let new_directory_lock = + lock_directory(sess, &session_dir, true).expect("should emit fatal error on lock fail"); + + // Now that we have the lock, we can actually create the session + // directory + create_dir(sess, &session_dir, "session"); + + // Find a suitable source directory to copy from. Ignore those that we + // have already tried before. + let source_directory = find_source_directory(sess, &crate_dir); + + let (old_session_directory, old_directory_lock) = + if let Some((source_directory, source_directory_lock)) = source_directory { + debug!("attempting to use: {}", source_directory.display()); + (Some(source_directory), Some(source_directory_lock)) } else { - debug!("copying failed - trying next directory"); - - // Something went wrong while trying to copy/link files from the - // source directory. Try again with a different one. - source_directories_already_tried.insert(source_directory); - - // Try to remove the session directory we just allocated. We don't - // know if there's any garbage in it from the failed copy action. - if let Err(err) = std_fs::remove_dir_all(&session_dir) { - sess.dcx().emit_warn(diagnostics::DeletePartial { path: &session_dir, err }); - } + debug!("no source directory found. Continuing with empty session directory."); + (None, None) + }; - delete_session_dir_lock_file(sess, &lock_file_path); - drop(directory_lock); - } - } + return IncrCompSession { + old_session_directory, + new_session_directory: session_dir, + _old_lock_file: old_directory_lock, + _lock_file: new_directory_lock, + }; } /// This function finalizes and thus 'publishes' the session directory by @@ -309,7 +307,7 @@ pub fn finalize_session_directory( let _timer = sess.timer("incr_comp_finalize_session_directory"); - let incr_comp_session_dir = incr_comp_session.session_directory.clone(); + let incr_comp_session_dir = incr_comp_session.new_session_directory.clone(); debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display()); @@ -350,71 +348,17 @@ pub fn finalize_session_directory( let _ = garbage_collect_session_directories(sess, &new_path); } -pub(crate) fn delete_all_session_dir_contents( - incr_comp_session: &IncrCompSession, +pub(crate) fn invalidate_old_session_dir( + incr_comp_session: &mut IncrCompSession, ) -> io::Result<()> { - let sess_dir_iterator = incr_comp_session.session_directory.read_dir()?; - for entry in sess_dir_iterator { - let entry = entry?; - safe_remove_file(&entry.path())? - } - Ok(()) -} - -fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result { - // We acquire a shared lock on the lock file of the directory, so that - // nobody deletes it out from under us while we are reading from it. - let lock_file_path = lock_file_path(source_dir); - - // not exclusive - let Ok(_lock) = flock::Lock::new( - &lock_file_path, - false, // don't wait, - false, // don't create - false, - ) else { - // Could not acquire the lock, don't try to copy from here - return Err(()); - }; - - let Ok(source_dir_iterator) = source_dir.read_dir() else { - return Err(()); - }; - - let mut files_linked = 0; - let mut files_copied = 0; - - for entry in source_dir_iterator { - match entry { - Ok(entry) => { - let file_name = entry.file_name(); - - let target_file_path = target_dir.join(file_name); - let source_path = entry.path(); - - debug!("copying into session dir: {}", source_path.display()); - match link_or_copy(source_path, target_file_path) { - Ok(LinkOrCopy::Link) => files_linked += 1, - Ok(LinkOrCopy::Copy) => files_copied += 1, - Err(_) => return Err(()), - } - } - Err(_) => return Err(()), + if let Some(old_incr_comp_session_dir) = incr_comp_session.old_session_directory.take() { + let sess_dir_iterator = old_incr_comp_session_dir.read_dir()?; + for entry in sess_dir_iterator { + let entry = entry?; + safe_remove_file(&entry.path())? } } - - if sess.opts.unstable_opts.incremental_info { - eprintln!( - "[incremental] session directory: \ - {files_linked} files hard-linked" - ); - eprintln!( - "[incremental] session directory: \ - {files_copied} files copied" - ); - } - - Ok(files_linked > 0 || files_copied == 0) + Ok(()) } /// Generates unique directory path of the form: @@ -448,7 +392,7 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { } /// Allocate the lock-file and lock it. -fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) { +fn lock_directory(sess: &Session, session_dir: &Path, fatal: bool) -> Option { let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); @@ -459,15 +403,21 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) true, ) { // the lock should be exclusive - Ok(lock) => (lock, lock_file_path), + Ok(lock) => Some(lock), Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - sess.dcx().emit_fatal(diagnostics::CreateLock { + let diag = diagnostics::CreateLock { lock_err, session_dir, is_unsupported_lock, is_cargo: rustc_session::utils::was_invoked_from_cargo(), - }); + }; + if fatal { + sess.dcx().emit_fatal(diag); + } else { + sess.dcx().emit_warn(diag); + None + } } } } @@ -478,24 +428,20 @@ fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) { } } -/// Finds the most recent published session directory that is not in the -/// ignore-list. -fn find_source_directory( - crate_dir: &Path, - source_directories_already_tried: &FxHashSet, -) -> Option { +/// Finds the most recent published session directory. +fn find_source_directory(sess: &Session, crate_dir: &Path) -> Option<(PathBuf, flock::Lock)> { let iter = crate_dir .read_dir() .unwrap() // FIXME .filter_map(|e| e.ok().map(|e| e.path())); - find_source_directory_in_iter(iter, source_directories_already_tried) + find_source_directory_in_iter(iter).and_then(|session_dir| { + let lock = lock_directory(sess, &session_dir, false)?; + Some((session_dir, lock)) + }) } -fn find_source_directory_in_iter( - iter: I, - source_directories_already_tried: &FxHashSet, -) -> Option +fn find_source_directory_in_iter(iter: I) -> Option where I: Iterator, { @@ -509,10 +455,7 @@ where continue; }; - if source_directories_already_tried.contains(&session_dir) - || !is_session_directory(&directory_name) - || !is_finalized(&directory_name) - { + if !is_session_directory(&directory_name) || !is_finalized(&directory_name) { debug!("find_source_directory_in_iter - ignoring"); continue; } diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 644b8187621c9..fc873b2b5bc5a 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -28,8 +28,6 @@ fn test_timestamp_serialization() { #[test] fn test_find_source_directory_in_iter() { - let already_visited = FxHashSet::default(); - // Find newest assert_eq!( find_source_directory_in_iter( @@ -39,7 +37,6 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-svh") ] .into_iter(), - &already_visited ), Some(PathBuf::from("crate-dir/s-3234-0000-svh")) ); @@ -53,13 +50,12 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-svh") ] .into_iter(), - &already_visited ), Some(PathBuf::from("crate-dir/s-2234-0000-svh")) ); // Handle empty - assert_eq!(find_source_directory_in_iter([].into_iter(), &already_visited), None); + assert_eq!(find_source_directory_in_iter([].into_iter()), None); // Handle only working assert_eq!( @@ -70,7 +66,6 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-working") ] .into_iter(), - &already_visited ), None ); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 3cf08961ed7b3..d00175774d1f1 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -16,8 +16,8 @@ use rustc_span::Symbol; use tracing::{debug, warn}; use super::data::*; +use super::file_format; use super::fs::*; -use super::{file_format, work_product}; use crate::diagnostics; use crate::persist::file_format::{OpenFile, OpenFileError}; @@ -32,15 +32,6 @@ enum LoadResult { IoError { path: PathBuf, err: io::Error }, } -fn delete_dirty_work_product( - sess: &Session, - incr_comp_session: &IncrCompSession, - swp: SerializedWorkProduct, -) { - debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(sess, incr_comp_session, &swp.work_product); -} - fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadResult { assert!(sess.opts.incremental.is_some()); @@ -48,12 +39,16 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. - let path = dep_graph_path(incr_comp_session); + let Some(path) = old_dep_graph_path(incr_comp_session) else { + return LoadResult::DataOutOfDate; + }; let expected_hash = sess.opts.dep_tracking_hash(false); let mut prev_work_products = UnordMap::default(); - let work_products_path = work_products_path(incr_comp_session); + let Some(work_products_path) = old_work_products_path(incr_comp_session) else { + return LoadResult::DataOutOfDate; + }; if let Ok(OpenFile { mmap, start_pos }) = file_format::open_incremental_file(sess, &work_products_path) @@ -68,7 +63,7 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe for swp in work_products { let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { - let exists = in_incr_comp_dir_sess(incr_comp_session, path).exists(); + let exists = in_old_incr_comp_dir_sess(incr_comp_session, path).unwrap().exists(); if !exists && sess.opts.unstable_opts.incremental_info { eprintln!("incremental: could not find file for work product: {path}",); } @@ -80,7 +75,7 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe prev_work_products.insert(swp.id, swp.work_product); } else { debug!("reconcile_work_products: some file for {:?} does not exist", swp); - delete_dirty_work_product(sess, incr_comp_session, swp); + return LoadResult::DataOutOfDate; } } } @@ -134,7 +129,9 @@ pub fn load_query_result_cache( let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); - let path = query_cache_path(incr_comp_session); + let Some(path) = old_query_cache_path(incr_comp_session) else { + return Some(OnDiskCache::new_empty()); + }; match file_format::open_incremental_file(sess, &path) { Ok(OpenFile { mmap, start_pos }) => { let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| { @@ -190,13 +187,13 @@ pub fn setup_dep_graph( } // `load_dep_graph` can only be called after `prepare_session_directory`. - let incr_comp_session = prepare_session_directory(sess, crate_name, stable_crate_id); + let mut incr_comp_session = prepare_session_directory(sess, crate_name, stable_crate_id); // Try to load the previous session's dep graph and work products. let load_result = load_dep_graph(sess, &incr_comp_session); sess.time("incr_comp_garbage_collect_session_directories", || { if let Err(e) = - garbage_collect_session_directories(sess, &incr_comp_session.session_directory) + garbage_collect_session_directories(sess, &incr_comp_session.new_session_directory) { warn!( "Error while trying to garbage collect incremental compilation \ @@ -211,10 +208,16 @@ pub fn setup_dep_graph( let (prev_graph, prev_work_products) = match load_result { LoadResult::IoError { path, err } => { sess.dcx().emit_warn(diagnostics::LoadDepGraph { path, err }); + if let Err(err) = invalidate_old_session_dir(&mut incr_comp_session) { + sess.dcx().emit_err(diagnostics::DeleteIncompatible { + path: dep_graph_path(&incr_comp_session), + err, + }); + } Default::default() } LoadResult::DataOutOfDate => { - if let Err(err) = delete_all_session_dir_contents(&incr_comp_session) { + if let Err(err) = invalidate_old_session_dir(&mut incr_comp_session) { sess.dcx().emit_err(diagnostics::DeleteIncompatible { path: dep_graph_path(&incr_comp_session), err, diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 7d486cc394b80..fb318357b26cb 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -10,7 +10,7 @@ mod load; mod save; mod work_product; -pub use fs::{finalize_session_directory, in_incr_comp_dir_sess}; +pub use fs::{finalize_session_directory, in_incr_comp_dir_sess, in_old_incr_comp_dir_sess}; pub use load::{load_query_result_cache, setup_dep_graph}; pub(crate) use save::save_dep_graph; pub use save::save_work_product_index; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 12f674fe2a859..46f47d6c8623c 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::data::*; use super::fs::*; -use super::{clean, file_format, work_product}; +use super::{clean, file_format}; use crate::assert_dep_graph::assert_dep_graph; use crate::diagnostics; @@ -112,23 +112,6 @@ pub fn save_work_product_index( e.finish() }); - // We also need to clean out old work-products, as not all of them are - // deleted during invalidation. Some object files don't change their - // content, they are just not needed anymore. - let previous_work_products = dep_graph.previous_work_products(); - for (id, wp) in previous_work_products.to_sorted_stable_ord() { - if !new_work_products.contains_key(id) { - work_product::delete_workproduct_files(sess, incr_comp_session.unwrap(), wp); - debug_assert!( - !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess( - incr_comp_session.unwrap(), - path - ) - .exists()) - ); - } - } - // Check that we did not delete one of the current work-products: debug_assert!({ new_work_products.items().all(|(_, wp)| { diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 7bb66fee4d1a3..0aaa9aa8e96c2 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -1,9 +1,8 @@ -//! Functions for saving and removing intermediate [work products]. +//! Function for saving intermediate [work products]. //! //! [work products]: WorkProduct -use std::fs as std_fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; @@ -23,7 +22,6 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( incr_comp_session: &IncrCompSession, cgu_name: &str, files: &[(&'static str, &Path)], - known_links: &[PathBuf], ) -> (WorkProductId, WorkProduct) { debug!(?cgu_name, ?files); assert!(sess.opts.incremental.is_some()); @@ -32,10 +30,6 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(incr_comp_session, &file_name); - if known_links.contains(&path_in_incr_dir) { - let _ = saved_files.insert(ext.to_string(), file_name); - continue; - } match link_or_copy(path, &path_in_incr_dir) { Ok(_) => { let _ = saved_files.insert(ext.to_string(), file_name); @@ -55,17 +49,3 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( let work_product_id = WorkProductId::from_cgu_name(cgu_name); (work_product_id, work_product) } - -/// Removes files for a given work product. -pub(crate) fn delete_workproduct_files( - sess: &Session, - incr_comp_session: &IncrCompSession, - work_product: &WorkProduct, -) { - for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { - let path = in_incr_comp_dir_sess(incr_comp_session, path); - if let Err(err) = std_fs::remove_file(&path) { - sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); - } - } -} diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 490888f87b38e..0c18a4443e5c5 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -101,7 +101,6 @@ impl Linker { incr_comp_session.as_ref().unwrap(), "metadata", &[("rmeta", path)], - &[], ); work_products.insert(id, product); } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b32a23f53f8cc..597b67703ab28 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2475,13 +2475,14 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // If the metadata dep-node is green, try to reuse the saved work product. if tcx.dep_graph.is_fully_enabled() + && let incr_comp_session = tcx.incr_comp_session.unwrap() + && let Some(old_incr_comp_session_dir) = &incr_comp_session.old_session_directory && let work_product_id = WorkProductId::from_cgu_name("metadata") && let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id) && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { let saved_path = &work_product.saved_files["rmeta"]; - let incr_comp_session_dir = &tcx.incr_comp_session.unwrap().session_directory; - let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path); + let source_file_in_incr_dir = &old_incr_comp_session_dir.join(saved_path); debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}"); match rustc_fs_util::link_or_copy(&source_file_in_incr_dir, path) { Ok(_) => {} diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 834424b8fbd84..fb00865fe2383 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1672,10 +1672,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) { /// Holds data on the current incremental compilation session, if there is one. pub struct IncrCompSession { - /// The directory containing all cached data. Cached data from a previous - /// session can be read out of it and new data for the current session will - /// be written into it. - pub session_directory: PathBuf, + /// The directory from which cached data of a previous session can be read. + pub old_session_directory: Option, + /// The directory to which cached data for the current session can be + /// written to. + pub new_session_directory: PathBuf, + /// `_old_lock_file` is never directly used, but its presence + /// alone has an effect, because the file will unlock when the session is + /// dropped. + pub _old_lock_file: Option, /// `_lock_file` is never directly used, but its presence /// alone has an effect, because the file will unlock when the session is /// dropped.