]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_incremental/src/persist/file_format.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / compiler / rustc_incremental / src / persist / file_format.rs
CommitLineData
9e0c209e
SL
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.
11
dfeec247
XL
12use std::env;
13use std::fs;
9e0c209e 14use std::io::{self, Read};
c295e0f8 15use std::path::{Path, PathBuf};
9e0c209e 16
c295e0f8 17use rustc_data_structures::memmap::Mmap;
5869c6ff 18use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
cdc7bbd5 19use rustc_serialize::Encoder;
c295e0f8 20use rustc_session::Session;
9e0c209e 21
9fa01778 22/// The first few bytes of files generated by incremental compilation.
0731742a 23const FILE_MAGIC: &[u8] = b"RSIC";
9e0c209e 24
9fa01778 25/// Change this if the header format changes.
9e0c209e
SL
26const HEADER_FORMAT_VERSION: u16 = 0;
27
28/// A version string that hopefully is always different for compiler versions
29/// with different encodings of incremental compilation artifacts. Contains
9fa01778 30/// the Git commit hash.
0731742a 31const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
9e0c209e 32
c295e0f8 33pub(crate) fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) -> FileEncodeResult {
5869c6ff
XL
34 stream.emit_raw_bytes(FILE_MAGIC)?;
35 stream.emit_raw_bytes(&[
36 (HEADER_FORMAT_VERSION >> 0) as u8,
37 (HEADER_FORMAT_VERSION >> 8) as u8,
38 ])?;
9e0c209e 39
fc512014 40 let rustc_version = rustc_version(nightly_build);
9e0c209e 41 assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
5869c6ff
XL
42 stream.emit_raw_bytes(&[rustc_version.len() as u8])?;
43 stream.emit_raw_bytes(rustc_version.as_bytes())
9e0c209e
SL
44}
45
c295e0f8
XL
46pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
47where
48 F: FnOnce(&mut FileEncoder) -> FileEncodeResult,
49{
50 debug!("save: storing data in {}", path_buf.display());
51
52 // Delete the old file, if any.
53 // Note: It's important that we actually delete the old file and not just
54 // truncate and overwrite it, since it might be a shared hard-link, the
55 // underlying data of which we don't want to modify.
56 //
57 // We have to ensure we have dropped the memory maps to this file
58 // before performing this removal.
59 match fs::remove_file(&path_buf) {
60 Ok(()) => {
61 debug!("save: remove old file");
62 }
63 Err(err) if err.kind() == io::ErrorKind::NotFound => (),
64 Err(err) => {
65 sess.err(&format!(
66 "unable to delete old {} at `{}`: {}",
67 name,
68 path_buf.display(),
69 err
70 ));
71 return;
72 }
73 }
74
75 let mut encoder = match FileEncoder::new(&path_buf) {
76 Ok(encoder) => encoder,
77 Err(err) => {
78 sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err));
79 return;
80 }
81 };
82
83 if let Err(err) = write_file_header(&mut encoder, sess.is_nightly_build()) {
84 sess.err(&format!("failed to write {} header to `{}`: {}", name, path_buf.display(), err));
85 return;
86 }
87
88 if let Err(err) = encode(&mut encoder) {
89 sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err));
90 return;
91 }
92
93 if let Err(err) = encoder.flush() {
94 sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err));
95 return;
96 }
97
3c0e092e
XL
98 sess.prof.artifact_size(
99 &name.replace(' ', "_"),
100 path_buf.file_name().unwrap().to_string_lossy(),
101 encoder.position() as u64,
102 );
103
c295e0f8
XL
104 debug!("save: data written to disk successfully");
105}
106
9e0c209e
SL
107/// Reads the contents of a file with a file header as defined in this module.
108///
abe05a73 109/// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a
9e0c209e 110/// compatible compiler version. `data` is the entire contents of the file
abe05a73 111/// and `pos` points to the first byte after the header.
9e0c209e
SL
112/// - Returns `Ok(None)` if the file did not exist or was generated by an
113/// incompatible version of the compiler.
114/// - Returns `Err(..)` if some kind of IO error occurred while reading the
115/// file.
dfeec247
XL
116pub fn read_file(
117 report_incremental_info: bool,
118 path: &Path,
fc512014 119 nightly_build: bool,
c295e0f8
XL
120) -> io::Result<Option<(Mmap, usize)>> {
121 let file = match fs::File::open(path) {
122 Ok(file) => file,
5869c6ff
XL
123 Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
124 Err(err) => return Err(err),
125 };
c295e0f8
XL
126 // SAFETY: This process must not modify nor remove the backing file while the memory map lives.
127 // For the dep-graph and the work product index, it is as soon as the decoding is done.
128 // For the query result cache, the memory map is dropped in save_dep_graph before calling
129 // save_in and trying to remove the backing file.
130 //
131 // There is no way to prevent another process from modifying this file.
132 let mmap = unsafe { Mmap::map(file) }?;
abe05a73 133
c295e0f8 134 let mut file = io::Cursor::new(&*mmap);
9e0c209e
SL
135
136 // Check FILE_MAGIC
137 {
138 debug_assert!(FILE_MAGIC.len() == 4);
139 let mut file_magic = [0u8; 4];
140 file.read_exact(&mut file_magic)?;
141 if file_magic != FILE_MAGIC {
ff7c6d11 142 report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
dfeec247 143 return Ok(None);
9e0c209e
SL
144 }
145 }
146
147 // Check HEADER_FORMAT_VERSION
148 {
149 debug_assert!(::std::mem::size_of_val(&HEADER_FORMAT_VERSION) == 2);
150 let mut header_format_version = [0u8; 2];
151 file.read_exact(&mut header_format_version)?;
dfeec247
XL
152 let header_format_version =
153 (header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8);
9e0c209e
SL
154
155 if header_format_version != HEADER_FORMAT_VERSION {
ff7c6d11 156 report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
dfeec247 157 return Ok(None);
9e0c209e
SL
158 }
159 }
160
161 // Check RUSTC_VERSION
162 {
163 let mut rustc_version_str_len = [0u8; 1];
164 file.read_exact(&mut rustc_version_str_len)?;
165 let rustc_version_str_len = rustc_version_str_len[0] as usize;
74b04a01 166 let mut buffer = vec![0; rustc_version_str_len];
cc61c64b 167 file.read_exact(&mut buffer)?;
9e0c209e 168
fc512014 169 if buffer != rustc_version(nightly_build).as_bytes() {
ff7c6d11 170 report_format_mismatch(report_incremental_info, path, "Different compiler version");
9e0c209e
SL
171 return Ok(None);
172 }
173 }
174
abe05a73 175 let post_header_start_pos = file.position() as usize;
c295e0f8 176 Ok(Some((mmap, post_header_start_pos)))
9e0c209e
SL
177}
178
ff7c6d11 179fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
476ff2be
SL
180 debug!("read_file: {}", message);
181
ff7c6d11 182 if report_incremental_info {
6a06907d 183 eprintln!(
dfeec247
XL
184 "[incremental] ignoring cache artifact `{}`: {}",
185 file.file_name().unwrap().to_string_lossy(),
186 message
187 );
476ff2be
SL
188 }
189}
190
fc512014
XL
191fn rustc_version(nightly_build: bool) -> String {
192 if nightly_build {
5099ac24 193 if let Some(val) = env::var_os("RUSTC_FORCE_RUSTC_VERSION") {
dfeec247 194 return val.to_string_lossy().into_owned();
9e0c209e
SL
195 }
196 }
197
dfeec247
XL
198 RUSTC_VERSION
199 .expect(
200 "Cannot use rustc without explicit version for \
201 incremental compilation",
202 )
203 .to_string()
9e0c209e 204}