From faf5eac854f9099b54854145f2307cae3f3aa768 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Apr 2019 17:22:36 +1000 Subject: [PATCH 1/3] Move metadata encoding earlier. This commit separates metadata encoding (`tcx.encode_metadata`) from the creation of the metadata module (which is now handled by `write_compressed_metadata`, formerly `write_metadata`). The metadata encoding now occurs slightly earlier in the pipeline, at the very start of code generation within `start_codegen`. Metadata *writing* still occurs near the end of compilation; that will be moved forward in subsequent commits. --- src/librustc_codegen_llvm/base.rs | 40 ++-------------- src/librustc_codegen_llvm/lib.rs | 14 ++++-- src/librustc_codegen_ssa/base.rs | 48 ++++++++----------- src/librustc_codegen_ssa/traits/backend.rs | 7 +-- src/librustc_codegen_utils/codegen_backend.rs | 4 +- src/librustc_interface/passes.rs | 44 +++++++++++++++-- .../hotplug_codegen_backend/the_backend.rs | 4 +- 7 files changed, 85 insertions(+), 76 deletions(-) diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 7ea5e91230905..9077e89a4020e 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -28,7 +28,7 @@ use rustc::mir::mono::{Linkage, Visibility, Stats}; use rustc::middle::cstore::{EncodedMetadata}; use rustc::ty::TyCtxt; use rustc::middle::exported_symbols; -use rustc::session::config::{self, DebugInfo}; +use rustc::session::config::DebugInfo; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_data_structures::small_c_str::SmallCStr; @@ -42,47 +42,16 @@ use rustc::hir::CodegenFnAttrs; use crate::value::Value; - -pub fn write_metadata<'a, 'gcx>( +pub fn write_compressed_metadata<'a, 'gcx>( tcx: TyCtxt<'a, 'gcx, 'gcx>, + metadata: &EncodedMetadata, llvm_module: &mut ModuleLlvm -) -> EncodedMetadata { +) { use std::io::Write; use flate2::Compression; use flate2::write::DeflateEncoder; let (metadata_llcx, metadata_llmod) = (&*llvm_module.llcx, llvm_module.llmod()); - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed - } - - let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { - match *ty { - config::CrateType::Executable | - config::CrateType::Staticlib | - config::CrateType::Cdylib => MetadataKind::None, - - config::CrateType::Rlib => MetadataKind::Uncompressed, - - config::CrateType::Dylib | - config::CrateType::ProcMacro => MetadataKind::Compressed, - } - }).max().unwrap_or(MetadataKind::None); - - if kind == MetadataKind::None { - return EncodedMetadata::new(); - } - - let metadata = tcx.encode_metadata(); - if kind == MetadataKind::Uncompressed { - return metadata; - } - - assert!(kind == MetadataKind::Compressed); let mut compressed = tcx.metadata_encoding_version(); DeflateEncoder::new(&mut compressed, Compression::fast()) .write_all(&metadata.raw_data).unwrap(); @@ -107,7 +76,6 @@ pub fn write_metadata<'a, 'gcx>( let directive = CString::new(directive).unwrap(); llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) } - return metadata; } pub struct ValueIter<'ll> { diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 08424e7c3229a..09b284052b3c4 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -110,12 +110,13 @@ impl ExtraBackendMethods for LlvmCodegenBackend { ModuleLlvm::new_metadata(tcx, mod_name) } - fn write_metadata<'b, 'gcx>( + fn write_compressed_metadata<'b, 'gcx>( &self, tcx: TyCtxt<'b, 'gcx, 'gcx>, - metadata: &mut ModuleLlvm - ) -> EncodedMetadata { - base::write_metadata(tcx, metadata) + metadata: &EncodedMetadata, + llvm_module: &mut ModuleLlvm + ) { + base::write_compressed_metadata(tcx, metadata, llvm_module) } fn codegen_allocator<'b, 'gcx>( &self, @@ -289,9 +290,12 @@ impl CodegenBackend for LlvmCodegenBackend { fn codegen_crate<'b, 'tcx>( &self, tcx: TyCtxt<'b, 'tcx, 'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, rx: mpsc::Receiver> ) -> Box { - box rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, rx) + box rustc_codegen_ssa::base::codegen_crate( + LlvmCodegenBackend(()), tcx, metadata, need_metadata_module, rx) } fn join_codegen_and_link( diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 3046c069981cb..3cd47dfbb29fb 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -17,6 +17,7 @@ use crate::{ModuleCodegen, ModuleKind, CachedModuleCodegen}; use rustc::dep_graph::cgu_reuse_tracker::CguReuse; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::middle::cstore::EncodedMetadata; use rustc::middle::lang_items::StartFnLangItem; use rustc::middle::weak_lang_items; use rustc::mir::mono::{Stats, CodegenUnitNameBuilder}; @@ -25,7 +26,7 @@ use rustc::ty::layout::{self, Align, TyLayout, LayoutOf, VariantIdx, HasTyCtxt}; use rustc::ty::query::Providers; use rustc::middle::cstore::{self, LinkagePreference}; use rustc::util::common::{time, print_time_passes_entry}; -use rustc::session::config::{self, CrateType, EntryFnType, Lto}; +use rustc::session::config::{self, EntryFnType, Lto}; use rustc::session::Session; use rustc_mir::monomorphize::item::DefPathBasedNames; use rustc_mir::monomorphize::Instance; @@ -530,26 +531,13 @@ pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; pub fn codegen_crate( backend: B, tcx: TyCtxt<'a, 'tcx, 'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, rx: mpsc::Receiver> ) -> OngoingCodegen { check_for_rustc_errors_attr(tcx); - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - - // Codegen the metadata. - tcx.sess.profiler(|p| p.start_activity("codegen crate metadata")); - - let metadata_cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE, - &["crate"], - Some("metadata")).as_str() - .to_string(); - let mut metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name); - let metadata = time(tcx.sess, "write metadata", || { - backend.write_metadata(tcx, &mut metadata_llvm_module) - }); - tcx.sess.profiler(|p| p.end_activity("codegen crate metadata")); - // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { @@ -569,6 +557,8 @@ pub fn codegen_crate( return ongoing_codegen; } + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + // Run the monomorphization collector and partition the collected items into // codegen units. let codegen_units = tcx.collect_and_partition_mono_items(LOCAL_CRATE).1; @@ -632,17 +622,21 @@ pub fn codegen_crate( ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); } - let needs_metadata_module = tcx.sess.crate_types.borrow().iter().any(|ct| { - match *ct { - CrateType::Dylib | - CrateType::ProcMacro => true, - CrateType::Executable | - CrateType::Rlib | - CrateType::Staticlib | - CrateType::Cdylib => false, - } - }); - if needs_metadata_module { + if need_metadata_module { + // Codegen the encoded metadata. + tcx.sess.profiler(|p| p.start_activity("codegen crate metadata")); + + let metadata_cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE, + &["crate"], + Some("metadata")).as_str() + .to_string(); + let mut metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name); + time(tcx.sess, "write compressed metadata", || { + backend.write_compressed_metadata(tcx, &ongoing_codegen.metadata, + &mut metadata_llvm_module); + }); + tcx.sess.profiler(|p| p.end_activity("codegen crate metadata")); + let metadata_module = ModuleCodegen { name: metadata_cgu_name, module_llvm: metadata_llvm_module, diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index a9e0eadb198a8..530eba516a6c0 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -33,11 +33,12 @@ impl<'tcx, T> Backend<'tcx> for T where pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send { fn new_metadata(&self, sess: TyCtxt<'_, '_, '_>, mod_name: &str) -> Self::Module; - fn write_metadata<'b, 'gcx>( + fn write_compressed_metadata<'b, 'gcx>( &self, tcx: TyCtxt<'b, 'gcx, 'gcx>, - metadata: &mut Self::Module, - ) -> EncodedMetadata; + metadata: &EncodedMetadata, + llvm_module: &mut Self::Module, + ); fn codegen_allocator<'b, 'gcx>( &self, tcx: TyCtxt<'b, 'gcx, 'gcx>, diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs index 56eaffb1ca31d..191c6605b43ff 100644 --- a/src/librustc_codegen_utils/codegen_backend.rs +++ b/src/librustc_codegen_utils/codegen_backend.rs @@ -18,7 +18,7 @@ use rustc::util::common::ErrorReported; use rustc::session::config::{OutputFilenames, PrintRequest}; use rustc::ty::TyCtxt; use rustc::ty::query::Providers; -use rustc::middle::cstore::MetadataLoader; +use rustc::middle::cstore::{EncodedMetadata, MetadataLoader}; use rustc::dep_graph::DepGraph; pub use rustc_data_structures::sync::MetadataRef; @@ -37,6 +37,8 @@ pub trait CodegenBackend { fn codegen_crate<'a, 'tcx>( &self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, rx: mpsc::Receiver> ) -> Box; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index f8b1271b8b5c6..38d641d2f6076 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -16,7 +16,7 @@ use rustc::traits; use rustc::util::common::{time, ErrorReported}; use rustc::util::profiling::ProfileCategory; use rustc::session::{CompileResult, CrateDisambiguator, Session}; -use rustc::session::config::{self, Input, OutputFilenames, OutputType}; +use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc_allocator as allocator; use rustc_borrowck as borrowck; @@ -999,6 +999,38 @@ fn analysis<'tcx>( Ok(()) } +fn encode_metadata<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> (middle::cstore::EncodedMetadata, bool) { + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed + } + + let metadata_kind = tcx.sess.crate_types.borrow().iter().map(|ty| { + match *ty { + CrateType::Executable | + CrateType::Staticlib | + CrateType::Cdylib => MetadataKind::None, + + CrateType::Rlib => MetadataKind::Uncompressed, + + CrateType::Dylib | + CrateType::ProcMacro => MetadataKind::Compressed, + } + }).max().unwrap_or(MetadataKind::None); + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + + let metadata = match metadata_kind { + MetadataKind::None => middle::cstore::EncodedMetadata::new(), + MetadataKind::Uncompressed | + MetadataKind::Compressed => tcx.encode_metadata(), + }; + + (metadata, need_metadata_module) +} + /// Runs the codegen backend, after which the AST and analysis can /// be discarded. pub fn start_codegen<'tcx>( @@ -1013,11 +1045,17 @@ pub fn start_codegen<'tcx>( } time(tcx.sess, "resolving dependency formats", || { - ::rustc::middle::dependency_format::calculate(tcx) + middle::dependency_format::calculate(tcx) + }); + + let (metadata, need_metadata_module) = time(tcx.sess, "metadata encoding", || { + encode_metadata(tcx) }); tcx.sess.profiler(|p| p.start_activity("codegen crate")); - let codegen = time(tcx.sess, "codegen", move || codegen_backend.codegen_crate(tcx, rx)); + let codegen = time(tcx.sess, "codegen", move || { + codegen_backend.codegen_crate(tcx, metadata, need_metadata_module, rx) + }); tcx.sess.profiler(|p| p.end_activity("codegen crate")); if log_enabled!(::log::Level::Info) { diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 5330470da16b0..4e43aa96e1d85 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -15,7 +15,7 @@ use rustc::session::Session; use rustc::session::config::OutputFilenames; use rustc::ty::TyCtxt; use rustc::ty::query::Providers; -use rustc::middle::cstore::MetadataLoader; +use rustc::middle::cstore::{EncodedMetadata, MetadataLoader}; use rustc::dep_graph::DepGraph; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; @@ -61,6 +61,8 @@ impl CodegenBackend for TheBackend { fn codegen_crate<'a, 'tcx>( &self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + _metadata: EncodedMetadata, + _need_metadata_module: bool, _rx: mpsc::Receiver> ) -> Box { use rustc::hir::def_id::LOCAL_CRATE; From 3da5d4a87e4fd3a96cf7e962211c875fe50650d4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 30 Apr 2019 15:52:16 +1000 Subject: [PATCH 2/3] Inline and remove `link_binary_output`. This change simplifies things for the subsequent commit. --- src/librustc_codegen_ssa/back/link.rs | 135 ++++++++++++-------------- 1 file changed, 61 insertions(+), 74 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 4cae20b698a1c..f80618441054a 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -63,12 +63,66 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, bug!("invalid output type `{:?}` for target os `{}`", crate_type, sess.opts.target_triple); } - link_binary_output::(sess, - codegen_results, - crate_type, - outputs, - crate_name, - target_cpu); + + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + check_file_is_writeable(obj, sess); + } + + if outputs.outputs.contains_key(&OutputType::Metadata) { + let out_filename = filename_for_metadata(sess, crate_name, outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with a `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = TempFileBuilder::new() + .prefix("rmeta") + .tempdir_in(out_filename.parent().unwrap()) + .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); + match fs::rename(&metadata, &out_filename) { + Ok(_) => { + if sess.opts.debugging_opts.emit_directives { + sess.parse_sess.span_diagnostic.maybe_emit_json_directive( + format!("metadata file written: {}", out_filename.display())); + } + } + Err(e) => sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)), + } + } + + 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 out_filename = out_filename(sess, crate_type, outputs, crate_name); + match crate_type { + config::CrateType::Rlib => { + link_rlib::(sess, + codegen_results, + RlibFlavor::Normal, + &out_filename, + &tmpdir).build(); + } + config::CrateType::Staticlib => { + link_staticlib::(sess, codegen_results, &out_filename, &tmpdir); + } + _ => { + link_natively::( + sess, + crate_type, + &out_filename, + codegen_results, + tmpdir.path(), + target_cpu, + ); + } + } + } + + if sess.opts.cg.save_temps { + let _ = tmpdir.into_path(); + } } // Remove the temporary object file and metadata if we aren't saving temps @@ -85,7 +139,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, if let Some(ref obj) = metadata_module.object { remove(sess, obj); } - } + } if let Some(ref allocator_module) = codegen_results.allocator_module { if let Some(ref obj) = allocator_module.object { remove(sess, obj); @@ -97,73 +151,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, } } -fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, - codegen_results: &CodegenResults, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str, - target_cpu: &str) { - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - check_file_is_writeable(obj, sess); - } - - if outputs.outputs.contains_key(&OutputType::Metadata) { - let out_filename = filename_for_metadata(sess, crate_name, outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with a `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() - .prefix("rmeta") - .tempdir_in(out_filename.parent().unwrap()) - .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); - let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); - match fs::rename(&metadata, &out_filename) { - Ok(_) => { - if sess.opts.debugging_opts.emit_directives { - sess.parse_sess.span_diagnostic.maybe_emit_json_directive( - format!("metadata file written: {}", out_filename.display())); - } - } - Err(e) => sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)), - } - } - - 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 out_filename = out_filename(sess, crate_type, outputs, crate_name); - match crate_type { - config::CrateType::Rlib => { - link_rlib::(sess, - codegen_results, - RlibFlavor::Normal, - &out_filename, - &tmpdir).build(); - } - config::CrateType::Staticlib => { - link_staticlib::(sess, codegen_results, &out_filename, &tmpdir); - } - _ => { - link_natively::( - sess, - crate_type, - &out_filename, - codegen_results, - tmpdir.path(), - target_cpu, - ); - } - } - } - - if sess.opts.cg.save_temps { - let _ = tmpdir.into_path(); - } -} - // The third parameter is for env vars, used on windows to set up the // path for MSVC to find its DLLs, and gcc to find its bundled // toolchain From 38dffeba21842adc9deb647b30f3a4a00abca133 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 30 Apr 2019 15:53:30 +1000 Subject: [PATCH 3/3] Move metadata writing earlier. The commit moves metadata writing from `link_binary` to `encode_metadata` (and renames the latter as `encode_and_write_metadata`). This is at the very start of code generation. --- Cargo.lock | 2 ++ src/librustc_codegen_ssa/back/link.rs | 35 ++++------------------ src/librustc_interface/Cargo.toml | 2 ++ src/librustc_interface/passes.rs | 43 +++++++++++++++++++++++---- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeddf35eb0dd9..a7b8447ef356a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2802,6 +2802,7 @@ dependencies = [ "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_borrowck 0.0.0", + "rustc_codegen_ssa 0.0.0", "rustc_codegen_utils 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -2821,6 +2822,7 @@ dependencies = [ "syntax 0.0.0", "syntax_ext 0.0.0", "syntax_pos 0.0.0", + "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index f80618441054a..f25891d77ce53 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -7,7 +7,7 @@ use rustc::session::config::{ }; use rustc::session::search_paths::PathKind; use rustc::middle::dependency_format::Linkage; -use rustc::middle::cstore::{LibSource, NativeLibrary, NativeLibraryKind}; +use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind}; use rustc::util::common::{time, time_ext}; use rustc::hir::def_id::CrateNum; use rustc_data_structures::fx::FxHashSet; @@ -50,9 +50,9 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, outputs: &OutputFilenames, crate_name: &str, target_cpu: &str) { + let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); for &crate_type in sess.crate_types.borrow().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. - let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) && !output_metadata && crate_type == config::CrateType::Executable { @@ -68,29 +68,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, check_file_is_writeable(obj, sess); } - if outputs.outputs.contains_key(&OutputType::Metadata) { - let out_filename = filename_for_metadata(sess, crate_name, outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with a `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() - .prefix("rmeta") - .tempdir_in(out_filename.parent().unwrap()) - .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); - let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); - match fs::rename(&metadata, &out_filename) { - Ok(_) => { - if sess.opts.debugging_opts.emit_directives { - sess.parse_sess.span_diagnostic.maybe_emit_json_directive( - format!("metadata file written: {}", out_filename.display())); - } - } - Err(e) => sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)), - } - } - let tmpdir = TempFileBuilder::new().prefix("rustc").tempdir().unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); @@ -248,13 +225,13 @@ pub fn each_linked_rlib(sess: &Session, /// 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. -fn emit_metadata<'a>( +pub fn emit_metadata<'a>( sess: &'a Session, - codegen_results: &CodegenResults, + metadata: &EncodedMetadata, tmpdir: &TempDir ) -> PathBuf { let out_filename = tmpdir.path().join(METADATA_FILENAME); - let result = fs::write(&out_filename, &codegen_results.metadata.raw_data); + let result = fs::write(&out_filename, &metadata.raw_data); if let Err(e) = result { sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); @@ -338,7 +315,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, RlibFlavor::Normal => { // Instead of putting the metadata in an object file section, rlibs // contain the metadata in a separate file. - ab.add_file(&emit_metadata(sess, codegen_results, tmpdir)); + ab.add_file(&emit_metadata(sess, &codegen_results.metadata, tmpdir)); // For LTO purposes, the bytecode of this library is also inserted // into the archive. diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index fa2a5d2fc89bc..bcaa4216109aa 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -24,6 +24,7 @@ rustc_borrowck = { path = "../librustc_borrowck" } rustc_incremental = { path = "../librustc_incremental" } rustc_traits = { path = "../librustc_traits" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } @@ -34,3 +35,4 @@ rustc_errors = { path = "../librustc_errors" } rustc_plugin = { path = "../librustc_plugin" } rustc_privacy = { path = "../librustc_privacy" } rustc_resolve = { path = "../librustc_resolve" } +tempfile = "3.0.5" diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 38d641d2f6076..6d3115c621343 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -20,7 +20,9 @@ use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType use rustc::session::search_paths::PathKind; use rustc_allocator as allocator; use rustc_borrowck as borrowck; +use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_codegen_utils::link::filename_for_metadata; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::StableHasher; @@ -50,6 +52,7 @@ use syntax_pos::{FileName, hygiene}; use syntax_ext; use serialize::json; +use tempfile::Builder as TempFileBuilder; use std::any::Any; use std::env; @@ -999,7 +1002,10 @@ fn analysis<'tcx>( Ok(()) } -fn encode_metadata<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> (middle::cstore::EncodedMetadata, bool) { +fn encode_and_write_metadata<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + outputs: &OutputFilenames, +) -> (middle::cstore::EncodedMetadata, bool) { #[derive(PartialEq, Eq, PartialOrd, Ord)] enum MetadataKind { None, @@ -1020,14 +1026,41 @@ fn encode_metadata<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> (middle::cstore::Encode } }).max().unwrap_or(MetadataKind::None); - let need_metadata_module = metadata_kind == MetadataKind::Compressed; - let metadata = match metadata_kind { MetadataKind::None => middle::cstore::EncodedMetadata::new(), MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(), }; + let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); + if need_metadata_file { + let crate_name = &tcx.crate_name(LOCAL_CRATE).as_str(); + let out_filename = filename_for_metadata(tcx.sess, crate_name, outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with an `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = TempFileBuilder::new() + .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_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir); + match std::fs::rename(&metadata_filename, &out_filename) { + Ok(_) => { + if tcx.sess.opts.debugging_opts.emit_directives { + tcx.sess.parse_sess.span_diagnostic.maybe_emit_json_directive( + format!("metadata file written: {}", out_filename.display())); + } + } + Err(e) => tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)), + } + } + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + (metadata, need_metadata_module) } @@ -1048,8 +1081,8 @@ pub fn start_codegen<'tcx>( middle::dependency_format::calculate(tcx) }); - let (metadata, need_metadata_module) = time(tcx.sess, "metadata encoding", || { - encode_metadata(tcx) + let (metadata, need_metadata_module) = time(tcx.sess, "metadata encoding and writing", || { + encode_and_write_metadata(tcx, outputs) }); tcx.sess.profiler(|p| p.start_activity("codegen crate"));