]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/load.rs
Update E0253.rs
[rust.git] / src / librustc_incremental / persist / load.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 //! Code to save/load the dep-graph from files.
12
13 use rbml::Error;
14 use rbml::opaque::Decoder;
15 use rustc::dep_graph::DepNode;
16 use rustc::hir::def_id::DefId;
17 use rustc::session::Session;
18 use rustc::ty::TyCtxt;
19 use rustc_data_structures::fnv::FnvHashSet;
20 use rustc_serialize::Decodable as RustcDecodable;
21 use std::io::Read;
22 use std::fs::{self, File};
23 use std::path::{Path};
24
25 use super::data::*;
26 use super::directory::*;
27 use super::dirty_clean;
28 use super::hash::*;
29 use super::util::*;
30
31 type DirtyNodes = FnvHashSet<DepNode<DefId>>;
32
33 type CleanEdges = Vec<(DepNode<DefId>, DepNode<DefId>)>;
34
35 /// If we are in incremental mode, and a previous dep-graph exists,
36 /// then load up those nodes/edges that are still valid into the
37 /// dep-graph for this session. (This is assumed to be running very
38 /// early in compilation, before we've really done any work, but
39 /// actually it doesn't matter all that much.) See `README.md` for
40 /// more general overview.
41 pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
42     if tcx.sess.opts.incremental.is_none() {
43         return;
44     }
45
46     let _ignore = tcx.dep_graph.in_ignore();
47     load_dep_graph_if_exists(tcx);
48     dirty_clean::check_dirty_clean_annotations(tcx);
49 }
50
51 fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
52     let dep_graph_path = dep_graph_path(tcx).unwrap();
53     let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) {
54         Some(p) => p,
55         None => return // no file
56     };
57
58     let work_products_path = tcx_work_products_path(tcx).unwrap();
59     let work_products_data = match load_data(tcx.sess, &work_products_path) {
60         Some(p) => p,
61         None => return // no file
62     };
63
64     match decode_dep_graph(tcx, &dep_graph_data, &work_products_data) {
65         Ok(()) => return,
66         Err(err) => {
67             tcx.sess.warn(
68                 &format!("decoding error in dep-graph from `{}` and `{}`: {}",
69                          dep_graph_path.display(),
70                          work_products_path.display(),
71                          err));
72         }
73     }
74 }
75
76 fn load_data(sess: &Session, path: &Path) -> Option<Vec<u8>> {
77     if !path.exists() {
78         return None;
79     }
80
81     let mut data = vec![];
82     match
83         File::open(path)
84         .and_then(|mut file| file.read_to_end(&mut data))
85     {
86         Ok(_) => {
87             Some(data)
88         }
89         Err(err) => {
90             sess.err(
91                 &format!("could not load dep-graph from `{}`: {}",
92                          path.display(), err));
93             None
94         }
95     }
96
97 }
98
99 /// Decode the dep graph and load the edges/nodes that are still clean
100 /// into `tcx.dep_graph`.
101 pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
102                                   dep_graph_data: &[u8],
103                                   work_products_data: &[u8])
104                                   -> Result<(), Error>
105 {
106     // Deserialize the directory and dep-graph.
107     let mut dep_graph_decoder = Decoder::new(dep_graph_data, 0);
108     let directory = try!(DefIdDirectory::decode(&mut dep_graph_decoder));
109     let serialized_dep_graph = try!(SerializedDepGraph::decode(&mut dep_graph_decoder));
110
111     debug!("decode_dep_graph: directory = {:#?}", directory);
112     debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph);
113
114     // Retrace the paths in the directory to find their current location (if any).
115     let retraced = directory.retrace(tcx);
116
117     debug!("decode_dep_graph: retraced = {:#?}", retraced);
118
119     // Compute the set of Hir nodes whose data has changed.
120     let mut dirty_nodes =
121         initial_dirty_nodes(tcx, &serialized_dep_graph.hashes, &retraced);
122
123     debug!("decode_dep_graph: initial dirty_nodes = {:#?}", dirty_nodes);
124
125     // Find all DepNodes reachable from that core set. This loop
126     // iterates repeatedly over the list of edges whose source is not
127     // known to be dirty (`clean_edges`). If it finds an edge whose
128     // source is dirty, it removes it from that list and adds the
129     // target to `dirty_nodes`. It stops when it reaches a fixed
130     // point.
131     let clean_edges = compute_clean_edges(&serialized_dep_graph.edges,
132                                           &retraced,
133                                           &mut dirty_nodes);
134
135     // Add synthetic `foo->foo` edges for each clean node `foo` that
136     // we had before. This is sort of a hack to create clean nodes in
137     // the graph, since the existence of a node is a signal that the
138     // work it represents need not be repeated.
139     let clean_nodes =
140         serialized_dep_graph.nodes
141                             .iter()
142                             .filter_map(|node| retraced.map(node))
143                             .filter(|node| !dirty_nodes.contains(node))
144                             .map(|node| (node.clone(), node));
145
146     // Add nodes and edges that are not dirty into our main graph.
147     let dep_graph = tcx.dep_graph.clone();
148     for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
149         debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
150
151         let _task = dep_graph.in_task(target);
152         dep_graph.read(source);
153     }
154
155     // Add in work-products that are still clean, and delete those that are
156     // dirty.
157     let mut work_product_decoder = Decoder::new(work_products_data, 0);
158     let work_products = try!(<Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder));
159     reconcile_work_products(tcx, work_products, &dirty_nodes);
160
161     Ok(())
162 }
163
164 fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
165                                  hashes: &[SerializedHash],
166                                  retraced: &RetracedDefIdDirectory)
167                                  -> DirtyNodes {
168     let mut hcx = HashContext::new(tcx);
169     let mut items_removed = false;
170     let mut dirty_nodes = FnvHashSet();
171     for hash in hashes {
172         match hash.node.map_def(|&i| retraced.def_id(i)) {
173             Some(dep_node) => {
174                 let current_hash = hcx.hash(&dep_node).unwrap();
175                 if current_hash != hash.hash {
176                     debug!("initial_dirty_nodes: {:?} is dirty as hash is {:?}, was {:?}",
177                            dep_node, current_hash, hash.hash);
178                     dirty_nodes.insert(dep_node);
179                 }
180             }
181             None => {
182                 items_removed = true;
183             }
184         }
185     }
186
187     // If any of the items in the krate have changed, then we consider
188     // the meta-node `Krate` to be dirty, since that means something
189     // which (potentially) read the contents of every single item.
190     if items_removed || !dirty_nodes.is_empty() {
191         dirty_nodes.insert(DepNode::Krate);
192     }
193
194     dirty_nodes
195 }
196
197 fn compute_clean_edges(serialized_edges: &[(SerializedEdge)],
198                        retraced: &RetracedDefIdDirectory,
199                        dirty_nodes: &mut DirtyNodes)
200                        -> CleanEdges {
201     // Build up an initial list of edges. Include an edge (source,
202     // target) if neither node has been removed. If the source has
203     // been removed, add target to the list of dirty nodes.
204     let mut clean_edges = Vec::with_capacity(serialized_edges.len());
205     for &(ref serialized_source, ref serialized_target) in serialized_edges {
206         if let Some(target) = retraced.map(serialized_target) {
207             if let Some(source) = retraced.map(serialized_source) {
208                 clean_edges.push((source, target))
209             } else {
210                 // source removed, target must be dirty
211                 debug!("compute_clean_edges: {:?} dirty because {:?} no longer exists",
212                        target, serialized_source);
213                 dirty_nodes.insert(target);
214             }
215         } else {
216             // target removed, ignore the edge
217         }
218     }
219
220     debug!("compute_clean_edges: dirty_nodes={:#?}", dirty_nodes);
221
222     // Propagate dirty marks by iterating repeatedly over
223     // `clean_edges`. If we find an edge `(source, target)` where
224     // `source` is dirty, add `target` to the list of dirty nodes and
225     // remove it. Keep doing this until we find no more dirty nodes.
226     let mut previous_size = 0;
227     while dirty_nodes.len() > previous_size {
228         debug!("compute_clean_edges: previous_size={}", previous_size);
229         previous_size = dirty_nodes.len();
230         let mut i = 0;
231         while i < clean_edges.len() {
232             if dirty_nodes.contains(&clean_edges[i].0) {
233                 let (source, target) = clean_edges.swap_remove(i);
234                 debug!("compute_clean_edges: dirty source {:?} -> {:?}",
235                        source, target);
236                 dirty_nodes.insert(target);
237             } else if dirty_nodes.contains(&clean_edges[i].1) {
238                 let (source, target) = clean_edges.swap_remove(i);
239                 debug!("compute_clean_edges: dirty target {:?} -> {:?}",
240                        source, target);
241             } else {
242                 i += 1;
243             }
244         }
245     }
246
247     clean_edges
248 }
249
250 /// Go through the list of work-products produced in the previous run.
251 /// Delete any whose nodes have been found to be dirty or which are
252 /// otherwise no longer applicable.
253 fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
254                                      work_products: Vec<SerializedWorkProduct>,
255                                      dirty_nodes: &DirtyNodes) {
256     debug!("reconcile_work_products({:?})", work_products);
257     for swp in work_products {
258         let dep_node = DepNode::WorkProduct(swp.id.clone());
259         if dirty_nodes.contains(&dep_node) {
260             debug!("reconcile_work_products: dep-node for {:?} is dirty", swp);
261             delete_dirty_work_product(tcx, swp);
262         } else {
263             let all_files_exist =
264                 swp.work_product
265                    .saved_files
266                    .iter()
267                    .all(|&(_, ref file_name)| {
268                        let path = in_incr_comp_dir(tcx.sess, &file_name).unwrap();
269                        path.exists()
270                    });
271             if all_files_exist {
272                 debug!("reconcile_work_products: all files for {:?} exist", swp);
273                 tcx.dep_graph.insert_previous_work_product(&swp.id, swp.work_product);
274             } else {
275                 debug!("reconcile_work_products: some file for {:?} does not exist", swp);
276                 delete_dirty_work_product(tcx, swp);
277             }
278         }
279     }
280 }
281
282 fn delete_dirty_work_product(tcx: TyCtxt,
283                              swp: SerializedWorkProduct) {
284     debug!("delete_dirty_work_product({:?})", swp);
285     for &(_, ref file_name) in &swp.work_product.saved_files {
286         let path = in_incr_comp_dir(tcx.sess, file_name).unwrap();
287         match fs::remove_file(&path) {
288             Ok(()) => { }
289             Err(err) => {
290                 tcx.sess.warn(
291                     &format!("file-system error deleting outdated file `{}`: {}",
292                              path.display(), err));
293             }
294         }
295     }
296 }