use rbml::opaque::Encoder;
use rustc::dep_graph::DepNode;
+use rustc::hir::def_id::DefId;
use rustc::middle::cstore::LOCAL_CRATE;
+use rustc::session::Session;
use rustc::ty::TyCtxt;
-use rustc_serialize::{Encodable as RustcEncodable};
-use std::hash::{Hasher, SipHasher};
+use rustc_data_structures::fnv::FnvHashMap;
+use rustc_serialize::Encodable as RustcEncodable;
+use std::hash::{Hash, Hasher, SipHasher};
use std::io::{self, Cursor, Write};
use std::fs::{self, File};
use std::path::PathBuf;
use super::data::*;
use super::directory::*;
use super::hash::*;
+use super::preds::*;
use super::util::*;
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+ debug!("save_dep_graph()");
let _ignore = tcx.dep_graph.in_ignore();
+ let sess = tcx.sess;
+ if sess.opts.incremental.is_none() {
+ return;
+ }
let mut hcx = HashContext::new(tcx);
- save_in(&mut hcx, dep_graph_path(tcx), encode_dep_graph);
- save_in(&mut hcx, metadata_hash_path(tcx, LOCAL_CRATE), encode_metadata_hashes);
+ let mut builder = DefIdDirectoryBuilder::new(tcx);
+ let query = tcx.dep_graph.query();
+ let preds = Predecessors::new(&query, &mut hcx);
+ save_in(sess,
+ dep_graph_path(tcx),
+ |e| encode_dep_graph(&preds, &mut builder, e));
+ save_in(sess,
+ metadata_hash_path(tcx, LOCAL_CRATE),
+ |e| encode_metadata_hashes(tcx, &preds, &mut builder, e));
}
-fn save_in<'a, 'tcx, F>(hcx: &mut HashContext<'a, 'tcx>,
- opt_path_buf: Option<PathBuf>,
- encode: F)
- where F: FnOnce(&mut HashContext<'a, 'tcx>, &mut Encoder) -> io::Result<()>
-{
- let tcx = hcx.tcx;
+pub fn save_work_products(sess: &Session, local_crate_name: &str) {
+ debug!("save_work_products()");
+ let _ignore = sess.dep_graph.in_ignore();
+ let path = sess_work_products_path(sess, local_crate_name);
+ save_in(sess, path, |e| encode_work_products(sess, e));
+}
+fn save_in<F>(sess: &Session, opt_path_buf: Option<PathBuf>, encode: F)
+ where F: FnOnce(&mut Encoder) -> io::Result<()>
+{
let path_buf = match opt_path_buf {
Some(p) => p,
- None => return
+ None => return,
};
// FIXME(#32754) lock file?
// delete the old dep-graph, if any
if path_buf.exists() {
match fs::remove_file(&path_buf) {
- Ok(()) => { }
+ Ok(()) => {}
Err(err) => {
- tcx.sess.err(
- &format!("unable to delete old dep-graph at `{}`: {}",
- path_buf.display(), err));
+ sess.err(&format!("unable to delete old dep-graph at `{}`: {}",
+ path_buf.display(),
+ err));
return;
}
}
// generate the data in a memory buffer
let mut wr = Cursor::new(Vec::new());
- match encode(hcx, &mut Encoder::new(&mut wr)) {
- Ok(()) => { }
+ match encode(&mut Encoder::new(&mut wr)) {
+ Ok(()) => {}
Err(err) => {
- tcx.sess.err(
- &format!("could not encode dep-graph to `{}`: {}",
- path_buf.display(), err));
+ sess.err(&format!("could not encode dep-graph to `{}`: {}",
+ path_buf.display(),
+ err));
return;
}
}
// write the data out
let data = wr.into_inner();
- match
- File::create(&path_buf)
- .and_then(|mut file| file.write_all(&data))
- {
- Ok(_) => { }
+ match File::create(&path_buf).and_then(|mut file| file.write_all(&data)) {
+ Ok(_) => {}
Err(err) => {
- tcx.sess.err(
- &format!("failed to write dep-graph to `{}`: {}",
- path_buf.display(), err));
+ sess.err(&format!("failed to write dep-graph to `{}`: {}",
+ path_buf.display(),
+ err));
return;
}
}
}
-pub fn encode_dep_graph<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
- encoder: &mut Encoder)
- -> io::Result<()>
-{
- let tcx = hcx.tcx;
- let query = tcx.dep_graph.query();
-
- let mut builder = DefIdDirectoryBuilder::new(tcx);
-
- // Create hashes for inputs.
- let hashes =
- query.nodes()
- .into_iter()
- .filter_map(|dep_node| {
- hcx.hash(&dep_node)
- .map(|hash| {
- let node = builder.map(dep_node);
- SerializedHash { node: node, hash: hash }
- })
- })
- .collect();
+pub fn encode_dep_graph(preds: &Predecessors,
+ builder: &mut DefIdDirectoryBuilder,
+ encoder: &mut Encoder)
+ -> io::Result<()> {
+ // First encode the commandline arguments hash
+ let tcx = builder.tcx();
+ try!(tcx.sess.opts.dep_tracking_hash().encode(encoder));
+
+ // Create a flat list of (Input, WorkProduct) edges for
+ // serialization.
+ let mut edges = vec![];
+ for (&target, sources) in &preds.inputs {
+ match *target {
+ DepNode::MetaData(ref def_id) => {
+ // Metadata *targets* are always local metadata nodes. We handle
+ // those in `encode_metadata_hashes`, which comes later.
+ assert!(def_id.is_local());
+ continue;
+ }
+ _ => (),
+ }
+ let target = builder.map(target);
+ for &source in sources {
+ let source = builder.map(source);
+ edges.push((source, target.clone()));
+ }
+ }
// Create the serialized dep-graph.
let graph = SerializedDepGraph {
- nodes: query.nodes().into_iter()
- .map(|node| builder.map(node))
- .collect(),
- edges: query.edges().into_iter()
- .map(|(source_node, target_node)| {
- let source = builder.map(source_node);
- let target = builder.map(target_node);
- (source, target)
- })
- .collect(),
- hashes: hashes,
+ edges: edges,
+ hashes: preds.hashes
+ .iter()
+ .map(|(&dep_node, &hash)| {
+ SerializedHash {
+ dep_node: builder.map(dep_node),
+ hash: hash,
+ }
+ })
+ .collect(),
};
debug!("graph = {:#?}", graph);
// Encode the directory and then the graph data.
- let directory = builder.into_directory();
- try!(directory.encode(encoder));
+ try!(builder.directory().encode(encoder));
try!(graph.encode(encoder));
Ok(())
}
-pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
- encoder: &mut Encoder)
- -> io::Result<()>
-{
- let tcx = hcx.tcx;
- let query = tcx.dep_graph.query();
+pub fn encode_metadata_hashes(tcx: TyCtxt,
+ preds: &Predecessors,
+ builder: &mut DefIdDirectoryBuilder,
+ encoder: &mut Encoder)
+ -> io::Result<()> {
+ let mut def_id_hashes = FnvHashMap();
+ let mut def_id_hash = |def_id: DefId| -> u64 {
+ *def_id_hashes.entry(def_id)
+ .or_insert_with(|| {
+ let index = builder.add(def_id);
+ let path = builder.lookup_def_path(index);
+ path.deterministic_hash(tcx)
+ })
+ };
- let serialized_hashes = {
- // Identify the `MetaData(X)` nodes where `X` is local. These are
- // the metadata items we export. Downstream crates will want to
- // see a hash that tells them whether we might have changed the
- // metadata for a given item since they last compiled.
- let meta_data_def_ids =
- query.nodes()
- .into_iter()
- .filter_map(|dep_node| match *dep_node {
- DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
- _ => None,
- });
+ // For each `MetaData(X)` node where `X` is local, accumulate a
+ // hash. These are the metadata items we export. Downstream
+ // crates will want to see a hash that tells them whether we might
+ // have changed the metadata for a given item since they last
+ // compiled.
+ //
+ // (I initially wrote this with an iterator, but it seemed harder to read.)
+ let mut serialized_hashes = SerializedMetadataHashes { hashes: vec![] };
+ for (&target, sources) in &preds.inputs {
+ let def_id = match *target {
+ DepNode::MetaData(def_id) => {
+ assert!(def_id.is_local());
+ def_id
+ }
+ _ => continue,
+ };
// To create the hash for each item `X`, we don't hash the raw
// bytes of the metadata (though in principle we
// from the dep-graph. This corresponds to all the inputs that
// were read to construct the metadata. To create the hash for
// the metadata, we hash (the hash of) all of those inputs.
- let hashes =
- meta_data_def_ids
- .map(|def_id| {
- assert!(def_id.is_local());
- let dep_node = DepNode::MetaData(def_id);
- let mut state = SipHasher::new();
- debug!("save: computing metadata hash for {:?}", dep_node);
- for node in query.transitive_predecessors(&dep_node) {
- if let Some(hash) = hcx.hash(&node) {
- debug!("save: predecessor {:?} has hash {}", node, hash);
- state.write_u64(hash.to_le());
- } else {
- debug!("save: predecessor {:?} cannot be hashed", node);
- }
- }
- let hash = state.finish();
- debug!("save: metadata hash for {:?} is {}", dep_node, hash);
- SerializedMetadataHash {
- def_index: def_id.index,
- hash: hash,
- }
- });
-
- // Collect these up into a vector.
- SerializedMetadataHashes {
- hashes: hashes.collect()
- }
- };
+ debug!("save: computing metadata hash for {:?}", def_id);
+
+ // Create a vector containing a pair of (source-id, hash).
+ // The source-id is stored as a `DepNode<u64>`, where the u64
+ // is the det. hash of the def-path. This is convenient
+ // because we can sort this to get a stable ordering across
+ // compilations, even if the def-ids themselves have changed.
+ let mut hashes: Vec<(DepNode<u64>, u64)> = sources.iter()
+ .map(|dep_node| {
+ let hash_dep_node = dep_node.map_def(|&def_id| Some(def_id_hash(def_id))).unwrap();
+ let hash = preds.hashes[dep_node];
+ (hash_dep_node, hash)
+ })
+ .collect();
+
+ hashes.sort();
+ let mut state = SipHasher::new();
+ hashes.hash(&mut state);
+ let hash = state.finish();
+
+ debug!("save: metadata hash for {:?} is {}", def_id, hash);
+ serialized_hashes.hashes.push(SerializedMetadataHash {
+ def_index: def_id.index,
+ hash: hash,
+ });
+ }
// Encode everything.
try!(serialized_hashes.encode(encoder));
Ok(())
}
+
+pub fn encode_work_products(sess: &Session, encoder: &mut Encoder) -> io::Result<()> {
+ let work_products: Vec<_> = sess.dep_graph
+ .work_products()
+ .iter()
+ .map(|(id, work_product)| {
+ SerializedWorkProduct {
+ id: id.clone(),
+ work_product: work_product.clone(),
+ }
+ })
+ .collect();
+
+ work_products.encode(encoder)
+}