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