]>
Commit | Line | Data |
---|---|---|
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 |
12 | use std::env; |
13 | use std::fs; | |
9e0c209e SL |
14 | use std::io::{self, Read}; |
15 | use std::path::Path; | |
9e0c209e | 16 | |
5869c6ff | 17 | use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; |
9e0c209e | 18 | |
9fa01778 | 19 | /// The first few bytes of files generated by incremental compilation. |
0731742a | 20 | const FILE_MAGIC: &[u8] = b"RSIC"; |
9e0c209e | 21 | |
9fa01778 | 22 | /// Change this if the header format changes. |
9e0c209e SL |
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 | |
9fa01778 | 27 | /// the Git commit hash. |
0731742a | 28 | const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION"); |
9e0c209e | 29 | |
5869c6ff XL |
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 | ])?; | |
9e0c209e | 36 | |
fc512014 | 37 | let rustc_version = rustc_version(nightly_build); |
9e0c209e | 38 | assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize); |
5869c6ff XL |
39 | stream.emit_raw_bytes(&[rustc_version.len() as u8])?; |
40 | stream.emit_raw_bytes(rustc_version.as_bytes()) | |
9e0c209e SL |
41 | } |
42 | ||
43 | /// Reads the contents of a file with a file header as defined in this module. | |
44 | /// | |
abe05a73 | 45 | /// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a |
9e0c209e | 46 | /// compatible compiler version. `data` is the entire contents of the file |
abe05a73 | 47 | /// and `pos` points to the first byte after the header. |
9e0c209e SL |
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. | |
dfeec247 XL |
52 | pub fn read_file( |
53 | report_incremental_info: bool, | |
54 | path: &Path, | |
fc512014 | 55 | nightly_build: bool, |
dfeec247 | 56 | ) -> io::Result<Option<(Vec<u8>, usize)>> { |
5869c6ff XL |
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 | }; | |
abe05a73 XL |
62 | |
63 | let mut file = io::Cursor::new(data); | |
9e0c209e SL |
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 { | |
ff7c6d11 | 71 | report_format_mismatch(report_incremental_info, path, "Wrong FILE_MAGIC"); |
dfeec247 | 72 | return Ok(None); |
9e0c209e SL |
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)?; | |
dfeec247 XL |
81 | let header_format_version = |
82 | (header_format_version[0] as u16) | ((header_format_version[1] as u16) << 8); | |
9e0c209e SL |
83 | |
84 | if header_format_version != HEADER_FORMAT_VERSION { | |
ff7c6d11 | 85 | report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION"); |
dfeec247 | 86 | return Ok(None); |
9e0c209e SL |
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; | |
74b04a01 | 95 | let mut buffer = vec![0; rustc_version_str_len]; |
cc61c64b | 96 | file.read_exact(&mut buffer)?; |
9e0c209e | 97 | |
fc512014 | 98 | if buffer != rustc_version(nightly_build).as_bytes() { |
ff7c6d11 | 99 | report_format_mismatch(report_incremental_info, path, "Different compiler version"); |
9e0c209e SL |
100 | return Ok(None); |
101 | } | |
102 | } | |
103 | ||
abe05a73 XL |
104 | let post_header_start_pos = file.position() as usize; |
105 | Ok(Some((file.into_inner(), post_header_start_pos))) | |
9e0c209e SL |
106 | } |
107 | ||
ff7c6d11 | 108 | fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) { |
476ff2be SL |
109 | debug!("read_file: {}", message); |
110 | ||
ff7c6d11 | 111 | if report_incremental_info { |
6a06907d | 112 | eprintln!( |
dfeec247 XL |
113 | "[incremental] ignoring cache artifact `{}`: {}", |
114 | file.file_name().unwrap().to_string_lossy(), | |
115 | message | |
116 | ); | |
476ff2be SL |
117 | } |
118 | } | |
119 | ||
fc512014 XL |
120 | fn rustc_version(nightly_build: bool) -> String { |
121 | if nightly_build { | |
9e0c209e | 122 | if let Some(val) = env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") { |
dfeec247 | 123 | return val.to_string_lossy().into_owned(); |
9e0c209e SL |
124 | } |
125 | } | |
126 | ||
dfeec247 XL |
127 | RUSTC_VERSION |
128 | .expect( | |
129 | "Cannot use rustc without explicit version for \ | |
130 | incremental compilation", | |
131 | ) | |
132 | .to_string() | |
9e0c209e | 133 | } |