rustc_incremental/persist/
load.rs1use std::path::{Path, PathBuf};
4use std::sync::Arc;
5
6use rustc_data_structures::memmap::Mmap;
7use rustc_data_structures::unord::UnordMap;
8use rustc_hashes::Hash64;
9use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProductMap};
10use rustc_middle::query::on_disk_cache::OnDiskCache;
11use rustc_serialize::Decodable;
12use rustc_serialize::opaque::MemDecoder;
13use rustc_session::Session;
14use rustc_session::config::IncrementalStateAssertion;
15use rustc_span::Symbol;
16use tracing::{debug, warn};
17
18use super::data::*;
19use super::fs::*;
20use super::save::build_dep_graph;
21use super::{file_format, work_product};
22use crate::errors;
23
24#[derive(Debug)]
25pub enum LoadResult<T> {
27 Ok {
29 #[allow(missing_docs)]
30 data: T,
31 },
32 DataOutOfDate,
34 LoadDepGraph(PathBuf, std::io::Error),
36}
37
38impl<T: Default> LoadResult<T> {
39 pub fn open(self, sess: &Session) -> T {
41 match (sess.opts.assert_incr_state, &self) {
43 (Some(IncrementalStateAssertion::NotLoaded), LoadResult::Ok { .. }) => {
44 sess.dcx().emit_fatal(errors::AssertNotLoaded);
45 }
46 (
47 Some(IncrementalStateAssertion::Loaded),
48 LoadResult::LoadDepGraph(..) | LoadResult::DataOutOfDate,
49 ) => {
50 sess.dcx().emit_fatal(errors::AssertLoaded);
51 }
52 _ => {}
53 };
54
55 match self {
56 LoadResult::LoadDepGraph(path, err) => {
57 sess.dcx().emit_warn(errors::LoadDepGraph { path, err });
58 Default::default()
59 }
60 LoadResult::DataOutOfDate => {
61 if let Err(err) = delete_all_session_dir_contents(sess) {
62 sess.dcx()
63 .emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err });
64 }
65 Default::default()
66 }
67 LoadResult::Ok { data } => data,
68 }
69 }
70}
71
72fn load_data(path: &Path, sess: &Session) -> LoadResult<(Mmap, usize)> {
73 match file_format::read_file(
74 path,
75 sess.opts.unstable_opts.incremental_info,
76 sess.is_nightly_build(),
77 sess.cfg_version,
78 ) {
79 Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
80 Ok(None) => {
81 LoadResult::DataOutOfDate
84 }
85 Err(err) => LoadResult::LoadDepGraph(path.to_path_buf(), err),
86 }
87}
88
89fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
90 debug!("delete_dirty_work_product({:?})", swp);
91 work_product::delete_workproduct_files(sess, &swp.work_product);
92}
93
94fn load_dep_graph(
95 sess: &Session,
96 deps: &DepsType,
97) -> LoadResult<(Arc<SerializedDepGraph>, WorkProductMap)> {
98 let prof = sess.prof.clone();
99
100 if sess.opts.incremental.is_none() {
101 return LoadResult::Ok { data: Default::default() };
103 }
104
105 let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
106
107 let path = dep_graph_path(sess);
110 let expected_hash = sess.opts.dep_tracking_hash(false);
111
112 let mut prev_work_products = UnordMap::default();
113
114 if sess.incr_comp_session_dir_opt().is_some() {
118 let work_products_path = work_products_path(sess);
119 let load_result = load_data(&work_products_path, sess);
120
121 if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
122 let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos)
124 else {
125 sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
126 return LoadResult::DataOutOfDate;
127 };
128 let work_products: Vec<SerializedWorkProduct> =
129 Decodable::decode(&mut work_product_decoder);
130
131 for swp in work_products {
132 let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| {
133 let exists = in_incr_comp_dir_sess(sess, path).exists();
134 if !exists && sess.opts.unstable_opts.incremental_info {
135 eprintln!("incremental: could not find file for work product: {path}",);
136 }
137 exists
138 });
139
140 if all_files_exist {
141 debug!("reconcile_work_products: all files for {:?} exist", swp);
142 prev_work_products.insert(swp.id, swp.work_product);
143 } else {
144 debug!("reconcile_work_products: some file for {:?} does not exist", swp);
145 delete_dirty_work_product(sess, swp);
146 }
147 }
148 }
149 }
150
151 let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
152
153 match load_data(&path, sess) {
154 LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
155 LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
156 LoadResult::Ok { data: (bytes, start_pos) } => {
157 let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
158 sess.dcx().emit_warn(errors::CorruptFile { path: &path });
159 return LoadResult::DataOutOfDate;
160 };
161 let prev_commandline_args_hash = Hash64::decode(&mut decoder);
162
163 if prev_commandline_args_hash != expected_hash {
164 if sess.opts.unstable_opts.incremental_info {
165 eprintln!(
166 "[incremental] completely ignoring cache because of \
167 differing commandline arguments"
168 );
169 }
170 debug!("load_dep_graph_new: differing commandline arg hashes");
172
173 return LoadResult::DataOutOfDate;
175 }
176
177 let dep_graph = SerializedDepGraph::decode::<DepsType>(&mut decoder, deps);
178
179 LoadResult::Ok { data: (dep_graph, prev_work_products) }
180 }
181 }
182}
183
184pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
190 if sess.opts.incremental.is_none() {
191 return None;
192 }
193
194 let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
195
196 let path = query_cache_path(sess);
197 match load_data(&path, sess) {
198 LoadResult::Ok { data: (bytes, start_pos) } => {
199 let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
200 sess.dcx().emit_warn(errors::CorruptFile { path: &path });
201 OnDiskCache::new_empty()
202 });
203 Some(cache)
204 }
205 _ => Some(OnDiskCache::new_empty()),
206 }
207}
208
209pub fn setup_dep_graph(sess: &Session, crate_name: Symbol, deps: &DepsType) -> DepGraph {
212 prepare_session_directory(sess, crate_name);
214
215 let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess, deps));
216
217 if sess.opts.incremental.is_some() {
218 sess.time("incr_comp_garbage_collect_session_directories", || {
219 if let Err(e) = garbage_collect_session_directories(sess) {
220 warn!(
221 "Error while trying to garbage collect incremental \
222 compilation cache directory: {}",
223 e
224 );
225 }
226 });
227 }
228
229 res.and_then(|result| {
230 let (prev_graph, prev_work_products) = result.open(sess);
231 build_dep_graph(sess, prev_graph, prev_work_products)
232 })
233 .unwrap_or_else(DepGraph::new_disabled)
234}