]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_incremental/src/persist/load.rs
Rollup merge of #99772 - ehuss:reenable-submodule-archive, r=Mark-Simulacrum
[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_data_structures::memmap::Mmap;
5 use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId};
6 use rustc_middle::ty::OnDiskCache;
7 use rustc_serialize::opaque::MemDecoder;
8 use rustc_serialize::Decodable;
9 use rustc_session::config::IncrementalStateAssertion;
10 use rustc_session::Session;
11 use std::path::Path;
12
13 use super::data::*;
14 use super::file_format;
15 use super::fs::*;
16 use super::work_product;
17
18 type WorkProductMap = FxHashMap<WorkProductId, WorkProduct>;
19
20 #[derive(Debug)]
21 /// Represents the result of an attempt to load incremental compilation data.
22 pub enum LoadResult<T> {
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.
29     DataOutOfDate,
30     /// An error occurred.
31     Error {
32         #[allow(missing_docs)]
33         message: String,
34     },
35 }
36
37 impl<T: Default> LoadResult<T> {
38     /// Accesses the data returned in [`LoadResult::Ok`].
39     pub fn open(self, sess: &Session) -> T {
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
60         match self {
61             LoadResult::Error { message } => {
62                 sess.warn(&message);
63                 Default::default()
64             }
65             LoadResult::DataOutOfDate => {
66                 if let Err(err) = delete_all_session_dir_contents(sess) {
67                     sess.err(&format!(
68                         "Failed to delete invalidated or incompatible \
69                          incremental compilation session directory contents `{}`: {}.",
70                         dep_graph_path(sess).display(),
71                         err
72                     ));
73                 }
74                 Default::default()
75             }
76             LoadResult::Ok { data } => data,
77         }
78     }
79 }
80
81 fn load_data(
82     report_incremental_info: bool,
83     path: &Path,
84     nightly_build: bool,
85 ) -> LoadResult<(Mmap, usize)> {
86     match file_format::read_file(report_incremental_info, path, nightly_build) {
87         Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
88         Ok(None) => {
89             // The file either didn't exist or was produced by an incompatible
90             // compiler version. Neither is an error.
91             LoadResult::DataOutOfDate
92         }
93         Err(err) => LoadResult::Error {
94             message: format!("could not load dep-graph from `{}`: {}", path.display(), err),
95         },
96     }
97 }
98
99 fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
100     debug!("delete_dirty_work_product({:?})", swp);
101     work_product::delete_workproduct_files(sess, &swp.work_product);
102 }
103
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),
109     Async(std::thread::JoinHandle<T>),
110 }
111
112 impl<T> MaybeAsync<LoadResult<T>> {
113     /// Accesses the data returned in [`LoadResult::Ok`] in an asynchronous way if possible.
114     pub fn open(self) -> LoadResult<T> {
115         match self {
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             }),
120         }
121     }
122 }
123
124 /// An asynchronous type for computing the dependency graph.
125 pub type DepGraphFuture = MaybeAsync<LoadResult<(SerializedDepGraph, WorkProductMap)>>;
126
127 /// Launch a thread and load the dependency graph in the background.
128 pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
129     // Since `sess` isn't `Sync`, we perform all accesses to `sess`
130     // before we fire the background thread.
131
132     let prof = sess.prof.clone();
133
134     if sess.opts.incremental.is_none() {
135         // No incremental compilation.
136         return MaybeAsync::Sync(LoadResult::Ok { data: Default::default() });
137     }
138
139     let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
140
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.
143     let path = dep_graph_path(&sess);
144     let report_incremental_info = sess.opts.unstable_opts.incremental_info;
145     let expected_hash = sess.opts.dep_tracking_hash(false);
146
147     let mut prev_work_products = FxHashMap::default();
148     let nightly_build = sess.is_nightly_build();
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);
155         let load_result = load_data(report_incremental_info, &work_products_path, nightly_build);
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 = MemDecoder::new(&work_products_data[..], start_pos);
160             let work_products: Vec<SerializedWorkProduct> =
161                 Decodable::decode(&mut work_product_decoder);
162
163             for swp in work_products {
164                 let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
165                     let exists = in_incr_comp_dir_sess(sess, path).exists();
166                     if !exists && sess.opts.unstable_opts.incremental_info {
167                         eprintln!("incremental: could not find file for work product: {path}",);
168                     }
169                     exists
170                 });
171
172                 if all_files_exist {
173                     debug!("reconcile_work_products: all files for {:?} exist", swp);
174                     prev_work_products.insert(swp.id, swp.work_product);
175                 } else {
176                     debug!("reconcile_work_products: some file for {:?} does not exist", swp);
177                     delete_dirty_work_product(sess, swp);
178                 }
179             }
180         }
181     }
182
183     MaybeAsync::Async(std::thread::spawn(move || {
184         let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
185
186         match load_data(report_incremental_info, &path, nightly_build) {
187             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
188             LoadResult::Error { message } => LoadResult::Error { message },
189             LoadResult::Ok { data: (bytes, start_pos) } => {
190                 let mut decoder = MemDecoder::new(&bytes, start_pos);
191                 let prev_commandline_args_hash = u64::decode(&mut decoder);
192
193                 if prev_commandline_args_hash != expected_hash {
194                     if report_incremental_info {
195                         eprintln!(
196                             "[incremental] completely ignoring cache because of \
197                                     differing commandline arguments"
198                         );
199                     }
200                     // We can't reuse the cache, purge it.
201                     debug!("load_dep_graph_new: differing commandline arg hashes");
202
203                     // No need to do any further work
204                     return LoadResult::DataOutOfDate;
205                 }
206
207                 let dep_graph = SerializedDepGraph::decode(&mut decoder);
208
209                 LoadResult::Ok { data: (dep_graph, prev_work_products) }
210             }
211         }
212     }))
213 }
214
215 /// Attempts to load the query result cache from disk
216 ///
217 /// If we are not in incremental compilation mode, returns `None`.
218 /// Otherwise, tries to load the query result cache from disk,
219 /// creating an empty cache if it could not be loaded.
220 pub fn load_query_result_cache<'a, C: OnDiskCache<'a>>(sess: &'a Session) -> Option<C> {
221     if sess.opts.incremental.is_none() {
222         return None;
223     }
224
225     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
226
227     match load_data(
228         sess.opts.unstable_opts.incremental_info,
229         &query_cache_path(sess),
230         sess.is_nightly_build(),
231     ) {
232         LoadResult::Ok { data: (bytes, start_pos) } => Some(C::new(sess, bytes, start_pos)),
233         _ => Some(C::new_empty(sess.source_map())),
234     }
235 }