From 2627eedde97e3e94e786faf8dfe612d65d8a6fa6 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 8 Aug 2020 21:05:50 -0400 Subject: [PATCH] Avoid deleting temporary files on error Previously if the compiler error'd, fatally, then temporary directories which should be preserved by -Csave-temps would be deleted due to fatal compiler errors being implemented as panics. --- Cargo.lock | 1 + src/librustc_codegen_ssa/back/link.rs | 39 +++++++++--------------- src/librustc_data_structures/Cargo.toml | 1 + src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/temp_dir.rs | 34 +++++++++++++++++++++ src/librustc_interface/passes.rs | 2 ++ 6 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 src/librustc_data_structures/temp_dir.rs diff --git a/Cargo.lock b/Cargo.lock index a3b37465f1a7d..684cd82c83730 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3363,6 +3363,7 @@ dependencies = [ "smallvec 1.4.0", "stable_deref_trait", "stacker", + "tempfile", "tracing", "winapi 0.3.8", ] diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9191c68d4537a..e06edb2fe6ccc 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1,4 +1,5 @@ use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib}; @@ -23,7 +24,7 @@ use super::rpath::{self, RPathConfig}; use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME}; use cc::windows_registry; -use tempfile::{Builder as TempFileBuilder, TempDir}; +use tempfile::Builder as TempFileBuilder; use std::ffi::OsString; use std::path::{Path, PathBuf}; @@ -70,27 +71,21 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( } }); - let tmpdir = TempFileBuilder::new() - .prefix("rustc") - .tempdir() - .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); - if outputs.outputs.should_codegen() { + let tmpdir = TempFileBuilder::new() + .prefix("rustc") + .tempdir() + .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let out_filename = out_filename(sess, crate_type, outputs, crate_name); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); - link_rlib::( - sess, - codegen_results, - RlibFlavor::Normal, - &out_filename, - &tmpdir, - ) - .build(); + link_rlib::(sess, codegen_results, RlibFlavor::Normal, &out_filename, &path) + .build(); } CrateType::Staticlib => { - link_staticlib::(sess, codegen_results, &out_filename, &tmpdir); + link_staticlib::(sess, codegen_results, &out_filename, &path); } _ => { link_natively::( @@ -98,7 +93,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( crate_type, &out_filename, codegen_results, - tmpdir.path(), + path.as_ref(), target_cpu, ); } @@ -107,10 +102,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link"); } } - - if sess.opts.cg.save_temps { - let _ = tmpdir.into_path(); - } } // Remove the temporary object file and metadata if we aren't saving temps @@ -279,8 +270,8 @@ pub fn each_linked_rlib( /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a /// directory being searched for `extern crate` (observing an incomplete file). /// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &TempDir) -> PathBuf { - let out_filename = tmpdir.path().join(METADATA_FILENAME); +pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &MaybeTempDir) -> PathBuf { + let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); let result = fs::write(&out_filename, &metadata.raw_data); if let Err(e) = result { @@ -301,7 +292,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( codegen_results: &CodegenResults, flavor: RlibFlavor, out_filename: &Path, - tmpdir: &TempDir, + tmpdir: &MaybeTempDir, ) -> B { info!("preparing rlib to {:?}", out_filename); let mut ab = ::new(sess, out_filename, None); @@ -406,7 +397,7 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, out_filename: &Path, - tempdir: &TempDir, + tempdir: &MaybeTempDir, ) { let mut ab = link_rlib::(sess, codegen_results, RlibFlavor::StaticlibBase, out_filename, tempdir); diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 811d1e49626c4..65812cc4e687d 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -30,6 +30,7 @@ bitflags = "1.2.1" measureme = "0.7.1" libc = "0.2" stacker = "0.1.9" +tempfile = "3.0.5" [dependencies.parking_lot] version = "0.10" diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 0b2e7cda1b4cc..3884fc051056e 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -95,6 +95,7 @@ pub mod vec_linked_list; pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; +pub mod temp_dir; pub struct OnDrop(pub F); diff --git a/src/librustc_data_structures/temp_dir.rs b/src/librustc_data_structures/temp_dir.rs new file mode 100644 index 0000000000000..0d9b3e3ca25c0 --- /dev/null +++ b/src/librustc_data_structures/temp_dir.rs @@ -0,0 +1,34 @@ +use std::mem::ManuallyDrop; +use std::path::Path; +use tempfile::TempDir; + +/// This is used to avoid TempDir being dropped on error paths unintentionally. +#[derive(Debug)] +pub struct MaybeTempDir { + dir: ManuallyDrop, + // Whether the TempDir should be deleted on drop. + keep: bool, +} + +impl Drop for MaybeTempDir { + fn drop(&mut self) { + // Safety: We are in the destructor, and no further access will + // occur. + let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; + if self.keep { + dir.into_path(); + } + } +} + +impl AsRef for MaybeTempDir { + fn as_ref(&self) -> &Path { + self.dir.path() + } +} + +impl MaybeTempDir { + pub fn new(dir: TempDir, keep_on_drop: bool) -> MaybeTempDir { + MaybeTempDir { dir: ManuallyDrop::new(dir), keep: keep_on_drop } + } +} diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 6c0343330c8c9..701fca8e4b534 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -9,6 +9,7 @@ use rustc_ast::{self, ast, visit}; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal}; +use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; use rustc_errors::{ErrorReported, PResult}; use rustc_expand::base::ExtCtxt; @@ -974,6 +975,7 @@ fn encode_and_write_metadata( .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap()) .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir); if let Err(e) = fs::rename(&metadata_filename, &out_filename) { tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));