2 FailedCreateEncodedMetadata
, FailedCreateFile
, FailedCreateTempdir
, FailedWriteError
,
4 use crate::{encode_metadata, EncodedMetadata}
;
6 use rustc_data_structures
::temp_dir
::MaybeTempDir
;
7 use rustc_hir
::def_id
::LOCAL_CRATE
;
8 use rustc_middle
::ty
::TyCtxt
;
9 use rustc_session
::config
::{CrateType, OutputType}
;
10 use rustc_session
::output
::filename_for_metadata
;
11 use rustc_session
::Session
;
12 use tempfile
::Builder
as TempFileBuilder
;
15 use std
::path
::{Path, PathBuf}
;
17 // FIXME(eddyb) maybe include the crate name in this?
18 pub const METADATA_FILENAME
: &str = "lib.rmeta";
20 /// We use a temp directory here to avoid races between concurrent rustc processes,
21 /// such as builds in the same directory using the same filename for metadata while
22 /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
23 /// directory being searched for `extern crate` (observing an incomplete file).
24 /// The returned path is the temporary file containing the complete metadata.
25 pub fn emit_wrapper_file(
28 tmpdir
: &MaybeTempDir
,
31 let out_filename
= tmpdir
.as_ref().join(name
);
32 let result
= fs
::write(&out_filename
, data
);
34 if let Err(err
) = result
{
35 sess
.emit_fatal(FailedWriteError { filename: out_filename, err }
);
41 pub fn encode_and_write_metadata(tcx
: TyCtxt
<'_
>) -> (EncodedMetadata
, bool
) {
42 #[derive(PartialEq, Eq, PartialOrd, Ord)]
49 let metadata_kind
= tcx
54 CrateType
::Executable
| CrateType
::Staticlib
| CrateType
::Cdylib
=> MetadataKind
::None
,
56 CrateType
::Rlib
=> MetadataKind
::Uncompressed
,
58 CrateType
::Dylib
| CrateType
::ProcMacro
=> MetadataKind
::Compressed
,
61 .unwrap_or(MetadataKind
::None
);
63 let crate_name
= tcx
.crate_name(LOCAL_CRATE
);
64 let out_filename
= filename_for_metadata(tcx
.sess
, crate_name
, tcx
.output_filenames(()));
65 // To avoid races with another rustc process scanning the output directory,
66 // we need to write the file somewhere else and atomically move it to its
67 // final destination, with an `fs::rename` call. In order for the rename to
68 // always succeed, the temporary file needs to be on the same filesystem,
69 // which is why we create it inside the output directory specifically.
70 let metadata_tmpdir
= TempFileBuilder
::new()
72 .tempdir_in(out_filename
.parent().unwrap_or_else(|| Path
::new("")))
73 .unwrap_or_else(|err
| tcx
.sess
.emit_fatal(FailedCreateTempdir { err }
));
74 let metadata_tmpdir
= MaybeTempDir
::new(metadata_tmpdir
, tcx
.sess
.opts
.cg
.save_temps
);
75 let metadata_filename
= metadata_tmpdir
.as_ref().join(METADATA_FILENAME
);
77 // Always create a file at `metadata_filename`, even if we have nothing to write to it.
78 // This simplifies the creation of the output `out_filename` when requested.
80 MetadataKind
::None
=> {
81 std
::fs
::File
::create(&metadata_filename
).unwrap_or_else(|err
| {
82 tcx
.sess
.emit_fatal(FailedCreateFile { filename: &metadata_filename, err }
);
85 MetadataKind
::Uncompressed
| MetadataKind
::Compressed
=> {
86 encode_metadata(tcx
, &metadata_filename
);
90 let _prof_timer
= tcx
.sess
.prof
.generic_activity("write_crate_metadata");
92 // If the user requests metadata as output, rename `metadata_filename`
93 // to the expected output `out_filename`. The match above should ensure
94 // this file always exists.
95 let need_metadata_file
= tcx
.sess
.opts
.output_types
.contains_key(&OutputType
::Metadata
);
96 let (metadata_filename
, metadata_tmpdir
) = if need_metadata_file
{
97 if let Err(err
) = non_durable_rename(&metadata_filename
, &out_filename
) {
98 tcx
.sess
.emit_fatal(FailedWriteError { filename: out_filename, err }
);
100 if tcx
.sess
.opts
.json_artifact_notifications
{
104 .emit_artifact_notification(&out_filename
, "metadata");
108 (metadata_filename
, Some(metadata_tmpdir
))
111 // Load metadata back to memory: codegen may need to include it in object files.
113 EncodedMetadata
::from_path(metadata_filename
, metadata_tmpdir
).unwrap_or_else(|err
| {
114 tcx
.sess
.emit_fatal(FailedCreateEncodedMetadata { err }
);
117 let need_metadata_module
= metadata_kind
== MetadataKind
::Compressed
;
119 (metadata
, need_metadata_module
)
122 #[cfg(not(target_os = "linux"))]
123 pub fn non_durable_rename(src
: &Path
, dst
: &Path
) -> std
::io
::Result
<()> {
124 std
::fs
::rename(src
, dst
)
127 /// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
128 /// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
129 /// write back the source file before committing the rename in case a developer forgot some of
130 /// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
132 /// To avoid triggering this heuristic we delete the destination first, if it exists.
133 /// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
134 #[cfg(target_os = "linux")]
135 pub fn non_durable_rename(src
: &Path
, dst
: &Path
) -> std
::io
::Result
<()> {
136 let _
= std
::fs
::remove_file(dst
);
137 std
::fs
::rename(src
, dst
)