]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/load.rs
Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung
[rust.git] / src / librustc_incremental / persist / load.rs
1 //! Code to save/load the dep-graph from files.
2
3 use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId};
4 use rustc::ty::query::OnDiskCache;
5 use rustc::ty::TyCtxt;
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_serialize::opaque::Decoder;
8 use rustc_serialize::Decodable as RustcDecodable;
9 use rustc_session::Session;
10 use std::path::Path;
11
12 use super::data::*;
13 use super::file_format;
14 use super::fs::*;
15 use super::work_product;
16
17 pub fn dep_graph_tcx_init(tcx: TyCtxt<'_>) {
18     if !tcx.dep_graph.is_fully_enabled() {
19         return;
20     }
21
22     tcx.allocate_metadata_dep_nodes();
23 }
24
25 type WorkProductMap = FxHashMap<WorkProductId, WorkProduct>;
26
27 pub enum LoadResult<T> {
28     Ok { data: T },
29     DataOutOfDate,
30     Error { message: String },
31 }
32
33 impl LoadResult<(PreviousDepGraph, WorkProductMap)> {
34     pub fn open(self, sess: &Session) -> (PreviousDepGraph, WorkProductMap) {
35         match self {
36             LoadResult::Error { message } => {
37                 sess.warn(&message);
38                 Default::default()
39             }
40             LoadResult::DataOutOfDate => {
41                 if let Err(err) = delete_all_session_dir_contents(sess) {
42                     sess.err(&format!(
43                         "Failed to delete invalidated or incompatible \
44                                       incremental compilation session directory contents `{}`: {}.",
45                         dep_graph_path(sess).display(),
46                         err
47                     ));
48                 }
49                 Default::default()
50             }
51             LoadResult::Ok { data } => data,
52         }
53     }
54 }
55
56 fn load_data(report_incremental_info: bool, path: &Path) -> LoadResult<(Vec<u8>, usize)> {
57     match file_format::read_file(report_incremental_info, path) {
58         Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
59         Ok(None) => {
60             // The file either didn't exist or was produced by an incompatible
61             // compiler version. Neither is an error.
62             LoadResult::DataOutOfDate
63         }
64         Err(err) => LoadResult::Error {
65             message: format!("could not load dep-graph from `{}`: {}", path.display(), err),
66         },
67     }
68 }
69
70 fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
71     debug!("delete_dirty_work_product({:?})", swp);
72     work_product::delete_workproduct_files(sess, &swp.work_product);
73 }
74
75 /// Either a result that has already be computed or a
76 /// handle that will let us wait until it is computed
77 /// by a background thread.
78 pub enum MaybeAsync<T> {
79     Sync(T),
80     Async(std::thread::JoinHandle<T>),
81 }
82 impl<T> MaybeAsync<T> {
83     pub fn open(self) -> std::thread::Result<T> {
84         match self {
85             MaybeAsync::Sync(result) => Ok(result),
86             MaybeAsync::Async(handle) => handle.join(),
87         }
88     }
89 }
90
91 pub type DepGraphFuture = MaybeAsync<LoadResult<(PreviousDepGraph, WorkProductMap)>>;
92
93 /// Launch a thread and load the dependency graph in the background.
94 pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
95     // Since `sess` isn't `Sync`, we perform all accesses to `sess`
96     // before we fire the background thread.
97
98     let prof = sess.prof.clone();
99
100     if sess.opts.incremental.is_none() {
101         // No incremental compilation.
102         return MaybeAsync::Sync(LoadResult::Ok { data: Default::default() });
103     }
104
105     let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
106
107     // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
108     // Fortunately, we just checked that this isn't the case.
109     let path = dep_graph_path_from(&sess.incr_comp_session_dir());
110     let report_incremental_info = sess.opts.debugging_opts.incremental_info;
111     let expected_hash = sess.opts.dep_tracking_hash();
112
113     let mut prev_work_products = FxHashMap::default();
114
115     // If we are only building with -Zquery-dep-graph but without an actual
116     // incr. comp. session directory, we skip this. Otherwise we'd fail
117     // when trying to load work products.
118     if sess.incr_comp_session_dir_opt().is_some() {
119         let work_products_path = work_products_path(sess);
120         let load_result = load_data(report_incremental_info, &work_products_path);
121
122         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
123             // Decode the list of work_products
124             let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos);
125             let work_products: Vec<SerializedWorkProduct> =
126                 RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| {
127                     let msg = format!(
128                         "Error decoding `work-products` from incremental \
129                                     compilation session directory: {}",
130                         e
131                     );
132                     sess.fatal(&msg[..])
133                 });
134
135             for swp in work_products {
136                 let mut all_files_exist = true;
137                 for &(_, ref file_name) in swp.work_product.saved_files.iter() {
138                     let path = in_incr_comp_dir_sess(sess, file_name);
139                     if !path.exists() {
140                         all_files_exist = false;
141
142                         if sess.opts.debugging_opts.incremental_info {
143                             eprintln!(
144                                 "incremental: could not find file for work \
145                                     product: {}",
146                                 path.display()
147                             );
148                         }
149                     }
150                 }
151
152                 if all_files_exist {
153                     debug!("reconcile_work_products: all files for {:?} exist", swp);
154                     prev_work_products.insert(swp.id, swp.work_product);
155                 } else {
156                     debug!("reconcile_work_products: some file for {:?} does not exist", swp);
157                     delete_dirty_work_product(sess, swp);
158                 }
159             }
160         }
161     }
162
163     MaybeAsync::Async(std::thread::spawn(move || {
164         let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
165
166         match load_data(report_incremental_info, &path) {
167             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
168             LoadResult::Error { message } => LoadResult::Error { message },
169             LoadResult::Ok { data: (bytes, start_pos) } => {
170                 let mut decoder = Decoder::new(&bytes, start_pos);
171                 let prev_commandline_args_hash = u64::decode(&mut decoder)
172                     .expect("Error reading commandline arg hash from cached dep-graph");
173
174                 if prev_commandline_args_hash != expected_hash {
175                     if report_incremental_info {
176                         println!(
177                             "[incremental] completely ignoring cache because of \
178                                     differing commandline arguments"
179                         );
180                     }
181                     // We can't reuse the cache, purge it.
182                     debug!("load_dep_graph_new: differing commandline arg hashes");
183
184                     // No need to do any further work
185                     return LoadResult::DataOutOfDate;
186                 }
187
188                 let dep_graph = SerializedDepGraph::decode(&mut decoder)
189                     .expect("Error reading cached dep-graph");
190
191                 LoadResult::Ok { data: (PreviousDepGraph::new(dep_graph), prev_work_products) }
192             }
193         }
194     }))
195 }
196
197 pub fn load_query_result_cache(sess: &Session) -> OnDiskCache<'_> {
198     if sess.opts.incremental.is_none() || !sess.opts.debugging_opts.incremental_queries {
199         return OnDiskCache::new_empty(sess.source_map());
200     }
201
202     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
203
204     match load_data(sess.opts.debugging_opts.incremental_info, &query_cache_path(sess)) {
205         LoadResult::Ok { data: (bytes, start_pos) } => OnDiskCache::new(sess, bytes, start_pos),
206         _ => OnDiskCache::new_empty(sess.source_map()),
207     }
208 }