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;
10 use std::path::PathBuf;
13 use super::dirty_clean;
14 use super::file_format;
16 use super::work_product;
18 /// Save and dump the DepGraph.
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(|| {
25 if sess.opts.incremental.is_none() {
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() {
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);
37 sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx));
38 sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx));
40 if sess.opts.debugging_opts.incremental_info {
41 tcx.dep_graph.print_incremental_info()
46 sess.time("incr_comp_persist_result_cache", || {
47 save_in(sess, query_cache_path, "query cache", |e| encode_query_cache(tcx, e));
51 sess.time("incr_comp_persist_dep_graph", || {
52 if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
54 "failed to write dependency graph to `{}`: {}",
55 staging_dep_graph_path.display(),
59 if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
61 "failed to move dependency graph from `{}` to `{}`: {}",
62 staging_dep_graph_path.display(),
63 dep_graph_path.display(),
73 pub fn save_work_product_index(
76 new_work_products: FxHashMap<WorkProductId, WorkProduct>,
78 if sess.opts.incremental.is_none() {
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() {
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));
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);
99 wp.saved_file.as_ref().map_or(true, |file_name| {
100 !in_incr_comp_dir_sess(sess, &file_name).exists()
106 // Check that we did not delete one of the current work-products:
110 .flat_map(|(_, wp)| wp.saved_file.iter())
111 .map(|name| in_incr_comp_dir_sess(sess, name))
112 .all(|path| path.exists())
116 pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
118 F: FnOnce(&mut FileEncoder) -> FileEncodeResult,
120 debug!("save: storing data in {}", path_buf.display());
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) {
128 debug!("save: remove old file");
130 Err(err) if err.kind() == io::ErrorKind::NotFound => (),
133 "unable to delete old {} at `{}`: {}",
142 let mut encoder = match FileEncoder::new(&path_buf) {
143 Ok(encoder) => encoder,
145 sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err));
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));
155 if let Err(err) = encode(&mut encoder) {
156 sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err));
160 if let Err(err) = encoder.flush() {
161 sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err));
165 debug!("save: data written to disk successfully");
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
174 .map(|(id, work_product)| SerializedWorkProduct {
176 work_product: work_product.clone(),
180 serialized_products.encode(encoder)
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))
187 pub fn build_dep_graph(
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.
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);
200 let mut encoder = match FileEncoder::new(&path_buf) {
201 Ok(encoder) => encoder,
204 "failed to create dependency graph at `{}`: {}",
212 if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) {
214 "failed to write dependency graph header to `{}`: {}",
221 // First encode the commandline arguments hash
222 if let Err(err) = sess.opts.dep_tracking_hash(false).encode(&mut encoder) {
224 "failed to write dependency graph hash `{}`: {}",
236 sess.opts.debugging_opts.query_dep_graph,
237 sess.opts.debugging_opts.incremental_info,