]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/save.rs
Code to save/load the work-products map from disk
[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 rbml::opaque::Encoder;
12 use rustc::dep_graph::DepNode;
13 use rustc::middle::cstore::LOCAL_CRATE;
14 use rustc::session::Session;
15 use rustc::ty::TyCtxt;
16 use rustc_serialize::{Encodable as RustcEncodable};
17 use std::hash::{Hasher, SipHasher};
18 use std::io::{self, Cursor, Write};
19 use std::fs::{self, File};
20 use std::path::PathBuf;
21
22 use super::data::*;
23 use super::directory::*;
24 use super::hash::*;
25 use super::util::*;
26
27 pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
28     debug!("save_dep_graph()");
29     let _ignore = tcx.dep_graph.in_ignore();
30     let sess = tcx.sess;
31     let mut hcx = HashContext::new(tcx);
32     save_in(sess, dep_graph_path(tcx), |e| encode_dep_graph(&mut hcx, e));
33     save_in(sess, metadata_hash_path(tcx, LOCAL_CRATE), |e| encode_metadata_hashes(&mut hcx, e));
34 }
35
36 pub fn save_work_products(sess: &Session, local_crate_name: &str) {
37     debug!("save_work_products()");
38     let _ignore = sess.dep_graph.in_ignore();
39     let path = sess_work_products_path(sess, local_crate_name);
40     save_in(sess, path, |e| encode_work_products(sess, e));
41 }
42
43 fn save_in<F>(sess: &Session,
44               opt_path_buf: Option<PathBuf>,
45               encode: F)
46     where F: FnOnce(&mut Encoder) -> io::Result<()>
47 {
48     let path_buf = match opt_path_buf {
49         Some(p) => p,
50         None => return
51     };
52
53     // FIXME(#32754) lock file?
54
55     // delete the old dep-graph, if any
56     if path_buf.exists() {
57         match fs::remove_file(&path_buf) {
58             Ok(()) => { }
59             Err(err) => {
60                 sess.err(
61                     &format!("unable to delete old dep-graph at `{}`: {}",
62                              path_buf.display(), err));
63                 return;
64             }
65         }
66     }
67
68     // generate the data in a memory buffer
69     let mut wr = Cursor::new(Vec::new());
70     match encode(&mut Encoder::new(&mut wr)) {
71         Ok(()) => { }
72         Err(err) => {
73             sess.err(
74                 &format!("could not encode dep-graph to `{}`: {}",
75                          path_buf.display(), err));
76             return;
77         }
78     }
79
80     // write the data out
81     let data = wr.into_inner();
82     match
83         File::create(&path_buf)
84         .and_then(|mut file| file.write_all(&data))
85     {
86         Ok(_) => { }
87         Err(err) => {
88             sess.err(
89                 &format!("failed to write dep-graph to `{}`: {}",
90                          path_buf.display(), err));
91             return;
92         }
93     }
94 }
95
96 pub fn encode_dep_graph<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
97                                   encoder: &mut Encoder)
98                                   -> io::Result<()>
99 {
100     let tcx = hcx.tcx;
101     let query = tcx.dep_graph.query();
102
103     let mut builder = DefIdDirectoryBuilder::new(tcx);
104
105     // Create hashes for inputs.
106     let hashes =
107         query.nodes()
108              .into_iter()
109              .filter_map(|dep_node| {
110                  hcx.hash(&dep_node)
111                     .map(|hash| {
112                         let node = builder.map(dep_node);
113                         SerializedHash { node: node, hash: hash }
114                     })
115              })
116              .collect();
117
118     // Create the serialized dep-graph.
119     let graph = SerializedDepGraph {
120         nodes: query.nodes().into_iter()
121                             .map(|node| builder.map(node))
122                             .collect(),
123         edges: query.edges().into_iter()
124                             .map(|(source_node, target_node)| {
125                                 let source = builder.map(source_node);
126                                 let target = builder.map(target_node);
127                                 (source, target)
128                             })
129                             .collect(),
130         hashes: hashes,
131     };
132
133     debug!("graph = {:#?}", graph);
134
135     // Encode the directory and then the graph data.
136     let directory = builder.into_directory();
137     try!(directory.encode(encoder));
138     try!(graph.encode(encoder));
139
140     Ok(())
141 }
142
143 pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
144                                         encoder: &mut Encoder)
145                                         -> io::Result<()>
146 {
147     let tcx = hcx.tcx;
148     let query = tcx.dep_graph.query();
149
150     let serialized_hashes = {
151         // Identify the `MetaData(X)` nodes where `X` is local. These are
152         // the metadata items we export. Downstream crates will want to
153         // see a hash that tells them whether we might have changed the
154         // metadata for a given item since they last compiled.
155         let meta_data_def_ids =
156             query.nodes()
157                  .into_iter()
158                  .filter_map(|dep_node| match *dep_node {
159                      DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
160                      _ => None,
161                  });
162
163         // To create the hash for each item `X`, we don't hash the raw
164         // bytes of the metadata (though in principle we
165         // could). Instead, we walk the predecessors of `MetaData(X)`
166         // from the dep-graph. This corresponds to all the inputs that
167         // were read to construct the metadata. To create the hash for
168         // the metadata, we hash (the hash of) all of those inputs.
169         let hashes =
170             meta_data_def_ids
171             .map(|def_id| {
172                 assert!(def_id.is_local());
173                 let dep_node = DepNode::MetaData(def_id);
174                 let mut state = SipHasher::new();
175                 debug!("save: computing metadata hash for {:?}", dep_node);
176                 for node in query.transitive_predecessors(&dep_node) {
177                     if let Some(hash) = hcx.hash(&node) {
178                         debug!("save: predecessor {:?} has hash {}", node, hash);
179                         state.write_u64(hash.to_le());
180                     } else {
181                         debug!("save: predecessor {:?} cannot be hashed", node);
182                     }
183                 }
184                 let hash = state.finish();
185                 debug!("save: metadata hash for {:?} is {}", dep_node, hash);
186                 SerializedMetadataHash {
187                     def_index: def_id.index,
188                     hash: hash,
189                 }
190             });
191
192         // Collect these up into a vector.
193         SerializedMetadataHashes {
194             hashes: hashes.collect()
195         }
196     };
197
198     // Encode everything.
199     try!(serialized_hashes.encode(encoder));
200
201     Ok(())
202 }
203
204 pub fn encode_work_products(sess: &Session,
205                             encoder: &mut Encoder)
206                             -> io::Result<()>
207 {
208     let work_products: Vec<_> =
209         sess.dep_graph.work_products()
210                      .iter()
211                      .map(|(id, work_product)| {
212                          SerializedWorkProduct {
213                              id: id.clone(),
214                              work_product: work_product.clone(),
215                          }
216                      })
217                      .collect();
218
219     work_products.encode(encoder)
220 }
221