1 //! Code to save/load the dep-graph from files.
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;
12 use super::file_format;
14 use super::work_product;
16 type WorkProductMap = FxHashMap<WorkProductId, WorkProduct>;
18 pub enum LoadResult<T> {
21 Error { message: String },
24 impl<T: Default> LoadResult<T> {
25 pub fn open(self, sess: &Session) -> T {
27 LoadResult::Error { message } => {
31 LoadResult::DataOutOfDate => {
32 if let Err(err) = delete_all_session_dir_contents(sess) {
34 "Failed to delete invalidated or incompatible \
35 incremental compilation session directory contents `{}`: {}.",
36 dep_graph_path(sess).display(),
42 LoadResult::Ok { data } => data,
48 report_incremental_info: 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 },
55 // The file either didn't exist or was produced by an incompatible
56 // compiler version. Neither is an error.
57 LoadResult::DataOutOfDate
59 Err(err) => LoadResult::Error {
60 message: format!("could not load dep-graph from `{}`: {}", path.display(), err),
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);
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> {
75 Async(std::thread::JoinHandle<T>),
78 impl<T> MaybeAsync<LoadResult<T>> {
79 pub fn open(self) -> LoadResult<T> {
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),
89 pub type DepGraphFuture = MaybeAsync<LoadResult<(SerializedDepGraph, WorkProductMap)>>;
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.
96 let prof = sess.prof.clone();
98 if sess.opts.incremental.is_none() {
99 // No incremental compilation.
100 return MaybeAsync::Sync(LoadResult::Ok { data: Default::default() });
103 let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
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);
111 let mut prev_work_products = FxHashMap::default();
112 let nightly_build = sess.is_nightly_build();
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);
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| {
127 "Error decoding `work-products` from incremental \
128 compilation session directory: {}",
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);
139 all_files_exist = false;
141 if sess.opts.debugging_opts.incremental_info {
143 "incremental: could not find file for work \
152 debug!("reconcile_work_products: all files for {:?} exist", swp);
153 prev_work_products.insert(swp.id, swp.work_product);
155 debug!("reconcile_work_products: some file for {:?} does not exist", swp);
156 delete_dirty_work_product(sess, swp);
162 MaybeAsync::Async(std::thread::spawn(move || {
163 let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
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");
173 if prev_commandline_args_hash != expected_hash {
174 if report_incremental_info {
176 "[incremental] completely ignoring cache because of \
177 differing commandline arguments"
180 // We can't reuse the cache, purge it.
181 debug!("load_dep_graph_new: differing commandline arg hashes");
183 // No need to do any further work
184 return LoadResult::DataOutOfDate;
187 let dep_graph = SerializedDepGraph::decode(&mut decoder)
188 .expect("Error reading cached dep-graph");
190 LoadResult::Ok { data: (dep_graph, prev_work_products) }
196 /// Attempts to load the query result cache from disk
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() {
206 let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
209 sess.opts.debugging_opts.incremental_info,
210 &query_cache_path(sess),
211 sess.is_nightly_build(),
213 LoadResult::Ok { data: (bytes, start_pos) } => {
214 Some(OnDiskCache::new(sess, bytes, start_pos))
216 _ => Some(OnDiskCache::new_empty(sess.source_map())),