11//! Code to load the dep-graph from files.
22
3+ use std:: io;
34use std:: path:: PathBuf ;
45use std:: sync:: Arc ;
56
67use rustc_data_structures:: unord:: UnordMap ;
78use rustc_hashes:: Hash64 ;
89use rustc_middle:: dep_graph:: { DepGraph , SerializedDepGraph , WorkProductMap } ;
910use rustc_middle:: query:: on_disk_cache:: OnDiskCache ;
10- use rustc_serialize:: Decodable ;
11- use rustc_serialize:: opaque :: MemDecoder ;
11+ use rustc_serialize:: opaque :: { FileEncoder , MemDecoder } ;
12+ use rustc_serialize:: { Decodable , Encodable } ;
1213use rustc_session:: config:: IncrementalStateAssertion ;
1314use rustc_session:: { Session , StableCrateId } ;
1415use rustc_span:: Symbol ;
1516use tracing:: { debug, warn} ;
1617
1718use super :: data:: * ;
1819use super :: fs:: * ;
19- use super :: save:: build_dep_graph;
2020use super :: { file_format, work_product} ;
2121use crate :: errors;
2222use crate :: persist:: file_format:: { OpenFile , OpenFileError } ;
2323
2424#[ derive( Debug ) ]
2525/// Represents the result of an attempt to load incremental compilation data.
26- pub enum LoadResult < T > {
26+ enum LoadResult {
2727 /// Loading was successful.
28- Ok {
29- #[ allow( missing_docs) ]
30- data : T ,
31- } ,
28+ Ok { prev_graph : Arc < SerializedDepGraph > , prev_work_products : WorkProductMap } ,
3229 /// The file either didn't exist or was produced by an incompatible compiler version.
3330 DataOutOfDate ,
34- /// Loading the dep graph failed.
35- LoadDepGraph ( PathBuf , std:: io:: Error ) ,
36- }
37-
38- impl < T : Default > LoadResult < T > {
39- /// Accesses the data returned in [`LoadResult::Ok`].
40- pub fn open ( self , sess : & Session ) -> T {
41- // Emit a fatal error if `-Zassert-incr-state` is present and unsatisfied.
42- maybe_assert_incr_state ( sess, & self ) ;
43-
44- match self {
45- LoadResult :: LoadDepGraph ( path, err) => {
46- sess. dcx ( ) . emit_warn ( errors:: LoadDepGraph { path, err } ) ;
47- Default :: default ( )
48- }
49- LoadResult :: DataOutOfDate => {
50- if let Err ( err) = delete_all_session_dir_contents ( sess) {
51- sess. dcx ( )
52- . emit_err ( errors:: DeleteIncompatible { path : dep_graph_path ( sess) , err } ) ;
53- }
54- Default :: default ( )
55- }
56- LoadResult :: Ok { data } => data,
57- }
58- }
31+ /// Loading failed due to an unexpected I/O error.
32+ IoError { path : PathBuf , err : io:: Error } ,
5933}
6034
6135fn delete_dirty_work_product ( sess : & Session , swp : SerializedWorkProduct ) {
6236 debug ! ( "delete_dirty_work_product({:?})" , swp) ;
6337 work_product:: delete_workproduct_files ( sess, & swp. work_product ) ;
6438}
6539
66- fn load_dep_graph ( sess : & Session ) -> LoadResult < ( Arc < SerializedDepGraph > , WorkProductMap ) > {
67- let prof = sess. prof . clone ( ) ;
68-
69- if sess. opts . incremental . is_none ( ) {
70- // No incremental compilation.
71- return LoadResult :: Ok { data : Default :: default ( ) } ;
72- }
40+ fn load_dep_graph ( sess : & Session ) -> LoadResult {
41+ assert ! ( sess. opts. incremental. is_some( ) ) ;
7342
7443 let _timer = sess. prof . generic_activity ( "incr_comp_prepare_load_dep_graph" ) ;
7544
@@ -117,11 +86,11 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
11786 }
11887 }
11988
120- let _prof_timer = prof. generic_activity ( "incr_comp_load_dep_graph" ) ;
89+ let _prof_timer = sess . prof . generic_activity ( "incr_comp_load_dep_graph" ) ;
12190
12291 match file_format:: open_incremental_file ( sess, & path) {
12392 Err ( OpenFileError :: NotFoundOrHeaderMismatch ) => LoadResult :: DataOutOfDate ,
124- Err ( OpenFileError :: IoError { err } ) => LoadResult :: LoadDepGraph ( path. to_owned ( ) , err) ,
93+ Err ( OpenFileError :: IoError { err } ) => LoadResult :: IoError { path : path . to_owned ( ) , err } ,
12594 Ok ( OpenFile { mmap, start_pos } ) => {
12695 let Ok ( mut decoder) = MemDecoder :: new ( & mmap, start_pos) else {
12796 sess. dcx ( ) . emit_warn ( errors:: CorruptFile { path : & path } ) ;
@@ -143,9 +112,9 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
143112 return LoadResult :: DataOutOfDate ;
144113 }
145114
146- let dep_graph = SerializedDepGraph :: decode ( & mut decoder) ;
115+ let prev_graph = SerializedDepGraph :: decode ( & mut decoder) ;
147116
148- LoadResult :: Ok { data : ( dep_graph , prev_work_products) }
117+ LoadResult :: Ok { prev_graph , prev_work_products }
149118 }
150119 }
151120}
@@ -179,14 +148,14 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
179148
180149/// Emits a fatal error if the assertion in `-Zassert-incr-state` doesn't match
181150/// the outcome of trying to load previous-session state.
182- fn maybe_assert_incr_state ( sess : & Session , load_result : & LoadResult < impl Sized > ) {
151+ fn maybe_assert_incr_state ( sess : & Session , load_result : & LoadResult ) {
183152 // Return immediately if there's nothing to assert.
184153 let Some ( assertion) = sess. opts . unstable_opts . assert_incr_state else { return } ;
185154
186155 // Match exhaustively to make sure we don't miss any cases.
187156 let loaded = match load_result {
188157 LoadResult :: Ok { .. } => true ,
189- LoadResult :: DataOutOfDate | LoadResult :: LoadDepGraph ( .. ) => false ,
158+ LoadResult :: DataOutOfDate | LoadResult :: IoError { .. } => false ,
190159 } ;
191160
192161 match assertion {
@@ -203,33 +172,64 @@ fn maybe_assert_incr_state(sess: &Session, load_result: &LoadResult<impl Sized>)
203172 }
204173}
205174
206- /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
207- /// new graph to an incremental session directory.
175+ /// Loads the previous session's dependency graph from disk if possible, and
176+ /// sets up streaming output for the current session's dep graph data into an
177+ /// incremental session directory.
178+ ///
179+ /// In non-incremental mode, a dummy dep graph is returned immediately.
208180pub fn setup_dep_graph (
209181 sess : & Session ,
210182 crate_name : Symbol ,
211183 stable_crate_id : StableCrateId ,
212184) -> DepGraph {
185+ if sess. opts . incremental . is_none ( ) {
186+ return DepGraph :: new_disabled ( ) ;
187+ }
188+
213189 // `load_dep_graph` can only be called after `prepare_session_directory`.
214190 prepare_session_directory ( sess, crate_name, stable_crate_id) ;
191+ // Try to load the previous session's dep graph and work products.
192+ let load_result = load_dep_graph ( sess) ;
193+
194+ sess. time ( "incr_comp_garbage_collect_session_directories" , || {
195+ if let Err ( e) = garbage_collect_session_directories ( sess) {
196+ warn ! (
197+ "Error while trying to garbage collect incremental compilation \
198+ cache directory: {e}",
199+ ) ;
200+ }
201+ } ) ;
215202
216- let res = sess. opts . build_dep_graph ( ) . then ( || load_dep_graph ( sess) ) ;
203+ // Emit a fatal error if `-Zassert-incr-state` is present and unsatisfied.
204+ maybe_assert_incr_state ( sess, & load_result) ;
217205
218- if sess . opts . incremental . is_some ( ) {
219- sess . time ( "incr_comp_garbage_collect_session_directories" , || {
220- if let Err ( e ) = garbage_collect_session_directories ( sess ) {
221- warn ! (
222- "Error while trying to garbage collect incremental \
223- compilation cache directory: {}" ,
224- e
225- ) ;
206+ let ( prev_graph , prev_work_products ) = match load_result {
207+ LoadResult :: IoError { path , err } => {
208+ sess . dcx ( ) . emit_warn ( errors :: LoadDepGraph { path , err } ) ;
209+ Default :: default ( )
210+ }
211+ LoadResult :: DataOutOfDate => {
212+ if let Err ( err ) = delete_all_session_dir_contents ( sess ) {
213+ sess . dcx ( ) . emit_err ( errors :: DeleteIncompatible { path : dep_graph_path ( sess ) , err } ) ;
226214 }
227- } ) ;
228- }
215+ Default :: default ( )
216+ }
217+ LoadResult :: Ok { prev_graph, prev_work_products } => ( prev_graph, prev_work_products) ,
218+ } ;
219+
220+ // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors.
221+ let path_buf = staging_dep_graph_path ( sess) ;
222+
223+ let mut encoder = FileEncoder :: new ( & path_buf) . unwrap_or_else ( |err| {
224+ // We're in incremental mode but couldn't set up streaming output of the dep graph.
225+ // Exit immediately instead of continuing in an inconsistent and untested state.
226+ sess. dcx ( ) . emit_fatal ( errors:: CreateDepGraph { path : & path_buf, err } )
227+ } ) ;
228+
229+ file_format:: write_file_header ( & mut encoder, sess) ;
230+
231+ // First encode the commandline arguments hash
232+ sess. opts . dep_tracking_hash ( false ) . encode ( & mut encoder) ;
229233
230- res. and_then ( |result| {
231- let ( prev_graph, prev_work_products) = result. open ( sess) ;
232- build_dep_graph ( sess, prev_graph, prev_work_products)
233- } )
234- . unwrap_or_else ( DepGraph :: new_disabled)
234+ DepGraph :: new ( sess, prev_graph, prev_work_products, encoder)
235235}
0 commit comments