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