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