]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/file_format.rs
Rollup merge of #47277 - tspiteri:log-correctness, r=frewsxcv
[rust.git] / src / librustc_incremental / persist / file_format.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This module defines a generic file format that allows to check if a given
12 //! file generated by incremental compilation was generated by a compatible
13 //! compiler version. This file format is used for the on-disk version of the
14 //! dependency graph and the exported metadata hashes.
15 //!
16 //! In practice "compatible compiler version" means "exactly the same compiler
17 //! version", since the header encodes the git commit hash of the compiler.
18 //! Since we can always just ignore the incremental compilation cache and
19 //! compiler versions don't change frequently for the typical user, being
20 //! conservative here practically has no downside.
21
22 use std::io::{self, Read};
23 use std::path::Path;
24 use std::fs;
25 use std::env;
26
27 use rustc::session::config::nightly_options;
28
29 /// The first few bytes of files generated by incremental compilation
30 const FILE_MAGIC: &'static [u8] = b"RSIC";
31
32 /// Change this if the header format changes
33 const HEADER_FORMAT_VERSION: u16 = 0;
34
35 /// A version string that hopefully is always different for compiler versions
36 /// with different encodings of incremental compilation artifacts. Contains
37 /// the git commit hash.
38 const RUSTC_VERSION: Option<&'static str> = option_env!("CFG_VERSION");
39
40 pub fn write_file_header<W: io::Write>(stream: &mut W) -> io::Result<()> {
41     stream.write_all(FILE_MAGIC)?;
42     stream.write_all(&[(HEADER_FORMAT_VERSION >> 0) as u8,
43                        (HEADER_FORMAT_VERSION >> 8) as u8])?;
44
45     let rustc_version = rustc_version();
46     assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
47     stream.write_all(&[rustc_version.len() as u8])?;
48     stream.write_all(rustc_version.as_bytes())?;
49
50     Ok(())
51 }
52
53 /// Reads the contents of a file with a file header as defined in this module.
54 ///
55 /// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a
56 ///   compatible compiler version. `data` is the entire contents of the file
57 ///   and `pos` points to the first byte after the header.
58 /// - Returns `Ok(None)` if the file did not exist or was generated by an
59 ///   incompatible version of the compiler.
60 /// - Returns `Err(..)` if some kind of IO error occurred while reading the
61 ///   file.
62 pub fn read_file(report_incremental_info: bool, path: &Path)
63     -> io::Result<Option<(Vec<u8>, usize)>>
64 {
65     if !path.exists() {
66         return Ok(None);
67     }
68
69     let data = fs::read(path)?;
70
71     let mut file = io::Cursor::new(data);
72
73     // Check FILE_MAGIC
74     {
75         debug_assert!(FILE_MAGIC.len() == 4);
76         let mut file_magic = [0u8; 4];
77         file.read_exact(&mut file_magic)?;
78         if file_magic != FILE_MAGIC {
79             report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC");
80             return Ok(None)
81         }
82     }
83
84     // Check HEADER_FORMAT_VERSION
85     {
86         debug_assert!(::std::mem::size_of_val(&HEADER_FORMAT_VERSION) == 2);
87         let mut header_format_version = [0u8; 2];
88         file.read_exact(&mut header_format_version)?;
89         let header_format_version = (header_format_version[0] as u16) |
90                                     ((header_format_version[1] as u16) << 8);
91
92         if header_format_version != HEADER_FORMAT_VERSION {
93             report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
94             return Ok(None)
95         }
96     }
97
98     // Check RUSTC_VERSION
99     {
100         let mut rustc_version_str_len = [0u8; 1];
101         file.read_exact(&mut rustc_version_str_len)?;
102         let rustc_version_str_len = rustc_version_str_len[0] as usize;
103         let mut buffer = Vec::with_capacity(rustc_version_str_len);
104         buffer.resize(rustc_version_str_len, 0);
105         file.read_exact(&mut buffer)?;
106
107         if buffer != rustc_version().as_bytes() {
108             report_format_mismatch(report_incremental_info, path, "Different compiler version");
109             return Ok(None);
110         }
111     }
112
113     let post_header_start_pos = file.position() as usize;
114     Ok(Some((file.into_inner(), post_header_start_pos)))
115 }
116
117 fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
118     debug!("read_file: {}", message);
119
120     if report_incremental_info {
121         println!("[incremental] ignoring cache artifact `{}`: {}",
122                   file.file_name().unwrap().to_string_lossy(),
123                   message);
124     }
125 }
126
127 fn rustc_version() -> String {
128     if nightly_options::is_nightly_build() {
129         if let Some(val) = env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") {
130             return val.to_string_lossy().into_owned()
131         }
132     }
133
134     RUSTC_VERSION.expect("Cannot use rustc without explicit version for \
135                           incremental compilation")
136                  .to_string()
137 }