rustc_incremental/persist/
load.rs
1use 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(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkProductMap)> {
95 let prof = sess.prof.clone();
96
97 if sess.opts.incremental.is_none() {
98 return LoadResult::Ok { data: Default::default() };
100 }
101
102 let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
103
104 let path = dep_graph_path(sess);
107 let expected_hash = sess.opts.dep_tracking_hash(false);
108
109 let mut prev_work_products = UnordMap::default();
110
111 if sess.incr_comp_session_dir_opt().is_some() {
115 let work_products_path = work_products_path(sess);
116 let load_result = load_data(&work_products_path, sess);
117
118 if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
119 let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos)
121 else {
122 sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
123 return LoadResult::DataOutOfDate;
124 };
125 let work_products: Vec<SerializedWorkProduct> =
126 Decodable::decode(&mut work_product_decoder);
127
128 for swp in work_products {
129 let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| {
130 let exists = in_incr_comp_dir_sess(sess, path).exists();
131 if !exists && sess.opts.unstable_opts.incremental_info {
132 eprintln!("incremental: could not find file for work product: {path}",);
133 }
134 exists
135 });
136
137 if all_files_exist {
138 debug!("reconcile_work_products: all files for {:?} exist", swp);
139 prev_work_products.insert(swp.id, swp.work_product);
140 } else {
141 debug!("reconcile_work_products: some file for {:?} does not exist", swp);
142 delete_dirty_work_product(sess, swp);
143 }
144 }
145 }
146 }
147
148 let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
149
150 match load_data(&path, sess) {
151 LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
152 LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
153 LoadResult::Ok { data: (bytes, start_pos) } => {
154 let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
155 sess.dcx().emit_warn(errors::CorruptFile { path: &path });
156 return LoadResult::DataOutOfDate;
157 };
158 let prev_commandline_args_hash = Hash64::decode(&mut decoder);
159
160 if prev_commandline_args_hash != expected_hash {
161 if sess.opts.unstable_opts.incremental_info {
162 eprintln!(
163 "[incremental] completely ignoring cache because of \
164 differing commandline arguments"
165 );
166 }
167 debug!("load_dep_graph_new: differing commandline arg hashes");
169
170 return LoadResult::DataOutOfDate;
172 }
173
174 let dep_graph = SerializedDepGraph::decode::<DepsType>(&mut decoder);
175
176 LoadResult::Ok { data: (dep_graph, prev_work_products) }
177 }
178 }
179}
180
181pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
187 if sess.opts.incremental.is_none() {
188 return None;
189 }
190
191 let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
192
193 let path = query_cache_path(sess);
194 match load_data(&path, sess) {
195 LoadResult::Ok { data: (bytes, start_pos) } => {
196 let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
197 sess.dcx().emit_warn(errors::CorruptFile { path: &path });
198 OnDiskCache::new_empty()
199 });
200 Some(cache)
201 }
202 _ => Some(OnDiskCache::new_empty()),
203 }
204}
205
206pub fn setup_dep_graph(sess: &Session, crate_name: Symbol) -> DepGraph {
209 prepare_session_directory(sess, crate_name);
211
212 let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
213
214 if sess.opts.incremental.is_some() {
215 sess.time("incr_comp_garbage_collect_session_directories", || {
216 if let Err(e) = garbage_collect_session_directories(sess) {
217 warn!(
218 "Error while trying to garbage collect incremental \
219 compilation cache directory: {}",
220 e
221 );
222 }
223 });
224 }
225
226 res.and_then(|result| {
227 let (prev_graph, prev_work_products) = result.open(sess);
228 build_dep_graph(sess, prev_graph, prev_work_products)
229 })
230 .unwrap_or_else(DepGraph::new_disabled)
231}