2022-04-24 23:49:04 +09:00
|
|
|
use std::path::{Path, PathBuf};
|
2023-02-26 15:27:27 -05:00
|
|
|
use std::{fs, io};
|
2022-04-24 23:49:04 +09:00
|
|
|
|
2022-04-24 19:34:35 +09:00
|
|
|
use rustc_data_structures::temp_dir::MaybeTempDir;
|
2025-04-20 15:29:17 +00:00
|
|
|
use rustc_fs_util::TempDirBuilder;
|
2022-04-24 23:49:04 +09:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2025-05-30 12:51:15 +00:00
|
|
|
use rustc_session::Session;
|
2025-03-28 17:58:23 +01:00
|
|
|
use rustc_session::config::{CrateType, OutFileName, OutputType};
|
2022-04-24 23:49:04 +09:00
|
|
|
use rustc_session::output::filename_for_metadata;
|
2022-04-24 19:34:35 +09:00
|
|
|
|
2022-04-24 23:49:04 +09:00
|
|
|
use crate::errors::{
|
|
|
|
|
BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile,
|
2023-06-06 17:54:34 -04:00
|
|
|
FailedCreateTempdir, FailedWriteError,
|
2024-07-29 08:13:50 +10:00
|
|
|
};
|
2022-04-24 23:49:04 +09:00
|
|
|
use crate::{EncodedMetadata, encode_metadata};
|
2022-04-24 19:34:35 +09:00
|
|
|
|
|
|
|
|
// FIXME(eddyb) maybe include the crate name in this?
|
|
|
|
|
pub const METADATA_FILENAME: &str = "lib.rmeta";
|
|
|
|
|
|
|
|
|
|
/// We use a temp directory here to avoid races between concurrent rustc processes,
|
|
|
|
|
/// such as builds in the same directory using the same filename for metadata while
|
|
|
|
|
/// 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.
|
2025-05-30 12:51:15 +00:00
|
|
|
pub fn emit_wrapper_file(sess: &Session, data: &[u8], tmpdir: &Path, name: &str) -> PathBuf {
|
|
|
|
|
let out_filename = tmpdir.join(name);
|
2022-10-16 17:05:53 +03:00
|
|
|
let result = fs::write(&out_filename, data);
|
2022-04-24 19:34:35 +09:00
|
|
|
|
2022-08-26 14:39:59 -06:00
|
|
|
if let Err(err) = result {
|
2023-12-18 22:21:37 +11:00
|
|
|
sess.dcx().emit_fatal(FailedWriteError { filename: out_filename, err });
|
2022-04-24 19:34:35 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out_filename
|
|
|
|
|
}
|
2022-04-24 23:49:04 +09:00
|
|
|
|
2025-05-30 12:51:15 +00:00
|
|
|
pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
|
2023-08-11 06:20:35 +02:00
|
|
|
let out_filename = filename_for_metadata(tcx.sess, tcx.output_filenames(()));
|
2022-04-25 20:58:30 +09:00
|
|
|
// 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.
|
2025-04-20 15:29:17 +00:00
|
|
|
let metadata_tmpdir = TempDirBuilder::new()
|
2022-04-25 20:58:30 +09:00
|
|
|
.prefix("rmeta")
|
2022-06-25 01:12:08 +09:00
|
|
|
.tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
|
2023-12-18 22:21:37 +11:00
|
|
|
.unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));
|
2022-04-25 20:58:30 +09:00
|
|
|
let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
|
2025-03-28 17:58:23 +01:00
|
|
|
let metadata_filename = metadata_tmpdir.as_ref().join("full.rmeta");
|
|
|
|
|
let metadata_stub_filename = if !tcx.sess.opts.unstable_opts.embed_metadata
|
|
|
|
|
&& !tcx.crate_types().contains(&CrateType::ProcMacro)
|
|
|
|
|
{
|
|
|
|
|
Some(metadata_tmpdir.as_ref().join("stub.rmeta"))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2022-06-01 00:14:42 +09:00
|
|
|
|
2025-05-30 12:51:15 +00:00
|
|
|
if tcx.needs_metadata() {
|
|
|
|
|
encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref());
|
|
|
|
|
} else {
|
|
|
|
|
// Always create a file at `metadata_filename`, even if we have nothing to write to it.
|
|
|
|
|
// This simplifies the creation of the output `out_filename` when requested.
|
|
|
|
|
std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {
|
|
|
|
|
tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err });
|
|
|
|
|
});
|
|
|
|
|
if let Some(metadata_stub_filename) = &metadata_stub_filename {
|
|
|
|
|
std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| {
|
|
|
|
|
tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err });
|
2022-06-01 00:14:42 +09:00
|
|
|
});
|
|
|
|
|
}
|
2025-05-30 12:51:15 +00:00
|
|
|
}
|
2022-05-02 16:37:26 +09:00
|
|
|
|
2022-06-01 00:14:42 +09:00
|
|
|
let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
|
|
|
|
|
|
2022-06-05 22:23:06 +09:00
|
|
|
// If the user requests metadata as output, rename `metadata_filename`
|
2022-11-16 20:34:16 +00:00
|
|
|
// to the expected output `out_filename`. The match above should ensure
|
2022-06-05 22:23:06 +09:00
|
|
|
// this file always exists.
|
2022-06-01 00:14:42 +09:00
|
|
|
let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
|
|
|
|
|
let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
|
2023-02-26 15:27:27 -05:00
|
|
|
let filename = match out_filename {
|
|
|
|
|
OutFileName::Real(ref path) => {
|
|
|
|
|
if let Err(err) = non_durable_rename(&metadata_filename, path) {
|
2023-12-18 22:21:37 +11:00
|
|
|
tcx.dcx().emit_fatal(FailedWriteError { filename: path.to_path_buf(), err });
|
2023-02-26 15:27:27 -05:00
|
|
|
}
|
|
|
|
|
path.clone()
|
|
|
|
|
}
|
|
|
|
|
OutFileName::Stdout => {
|
|
|
|
|
if out_filename.is_tty() {
|
2023-12-18 22:21:37 +11:00
|
|
|
tcx.dcx().emit_err(BinaryOutputToTty);
|
2023-02-26 15:27:27 -05:00
|
|
|
} else if let Err(err) = copy_to_stdout(&metadata_filename) {
|
2023-12-18 22:21:37 +11:00
|
|
|
tcx.dcx()
|
2023-02-26 15:27:27 -05:00
|
|
|
.emit_err(FailedCopyToStdout { filename: metadata_filename.clone(), err });
|
|
|
|
|
}
|
|
|
|
|
metadata_filename
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-06-01 00:14:42 +09:00
|
|
|
if tcx.sess.opts.json_artifact_notifications {
|
2023-12-18 22:21:37 +11:00
|
|
|
tcx.dcx().emit_artifact_notification(out_filename.as_path(), "metadata");
|
2022-06-01 00:14:42 +09:00
|
|
|
}
|
2023-02-26 15:27:27 -05:00
|
|
|
(filename, None)
|
2022-06-01 00:14:42 +09:00
|
|
|
} else {
|
|
|
|
|
(metadata_filename, Some(metadata_tmpdir))
|
2022-04-24 23:49:04 +09:00
|
|
|
};
|
|
|
|
|
|
2022-06-05 22:23:06 +09:00
|
|
|
// Load metadata back to memory: codegen may need to include it in object files.
|
2023-07-23 09:31:34 +02:00
|
|
|
let metadata =
|
2025-03-28 17:58:23 +01:00
|
|
|
EncodedMetadata::from_path(metadata_filename, metadata_stub_filename, metadata_tmpdir)
|
|
|
|
|
.unwrap_or_else(|err| {
|
|
|
|
|
tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err });
|
|
|
|
|
});
|
2022-06-01 00:14:42 +09:00
|
|
|
|
2025-05-30 12:51:15 +00:00
|
|
|
metadata
|
2022-04-24 23:49:04 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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)
|
|
|
|
|
}
|
2023-02-26 15:27:27 -05:00
|
|
|
|
|
|
|
|
pub fn copy_to_stdout(from: &Path) -> io::Result<()> {
|
2024-09-24 14:25:16 -07:00
|
|
|
let mut reader = fs::File::open_buffered(from)?;
|
2023-02-26 15:27:27 -05:00
|
|
|
let mut stdout = io::stdout();
|
|
|
|
|
io::copy(&mut reader, &mut stdout)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|