diff --git a/mk/clean.mk b/mk/clean.mk index 002db59ad387..bc5961a99813 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -58,6 +58,7 @@ clean-generic-$(2)-$(1): -name '*.[odasS]' -o \ -name '*.so' -o \ -name '*.dylib' -o \ + -name '*.rlib' -o \ -name 'stamp.*' -o \ -name '*.lib' -o \ -name '*.dll' -o \ diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 33d3a1c67f2e..bacc98a01356 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use back::archive::{Archive, METADATA_FILENAME}; use back::rpath; +use back::svh::Svh; use driver::driver::{CrateTranslation, OutputFilenames}; use driver::session::Session; use driver::session; @@ -499,30 +499,33 @@ pub mod write { * system linkers understand. */ -pub fn build_link_meta(attrs: &[ast::Attribute], - output: &OutputFilenames, - symbol_hasher: &mut Sha256) - -> LinkMeta { - // This calculates CMH as defined above - fn crate_hash(symbol_hasher: &mut Sha256, crateid: &CrateId) -> ~str { - symbol_hasher.reset(); - symbol_hasher.input_str(crateid.to_str()); - truncated_hash_result(symbol_hasher) - } - - let crateid = match attr::find_crateid(attrs) { +pub fn find_crate_id(attrs: &[ast::Attribute], + output: &OutputFilenames) -> CrateId { + match attr::find_crateid(attrs) { None => from_str(output.out_filestem).unwrap(), Some(s) => s, - }; - - let hash = crate_hash(symbol_hasher, &crateid); - - LinkMeta { - crateid: crateid, - crate_hash: hash, } } +pub fn crate_id_hash(crate_id: &CrateId) -> ~str { + // This calculates CMH as defined above. Note that we don't use the path of + // the crate id in the hash because lookups are only done by (name/vers), + // not by path. + let mut s = Sha256::new(); + s.input_str(crate_id.short_name_with_version()); + truncated_hash_result(&mut s).slice_to(8).to_owned() +} + +pub fn build_link_meta(krate: &ast::Crate, + output: &OutputFilenames) -> LinkMeta { + let r = LinkMeta { + crateid: find_crate_id(krate.attrs, output), + crate_hash: Svh::calculate(krate), + }; + info!("{}", r); + return r; +} + fn truncated_hash_result(symbol_hasher: &mut Sha256) -> ~str { let output = symbol_hasher.result_bytes(); // 64 bits should be enough to avoid collisions. @@ -539,7 +542,7 @@ fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &mut Sha256, symbol_hasher.reset(); symbol_hasher.input_str(link_meta.crateid.name); symbol_hasher.input_str("-"); - symbol_hasher.input_str(link_meta.crate_hash); + symbol_hasher.input_str(link_meta.crate_hash.as_str()); symbol_hasher.input_str("-"); symbol_hasher.input_str(encoder::encoded_ty(tcx, t)); let mut hash = truncated_hash_result(symbol_hasher); @@ -712,11 +715,8 @@ pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> ~str mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None) } -pub fn output_lib_filename(lm: &LinkMeta) -> ~str { - format!("{}-{}-{}", - lm.crateid.name, - lm.crate_hash.slice_chars(0, 8), - lm.crateid.version_or_default()) +pub fn output_lib_filename(id: &CrateId) -> ~str { + format!("{}-{}-{}", id.name, crate_id_hash(id), id.version_or_default()) } pub fn get_cc_prog(sess: Session) -> ~str { @@ -779,11 +779,11 @@ fn remove(sess: Session, path: &Path) { pub fn link_binary(sess: Session, trans: &CrateTranslation, outputs: &OutputFilenames, - lm: &LinkMeta) -> ~[Path] { + id: &CrateId) -> ~[Path] { let mut out_filenames = ~[]; let crate_types = sess.crate_types.borrow(); for &crate_type in crate_types.get().iter() { - let out_file = link_binary_output(sess, trans, crate_type, outputs, lm); + let out_file = link_binary_output(sess, trans, crate_type, outputs, id); out_filenames.push(out_file); } @@ -807,8 +807,8 @@ fn is_writeable(p: &Path) -> bool { } pub fn filename_for_input(sess: &Session, crate_type: session::CrateType, - lm: &LinkMeta, out_filename: &Path) -> Path { - let libname = output_lib_filename(lm); + id: &CrateId, out_filename: &Path) -> Path { + let libname = output_lib_filename(id); match crate_type { session::CrateTypeRlib => { out_filename.with_filename(format!("lib{}.rlib", libname)) @@ -834,13 +834,13 @@ fn link_binary_output(sess: Session, trans: &CrateTranslation, crate_type: session::CrateType, outputs: &OutputFilenames, - lm: &LinkMeta) -> Path { + id: &CrateId) -> Path { let obj_filename = outputs.temp_path(OutputTypeObject); let out_filename = match outputs.single_output_file { Some(ref file) => file.clone(), None => { let out_filename = outputs.path(OutputTypeExe); - filename_for_input(&sess, crate_type, lm, &out_filename) + filename_for_input(&sess, crate_type, id, &out_filename) } }; diff --git a/src/librustc/back/svh.rs b/src/librustc/back/svh.rs new file mode 100644 index 000000000000..5f8a12b022a5 --- /dev/null +++ b/src/librustc/back/svh.rs @@ -0,0 +1,112 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Calculation and management of a Strict Version Hash for crates +//! +//! # Today's ABI problem +//! +//! In today's implementation of rustc, it is incredibly difficult to achieve +//! forward binary compatibility without resorting to C-like interfaces. Within +//! rust code itself, abi details such as symbol names suffer from a variety of +//! unrelated factors to code changing such as the "def id drift" problem. This +//! ends up yielding confusing error messages about metadata mismatches and +//! such. +//! +//! The core of this problem is when when an upstream dependency changes and +//! downstream dependants are not recompiled. This causes compile errors because +//! the upstream crate's metadata has changed but the downstream crates are +//! still referencing the older crate's metadata. +//! +//! This problem exists for many reasons, the primary of which is that rust does +//! not currently support forwards ABI compatibility (in place upgrades of a +//! crate). +//! +//! # SVH and how it alleviates the problem +//! +//! With all of this knowledge on hand, this module contains the implementation +//! of a notion of a "Strict Version Hash" for a crate. This is essentially a +//! hash of all contents of a crate which can somehow be exposed to downstream +//! crates. +//! +//! This hash is currently calculated by just hashing the AST, but this is +//! obviously wrong (doc changes should not result in an incompatible ABI). +//! Implementation-wise, this is required at this moment in time. +//! +//! By encoding this strict version hash into all crate's metadata, stale crates +//! can be detected immediately and error'd about by rustc itself. +//! +//! # Relevant links +//! +//! Original issue: https://github.com/mozilla/rust/issues/10207 + +use std::fmt; +use std::hash::Hash; +use std::hash::sip::SipState; +use std::iter::range_step; +use syntax::ast; + +#[deriving(Clone, Eq)] +pub struct Svh { + priv hash: ~str, +} + +impl Svh { + pub fn new(hash: &str) -> Svh { + assert!(hash.len() == 16); + Svh { hash: hash.to_owned() } + } + + pub fn as_str<'a>(&'a self) -> &'a str { + self.hash.as_slice() + } + + pub fn calculate(krate: &ast::Crate) -> Svh { + // FIXME: see above for why this is wrong, it shouldn't just hash the + // crate. Fixing this would require more in-depth analysis in + // this function about what portions of the crate are reachable + // in tandem with bug fixes throughout the rest of the compiler. + // + // Note that for now we actually exclude some top-level things + // from the crate like the CrateConfig/span. The CrateConfig + // contains command-line `--cfg` flags, so this means that the + // stage1/stage2 AST for libstd and such is different hash-wise + // when it's actually the exact same representation-wise. + // + // As a first stab at only hashing the relevant parts of the + // AST, this only hashes the module/attrs, not the CrateConfig + // field. + // + // FIXME: this should use SHA1, not SipHash. SipHash is not built to + // avoid collisions. + let mut state = SipState::new(); + krate.module.hash(&mut state); + krate.attrs.hash(&mut state); + + let hash = state.result(); + return Svh { + hash: range_step(0, 64, 4).map(|i| hex(hash >> i)).collect() + }; + + fn hex(b: u64) -> char { + let b = (b & 0xf) as u8; + let b = match b { + 0 .. 9 => '0' as u8 + b, + _ => 'a' as u8 + b - 10, + }; + b as char + } + } +} + +impl fmt::Show for Svh { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(self.as_str()) + } +} diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4c552acc9369..d5ee736b6fb7 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -433,7 +433,7 @@ pub fn phase_6_link_output(sess: Session, link::link_binary(sess, trans, outputs, - &trans.link)); + &trans.link.crateid)); } pub fn stop_after_phase_3(sess: Session) -> bool { @@ -472,8 +472,7 @@ fn write_out_deps(sess: Session, input: &Input, outputs: &OutputFilenames, krate: &ast::Crate) -> io::IoResult<()> { - let lm = link::build_link_meta(krate.attrs, outputs, - &mut ::util::sha2::Sha256::new()); + let id = link::find_crate_id(krate.attrs, outputs); let mut out_filenames = ~[]; for output_type in sess.opts.output_types.iter() { @@ -482,7 +481,7 @@ fn write_out_deps(sess: Session, link::OutputTypeExe => { let crate_types = sess.crate_types.borrow(); for output in crate_types.get().iter() { - let p = link::filename_for_input(&sess, *output, &lm, &file); + let p = link::filename_for_input(&sess, *output, &id, &file); out_filenames.push(p); } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ff1a6bb7f7ef..2e647e5ca829 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -55,7 +55,6 @@ use std::str; use std::task; use std::vec; use syntax::ast; -use syntax::attr; use syntax::diagnostic::Emitter; use syntax::diagnostic; use syntax::parse; @@ -104,16 +103,17 @@ pub mod front { } pub mod back { - pub mod archive; - pub mod link; pub mod abi; + pub mod archive; pub mod arm; + pub mod link; + pub mod lto; pub mod mips; + pub mod rpath; + pub mod svh; + pub mod target_strs; pub mod x86; pub mod x86_64; - pub mod rpath; - pub mod target_strs; - pub mod lto; } pub mod metadata; @@ -312,28 +312,18 @@ pub fn run_compiler(args: &[~str]) { let attrs = parse_crate_attrs(sess, &input); let t_outputs = d::build_output_filenames(&input, &odir, &ofile, attrs, sess); - if crate_id || crate_name { - let crateid = match attr::find_crateid(attrs) { - Some(crateid) => crateid, - None => { - sess.fatal("No crate_id and --crate-id or \ - --crate-name requested") - } - }; - if crate_id { - println!("{}", crateid.to_str()); - } - if crate_name { - println!("{}", crateid.name); - } - } + let id = link::find_crate_id(attrs, &t_outputs); + if crate_id { + println!("{}", id.to_str()); + } + if crate_name { + println!("{}", id.name); + } if crate_file_name { - let lm = link::build_link_meta(attrs, &t_outputs, - &mut ::util::sha2::Sha256::new()); let crate_types = session::collect_crate_types(&sess, attrs); for &style in crate_types.iter() { - let fname = link::filename_for_input(&sess, style, &lm, + let fname = link::filename_for_input(&sess, style, &id, &t_outputs.with_extension("")); println!("{}", fname.filename_display()); } diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 62f1dcedab45..7b7d526411c9 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -12,6 +12,7 @@ use std::cast; use syntax::crateid::CrateId; +use back::svh::Svh; // EBML enum definitions and utils shared by the encoder and decoder @@ -207,8 +208,8 @@ pub static tag_macro_registrar_fn: uint = 0x63; pub static tag_exported_macros: uint = 0x64; pub static tag_macro_def: uint = 0x65; -#[deriving(Clone)] +#[deriving(Clone, Show)] pub struct LinkMeta { crateid: CrateId, - crate_hash: ~str, + crate_hash: Svh, } diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 1108917cdb1d..165c1abdeedf 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -12,6 +12,8 @@ //! Validates all used crates and extern libraries and loads their metadata +use back::link; +use back::svh::Svh; use driver::{driver, session}; use driver::session::Session; use metadata::csearch; @@ -78,7 +80,7 @@ impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> { struct cache_entry { cnum: ast::CrateNum, span: Span, - hash: ~str, + hash: Svh, crate_id: CrateId, } @@ -148,7 +150,7 @@ fn visit_view_item(e: &mut Env, i: &ast::ViewItem) { match extract_crate_info(e, i) { Some(info) => { - let cnum = resolve_crate(e, None, info.ident, &info.crate_id, "", + let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None, i.span); e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum); } @@ -276,12 +278,13 @@ fn visit_item(e: &Env, i: &ast::Item) { } fn existing_match(e: &Env, crate_id: &CrateId, - hash: &str) -> Option { + hash: Option<&Svh>) -> Option { let crate_cache = e.crate_cache.borrow(); for c in crate_cache.get().iter() { - if crate_id.matches(&c.crate_id) && - (hash.is_empty() || hash == c.hash.as_slice()) { - return Some(c.cnum) + if !crate_id.matches(&c.crate_id) { continue } + match hash { + Some(hash) if *hash != c.hash => {} + Some(..) | None => return Some(c.cnum) } } None @@ -291,19 +294,22 @@ fn resolve_crate(e: &mut Env, root_ident: Option<&str>, ident: &str, crate_id: &CrateId, - hash: &str, + hash: Option<&Svh>, span: Span) -> ast::CrateNum { match existing_match(e, crate_id, hash) { None => { - let load_ctxt = loader::Context { + let id_hash = link::crate_id_hash(crate_id); + let mut load_ctxt = loader::Context { sess: e.sess, span: span, ident: ident, crate_id: crate_id, - hash: hash, + id_hash: id_hash, + hash: hash.map(|a| &*a), os: e.os, - intr: e.intr + intr: e.intr, + rejected_via_hash: false, }; let loader::Library { dylib, rlib, metadata @@ -375,7 +381,7 @@ fn resolve_crate_deps(e: &mut Env, let local_cnum = resolve_crate(e, root_ident, dep.crate_id.name.as_slice(), &dep.crate_id, - dep.hash, + Some(&dep.hash), span); cnum_map.insert(extrn_cnum, local_cnum); } @@ -406,7 +412,7 @@ impl CrateLoader for Loader { fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate { let info = extract_crate_info(&self.env, krate).unwrap(); let cnum = resolve_crate(&mut self.env, None, info.ident, - &info.crate_id, "", krate.span); + &info.crate_id, None, krate.span); let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap(); MacroCrate { lib: library.dylib, diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index 12461ddbe715..baca85d50ae9 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -13,6 +13,7 @@ // The crate store - a central repo for information collected about external // crates and libraries +use back::svh::Svh; use metadata::decoder; use metadata::loader; @@ -92,7 +93,7 @@ impl CStore { *metas.get().get(&cnum) } - pub fn get_crate_hash(&self, cnum: ast::CrateNum) -> ~str { + pub fn get_crate_hash(&self, cnum: ast::CrateNum) -> Svh { let cdata = self.get_crate_data(cnum); decoder::get_crate_hash(cdata.data()) } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index f365ddef94f5..42754aedba70 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -12,6 +12,7 @@ #[allow(non_camel_case_types)]; +use back::svh::Svh; use metadata::cstore::crate_metadata; use metadata::common::*; use metadata::csearch::StaticMethodInfo; @@ -1089,9 +1090,9 @@ fn get_attributes(md: ebml::Doc) -> ~[ast::Attribute] { return attrs; } -fn list_crate_attributes(md: ebml::Doc, hash: &str, +fn list_crate_attributes(md: ebml::Doc, hash: &Svh, out: &mut io::Writer) -> io::IoResult<()> { - try!(write!(out, "=Crate Attributes ({})=\n", hash)); + try!(write!(out, "=Crate Attributes ({})=\n", *hash)); let r = get_attributes(md); for attr in r.iter() { @@ -1109,7 +1110,7 @@ pub fn get_crate_attributes(data: &[u8]) -> ~[ast::Attribute] { pub struct CrateDep { cnum: ast::CrateNum, crate_id: CrateId, - hash: ~str, + hash: Svh, } pub fn get_crate_deps(data: &[u8]) -> ~[CrateDep] { @@ -1123,7 +1124,7 @@ pub fn get_crate_deps(data: &[u8]) -> ~[CrateDep] { } reader::tagged_docs(depsdoc, tag_crate_dep, |depdoc| { let crate_id = from_str(docstr(depdoc, tag_crate_dep_crateid)).unwrap(); - let hash = docstr(depdoc, tag_crate_dep_hash); + let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash)); deps.push(CrateDep { cnum: crate_num, crate_id: crate_id, @@ -1144,10 +1145,10 @@ fn list_crate_deps(data: &[u8], out: &mut io::Writer) -> io::IoResult<()> { Ok(()) } -pub fn get_crate_hash(data: &[u8]) -> ~str { +pub fn get_crate_hash(data: &[u8]) -> Svh { let cratedoc = reader::Doc(data); let hashdoc = reader::get_doc(cratedoc, tag_crate_hash); - hashdoc.as_str_slice().to_str() + Svh::new(hashdoc.as_str_slice()) } pub fn get_crate_id(data: &[u8]) -> CrateId { @@ -1159,7 +1160,7 @@ pub fn get_crate_id(data: &[u8]) -> CrateId { pub fn list_crate_metadata(bytes: &[u8], out: &mut io::Writer) -> io::IoResult<()> { let hash = get_crate_hash(bytes); let md = reader::Doc(bytes); - try!(list_crate_attributes(md, hash, out)); + try!(list_crate_attributes(md, &hash, out)); list_crate_deps(bytes, out) } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 976c1ee92d3d..5bcc113ef946 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -13,6 +13,7 @@ #[allow(unused_must_use)]; // everything is just a MemWriter, can't fail #[allow(non_camel_case_types)]; +use back::svh::Svh; use metadata::common::*; use metadata::cstore; use metadata::decoder; @@ -1733,14 +1734,14 @@ fn encode_crate_dep(ebml_w: &mut writer::Encoder, ebml_w.writer.write(dep.crate_id.to_str().as_bytes()); ebml_w.end_tag(); ebml_w.start_tag(tag_crate_dep_hash); - ebml_w.writer.write(dep.hash.as_bytes()); + ebml_w.writer.write(dep.hash.as_str().as_bytes()); ebml_w.end_tag(); ebml_w.end_tag(); } -fn encode_hash(ebml_w: &mut writer::Encoder, hash: &str) { +fn encode_hash(ebml_w: &mut writer::Encoder, hash: &Svh) { ebml_w.start_tag(tag_crate_hash); - ebml_w.writer.write(hash.as_bytes()); + ebml_w.writer.write(hash.as_str().as_bytes()); ebml_w.end_tag(); } @@ -1809,7 +1810,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) let mut ebml_w = writer::Encoder(wr); encode_crate_id(&mut ebml_w, &ecx.link_meta.crateid); - encode_hash(&mut ebml_w, ecx.link_meta.crate_hash); + encode_hash(&mut ebml_w, &ecx.link_meta.crate_hash); let mut i = ebml_w.writer.tell().unwrap(); let crate_attrs = synthesize_crate_attrs(&ecx, krate); diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index faef2412e780..9c61191ff991 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -11,6 +11,7 @@ //! Finds crate binaries and loads their metadata use back::archive::{ArchiveRO, METADATA_FILENAME}; +use back::svh::Svh; use driver::session::Session; use lib::llvm::{False, llvm, ObjectFile, mk_section_iter}; use metadata::cstore::{MetadataBlob, MetadataVec, MetadataArchive}; @@ -21,7 +22,6 @@ use syntax::codemap::Span; use syntax::diagnostic::SpanHandler; use syntax::parse::token::IdentInterner; use syntax::crateid::CrateId; -use syntax::attr; use syntax::attr::AttrMetaMethods; use std::c_str::ToCStr; @@ -49,9 +49,11 @@ pub struct Context<'a> { span: Span, ident: &'a str, crate_id: &'a CrateId, - hash: &'a str, + id_hash: &'a str, + hash: Option<&'a Svh>, os: Os, - intr: @IdentInterner + intr: @IdentInterner, + rejected_via_hash: bool, } pub struct Library { @@ -79,23 +81,34 @@ fn realpath(p: &Path) -> Path { } impl<'a> Context<'a> { - pub fn load_library_crate(&self, root_ident: Option<&str>) -> Library { + pub fn load_library_crate(&mut self, root_ident: Option<&str>) -> Library { match self.find_library_crate() { Some(t) => t, None => { self.sess.abort_if_errors(); - let message = match root_ident { - None => format!("can't find crate for `{}`", self.ident), - Some(c) => format!("can't find crate for `{}` which `{}` depends on", - self.ident, - c) + let message = if self.rejected_via_hash { + format!("found possibly newer version of crate `{}`", + self.ident) + } else { + format!("can't find crate for `{}`", self.ident) }; - self.sess.span_fatal(self.span, message); + let message = match root_ident { + None => message, + Some(c) => format!("{} which `{}` depends on", message, c), + }; + self.sess.span_err(self.span, message); + + if self.rejected_via_hash { + self.sess.span_note(self.span, "perhaps this crate needs \ + to be recompiled?"); + } + self.sess.abort_if_errors(); + unreachable!() } } } - fn find_library_crate(&self) -> Option { + fn find_library_crate(&mut self) -> Option { let filesearch = self.sess.filesearch; let (dyprefix, dysuffix) = self.dylibname(); @@ -212,13 +225,8 @@ impl<'a> Context<'a> { None => {} } let data = lib.metadata.as_slice(); - let attrs = decoder::get_crate_attributes(data); - match attr::find_crateid(attrs) { - None => {} - Some(crateid) => { - note_crateid_attr(self.sess.diagnostic(), &crateid); - } - } + let crate_id = decoder::get_crate_id(data); + note_crateid_attr(self.sess.diagnostic(), &crate_id); } None } @@ -240,18 +248,21 @@ impl<'a> Context<'a> { debug!("matching -- {}, middle: {}", file, middle); let mut parts = middle.splitn('-', 1); let hash = match parts.next() { Some(h) => h, None => return None }; - debug!("matching -- {}, hash: {}", file, hash); + debug!("matching -- {}, hash: {} (want {})", file, hash, self.id_hash); let vers = match parts.next() { Some(v) => v, None => return None }; - debug!("matching -- {}, vers: {}", file, vers); + debug!("matching -- {}, vers: {} (want {})", file, vers, + self.crate_id.version); match self.crate_id.version { Some(ref version) if version.as_slice() != vers => return None, - Some(..) | None => {} + Some(..) => {} // check the hash + + // hash is irrelevant, no version specified + None => return Some(hash.to_owned()) } - debug!("matching -- {}, vers ok (requested {})", file, - self.crate_id.version); + debug!("matching -- {}, vers ok", file); // hashes in filenames are prefixes of the "true hash" - if self.hash.is_empty() || self.hash.starts_with(hash) { - debug!("matching -- {}, hash ok (requested {})", file, self.hash); + if self.id_hash == hash.as_slice() { + debug!("matching -- {}, hash ok", file); Some(hash.to_owned()) } else { None @@ -270,7 +281,7 @@ impl<'a> Context<'a> { // FIXME(#10786): for an optimization, we only read one of the library's // metadata sections. In theory we should read both, but // reading dylib metadata is quite slow. - fn extract_one(&self, m: HashSet, flavor: &str, + fn extract_one(&mut self, m: HashSet, flavor: &str, slot: &mut Option) -> Option { if m.len() == 0 { return None } if m.len() > 1 { @@ -290,7 +301,7 @@ impl<'a> Context<'a> { info!("{} reading meatadata from: {}", flavor, lib.display()); match get_metadata_section(self.os, &lib) { Some(blob) => { - if crate_matches(blob.as_slice(), self.crate_id, self.hash){ + if self.crate_matches(blob.as_slice()) { *slot = Some(blob); } else { info!("metadata mismatch"); @@ -306,6 +317,22 @@ impl<'a> Context<'a> { return Some(lib); } + fn crate_matches(&mut self, crate_data: &[u8]) -> bool { + let other_id = decoder::get_crate_id(crate_data); + if !self.crate_id.matches(&other_id) { return false } + match self.hash { + None => true, + Some(hash) => { + if *hash != decoder::get_crate_hash(crate_data) { + self.rejected_via_hash = true; + false + } else { + true + } + } + } + } + // Returns the corresponding (prefix, suffix) that files need to have for // dynamic libraries fn dylibname(&self) -> (&'static str, &'static str) { @@ -323,15 +350,6 @@ pub fn note_crateid_attr(diag: @SpanHandler, crateid: &CrateId) { diag.handler().note(format!("crate_id: {}", crateid.to_str())); } -fn crate_matches(crate_data: &[u8], crate_id: &CrateId, hash: &str) -> bool { - let other_id = decoder::get_crate_id(crate_data); - if !crate_id.matches(&other_id) { return false } - if hash != "" && hash != decoder::get_crate_hash(crate_data).as_slice() { - return false - } - return true; -} - impl ArchiveMetadata { fn new(ar: ArchiveRO) -> Option { let data: &'static [u8] = { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 553dbe2ae6e3..683246f3333b 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2453,7 +2453,8 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta, let sym_name = if is_top { ~"_rust_crate_map_toplevel" } else { - symname("_rust_crate_map_" + mapmeta.crateid.name, mapmeta.crate_hash, + symname("_rust_crate_map_" + mapmeta.crateid.name, + mapmeta.crate_hash.as_str(), mapmeta.crateid.version_or_default()) }; @@ -2487,7 +2488,7 @@ pub fn fill_crate_map(ccx: @CrateContext, map: ValueRef) { while cstore.have_crate_data(i) { let cdata = cstore.get_crate_data(i); let nm = symname(format!("_rust_crate_map_{}", cdata.name), - cstore.get_crate_hash(i), + cstore.get_crate_hash(i).as_str(), cstore.get_crate_id(i).version_or_default()); let cr = nm.with_c_str(|buf| { unsafe { @@ -2609,9 +2610,7 @@ pub fn trans_crate(sess: session::Session, } } - let mut symbol_hasher = Sha256::new(); - let link_meta = link::build_link_meta(krate.attrs, output, - &mut symbol_hasher); + let link_meta = link::build_link_meta(&krate, output); // Append ".rs" to crate name as LLVM module identifier. // @@ -2621,16 +2620,16 @@ pub fn trans_crate(sess: session::Session, // crashes if the module identifer is same as other symbols // such as a function name in the module. // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = link_meta.crateid.name.clone() + ".rs"; + let llmod_id = link_meta.crateid.name + ".rs"; let ccx = @CrateContext::new(sess, - llmod_id, - analysis.ty_cx, - analysis.exp_map2, - analysis.maps, - symbol_hasher, - link_meta, - analysis.reachable); + llmod_id, + analysis.ty_cx, + analysis.exp_map2, + analysis.maps, + Sha256::new(), + link_meta, + analysis.reachable); { let _icx = push_ctxt("text"); trans_mod(ccx, &krate.module); diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 4abc114fef66..8350b24c451c 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -329,7 +329,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, let hash = ty::hash_crate_independent( ccx.tcx, substs.tys[0], - ccx.link_meta.crate_hash.clone()); + &ccx.link_meta.crate_hash); // NB: This needs to be kept in lockstep with the TypeId struct in // libstd/unstable/intrinsics.rs let val = C_named_struct(type_of::type_of(ccx, output_type), [C_u64(hash)]); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 6b26e268057a..2ca19f0b61d8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -10,6 +10,7 @@ #[allow(non_camel_case_types)]; +use back::svh::Svh; use driver::session; use metadata::csearch; use metadata; @@ -4882,7 +4883,7 @@ pub fn trait_method_of_method(tcx: ctxt, /// Creates a hash of the type `t` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. -pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 { +pub fn hash_crate_independent(tcx: ctxt, t: t, svh: &Svh) -> u64 { let mut state = sip::SipState::new(); macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } ); macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } ); @@ -4913,11 +4914,11 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 { }; let did = |state: &mut sip::SipState, did: DefId| { let h = if ast_util::is_local(did) { - local_hash.clone() + svh.clone() } else { tcx.sess.cstore.get_crate_hash(did.krate) }; - h.as_bytes().hash(state); + h.as_str().hash(state); did.node.hash(state); }; let mt = |state: &mut sip::SipState, mt: mt| { diff --git a/src/test/auxiliary/changing-crates-a1.rs b/src/test/auxiliary/changing-crates-a1.rs new file mode 100644 index 000000000000..c0bdbf81772a --- /dev/null +++ b/src/test/auxiliary/changing-crates-a1.rs @@ -0,0 +1,13 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[crate_id = "a"]; + +pub fn foo() {} diff --git a/src/test/auxiliary/changing-crates-a2.rs b/src/test/auxiliary/changing-crates-a2.rs new file mode 100644 index 000000000000..cc123c0f65dd --- /dev/null +++ b/src/test/auxiliary/changing-crates-a2.rs @@ -0,0 +1,14 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[crate_id = "a"]; + +pub fn foo() { println!("hello!"); } + diff --git a/src/test/auxiliary/changing-crates-b.rs b/src/test/auxiliary/changing-crates-b.rs new file mode 100644 index 000000000000..9b80583bb84a --- /dev/null +++ b/src/test/auxiliary/changing-crates-b.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[crate_id = "b"]; + +extern crate a; + +pub fn foo() { a::foo::(); } diff --git a/src/test/compile-fail/bad-crate-id.rs b/src/test/compile-fail/bad-crate-id.rs new file mode 100644 index 000000000000..43956752cd9b --- /dev/null +++ b/src/test/compile-fail/bad-crate-id.rs @@ -0,0 +1,14 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate foo = ""; //~ ERROR: malformed crate id +extern crate bar = "#a"; //~ ERROR: malformed crate id + +fn main() {} diff --git a/src/test/compile-fail/changing-crates.rs b/src/test/compile-fail/changing-crates.rs new file mode 100644 index 000000000000..ae3ef7606671 --- /dev/null +++ b/src/test/compile-fail/changing-crates.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// note that these aux-build directives must be in this order +// aux-build:changing-crates-a1.rs +// aux-build:changing-crates-b.rs +// aux-build:changing-crates-a2.rs + +extern crate a; +extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on +//~^ NOTE: perhaps this crate needs to be recompiled + +fn main() {}