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