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