]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_incremental/src/persist/save.rs
Auto merge of #88717 - tabokie:vecdeque-fast-append, r=m-ou-se
[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
10 use super::data::*;
11 use super::dirty_clean;
12 use super::file_format;
13 use super::fs::*;
14 use super::work_product;
15
16 /// Save and dump the DepGraph.
17 ///
18 /// No query must be invoked after this function.
19 pub fn save_dep_graph(tcx: TyCtxt<'_>) {
20     debug!("save_dep_graph()");
21     tcx.dep_graph.with_ignore(|| {
22         let sess = tcx.sess;
23         if sess.opts.incremental.is_none() {
24             return;
25         }
26         // This is going to be deleted in finalize_session_directory, so let's not create it
27         if sess.has_errors_or_delayed_span_bugs() {
28             return;
29         }
30
31         let query_cache_path = query_cache_path(sess);
32         let dep_graph_path = dep_graph_path(sess);
33         let staging_dep_graph_path = staging_dep_graph_path(sess);
34
35         sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx));
36         sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx));
37
38         if sess.opts.debugging_opts.incremental_info {
39             tcx.dep_graph.print_incremental_info()
40         }
41
42         join(
43             move || {
44                 sess.time("incr_comp_persist_result_cache", || {
45                     // Drop the memory map so that we can remove the file and write to it.
46                     if let Some(odc) = &tcx.on_disk_cache {
47                         odc.drop_serialized_data(tcx);
48                     }
49
50                     file_format::save_in(sess, query_cache_path, "query cache", |e| {
51                         encode_query_cache(tcx, e)
52                     });
53                 });
54             },
55             move || {
56                 sess.time("incr_comp_persist_dep_graph", || {
57                     if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
58                         sess.err(&format!(
59                             "failed to write dependency graph to `{}`: {}",
60                             staging_dep_graph_path.display(),
61                             err
62                         ));
63                     }
64                     if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
65                         sess.err(&format!(
66                             "failed to move dependency graph from `{}` to `{}`: {}",
67                             staging_dep_graph_path.display(),
68                             dep_graph_path.display(),
69                             err
70                         ));
71                     }
72                 });
73             },
74         );
75     })
76 }
77
78 pub fn save_work_product_index(
79     sess: &Session,
80     dep_graph: &DepGraph,
81     new_work_products: FxHashMap<WorkProductId, WorkProduct>,
82 ) {
83     if sess.opts.incremental.is_none() {
84         return;
85     }
86     // This is going to be deleted in finalize_session_directory, so let's not create it
87     if sess.has_errors_or_delayed_span_bugs() {
88         return;
89     }
90
91     debug!("save_work_product_index()");
92     dep_graph.assert_ignored();
93     let path = work_products_path(sess);
94     file_format::save_in(sess, path, "work product index", |e| {
95         encode_work_product_index(&new_work_products, e)
96     });
97
98     // We also need to clean out old work-products, as not all of them are
99     // deleted during invalidation. Some object files don't change their
100     // content, they are just not needed anymore.
101     let previous_work_products = dep_graph.previous_work_products();
102     for (id, wp) in previous_work_products.iter() {
103         if !new_work_products.contains_key(id) {
104             work_product::delete_workproduct_files(sess, wp);
105             debug_assert!(
106                 wp.saved_file.as_ref().map_or(true, |file_name| {
107                     !in_incr_comp_dir_sess(sess, &file_name).exists()
108                 })
109             );
110         }
111     }
112
113     // Check that we did not delete one of the current work-products:
114     debug_assert!({
115         new_work_products
116             .iter()
117             .flat_map(|(_, wp)| wp.saved_file.iter())
118             .map(|name| in_incr_comp_dir_sess(sess, name))
119             .all(|path| path.exists())
120     });
121 }
122
123 fn encode_work_product_index(
124     work_products: &FxHashMap<WorkProductId, WorkProduct>,
125     encoder: &mut FileEncoder,
126 ) -> FileEncodeResult {
127     let serialized_products: Vec<_> = work_products
128         .iter()
129         .map(|(id, work_product)| SerializedWorkProduct {
130             id: *id,
131             work_product: work_product.clone(),
132         })
133         .collect();
134
135     serialized_products.encode(encoder)
136 }
137
138 fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult {
139     tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder))
140 }
141
142 pub fn build_dep_graph(
143     sess: &Session,
144     prev_graph: SerializedDepGraph,
145     prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
146 ) -> Option<DepGraph> {
147     if sess.opts.incremental.is_none() {
148         // No incremental compilation.
149         return None;
150     }
151
152     // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors.
153     let path_buf = staging_dep_graph_path(sess);
154
155     let mut encoder = match FileEncoder::new(&path_buf) {
156         Ok(encoder) => encoder,
157         Err(err) => {
158             sess.err(&format!(
159                 "failed to create dependency graph at `{}`: {}",
160                 path_buf.display(),
161                 err
162             ));
163             return None;
164         }
165     };
166
167     if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
168         sess.err(&format!(
169             "failed to write dependency graph header to `{}`: {}",
170             path_buf.display(),
171             err
172         ));
173         return None;
174     }
175
176     // First encode the commandline arguments hash
177     if let Err(err) = sess.opts.dep_tracking_hash(false).encode(&mut encoder) {
178         sess.err(&format!(
179             "failed to write dependency graph hash `{}`: {}",
180             path_buf.display(),
181             err
182         ));
183         return None;
184     }
185
186     Some(DepGraph::new(
187         &sess.prof,
188         prev_graph,
189         prev_work_products,
190         encoder,
191         sess.opts.debugging_opts.query_dep_graph,
192         sess.opts.debugging_opts.incremental_info,
193     ))
194 }