rustc_incremental/persist/
file_format.rs
1use std::borrow::Cow;
13use std::io::{self, Read};
14use std::path::{Path, PathBuf};
15use std::{env, fs};
16
17use rustc_data_structures::memmap::Mmap;
18use rustc_serialize::Encoder;
19use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
20use rustc_session::Session;
21use tracing::debug;
22
23use crate::errors;
24
25const FILE_MAGIC: &[u8] = b"RSIC";
27
28const HEADER_FORMAT_VERSION: u16 = 0;
30
31pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
32 stream.emit_raw_bytes(FILE_MAGIC);
33 stream
34 .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
35
36 let rustc_version = rustc_version(sess.is_nightly_build(), sess.cfg_version);
37 assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
38 stream.emit_raw_bytes(&[rustc_version.len() as u8]);
39 stream.emit_raw_bytes(rustc_version.as_bytes());
40}
41
42pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
43where
44 F: FnOnce(FileEncoder) -> FileEncodeResult,
45{
46 debug!("save: storing data in {}", path_buf.display());
47
48 match fs::remove_file(&path_buf) {
56 Ok(()) => {
57 debug!("save: remove old file");
58 }
59 Err(err) if err.kind() == io::ErrorKind::NotFound => (),
60 Err(err) => sess.dcx().emit_fatal(errors::DeleteOld { name, path: path_buf, err }),
61 }
62
63 let mut encoder = match FileEncoder::new(&path_buf) {
64 Ok(encoder) => encoder,
65 Err(err) => sess.dcx().emit_fatal(errors::CreateNew { name, path: path_buf, err }),
66 };
67
68 write_file_header(&mut encoder, sess);
69
70 match encode(encoder) {
71 Ok(position) => {
72 sess.prof.artifact_size(
73 &name.replace(' ', "_"),
74 path_buf.file_name().unwrap().to_string_lossy(),
75 position as u64,
76 );
77 debug!("save: data written to disk successfully");
78 }
79 Err((path, err)) => sess.dcx().emit_fatal(errors::WriteNew { name, path, err }),
80 }
81}
82
83pub(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)>> {
98 let file = match fs::File::open(path) {
99 Ok(file) => file,
100 Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
101 Err(err) => return Err(err),
102 };
103 let mmap = unsafe { Mmap::map(file) }?;
110
111 let mut file = io::Cursor::new(&*mmap);
112
113 {
115 debug_assert!(FILE_MAGIC.len() == 4);
116 let mut file_magic = [0u8; 4];
117 file.read_exact(&mut file_magic)?;
118 if file_magic != FILE_MAGIC {
119 report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
120 return Ok(None);
121 }
122 }
123
124 {
126 debug_assert!(::std::mem::size_of_val(&HEADER_FORMAT_VERSION) == 2);
127 let mut header_format_version = [0u8; 2];
128 file.read_exact(&mut header_format_version)?;
129 let header_format_version =
130 (header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8);
131
132 if header_format_version != HEADER_FORMAT_VERSION {
133 report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
134 return Ok(None);
135 }
136 }
137
138 {
140 let mut rustc_version_str_len = [0u8; 1];
141 file.read_exact(&mut rustc_version_str_len)?;
142 let rustc_version_str_len = rustc_version_str_len[0] as usize;
143 let mut buffer = vec![0; rustc_version_str_len];
144 file.read_exact(&mut buffer)?;
145
146 if buffer != rustc_version(is_nightly_build, cfg_version).as_bytes() {
147 report_format_mismatch(report_incremental_info, path, "Different compiler version");
148 return Ok(None);
149 }
150 }
151
152 let post_header_start_pos = file.position() as usize;
153 Ok(Some((mmap, post_header_start_pos)))
154}
155
156fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
157 debug!("read_file: {}", message);
158
159 if report_incremental_info {
160 eprintln!(
161 "[incremental] ignoring cache artifact `{}`: {}",
162 file.file_name().unwrap().to_string_lossy(),
163 message
164 );
165 }
166}
167
168fn rustc_version(nightly_build: bool, cfg_version: &'static str) -> Cow<'static, str> {
172 if nightly_build {
173 if let Ok(val) = env::var("RUSTC_FORCE_RUSTC_VERSION") {
174 return val.into();
175 }
176 }
177
178 cfg_version.into()
179}