]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_incremental/src/persist/save.rs
New upstream version 1.61.0+dfsg1
[rustc.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 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.debugging_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 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", |e| {
100 encode_work_product_index(&new_work_products, e)
101 });
102
103 // We also need to clean out old work-products, as not all of them are
104 // deleted during invalidation. Some object files don't change their
105 // content, they are just not needed anymore.
106 let previous_work_products = dep_graph.previous_work_products();
107 for (id, wp) in previous_work_products.iter() {
108 if !new_work_products.contains_key(id) {
109 work_product::delete_workproduct_files(sess, wp);
110 debug_assert!(
111 wp.saved_file.as_ref().map_or(true, |file_name| {
112 !in_incr_comp_dir_sess(sess, &file_name).exists()
113 })
114 );
115 }
116 }
117
118 // Check that we did not delete one of the current work-products:
119 debug_assert!({
120 new_work_products
121 .iter()
122 .flat_map(|(_, wp)| wp.saved_file.iter())
123 .map(|name| in_incr_comp_dir_sess(sess, name))
124 .all(|path| path.exists())
125 });
126 }
127
128 fn encode_work_product_index(
129 work_products: &FxHashMap<WorkProductId, WorkProduct>,
130 encoder: &mut FileEncoder,
131 ) -> FileEncodeResult {
132 let serialized_products: Vec<_> = work_products
133 .iter()
134 .map(|(id, work_product)| SerializedWorkProduct {
135 id: *id,
136 work_product: work_product.clone(),
137 })
138 .collect();
139
140 serialized_products.encode(encoder)
141 }
142
143 fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult {
144 tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder))
145 }
146
147 /// Builds the dependency graph.
148 ///
149 /// This function creates the *staging dep-graph*. When the dep-graph is modified by a query
150 /// execution, the new dependency information is not kept in memory but directly
151 /// output to this file. `save_dep_graph` then finalizes the staging dep-graph
152 /// and moves it to the permanent dep-graph path
153 pub fn build_dep_graph(
154 sess: &Session,
155 prev_graph: SerializedDepGraph,
156 prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
157 ) -> Option<DepGraph> {
158 if sess.opts.incremental.is_none() {
159 // No incremental compilation.
160 return None;
161 }
162
163 // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors.
164 let path_buf = staging_dep_graph_path(sess);
165
166 let mut encoder = match FileEncoder::new(&path_buf) {
167 Ok(encoder) => encoder,
168 Err(err) => {
169 sess.err(&format!(
170 "failed to create dependency graph at `{}`: {}",
171 path_buf.display(),
172 err
173 ));
174 return None;
175 }
176 };
177
178 if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
179 sess.err(&format!(
180 "failed to write dependency graph header to `{}`: {}",
181 path_buf.display(),
182 err
183 ));
184 return None;
185 }
186
187 // First encode the commandline arguments hash
188 if let Err(err) = sess.opts.dep_tracking_hash(false).encode(&mut encoder) {
189 sess.err(&format!(
190 "failed to write dependency graph hash `{}`: {}",
191 path_buf.display(),
192 err
193 ));
194 return None;
195 }
196
197 Some(DepGraph::new(
198 &sess.prof,
199 prev_graph,
200 prev_work_products,
201 encoder,
202 sess.opts.debugging_opts.query_dep_graph,
203 sess.opts.debugging_opts.incremental_info,
204 ))
205 }