diff options
| author | Yoshiki Matsuda <myskjp@gmail.com> | 2022-04-24 23:49:04 +0900 |
|---|---|---|
| committer | Yoshiki Matsuda <myskjp@gmail.com> | 2022-07-02 22:51:40 +0900 |
| commit | 5d9ba49bb9ca0e8c2e563ca499a5e54e90c9fb88 (patch) | |
| tree | b4f690f4d2d58ca5246de185b8c2d4c0bb8f353d /compiler/rustc_metadata/src | |
| parent | 709a78226b12025385976bdafa6d7793ec8a4a5b (diff) | |
move encode_and_write_metadata to rustc_metadata::fs
Diffstat (limited to 'compiler/rustc_metadata/src')
| -rw-r--r-- | compiler/rustc_metadata/src/fs.rs | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index f57ace6f28a..b66f37d5d61 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,8 +1,15 @@ +use crate::{encode_metadata, EncodedMetadata}; + use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, OutputFilenames, OutputType}; +use rustc_session::output::filename_for_metadata; use rustc_session::Session; +use tempfile::Builder as TempFileBuilder; use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; // FIXME(eddyb) maybe include the crate name in this? pub const METADATA_FILENAME: &str = "lib.rmeta"; @@ -22,3 +29,84 @@ pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> out_filename } + +pub fn encode_and_write_metadata( + tcx: TyCtxt<'_>, + outputs: &OutputFilenames, +) -> (EncodedMetadata, bool) { + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed, + } + + let metadata_kind = tcx + .sess + .crate_types() + .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 metadata = match metadata_kind { + MetadataKind::None => EncodedMetadata::new(), + MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx), + }; + + let _prof_timer = tcx.sess.prof.generic_activity("write_crate_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); + let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), 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_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); + let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir); + if let Err(e) = non_durable_rename(&metadata_filename, &out_filename) { + tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + if tcx.sess.opts.json_artifact_notifications { + tcx.sess + .parse_sess + .span_diagnostic + .emit_artifact_notification(&out_filename, "metadata"); + } + } + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + + (metadata, need_metadata_module) +} + +#[cfg(not(target_os = "linux"))] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + std::fs::rename(src, dst) +} + +/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems +/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" +/// write back the source file before committing the rename in case a developer forgot some of +/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. +/// +/// To avoid triggering this heuristic we delete the destination first, if it exists. +/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. +#[cfg(target_os = "linux")] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + let _ = std::fs::remove_file(dst); + std::fs::rename(src, dst) +} |
