]>
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, |
5e7ed085 | 30 | /// An error occurred. |
a2a8927a XL |
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> = | |
5099ac24 | 161 | Decodable::decode(&mut work_product_decoder); |
94b46f34 XL |
162 | |
163 | for swp in work_products { | |
164 | let mut all_files_exist = true; | |
f9f354fc | 165 | if let Some(ref file_name) = swp.work_product.saved_file { |
94b46f34 XL |
166 | let path = in_incr_comp_dir_sess(sess, file_name); |
167 | if !path.exists() { | |
168 | all_files_exist = false; | |
169 | ||
170 | if sess.opts.debugging_opts.incremental_info { | |
dfeec247 XL |
171 | eprintln!( |
172 | "incremental: could not find file for work \ | |
173 | product: {}", | |
174 | path.display() | |
175 | ); | |
94b46f34 XL |
176 | } |
177 | } | |
178 | } | |
179 | ||
180 | if all_files_exist { | |
181 | debug!("reconcile_work_products: all files for {:?} exist", swp); | |
182 | prev_work_products.insert(swp.id, swp.work_product); | |
183 | } else { | |
184 | debug!("reconcile_work_products: some file for {:?} does not exist", swp); | |
185 | delete_dirty_work_product(sess, swp); | |
186 | } | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
ff7c6d11 | 191 | MaybeAsync::Async(std::thread::spawn(move || { |
dfeec247 XL |
192 | let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph"); |
193 | ||
fc512014 | 194 | match load_data(report_incremental_info, &path, nightly_build) { |
dfeec247 XL |
195 | LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, |
196 | LoadResult::Error { message } => LoadResult::Error { message }, | |
197 | LoadResult::Ok { data: (bytes, start_pos) } => { | |
198 | let mut decoder = Decoder::new(&bytes, start_pos); | |
5099ac24 | 199 | let prev_commandline_args_hash = u64::decode(&mut decoder); |
dfeec247 XL |
200 | |
201 | if prev_commandline_args_hash != expected_hash { | |
202 | if report_incremental_info { | |
6a06907d | 203 | eprintln!( |
dfeec247 XL |
204 | "[incremental] completely ignoring cache because of \ |
205 | differing commandline arguments" | |
206 | ); | |
ff7c6d11 | 207 | } |
dfeec247 XL |
208 | // We can't reuse the cache, purge it. |
209 | debug!("load_dep_graph_new: differing commandline arg hashes"); | |
041b39d2 | 210 | |
dfeec247 XL |
211 | // No need to do any further work |
212 | return LoadResult::DataOutOfDate; | |
ff7c6d11 | 213 | } |
dfeec247 | 214 | |
5099ac24 | 215 | let dep_graph = SerializedDepGraph::decode(&mut decoder); |
dfeec247 | 216 | |
17df50a5 | 217 | LoadResult::Ok { data: (dep_graph, prev_work_products) } |
ff7c6d11 | 218 | } |
dfeec247 | 219 | } |
ff7c6d11 | 220 | })) |
8bb4bdeb | 221 | } |
abe05a73 | 222 | |
fc512014 XL |
223 | /// Attempts to load the query result cache from disk |
224 | /// | |
225 | /// If we are not in incremental compilation mode, returns `None`. | |
226 | /// Otherwise, tries to load the query result cache from disk, | |
227 | /// creating an empty cache if it could not be loaded. | |
136023e0 | 228 | pub fn load_query_result_cache<'a, C: OnDiskCache<'a>>(sess: &'a Session) -> Option<C> { |
ba9703b0 | 229 | if sess.opts.incremental.is_none() { |
fc512014 | 230 | return None; |
abe05a73 XL |
231 | } |
232 | ||
e74abb32 XL |
233 | let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); |
234 | ||
fc512014 XL |
235 | match load_data( |
236 | sess.opts.debugging_opts.incremental_info, | |
237 | &query_cache_path(sess), | |
238 | sess.is_nightly_build(), | |
239 | ) { | |
136023e0 XL |
240 | LoadResult::Ok { data: (bytes, start_pos) } => Some(C::new(sess, bytes, start_pos)), |
241 | _ => Some(C::new_empty(sess.source_map())), | |
abe05a73 XL |
242 | } |
243 | } |