1//! This module defines a generic file format that allows to check if a given
2//! file generated by incremental compilation was generated by a compatible
3//! compiler version. This file format is used for the on-disk version of the
4//! dependency graph and the exported metadata hashes.
5//!
6//! In practice "compatible compiler version" means "exactly the same compiler
7//! version", since the header encodes the git commit hash of the compiler.
8//! Since we can always just ignore the incremental compilation cache and
9//! compiler versions don't change frequently for the typical user, being
10//! conservative here practically has no downside.
1112use std::borrow::Cow;
13use std::io::{self, Read};
14use std::path::{Path, PathBuf};
15use std::{env, fs};
1617use rustc_data_structures::memmap::Mmap;
18use rustc_serialize::Encoder;
19use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
20use rustc_session::Session;
21use tracing::debug;
2223use crate::errors;
2425/// The first few bytes of files generated by incremental compilation.
26const FILE_MAGIC: &[u8] = b"RSIC";
2728/// Change this if the header format changes.
29const HEADER_FORMAT_VERSION: u16 = 0;
3031pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
32stream.emit_raw_bytes(FILE_MAGIC);
33stream34 .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
3536let rustc_version = rustc_version(sess.is_nightly_build(), sess.cfg_version);
37match (&rustc_version.len(), &((rustc_version.len() as u8) as usize)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
38stream.emit_raw_bytes(&[rustc_version.len() as u8]);
39stream.emit_raw_bytes(rustc_version.as_bytes());
40}
4142pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
43where
44F: FnOnce(FileEncoder) -> FileEncodeResult,
45{
46{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/file_format.rs:46",
"rustc_incremental::persist::file_format",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/file_format.rs"),
::tracing_core::__macro_support::Option::Some(46u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::file_format"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("save: storing data in {0}",
path_buf.display()) as &dyn Value))])
});
} else { ; }
};debug!("save: storing data in {}", path_buf.display());
4748// Delete the old file, if any.
49 // Note: It's important that we actually delete the old file and not just
50 // truncate and overwrite it, since it might be a shared hard-link, the
51 // underlying data of which we don't want to modify.
52 //
53 // We have to ensure we have dropped the memory maps to this file
54 // before performing this removal.
55match fs::remove_file(&path_buf) {
56Ok(()) => {
57{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/file_format.rs:57",
"rustc_incremental::persist::file_format",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/file_format.rs"),
::tracing_core::__macro_support::Option::Some(57u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::file_format"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("save: remove old file")
as &dyn Value))])
});
} else { ; }
};debug!("save: remove old file");
58 }
59Err(err) if err.kind() == io::ErrorKind::NotFound => (),
60Err(err) => sess.dcx().emit_fatal(errors::DeleteOld { name, path: path_buf, err }),
61 }
6263let mut encoder = match FileEncoder::new(&path_buf) {
64Ok(encoder) => encoder,
65Err(err) => sess.dcx().emit_fatal(errors::CreateNew { name, path: path_buf, err }),
66 };
6768write_file_header(&mut encoder, sess);
6970match encode(encoder) {
71Ok(position) => {
72sess.prof.artifact_size(
73&name.replace(' ', "_"),
74path_buf.file_name().unwrap().to_string_lossy(),
75positionas u64,
76 );
77{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/file_format.rs:77",
"rustc_incremental::persist::file_format",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/file_format.rs"),
::tracing_core::__macro_support::Option::Some(77u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::file_format"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("save: data written to disk successfully")
as &dyn Value))])
});
} else { ; }
};debug!("save: data written to disk successfully");
78 }
79Err((path, err)) => sess.dcx().emit_fatal(errors::WriteNew { name, path, err }),
80 }
81}
8283/// Reads the contents of a file with a file header as defined in this module.
84///
85/// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a
86/// compatible compiler version. `data` is the entire contents of the file
87/// and `pos` points to the first byte after the header.
88/// - Returns `Ok(None)` if the file did not exist or was generated by an
89/// incompatible version of the compiler.
90/// - Returns `Err(..)` if some kind of IO error occurred while reading the
91/// file.
92pub(crate) fn read_file(
93 path: &Path,
94 report_incremental_info: bool,
95 is_nightly_build: bool,
96 cfg_version: &'static str,
97) -> io::Result<Option<(Mmap, usize)>> {
98let file = match fs::File::open(path) {
99Ok(file) => file,
100Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
101Err(err) => return Err(err),
102 };
103// SAFETY: This process must not modify nor remove the backing file while the memory map lives.
104 // For the dep-graph and the work product index, it is as soon as the decoding is done.
105 // For the query result cache, the memory map is dropped in save_dep_graph before calling
106 // save_in and trying to remove the backing file.
107 //
108 // There is no way to prevent another process from modifying this file.
109let mmap = unsafe { Mmap::map(file) }?;
110111let mut file = io::Cursor::new(&*mmap);
112113// Check FILE_MAGIC
114{
115if true {
if !(FILE_MAGIC.len() == 4) {
::core::panicking::panic("assertion failed: FILE_MAGIC.len() == 4")
};
};debug_assert!(FILE_MAGIC.len() == 4);
116let mut file_magic = [0u8; 4];
117file.read_exact(&mut file_magic)?;
118if file_magic != FILE_MAGIC {
119report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
120return Ok(None);
121 }
122 }
123124// Check HEADER_FORMAT_VERSION
125{
126if true {
if !(size_of_val(&HEADER_FORMAT_VERSION) == 2) {
::core::panicking::panic("assertion failed: size_of_val(&HEADER_FORMAT_VERSION) == 2")
};
};debug_assert!(size_of_val(&HEADER_FORMAT_VERSION) == 2);
127let mut header_format_version = [0u8; 2];
128file.read_exact(&mut header_format_version)?;
129let header_format_version =
130 (header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8);
131132if header_format_version != HEADER_FORMAT_VERSION {
133report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
134return Ok(None);
135 }
136 }
137138// Check RUSTC_VERSION
139{
140let mut rustc_version_str_len = [0u8; 1];
141file.read_exact(&mut rustc_version_str_len)?;
142let rustc_version_str_len = rustc_version_str_len[0] as usize;
143let mut buffer = ::alloc::vec::from_elem(0, rustc_version_str_len)vec![0; rustc_version_str_len];
144file.read_exact(&mut buffer)?;
145146if buffer != rustc_version(is_nightly_build, cfg_version).as_bytes() {
147report_format_mismatch(report_incremental_info, path, "Different compiler version");
148return Ok(None);
149 }
150 }
151152let post_header_start_pos = file.position() as usize;
153Ok(Some((mmap, post_header_start_pos)))
154}
155156fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
157{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/file_format.rs:157",
"rustc_incremental::persist::file_format",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/file_format.rs"),
::tracing_core::__macro_support::Option::Some(157u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::file_format"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("read_file: {0}",
message) as &dyn Value))])
});
} else { ; }
};debug!("read_file: {}", message);
158159if report_incremental_info {
160{
::std::io::_eprint(format_args!("[incremental] ignoring cache artifact `{0}`: {1}\n",
file.file_name().unwrap().to_string_lossy(), message));
};eprintln!(
161"[incremental] ignoring cache artifact `{}`: {}",
162 file.file_name().unwrap().to_string_lossy(),
163 message
164 );
165 }
166}
167168/// A version string that hopefully is always different for compiler versions
169/// with different encodings of incremental compilation artifacts. Contains
170/// the Git commit hash.
171fn rustc_version(nightly_build: bool, cfg_version: &'static str) -> Cow<'static, str> {
172if nightly_build {
173if let Ok(val) = env::var("RUSTC_FORCE_RUSTC_VERSION") {
174return val.into();
175 }
176 }
177178cfg_version.into()
179}