mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Clean up the API for opening/checking incremental-compilation files
Returning dedicated structs and enums makes the meaning of each return value more obvious, and provides a more natural home for documentation. The intermediate `load_data` function was unhelpful, and has been inlined into the main function.
This commit is contained in:
@@ -33,7 +33,7 @@ pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
|
||||
stream
|
||||
.emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
|
||||
|
||||
let rustc_version = rustc_version(sess.is_nightly_build(), sess.cfg_version);
|
||||
let rustc_version = rustc_version(sess);
|
||||
assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
|
||||
stream.emit_raw_bytes(&[rustc_version.len() as u8]);
|
||||
stream.emit_raw_bytes(rustc_version.as_bytes());
|
||||
@@ -80,26 +80,47 @@ pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode:
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the contents of a file with a file header as defined in this module.
|
||||
///
|
||||
/// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a
|
||||
/// compatible compiler version. `data` is the entire contents of the file
|
||||
/// and `pos` points to the first byte after the header.
|
||||
/// - Returns `Ok(None)` if the file did not exist or was generated by an
|
||||
/// incompatible version of the compiler.
|
||||
/// - Returns `Err(..)` if some kind of IO error occurred while reading the
|
||||
/// file.
|
||||
pub(crate) fn read_file(
|
||||
pub(crate) struct OpenFile {
|
||||
/// A read-only mmap view of the file contents.
|
||||
pub(crate) mmap: Mmap,
|
||||
/// File position to start reading normal data from, just after the end of the file header.
|
||||
pub(crate) start_pos: usize,
|
||||
}
|
||||
|
||||
pub(crate) enum OpenFileError {
|
||||
/// Either the file was not found, or one of the header checks failed.
|
||||
///
|
||||
/// These conditions prevent us from reading the file contents, but should
|
||||
/// not trigger an error or even a warning, because they routinely happen
|
||||
/// during normal operation:
|
||||
/// - File-not-found occurs in a fresh build, or after clearing the build directory.
|
||||
/// - Header-mismatch occurs after upgrading or switching compiler versions.
|
||||
NotFoundOrHeaderMismatch,
|
||||
|
||||
/// An unexpected I/O error occurred while opening or checking the file.
|
||||
IoError { err: io::Error },
|
||||
}
|
||||
|
||||
impl From<io::Error> for OpenFileError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
OpenFileError::IoError { err }
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to open a file that was written by the previous incremental-compilation
|
||||
/// session, and checks that it was produced by a matching compiler version.
|
||||
pub(crate) fn open_incremental_file(
|
||||
sess: &Session,
|
||||
path: &Path,
|
||||
report_incremental_info: bool,
|
||||
is_nightly_build: bool,
|
||||
cfg_version: &'static str,
|
||||
) -> io::Result<Option<(Mmap, usize)>> {
|
||||
let file = match fs::File::open(path) {
|
||||
Ok(file) => file,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
) -> Result<OpenFile, OpenFileError> {
|
||||
let file = fs::File::open(path).map_err(|err| {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
OpenFileError::NotFoundOrHeaderMismatch
|
||||
} else {
|
||||
OpenFileError::IoError { err }
|
||||
}
|
||||
})?;
|
||||
|
||||
// SAFETY: This process must not modify nor remove the backing file while the memory map lives.
|
||||
// For the dep-graph and the work product index, it is as soon as the decoding is done.
|
||||
// For the query result cache, the memory map is dropped in save_dep_graph before calling
|
||||
@@ -116,8 +137,8 @@ pub(crate) fn read_file(
|
||||
let mut file_magic = [0u8; 4];
|
||||
file.read_exact(&mut file_magic)?;
|
||||
if file_magic != FILE_MAGIC {
|
||||
report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
|
||||
return Ok(None);
|
||||
report_format_mismatch(sess, path, "Wrong FILE_MAGIC");
|
||||
return Err(OpenFileError::NotFoundOrHeaderMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,8 +151,8 @@ pub(crate) fn read_file(
|
||||
(header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8);
|
||||
|
||||
if header_format_version != HEADER_FORMAT_VERSION {
|
||||
report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
|
||||
return Ok(None);
|
||||
report_format_mismatch(sess, path, "Wrong HEADER_FORMAT_VERSION");
|
||||
return Err(OpenFileError::NotFoundOrHeaderMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,20 +164,20 @@ pub(crate) fn read_file(
|
||||
let mut buffer = vec![0; rustc_version_str_len];
|
||||
file.read_exact(&mut buffer)?;
|
||||
|
||||
if buffer != rustc_version(is_nightly_build, cfg_version).as_bytes() {
|
||||
report_format_mismatch(report_incremental_info, path, "Different compiler version");
|
||||
return Ok(None);
|
||||
if buffer != rustc_version(sess).as_bytes() {
|
||||
report_format_mismatch(sess, path, "Different compiler version");
|
||||
return Err(OpenFileError::NotFoundOrHeaderMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
let post_header_start_pos = file.position() as usize;
|
||||
Ok(Some((mmap, post_header_start_pos)))
|
||||
let start_pos = file.position() as usize;
|
||||
Ok(OpenFile { mmap, start_pos })
|
||||
}
|
||||
|
||||
fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
|
||||
fn report_format_mismatch(sess: &Session, file: &Path, message: &str) {
|
||||
debug!("read_file: {}", message);
|
||||
|
||||
if report_incremental_info {
|
||||
if sess.opts.unstable_opts.incremental_info {
|
||||
eprintln!(
|
||||
"[incremental] ignoring cache artifact `{}`: {}",
|
||||
file.file_name().unwrap().to_string_lossy(),
|
||||
@@ -168,12 +189,13 @@ fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &
|
||||
/// A version string that hopefully is always different for compiler versions
|
||||
/// with different encodings of incremental compilation artifacts. Contains
|
||||
/// the Git commit hash.
|
||||
fn rustc_version(nightly_build: bool, cfg_version: &'static str) -> Cow<'static, str> {
|
||||
if nightly_build {
|
||||
if let Ok(val) = env::var("RUSTC_FORCE_RUSTC_VERSION") {
|
||||
return val.into();
|
||||
}
|
||||
fn rustc_version(sess: &Session) -> Cow<'static, str> {
|
||||
// Allow version string overrides so that tests can produce a header-mismatch on demand.
|
||||
if sess.is_nightly_build()
|
||||
&& let Ok(env_version) = env::var("RUSTC_FORCE_RUSTC_VERSION")
|
||||
{
|
||||
Cow::Owned(env_version)
|
||||
} else {
|
||||
Cow::Borrowed(sess.cfg_version)
|
||||
}
|
||||
|
||||
cfg_version.into()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Code to load the dep-graph from files.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_hashes::Hash64;
|
||||
use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProductMap};
|
||||
@@ -20,6 +19,7 @@
|
||||
use super::save::build_dep_graph;
|
||||
use super::{file_format, work_product};
|
||||
use crate::errors;
|
||||
use crate::persist::file_format::{OpenFile, OpenFileError};
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Represents the result of an attempt to load incremental compilation data.
|
||||
@@ -69,23 +69,6 @@ pub fn open(self, sess: &Session) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_data(path: &Path, sess: &Session) -> LoadResult<(Mmap, usize)> {
|
||||
match file_format::read_file(
|
||||
path,
|
||||
sess.opts.unstable_opts.incremental_info,
|
||||
sess.is_nightly_build(),
|
||||
sess.cfg_version,
|
||||
) {
|
||||
Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
|
||||
Ok(None) => {
|
||||
// The file either didn't exist or was produced by an incompatible
|
||||
// compiler version. Neither is an error.
|
||||
LoadResult::DataOutOfDate
|
||||
}
|
||||
Err(err) => LoadResult::LoadDepGraph(path.to_path_buf(), err),
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
|
||||
debug!("delete_dirty_work_product({:?})", swp);
|
||||
work_product::delete_workproduct_files(sess, &swp.work_product);
|
||||
@@ -113,12 +96,12 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
|
||||
// when trying to load work products.
|
||||
if sess.incr_comp_session_dir_opt().is_some() {
|
||||
let work_products_path = work_products_path(sess);
|
||||
let load_result = load_data(&work_products_path, sess);
|
||||
|
||||
if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
|
||||
if let Ok(OpenFile { mmap, start_pos }) =
|
||||
file_format::open_incremental_file(sess, &work_products_path)
|
||||
{
|
||||
// Decode the list of work_products
|
||||
let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos)
|
||||
else {
|
||||
let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else {
|
||||
sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
|
||||
return LoadResult::DataOutOfDate;
|
||||
};
|
||||
@@ -147,11 +130,11 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
|
||||
|
||||
let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
|
||||
|
||||
match load_data(&path, sess) {
|
||||
LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
|
||||
LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
|
||||
LoadResult::Ok { data: (bytes, start_pos) } => {
|
||||
let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
|
||||
match file_format::open_incremental_file(sess, &path) {
|
||||
Err(OpenFileError::NotFoundOrHeaderMismatch) => LoadResult::DataOutOfDate,
|
||||
Err(OpenFileError::IoError { err }) => LoadResult::LoadDepGraph(path.to_owned(), err),
|
||||
Ok(OpenFile { mmap, start_pos }) => {
|
||||
let Ok(mut decoder) = MemDecoder::new(&mmap, start_pos) else {
|
||||
sess.dcx().emit_warn(errors::CorruptFile { path: &path });
|
||||
return LoadResult::DataOutOfDate;
|
||||
};
|
||||
@@ -191,15 +174,17 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
|
||||
let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
|
||||
|
||||
let path = query_cache_path(sess);
|
||||
match load_data(&path, sess) {
|
||||
LoadResult::Ok { data: (bytes, start_pos) } => {
|
||||
let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
|
||||
match file_format::open_incremental_file(sess, &path) {
|
||||
Ok(OpenFile { mmap, start_pos }) => {
|
||||
let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| {
|
||||
sess.dcx().emit_warn(errors::CorruptFile { path: &path });
|
||||
OnDiskCache::new_empty()
|
||||
});
|
||||
Some(cache)
|
||||
}
|
||||
_ => Some(OnDiskCache::new_empty()),
|
||||
Err(OpenFileError::NotFoundOrHeaderMismatch | OpenFileError::IoError { .. }) => {
|
||||
Some(OnDiskCache::new_empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user