Skip to content

Commit 58ea029

Browse files
committed
auto merge of #12533 : alexcrichton/rust/svh, r=brson
These hashes are used to detect changes to upstream crates and generate errors which mention that crates possibly need recompilation. More details can be found in the respective commit messages below. This change is also accompanied with a much needed refactoring of some of the crate loading code to focus more on crate ids instead of name/version pairs. Closes #12601
2 parents 9b1be3d + 017c504 commit 58ea029

23 files changed

+522
-367
lines changed

Diff for: mk/clean.mk

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ clean-generic-$(2)-$(1):
5858
-name '*.[odasS]' -o \
5959
-name '*.so' -o \
6060
-name '*.dylib' -o \
61+
-name '*.rlib' -o \
6162
-name 'stamp.*' -o \
6263
-name '*.lib' -o \
6364
-name '*.dll' -o \

Diff for: src/librustc/back/link.rs

+31-31
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
1211
use back::archive::{Archive, METADATA_FILENAME};
1312
use back::rpath;
13+
use back::svh::Svh;
1414
use driver::driver::{CrateTranslation, OutputFilenames};
1515
use driver::session::Session;
1616
use driver::session;
@@ -499,28 +499,31 @@ pub mod write {
499499
* system linkers understand.
500500
*/
501501

502-
pub fn build_link_meta(attrs: &[ast::Attribute],
503-
output: &OutputFilenames,
504-
symbol_hasher: &mut Sha256)
505-
-> LinkMeta {
506-
// This calculates CMH as defined above
507-
fn crate_hash(symbol_hasher: &mut Sha256, crateid: &CrateId) -> ~str {
508-
symbol_hasher.reset();
509-
symbol_hasher.input_str(crateid.to_str());
510-
truncated_hash_result(symbol_hasher)
511-
}
512-
513-
let crateid = match attr::find_crateid(attrs) {
502+
pub fn find_crate_id(attrs: &[ast::Attribute],
503+
output: &OutputFilenames) -> CrateId {
504+
match attr::find_crateid(attrs) {
514505
None => from_str(output.out_filestem).unwrap(),
515506
Some(s) => s,
516-
};
507+
}
508+
}
517509

518-
let hash = crate_hash(symbol_hasher, &crateid);
510+
pub fn crate_id_hash(crate_id: &CrateId) -> ~str {
511+
// This calculates CMH as defined above. Note that we don't use the path of
512+
// the crate id in the hash because lookups are only done by (name/vers),
513+
// not by path.
514+
let mut s = Sha256::new();
515+
s.input_str(crate_id.short_name_with_version());
516+
truncated_hash_result(&mut s).slice_to(8).to_owned()
517+
}
519518

520-
LinkMeta {
521-
crateid: crateid,
522-
crate_hash: hash,
523-
}
519+
pub fn build_link_meta(krate: &ast::Crate,
520+
output: &OutputFilenames) -> LinkMeta {
521+
let r = LinkMeta {
522+
crateid: find_crate_id(krate.attrs, output),
523+
crate_hash: Svh::calculate(krate),
524+
};
525+
info!("{}", r);
526+
return r;
524527
}
525528

526529
fn truncated_hash_result(symbol_hasher: &mut Sha256) -> ~str {
@@ -539,7 +542,7 @@ fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &mut Sha256,
539542
symbol_hasher.reset();
540543
symbol_hasher.input_str(link_meta.crateid.name);
541544
symbol_hasher.input_str("-");
542-
symbol_hasher.input_str(link_meta.crate_hash);
545+
symbol_hasher.input_str(link_meta.crate_hash.as_str());
543546
symbol_hasher.input_str("-");
544547
symbol_hasher.input_str(encoder::encoded_ty(tcx, t));
545548
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
712715
mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None)
713716
}
714717

715-
pub fn output_lib_filename(lm: &LinkMeta) -> ~str {
716-
format!("{}-{}-{}",
717-
lm.crateid.name,
718-
lm.crate_hash.slice_chars(0, 8),
719-
lm.crateid.version_or_default())
718+
pub fn output_lib_filename(id: &CrateId) -> ~str {
719+
format!("{}-{}-{}", id.name, crate_id_hash(id), id.version_or_default())
720720
}
721721

722722
pub fn get_cc_prog(sess: Session) -> ~str {
@@ -779,11 +779,11 @@ fn remove(sess: Session, path: &Path) {
779779
pub fn link_binary(sess: Session,
780780
trans: &CrateTranslation,
781781
outputs: &OutputFilenames,
782-
lm: &LinkMeta) -> ~[Path] {
782+
id: &CrateId) -> ~[Path] {
783783
let mut out_filenames = ~[];
784784
let crate_types = sess.crate_types.borrow();
785785
for &crate_type in crate_types.get().iter() {
786-
let out_file = link_binary_output(sess, trans, crate_type, outputs, lm);
786+
let out_file = link_binary_output(sess, trans, crate_type, outputs, id);
787787
out_filenames.push(out_file);
788788
}
789789

@@ -807,8 +807,8 @@ fn is_writeable(p: &Path) -> bool {
807807
}
808808

809809
pub fn filename_for_input(sess: &Session, crate_type: session::CrateType,
810-
lm: &LinkMeta, out_filename: &Path) -> Path {
811-
let libname = output_lib_filename(lm);
810+
id: &CrateId, out_filename: &Path) -> Path {
811+
let libname = output_lib_filename(id);
812812
match crate_type {
813813
session::CrateTypeRlib => {
814814
out_filename.with_filename(format!("lib{}.rlib", libname))
@@ -834,13 +834,13 @@ fn link_binary_output(sess: Session,
834834
trans: &CrateTranslation,
835835
crate_type: session::CrateType,
836836
outputs: &OutputFilenames,
837-
lm: &LinkMeta) -> Path {
837+
id: &CrateId) -> Path {
838838
let obj_filename = outputs.temp_path(OutputTypeObject);
839839
let out_filename = match outputs.single_output_file {
840840
Some(ref file) => file.clone(),
841841
None => {
842842
let out_filename = outputs.path(OutputTypeExe);
843-
filename_for_input(&sess, crate_type, lm, &out_filename)
843+
filename_for_input(&sess, crate_type, id, &out_filename)
844844
}
845845
};
846846

Diff for: src/librustc/back/svh.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2012-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+
//! Calculation and management of a Strict Version Hash for crates
12+
//!
13+
//! # Today's ABI problem
14+
//!
15+
//! In today's implementation of rustc, it is incredibly difficult to achieve
16+
//! forward binary compatibility without resorting to C-like interfaces. Within
17+
//! rust code itself, abi details such as symbol names suffer from a variety of
18+
//! unrelated factors to code changing such as the "def id drift" problem. This
19+
//! ends up yielding confusing error messages about metadata mismatches and
20+
//! such.
21+
//!
22+
//! The core of this problem is when when an upstream dependency changes and
23+
//! downstream dependants are not recompiled. This causes compile errors because
24+
//! the upstream crate's metadata has changed but the downstream crates are
25+
//! still referencing the older crate's metadata.
26+
//!
27+
//! This problem exists for many reasons, the primary of which is that rust does
28+
//! not currently support forwards ABI compatibility (in place upgrades of a
29+
//! crate).
30+
//!
31+
//! # SVH and how it alleviates the problem
32+
//!
33+
//! With all of this knowledge on hand, this module contains the implementation
34+
//! of a notion of a "Strict Version Hash" for a crate. This is essentially a
35+
//! hash of all contents of a crate which can somehow be exposed to downstream
36+
//! crates.
37+
//!
38+
//! This hash is currently calculated by just hashing the AST, but this is
39+
//! obviously wrong (doc changes should not result in an incompatible ABI).
40+
//! Implementation-wise, this is required at this moment in time.
41+
//!
42+
//! By encoding this strict version hash into all crate's metadata, stale crates
43+
//! can be detected immediately and error'd about by rustc itself.
44+
//!
45+
//! # Relevant links
46+
//!
47+
//! Original issue: https://github.com/mozilla/rust/issues/10207
48+
49+
use std::fmt;
50+
use std::hash::Hash;
51+
use std::hash::sip::SipState;
52+
use std::iter::range_step;
53+
use syntax::ast;
54+
55+
#[deriving(Clone, Eq)]
56+
pub struct Svh {
57+
priv hash: ~str,
58+
}
59+
60+
impl Svh {
61+
pub fn new(hash: &str) -> Svh {
62+
assert!(hash.len() == 16);
63+
Svh { hash: hash.to_owned() }
64+
}
65+
66+
pub fn as_str<'a>(&'a self) -> &'a str {
67+
self.hash.as_slice()
68+
}
69+
70+
pub fn calculate(krate: &ast::Crate) -> Svh {
71+
// FIXME: see above for why this is wrong, it shouldn't just hash the
72+
// crate. Fixing this would require more in-depth analysis in
73+
// this function about what portions of the crate are reachable
74+
// in tandem with bug fixes throughout the rest of the compiler.
75+
//
76+
// Note that for now we actually exclude some top-level things
77+
// from the crate like the CrateConfig/span. The CrateConfig
78+
// contains command-line `--cfg` flags, so this means that the
79+
// stage1/stage2 AST for libstd and such is different hash-wise
80+
// when it's actually the exact same representation-wise.
81+
//
82+
// As a first stab at only hashing the relevant parts of the
83+
// AST, this only hashes the module/attrs, not the CrateConfig
84+
// field.
85+
//
86+
// FIXME: this should use SHA1, not SipHash. SipHash is not built to
87+
// avoid collisions.
88+
let mut state = SipState::new();
89+
krate.module.hash(&mut state);
90+
krate.attrs.hash(&mut state);
91+
92+
let hash = state.result();
93+
return Svh {
94+
hash: range_step(0, 64, 4).map(|i| hex(hash >> i)).collect()
95+
};
96+
97+
fn hex(b: u64) -> char {
98+
let b = (b & 0xf) as u8;
99+
let b = match b {
100+
0 .. 9 => '0' as u8 + b,
101+
_ => 'a' as u8 + b - 10,
102+
};
103+
b as char
104+
}
105+
}
106+
}
107+
108+
impl fmt::Show for Svh {
109+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110+
f.pad(self.as_str())
111+
}
112+
}

Diff for: src/librustc/driver/driver.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ pub fn phase_6_link_output(sess: Session,
433433
link::link_binary(sess,
434434
trans,
435435
outputs,
436-
&trans.link));
436+
&trans.link.crateid));
437437
}
438438

439439
pub fn stop_after_phase_3(sess: Session) -> bool {
@@ -472,8 +472,7 @@ fn write_out_deps(sess: Session,
472472
input: &Input,
473473
outputs: &OutputFilenames,
474474
krate: &ast::Crate) -> io::IoResult<()> {
475-
let lm = link::build_link_meta(krate.attrs, outputs,
476-
&mut ::util::sha2::Sha256::new());
475+
let id = link::find_crate_id(krate.attrs, outputs);
477476

478477
let mut out_filenames = ~[];
479478
for output_type in sess.opts.output_types.iter() {
@@ -482,7 +481,7 @@ fn write_out_deps(sess: Session,
482481
link::OutputTypeExe => {
483482
let crate_types = sess.crate_types.borrow();
484483
for output in crate_types.get().iter() {
485-
let p = link::filename_for_input(&sess, *output, &lm, &file);
484+
let p = link::filename_for_input(&sess, *output, &id, &file);
486485
out_filenames.push(p);
487486
}
488487
}

Diff for: src/librustc/lib.rs

+14-24
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ use std::str;
5555
use std::task;
5656
use std::vec;
5757
use syntax::ast;
58-
use syntax::attr;
5958
use syntax::diagnostic::Emitter;
6059
use syntax::diagnostic;
6160
use syntax::parse;
@@ -104,16 +103,17 @@ pub mod front {
104103
}
105104

106105
pub mod back {
107-
pub mod archive;
108-
pub mod link;
109106
pub mod abi;
107+
pub mod archive;
110108
pub mod arm;
109+
pub mod link;
110+
pub mod lto;
111111
pub mod mips;
112-
pub mod x86;
113-
pub mod x86_64;
114112
pub mod rpath;
113+
pub mod svh;
115114
pub mod target_strs;
116-
pub mod lto;
115+
pub mod x86;
116+
pub mod x86_64;
117117
}
118118

119119
pub mod metadata;
@@ -312,28 +312,18 @@ pub fn run_compiler(args: &[~str]) {
312312
let attrs = parse_crate_attrs(sess, &input);
313313
let t_outputs = d::build_output_filenames(&input, &odir, &ofile,
314314
attrs, sess);
315-
if crate_id || crate_name {
316-
let crateid = match attr::find_crateid(attrs) {
317-
Some(crateid) => crateid,
318-
None => {
319-
sess.fatal("No crate_id and --crate-id or \
320-
--crate-name requested")
321-
}
322-
};
323-
if crate_id {
324-
println!("{}", crateid.to_str());
325-
}
326-
if crate_name {
327-
println!("{}", crateid.name);
328-
}
329-
}
315+
let id = link::find_crate_id(attrs, &t_outputs);
330316

317+
if crate_id {
318+
println!("{}", id.to_str());
319+
}
320+
if crate_name {
321+
println!("{}", id.name);
322+
}
331323
if crate_file_name {
332-
let lm = link::build_link_meta(attrs, &t_outputs,
333-
&mut ::util::sha2::Sha256::new());
334324
let crate_types = session::collect_crate_types(&sess, attrs);
335325
for &style in crate_types.iter() {
336-
let fname = link::filename_for_input(&sess, style, &lm,
326+
let fname = link::filename_for_input(&sess, style, &id,
337327
&t_outputs.with_extension(""));
338328
println!("{}", fname.filename_display());
339329
}

Diff for: src/librustc/metadata/common.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use std::cast;
1414
use syntax::crateid::CrateId;
15+
use back::svh::Svh;
1516

1617
// EBML enum definitions and utils shared by the encoder and decoder
1718

@@ -70,12 +71,12 @@ pub static tag_crate_deps: uint = 0x18;
7071
pub static tag_crate_dep: uint = 0x19;
7172

7273
pub static tag_crate_hash: uint = 0x1a;
74+
pub static tag_crate_crateid: uint = 0x1b;
7375

74-
pub static tag_parent_item: uint = 0x1b;
76+
pub static tag_parent_item: uint = 0x1c;
7577

76-
pub static tag_crate_dep_name: uint = 0x1c;
77-
pub static tag_crate_dep_hash: uint = 0x1d;
78-
pub static tag_crate_dep_vers: uint = 0x1e;
78+
pub static tag_crate_dep_crateid: uint = 0x1d;
79+
pub static tag_crate_dep_hash: uint = 0x1e;
7980

8081
pub static tag_mod_impl: uint = 0x1f;
8182

@@ -207,8 +208,8 @@ pub static tag_macro_registrar_fn: uint = 0x63;
207208
pub static tag_exported_macros: uint = 0x64;
208209
pub static tag_macro_def: uint = 0x65;
209210

210-
#[deriving(Clone)]
211+
#[deriving(Clone, Show)]
211212
pub struct LinkMeta {
212213
crateid: CrateId,
213-
crate_hash: ~str,
214+
crate_hash: Svh,
214215
}

0 commit comments

Comments
 (0)