]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_incremental/src/persist/file_format.rs
Rollup merge of #82220 - henryboisdequin:fixes-80853, r=varkor
[rust.git] / compiler / rustc_incremental / src / persist / file_format.rs
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
12 use std::env;
13 use std::fs;
14 use std::io::{self, Read};
15 use std::path::Path;
16
17 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
18
19 /// The first few bytes of files generated by incremental compilation.
20 const FILE_MAGIC: &[u8] = b"RSIC";
21
22 /// Change this if the header format changes.
23 const HEADER_FORMAT_VERSION: u16 = 0;
24
25 /// A version string that hopefully is always different for compiler versions
26 /// with different encodings of incremental compilation artifacts. Contains
27 /// the Git commit hash.
28 const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
29
30 pub fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) -> FileEncodeResult {
31     stream.emit_raw_bytes(FILE_MAGIC)?;
32     stream.emit_raw_bytes(&[
33         (HEADER_FORMAT_VERSION >> 0) as u8,
34         (HEADER_FORMAT_VERSION >> 8) as u8,
35     ])?;
36
37     let rustc_version = rustc_version(nightly_build);
38     assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
39     stream.emit_raw_bytes(&[rustc_version.len() as u8])?;
40     stream.emit_raw_bytes(rustc_version.as_bytes())
41 }
42
43 /// Reads the contents of a file with a file header as defined in this module.
44 ///
45 /// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a
46 ///   compatible compiler version. `data` is the entire contents of the file
47 ///   and `pos` points to the first byte after the header.
48 /// - Returns `Ok(None)` if the file did not exist or was generated by an
49 ///   incompatible version of the compiler.
50 /// - Returns `Err(..)` if some kind of IO error occurred while reading the
51 ///   file.
52 pub fn read_file(
53     report_incremental_info: bool,
54     path: &Path,
55     nightly_build: bool,
56 ) -> io::Result<Option<(Vec<u8>, usize)>> {
57     let data = match fs::read(path) {
58         Ok(data) => data,
59         Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
60         Err(err) => return Err(err),
61     };
62
63     let mut file = io::Cursor::new(data);
64
65     // Check FILE_MAGIC
66     {
67         debug_assert!(FILE_MAGIC.len() == 4);
68         let mut file_magic = [0u8; 4];
69         file.read_exact(&mut file_magic)?;
70         if file_magic != FILE_MAGIC {
71             report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
72             return Ok(None);
73         }
74     }
75
76     // Check HEADER_FORMAT_VERSION
77     {
78         debug_assert!(::std::mem::size_of_val(&HEADER_FORMAT_VERSION) == 2);
79         let mut header_format_version = [0u8; 2];
80         file.read_exact(&mut header_format_version)?;
81         let header_format_version =
82             (header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8);
83
84         if header_format_version != HEADER_FORMAT_VERSION {
85             report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
86             return Ok(None);
87         }
88     }
89
90     // Check RUSTC_VERSION
91     {
92         let mut rustc_version_str_len = [0u8; 1];
93         file.read_exact(&mut rustc_version_str_len)?;
94         let rustc_version_str_len = rustc_version_str_len[0] as usize;
95         let mut buffer = vec![0; rustc_version_str_len];
96         file.read_exact(&mut buffer)?;
97
98         if buffer != rustc_version(nightly_build).as_bytes() {
99             report_format_mismatch(report_incremental_info, path, "Different compiler version");
100             return Ok(None);
101         }
102     }
103
104     let post_header_start_pos = file.position() as usize;
105     Ok(Some((file.into_inner(), post_header_start_pos)))
106 }
107
108 fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
109     debug!("read_file: {}", message);
110
111     if report_incremental_info {
112         eprintln!(
113             "[incremental] ignoring cache artifact `{}`: {}",
114             file.file_name().unwrap().to_string_lossy(),
115             message
116         );
117     }
118 }
119
120 fn rustc_version(nightly_build: bool) -> String {
121     if nightly_build {
122         if let Some(val) = env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") {
123             return val.to_string_lossy().into_owned();
124         }
125     }
126
127     RUSTC_VERSION
128         .expect(
129             "Cannot use rustc without explicit version for \
130                           incremental compilation",
131         )
132         .to_string()
133 }