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