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.
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.
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.
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.
22 use std::io::{self, Read};
27 use rustc::session::config::nightly_options;
29 /// The first few bytes of files generated by incremental compilation
30 const FILE_MAGIC: &'static [u8] = b"RSIC";
32 /// Change this if the header format changes
33 const HEADER_FORMAT_VERSION: u16 = 0;
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");
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])?;
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())?;
53 /// Reads the contents of a file with a file header as defined in this module.
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
62 pub fn read_file(report_incremental_info: bool, path: &Path)
63 -> io::Result<Option<(Vec<u8>, usize)>>
69 let data = fs::read(path)?;
71 let mut file = io::Cursor::new(data);
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");
84 // Check HEADER_FORMAT_VERSION
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);
92 if header_format_version != HEADER_FORMAT_VERSION {
93 report_format_mismatch(report_incremental_info, path, "Wrong HEADER_FORMAT_VERSION");
98 // Check RUSTC_VERSION
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)?;
107 if buffer != rustc_version().as_bytes() {
108 report_format_mismatch(report_incremental_info, path, "Different compiler version");
113 let post_header_start_pos = file.position() as usize;
114 Ok(Some((file.into_inner(), post_header_start_pos)))
117 fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &str) {
118 debug!("read_file: {}", message);
120 if report_incremental_info {
121 println!("[incremental] ignoring cache artifact `{}`: {}",
122 file.file_name().unwrap().to_string_lossy(),
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()
134 RUSTC_VERSION.expect("Cannot use rustc without explicit version for \
135 incremental compilation")