]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_incremental/src/persist/save.rs
Rollup merge of #86344 - est31:maybe-uninit-extra, r=RalfJung
[rust.git] / compiler / rustc_incremental / src / persist / save.rs
1 use rustc_data_structures::fx::FxHashMap;
2 use rustc_data_structures::sync::join;
3 use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId};
4 use rustc_middle::ty::TyCtxt;
5 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
6 use rustc_serialize::Encodable as RustcEncodable;
7 use rustc_session::Session;
8 use std::fs;
9 use std::io;
10 use std::path::PathBuf;
11
12 use super::data::*;
13 use super::dirty_clean;
14 use super::file_format;
15 use super::fs::*;
16 use super::work_product;
17
18 /// Save and dump the DepGraph.
19 ///
20 /// No query must be invoked after this function.
21 pub fn save_dep_graph(tcx: TyCtxt<'_>) {
22     debug!("save_dep_graph()");
23     tcx.dep_graph.with_ignore(|| {
24         let sess = tcx.sess;
25         if sess.opts.incremental.is_none() {
26             return;
27         }
28         // This is going to be deleted in finalize_session_directory, so let's not create it
29         if sess.has_errors_or_delayed_span_bugs() {
30             return;
31         }
32
33         let query_cache_path = query_cache_path(sess);
34         let dep_graph_path = dep_graph_path(sess);
35         let staging_dep_graph_path = staging_dep_graph_path(sess);
36
37         sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx));
38         sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx));
39
40         if sess.opts.debugging_opts.incremental_info {
41             tcx.dep_graph.print_incremental_info()
42         }
43
44         join(
45             move || {
46                 sess.time("incr_comp_persist_result_cache", || {
47                     save_in(sess, query_cache_path, "query cache", |e| encode_query_cache(tcx, e));
48                 });
49             },
50             move || {
51                 sess.time("incr_comp_persist_dep_graph", || {
52                     if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
53                         sess.err(&format!(
54                             "failed to write dependency graph to `{}`: {}",
55                             staging_dep_graph_path.display(),
56                             err
57                         ));
58                     }
59                     if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
60                         sess.err(&format!(
61                             "failed to move dependency graph from `{}` to `{}`: {}",
62                             staging_dep_graph_path.display(),
63                             dep_graph_path.display(),
64                             err
65                         ));
66                     }
67                 });
68             },
69         );
70     })
71 }
72
73 pub fn save_work_product_index(
74     sess: &Session,
75     dep_graph: &DepGraph,
76     new_work_products: FxHashMap<WorkProductId, WorkProduct>,
77 ) {
78     if sess.opts.incremental.is_none() {
79         return;
80     }
81     // This is going to be deleted in finalize_session_directory, so let's not create it
82     if sess.has_errors_or_delayed_span_bugs() {
83         return;
84     }
85
86     debug!("save_work_product_index()");
87     dep_graph.assert_ignored();
88     let path = work_products_path(sess);
89     save_in(sess, path, "work product index", |e| encode_work_product_index(&new_work_products, e));
90
91     // We also need to clean out old work-products, as not all of them are
92     // deleted during invalidation. Some object files don't change their
93     // content, they are just not needed anymore.
94     let previous_work_products = dep_graph.previous_work_products();
95     for (id, wp) in previous_work_products.iter() {
96         if !new_work_products.contains_key(id) {
97             work_product::delete_workproduct_files(sess, wp);
98             debug_assert!(
99                 wp.saved_file.as_ref().map_or(true, |file_name| {
100                     !in_incr_comp_dir_sess(sess, &file_name).exists()
101                 })
102             );
103         }
104     }
105
106     // Check that we did not delete one of the current work-products:
107     debug_assert!({
108         new_work_products
109             .iter()
110             .flat_map(|(_, wp)| wp.saved_file.iter())
111             .map(|name| in_incr_comp_dir_sess(sess, name))
112             .all(|path| path.exists())
113     });
114 }
115
116 pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
117 where
118     F: FnOnce(&mut FileEncoder) -> FileEncodeResult,
119 {
120     debug!("save: storing data in {}", path_buf.display());
121
122     // Delete the old file, if any.
123     // Note: It's important that we actually delete the old file and not just
124     // truncate and overwrite it, since it might be a shared hard-link, the
125     // underlying data of which we don't want to modify
126     match fs::remove_file(&path_buf) {
127         Ok(()) => {
128             debug!("save: remove old file");
129         }
130         Err(err) if err.kind() == io::ErrorKind::NotFound => (),
131         Err(err) => {
132             sess.err(&format!(
133                 "unable to delete old {} at `{}`: {}",
134                 name,
135                 path_buf.display(),
136                 err
137             ));
138             return;
139         }
140     }
141
142     let mut encoder = match FileEncoder::new(&path_buf) {
143         Ok(encoder) => encoder,
144         Err(err) => {
145             sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err));
146             return;
147         }
148     };
149
150     if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
151         sess.err(&format!("failed to write {} header to `{}`: {}", name, path_buf.display(), err));
152         return;
153     }
154
155     if let Err(err) = encode(&mut encoder) {
156         sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err));
157         return;
158     }
159
160     if let Err(err) = encoder.flush() {
161         sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err));
162         return;
163     }
164
165     debug!("save: data written to disk successfully");
166 }
167
168 fn encode_work_product_index(
169     work_products: &FxHashMap<WorkProductId, WorkProduct>,
170     encoder: &mut FileEncoder,
171 ) -> FileEncodeResult {
172     let serialized_products: Vec<_> = work_products
173         .iter()
174         .map(|(id, work_product)| SerializedWorkProduct {
175             id: *id,
176             work_product: work_product.clone(),
177         })
178         .collect();
179
180     serialized_products.encode(encoder)
181 }
182
183 fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult {
184     tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder))
185 }
186
187 pub fn build_dep_graph(
188     sess: &Session,
189     prev_graph: SerializedDepGraph,
190     prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
191 ) -> Option<DepGraph> {
192     if sess.opts.incremental.is_none() {
193         // No incremental compilation.
194         return None;
195     }
196
197     // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors.
198     let path_buf = staging_dep_graph_path(sess);
199
200     let mut encoder = match FileEncoder::new(&path_buf) {
201         Ok(encoder) => encoder,
202         Err(err) => {
203             sess.err(&format!(
204                 "failed to create dependency graph at `{}`: {}",
205                 path_buf.display(),
206                 err
207             ));
208             return None;
209         }
210     };
211
212     if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
213         sess.err(&format!(
214             "failed to write dependency graph header to `{}`: {}",
215             path_buf.display(),
216             err
217         ));
218         return None;
219     }
220
221     // First encode the commandline arguments hash
222     if let Err(err) = sess.opts.dep_tracking_hash(false).encode(&mut encoder) {
223         sess.err(&format!(
224             "failed to write dependency graph hash `{}`: {}",
225             path_buf.display(),
226             err
227         ));
228         return None;
229     }
230
231     Some(DepGraph::new(
232         &sess.prof,
233         prev_graph,
234         prev_work_products,
235         encoder,
236         sess.opts.debugging_opts.query_dep_graph,
237         sess.opts.debugging_opts.incremental_info,
238     ))
239 }