1 //! Code to save/load the dep-graph from files.
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
::Decoder
;
8 use rustc_serialize
::Decodable
;
9 use rustc_session
::config
::IncrementalStateAssertion
;
10 use rustc_session
::Session
;
14 use super::file_format
;
16 use super::work_product
;
18 type WorkProductMap
= FxHashMap
<WorkProductId
, WorkProduct
>;
21 /// Represents the result of an attempt to load incremental compilation data.
22 pub enum LoadResult
<T
> {
23 /// Loading was successful.
25 #[allow(missing_docs)]
28 /// The file either didn't exist or was produced by an incompatible compiler version.
30 /// An error occurred.
32 #[allow(missing_docs)]
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 { .. }
) => {
44 "We asserted that the incremental cache should not be loaded, \
49 Some(IncrementalStateAssertion
::Loaded
),
50 LoadResult
::Error { .. }
| LoadResult
::DataOutOfDate
,
53 "We asserted that an existing incremental cache directory should \
54 be successfully loaded, but it was not.",
61 LoadResult
::Error { message }
=> {
65 LoadResult
::DataOutOfDate
=> {
66 if let Err(err
) = delete_all_session_dir_contents(sess
) {
68 "Failed to delete invalidated or incompatible \
69 incremental compilation session directory contents `{}`: {}.",
70 dep_graph_path(sess
).display(),
76 LoadResult
::Ok { data }
=> data
,
82 report_incremental_info
: 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 }
,
89 // The file either didn't exist or was produced by an incompatible
90 // compiler version. Neither is an error.
91 LoadResult
::DataOutOfDate
93 Err(err
) => LoadResult
::Error
{
94 message
: format
!("could not load dep-graph from `{}`: {}", path
.display(), err
),
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
);
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
> {
109 Async(std
::thread
::JoinHandle
<T
>),
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
> {
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
),
124 /// An asynchronous type for computing the dependency graph.
125 pub type DepGraphFuture
= MaybeAsync
<LoadResult
<(SerializedDepGraph
, WorkProductMap
)>>;
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.
132 let prof
= sess
.prof
.clone();
134 if sess
.opts
.incremental
.is_none() {
135 // No incremental compilation.
136 return MaybeAsync
::Sync(LoadResult
::Ok { data: Default::default() }
);
139 let _timer
= sess
.prof
.generic_activity("incr_comp_prepare_load_dep_graph");
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
.debugging_opts
.incremental_info
;
145 let expected_hash
= sess
.opts
.dep_tracking_hash(false);
147 let mut prev_work_products
= FxHashMap
::default();
148 let nightly_build
= sess
.is_nightly_build();
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
);
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
= Decoder
::new(&work_products_data
[..], start_pos
);
160 let work_products
: Vec
<SerializedWorkProduct
> =
161 Decodable
::decode(&mut work_product_decoder
);
163 for swp
in work_products
{
164 let mut all_files_exist
= true;
165 if let Some(ref file_name
) = swp
.work_product
.saved_file
{
166 let path
= in_incr_comp_dir_sess(sess
, file_name
);
168 all_files_exist
= false;
170 if sess
.opts
.debugging_opts
.incremental_info
{
172 "incremental: could not find file for work \
181 debug
!("reconcile_work_products: all files for {:?} exist", swp
);
182 prev_work_products
.insert(swp
.id
, swp
.work_product
);
184 debug
!("reconcile_work_products: some file for {:?} does not exist", swp
);
185 delete_dirty_work_product(sess
, swp
);
191 MaybeAsync
::Async(std
::thread
::spawn(move || {
192 let _prof_timer
= prof
.generic_activity("incr_comp_load_dep_graph");
194 match load_data(report_incremental_info
, &path
, nightly_build
) {
195 LoadResult
::DataOutOfDate
=> LoadResult
::DataOutOfDate
,
196 LoadResult
::Error { message }
=> LoadResult
::Error { message }
,
197 LoadResult
::Ok { data: (bytes, start_pos) }
=> {
198 let mut decoder
= Decoder
::new(&bytes
, start_pos
);
199 let prev_commandline_args_hash
= u64::decode(&mut decoder
);
201 if prev_commandline_args_hash
!= expected_hash
{
202 if report_incremental_info
{
204 "[incremental] completely ignoring cache because of \
205 differing commandline arguments"
208 // We can't reuse the cache, purge it.
209 debug
!("load_dep_graph_new: differing commandline arg hashes");
211 // No need to do any further work
212 return LoadResult
::DataOutOfDate
;
215 let dep_graph
= SerializedDepGraph
::decode(&mut decoder
);
217 LoadResult
::Ok { data: (dep_graph, prev_work_products) }
223 /// Attempts to load the query result cache from disk
225 /// If we are not in incremental compilation mode, returns `None`.
226 /// Otherwise, tries to load the query result cache from disk,
227 /// creating an empty cache if it could not be loaded.
228 pub fn load_query_result_cache
<'a
, C
: OnDiskCache
<'a
>>(sess
: &'a Session
) -> Option
<C
> {
229 if sess
.opts
.incremental
.is_none() {
233 let _prof_timer
= sess
.prof
.generic_activity("incr_comp_load_query_result_cache");
236 sess
.opts
.debugging_opts
.incremental_info
,
237 &query_cache_path(sess
),
238 sess
.is_nightly_build(),
240 LoadResult
::Ok { data: (bytes, start_pos) }
=> Some(C
::new(sess
, bytes
, start_pos
)),
241 _
=> Some(C
::new_empty(sess
.source_map())),