]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/save.rs
Rollup merge of #41141 - michaelwoerister:direct-metadata-ich-final, r=nikomatsakis
[rust.git] / src / librustc_incremental / persist / save.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::dep_graph::DepNode;
12 use rustc::hir::def_id::DefId;
13 use rustc::hir::svh::Svh;
14 use rustc::ich::Fingerprint;
15 use rustc::middle::cstore::EncodedMetadataHash;
16 use rustc::session::Session;
17 use rustc::ty::TyCtxt;
18 use rustc_data_structures::fx::FxHashMap;
19 use rustc_serialize::Encodable as RustcEncodable;
20 use rustc_serialize::opaque::Encoder;
21 use std::io::{self, Cursor, Write};
22 use std::fs::{self, File};
23 use std::path::PathBuf;
24
25 use IncrementalHashesMap;
26 use super::data::*;
27 use super::directory::*;
28 use super::hash::*;
29 use super::preds::*;
30 use super::fs::*;
31 use super::dirty_clean;
32 use super::file_format;
33 use super::work_product;
34
35 pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
36                                 incremental_hashes_map: &IncrementalHashesMap,
37                                 metadata_hashes: &[EncodedMetadataHash],
38                                 svh: Svh) {
39     debug!("save_dep_graph()");
40     let _ignore = tcx.dep_graph.in_ignore();
41     let sess = tcx.sess;
42     if sess.opts.incremental.is_none() {
43         return;
44     }
45
46     let mut builder = DefIdDirectoryBuilder::new(tcx);
47     let query = tcx.dep_graph.query();
48
49     if tcx.sess.opts.debugging_opts.incremental_info {
50         println!("incremental: {} nodes in dep-graph", query.graph.len_nodes());
51         println!("incremental: {} edges in dep-graph", query.graph.len_edges());
52     }
53
54     let mut hcx = HashContext::new(tcx, incremental_hashes_map);
55     let preds = Predecessors::new(&query, &mut hcx);
56     let mut current_metadata_hashes = FxHashMap();
57
58     // IMPORTANT: We are saving the metadata hashes *before* the dep-graph,
59     //            since metadata-encoding might add new entries to the
60     //            DefIdDirectory (which is saved in the dep-graph file).
61     if sess.opts.debugging_opts.incremental_cc ||
62        sess.opts.debugging_opts.query_dep_graph {
63         save_in(sess,
64                 metadata_hash_export_path(sess),
65                 |e| encode_metadata_hashes(tcx,
66                                            svh,
67                                            metadata_hashes,
68                                            &mut builder,
69                                            &mut current_metadata_hashes,
70                                            e));
71     }
72
73     save_in(sess,
74             dep_graph_path(sess),
75             |e| encode_dep_graph(&preds, &mut builder, e));
76
77     let prev_metadata_hashes = incremental_hashes_map.prev_metadata_hashes.borrow();
78     dirty_clean::check_dirty_clean_metadata(tcx,
79                                             &*prev_metadata_hashes,
80                                             &current_metadata_hashes);
81 }
82
83 pub fn save_work_products(sess: &Session) {
84     if sess.opts.incremental.is_none() {
85         return;
86     }
87
88     debug!("save_work_products()");
89     let _ignore = sess.dep_graph.in_ignore();
90     let path = work_products_path(sess);
91     save_in(sess, path, |e| encode_work_products(sess, e));
92
93     // We also need to clean out old work-products, as not all of them are
94     // deleted during invalidation. Some object files don't change their
95     // content, they are just not needed anymore.
96     let new_work_products = sess.dep_graph.work_products();
97     let previous_work_products = sess.dep_graph.previous_work_products();
98
99     for (id, wp) in previous_work_products.iter() {
100         if !new_work_products.contains_key(id) {
101             work_product::delete_workproduct_files(sess, wp);
102             debug_assert!(wp.saved_files.iter().all(|&(_, ref file_name)| {
103                 !in_incr_comp_dir_sess(sess, file_name).exists()
104             }));
105         }
106     }
107
108     // Check that we did not delete one of the current work-products:
109     debug_assert!({
110         new_work_products.iter()
111                          .flat_map(|(_, wp)| wp.saved_files
112                                                .iter()
113                                                .map(|&(_, ref name)| name))
114                          .map(|name| in_incr_comp_dir_sess(sess, name))
115                          .all(|path| path.exists())
116     });
117 }
118
119 fn save_in<F>(sess: &Session, path_buf: PathBuf, encode: F)
120     where F: FnOnce(&mut Encoder) -> io::Result<()>
121 {
122     debug!("save: storing data in {}", path_buf.display());
123
124     // delete the old dep-graph, if any
125     // Note: It's important that we actually delete the old file and not just
126     // truncate and overwrite it, since it might be a shared hard-link, the
127     // underlying data of which we don't want to modify
128     if path_buf.exists() {
129         match fs::remove_file(&path_buf) {
130             Ok(()) => {
131                 debug!("save: remove old file");
132             }
133             Err(err) => {
134                 sess.err(&format!("unable to delete old dep-graph at `{}`: {}",
135                                   path_buf.display(),
136                                   err));
137                 return;
138             }
139         }
140     }
141
142     // generate the data in a memory buffer
143     let mut wr = Cursor::new(Vec::new());
144     file_format::write_file_header(&mut wr).unwrap();
145     match encode(&mut Encoder::new(&mut wr)) {
146         Ok(()) => {}
147         Err(err) => {
148             sess.err(&format!("could not encode dep-graph to `{}`: {}",
149                               path_buf.display(),
150                               err));
151             return;
152         }
153     }
154
155     // write the data out
156     let data = wr.into_inner();
157     match File::create(&path_buf).and_then(|mut file| file.write_all(&data)) {
158         Ok(_) => {
159             debug!("save: data written to disk successfully");
160         }
161         Err(err) => {
162             sess.err(&format!("failed to write dep-graph to `{}`: {}",
163                               path_buf.display(),
164                               err));
165             return;
166         }
167     }
168 }
169
170 pub fn encode_dep_graph(preds: &Predecessors,
171                         builder: &mut DefIdDirectoryBuilder,
172                         encoder: &mut Encoder)
173                         -> io::Result<()> {
174     // First encode the commandline arguments hash
175     let tcx = builder.tcx();
176     tcx.sess.opts.dep_tracking_hash().encode(encoder)?;
177
178     // Create a flat list of (Input, WorkProduct) edges for
179     // serialization.
180     let mut edges = FxHashMap();
181     for edge in preds.reduced_graph.all_edges() {
182         let source = *preds.reduced_graph.node_data(edge.source());
183         let target = *preds.reduced_graph.node_data(edge.target());
184         match *target {
185             DepNode::MetaData(ref def_id) => {
186                 // Metadata *targets* are always local metadata nodes. We have
187                 // already handled those in `encode_metadata_hashes`.
188                 assert!(def_id.is_local());
189                 continue;
190             }
191             _ => (),
192         }
193         debug!("serialize edge: {:?} -> {:?}", source, target);
194         let source = builder.map(source);
195         let target = builder.map(target);
196         edges.entry(source).or_insert(vec![]).push(target);
197     }
198
199     if tcx.sess.opts.debugging_opts.incremental_dump_hash {
200         for (dep_node, hash) in &preds.hashes {
201             println!("HIR hash for {:?} is {}", dep_node, hash);
202         }
203     }
204
205     // Create the serialized dep-graph.
206     let bootstrap_outputs = preds.bootstrap_outputs.iter()
207                                                    .map(|n| builder.map(n))
208                                                    .collect();
209     let edges = edges.into_iter()
210                      .map(|(k, v)| SerializedEdgeSet { source: k, targets: v })
211                      .collect();
212     let graph = SerializedDepGraph {
213         bootstrap_outputs,
214         edges,
215         hashes: preds.hashes
216             .iter()
217             .map(|(&dep_node, &hash)| {
218                 SerializedHash {
219                     dep_node: builder.map(dep_node),
220                     hash: hash,
221                 }
222             })
223             .collect(),
224     };
225
226     if tcx.sess.opts.debugging_opts.incremental_info {
227         println!("incremental: {} nodes in reduced dep-graph", preds.reduced_graph.len_nodes());
228         println!("incremental: {} edges in serialized dep-graph", graph.edges.len());
229         println!("incremental: {} hashes in serialized dep-graph", graph.hashes.len());
230     }
231
232     debug!("graph = {:#?}", graph);
233
234     // Encode the directory and then the graph data.
235     builder.directory().encode(encoder)?;
236     graph.encode(encoder)?;
237
238     Ok(())
239 }
240
241 pub fn encode_metadata_hashes(tcx: TyCtxt,
242                               svh: Svh,
243                               metadata_hashes: &[EncodedMetadataHash],
244                               builder: &mut DefIdDirectoryBuilder,
245                               current_metadata_hashes: &mut FxHashMap<DefId, Fingerprint>,
246                               encoder: &mut Encoder)
247                               -> io::Result<()> {
248     let mut serialized_hashes = SerializedMetadataHashes {
249         hashes: metadata_hashes.to_vec(),
250         index_map: FxHashMap()
251     };
252
253     if tcx.sess.opts.debugging_opts.query_dep_graph {
254         for serialized_hash in &serialized_hashes.hashes {
255             let def_id = DefId::local(serialized_hash.def_index);
256
257             // Store entry in the index_map
258             let def_path_index = builder.add(def_id);
259             serialized_hashes.index_map.insert(def_id.index, def_path_index);
260
261             // Record hash in current_metadata_hashes
262             current_metadata_hashes.insert(def_id, serialized_hash.hash);
263         }
264
265         debug!("save: stored index_map (len={}) for serialized hashes",
266                serialized_hashes.index_map.len());
267     }
268
269     // Encode everything.
270     svh.encode(encoder)?;
271     serialized_hashes.encode(encoder)?;
272
273     Ok(())
274 }
275
276 pub fn encode_work_products(sess: &Session, encoder: &mut Encoder) -> io::Result<()> {
277     let work_products: Vec<_> = sess.dep_graph
278         .work_products()
279         .iter()
280         .map(|(id, work_product)| {
281             SerializedWorkProduct {
282                 id: id.clone(),
283                 work_product: work_product.clone(),
284             }
285         })
286         .collect();
287
288     work_products.encode(encoder)
289 }