]> git.proxmox.com Git - rustc.git/blame - src/librustc_incremental/persist/file_format.rs
New upstream version 1.32.0+dfsg1
[rustc.git] / src / librustc_incremental / persist / file_format.rs
CommitLineData
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
22use std::io::{self, Read};
23use std::path::Path;
2c00a5a8 24use std::fs;
9e0c209e
SL
25use std::env;
26
27use rustc::session::config::nightly_options;
8faf50e0 28use rustc_serialize::opaque::Encoder;
9e0c209e
SL
29
30/// The first few bytes of files generated by incremental compilation
31const FILE_MAGIC: &'static [u8] = b"RSIC";
32
33/// Change this if the header format changes
34const 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.
39const RUSTC_VERSION: Option<&'static str> = option_env!("CFG_VERSION");
40
8faf50e0
XL
41pub 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
61pub 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 116fn 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
126fn 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}