diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 08eb2280fdb52..31a53a108fecb 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1940,7 +1940,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { /// Returns a pair of boolean indicating whether we should preserve the object and /// dwarf object files on the filesystem for their debug information. This is often /// useful with split-dwarf like schemes. -fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { +pub(crate) fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { // If the objects don't have debuginfo there's nothing to preserve. if sess.opts.debuginfo == config::DebugInfo::None { return (false, false); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index e8b25eb4359d0..c0d0316760286 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -22,7 +22,8 @@ use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ - self, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, + self, DWARF_OBJECT_EXT, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, + SwitchWithOptPath, }; use rustc_session::{IncrCompSession, Session}; use rustc_span::source_map::SourceMap; @@ -31,7 +32,7 @@ use rustc_structures::CrateType; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; -use crate::back::link::ensure_removed; +use crate::back::link::{ensure_removed, preserve_objects_for_their_debuginfo}; use crate::back::lto::{self, SerializedModule, check_lto_allowed}; use crate::diagnostics::ErrorCreatingRemarkDir; use crate::traits::*; @@ -463,6 +464,7 @@ pub(crate) fn start_async_codegen( fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( sess: &Session, incr_comp_session: Option<&IncrCompSession>, + output_filenames: &OutputFilenames, compiled_modules: &CompiledModules, ) -> WorkProductMap { let mut work_products = WorkProductMap::default(); @@ -474,16 +476,36 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); + let (preserved_objects, preserved_dwarf_objects) = + if sess.opts.cg.save_temps || !sess.opts.output_types.should_link() { + // With `-Csave-temps` the user wants these files kept, so don't schedule their removal. + // Without a linked output the objects are the requested outputs themselves rather than + // files kept for the debuginfo of a linked artifact. + (false, false) + } else { + preserve_objects_for_their_debuginfo(sess) + }; + for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { let mut files = Vec::new(); + let mut preserved_debuginfo_exts = Vec::new(); if let Some(object_file_path) = &module.object { files.push((OutputType::Object.extension(), object_file_path.as_path())); + if preserved_objects { + preserved_debuginfo_exts.push(OutputType::Object.extension()); + } } if let Some(global_asm_object_file_path) = &module.global_asm_object { files.push(("asm.o", global_asm_object_file_path.as_path())); + if preserved_objects { + preserved_debuginfo_exts.push("asm.o"); + } } if let Some(dwarf_object_file_path) = &module.dwarf_object { - files.push(("dwo", dwarf_object_file_path.as_path())); + files.push((DWARF_OBJECT_EXT, dwarf_object_file_path.as_path())); + if preserved_dwarf_objects { + preserved_debuginfo_exts.push(DWARF_OBJECT_EXT); + } } if let Some(path) = &module.assembly { files.push((OutputType::Assembly.extension(), path.as_path())); @@ -500,6 +522,8 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( &module.name, files.as_slice(), &module.links_from_incr_cache, + output_filenames.invocation_temp.as_deref(), + &preserved_debuginfo_exts, ); work_products.insert(id, product); } @@ -2221,6 +2245,7 @@ impl OngoingCodegen { let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( sess, incr_comp_session, + &self.output_filenames, &compiled_modules, ); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 12f674fe2a859..cf24cdb9d9993 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -6,6 +6,7 @@ use rustc_middle::query::on_disk_cache; use rustc_middle::ty::TyCtxt; use rustc_serialize::Encodable as RustcEncodable; use rustc_serialize::opaque::FileEncoder; +use rustc_session::config::OutputFilenames; use rustc_session::{IncrCompSession, Session}; use tracing::debug; @@ -94,6 +95,7 @@ pub fn save_work_product_index( sess: &Session, incr_comp_session: Option<&IncrCompSession>, dep_graph: &DepGraph, + output_filenames: &OutputFilenames, new_work_products: WorkProductMap, ) { if sess.opts.incremental.is_none() { @@ -115,6 +117,9 @@ pub fn save_work_product_index( // 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. + // + // The same goes for files the previous session left in the output directory + // for debuginfo: this session replaces the artifact that referred to them. 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) { @@ -127,6 +132,7 @@ pub fn save_work_product_index( .exists()) ); } + work_product::delete_preserved_debuginfo_files(sess, output_filenames, wp); } // Check that we did not delete one of the current work-products: diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 7bb66fee4d1a3..a6438f699e1af 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -2,12 +2,13 @@ //! //! [work products]: WorkProduct -use std::fs as std_fs; use std::path::{Path, PathBuf}; +use std::{fs as std_fs, io}; use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_session::config::{DWARF_OBJECT_EXT, OutputFilenames}; use rustc_session::{IncrCompSession, Session}; use tracing::debug; @@ -24,6 +25,8 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( cgu_name: &str, files: &[(&'static str, &Path)], known_links: &[PathBuf], + invocation_temp: Option<&str>, + preserved_debuginfo_extensions: &[&str], ) -> (WorkProductId, WorkProduct) { debug!(?cgu_name, ?files); assert!(sess.opts.incremental.is_some()); @@ -50,12 +53,49 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( } } - let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files }; + let work_product = WorkProduct { + cgu_name: cgu_name.to_string(), + saved_files, + invocation_temp: invocation_temp.map(String::from), + preserved_debuginfo_extensions: preserved_debuginfo_extensions + .iter() + .map(|s| s.to_string()) + .collect(), + }; debug!(?work_product); let work_product_id = WorkProductId::from_cgu_name(cgu_name); (work_product_id, work_product) } +/// Removes the temporaries a previous session's work product left in the output directory for +/// debuginfo. Their paths are derived from this session's output settings, so only the +/// directories this session writes to are touched. Files that are already gone are not an error. +pub(crate) fn delete_preserved_debuginfo_files( + sess: &Session, + output_filenames: &OutputFilenames, + work_product: &WorkProduct, +) { + // Files with this session's own invocation string are its own temporaries. + if output_filenames.invocation_temp == work_product.invocation_temp { + return; + } + + let mut previous_output_filenames = output_filenames.clone(); + previous_output_filenames.invocation_temp = work_product.invocation_temp.clone(); + for ext in &work_product.preserved_debuginfo_extensions { + let path = if ext == DWARF_OBJECT_EXT { + previous_output_filenames.temp_path_dwo_for_cgu(&work_product.cgu_name) + } else { + previous_output_filenames.temp_path_ext_for_cgu(ext, &work_product.cgu_name) + }; + if let Err(err) = std_fs::remove_file(&path) + && err.kind() != io::ErrorKind::NotFound + { + sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); + } + } +} + /// Removes files for a given work product. pub(crate) fn delete_workproduct_files( sess: &Session, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 51e26d4ba5044..1bc4856c64f48 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -102,6 +102,8 @@ impl Linker { "metadata", &[("rmeta", path)], &[], + None, + &[], ); work_products.insert(id, product); } @@ -117,6 +119,7 @@ impl Linker { sess, incr_comp_session.as_ref(), &self.dep_graph, + &self.output_filenames, work_products, ) }); diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index a4165d793069d..637255bd0cf50 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -1141,6 +1141,12 @@ pub struct WorkProduct { /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to /// the object file's path, and "dwo" to the dwarf object file's path. pub saved_files: UnordMap, + /// The per-invocation string that is part of the temporaries' file names. + pub invocation_temp: Option, + /// The extensions of temporaries this CGU left in the output directory + /// after linking because the linked artifact's debuginfo refers to them. A + /// later session deletes them once it has replaced that artifact. + pub preserved_debuginfo_extensions: Vec, } pub type WorkProductMap = UnordMap; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2ae9dfdc9c2ad..28adc7e2a3990 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1201,7 +1201,7 @@ pub struct OutputFilenames { /// hard linked. // This does not affect incr comp outputs, only where temp files are stored. #[stable_hash(ignore)] - invocation_temp: Option, + pub invocation_temp: Option, explicit_dwo_out_directory: Option, pub outputs: OutputTypes,