Skip to content

Commit 7408c14

Browse files
committed
Make crate hash stable and externally computable.
This replaces the link meta attributes with a pkgid attribute and uses a hash of this as the crate hash. This makes the crate hash computable by things other than the Rust compiler. It also switches the hash function ot SHA1 since that is much more likely to be available in shell, Python, etc than SipHash. Fixes #10188, #8523.
1 parent 8464004 commit 7408c14

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+197
-170
lines changed

src/etc/combine-tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def scrub(b):
3939
c = open("tmp/run_pass_stage2.rc", "w")
4040
i = 0
4141
c.write("// AUTO-GENERATED FILE: DO NOT EDIT\n")
42+
c.write("#[pkgid=\"run_pass_stage2#0.1\"];\n")
4243
c.write("#[link(name=\"run_pass_stage2\", vers=\"0.1\")];\n")
4344
c.write("#[feature(globs, macro_rules, struct_variant, managed_boxes)];\n")
4445
for t in stage2_tests:

src/libextra/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Rust extras are part of the standard Rust distribution.
2020
2121
*/
2222

23+
#[pkgid="extra#0.9-pre"];
24+
// NOTE: remove after the next snapshot
2325
#[link(name = "extra",
2426
package_id = "extra",
2527
vers = "0.9-pre",

src/librustc/back/link.rs

+80-153
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ use middle::trans::context::CrateContext;
2121
use middle::trans::common::gensym_name;
2222
use middle::ty;
2323
use util::ppaux;
24+
use util::sha1::{Sha1, Digest};
2425

2526
use std::c_str::ToCStr;
2627
use std::char;
27-
use std::hash::Streaming;
28-
use std::hash;
2928
use std::os::consts::{macos, freebsd, linux, android, win32};
3029
use std::ptr;
3130
use std::run;
@@ -37,7 +36,6 @@ use syntax::ast;
3736
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
3837
use syntax::attr;
3938
use syntax::attr::{AttrMetaMethods};
40-
use syntax::print::pprust;
4139

4240
#[deriving(Clone, Eq)]
4341
pub enum output_type {
@@ -49,10 +47,6 @@ pub enum output_type {
4947
output_type_exe,
5048
}
5149

52-
fn write_string<W:Writer>(writer: &mut W, string: &str) {
53-
writer.write(string.as_bytes());
54-
}
55-
5650
pub fn llvm_err(sess: Session, msg: ~str) -> ! {
5751
unsafe {
5852
let cstr = llvm::LLVMRustGetLastError();
@@ -484,209 +478,141 @@ pub mod write {
484478
* - Symbols in different crates but with same names "within" the crate need
485479
* to get different linkage-names.
486480
*
487-
* So here is what we do:
481+
* - The hash shown in the filename needs to be predictable and stable for
482+
* build tooling integration. It also needs to be using a hash function
483+
* which is easy to use from Python, make, etc.
488484
*
489-
* - Separate the meta tags into two sets: exported and local. Only work with
490-
* the exported ones when considering linkage.
485+
* So here is what we do:
491486
*
492-
* - Consider two exported tags as special (and mandatory): name and vers.
493-
* Every crate gets them; if it doesn't name them explicitly we infer them
494-
* as basename(crate) and "0.1", respectively. Call these CNAME, CVERS.
487+
* - Consider the package id; every crate has one (specified with pkgid
488+
* attribute). If a package id isn't provided explicitly, we infer a
489+
* versionless one from the output name. The version will end up being 0.0
490+
* in this case. CNAME and CVERS are taken from this package id. For
491+
* example, github.com/mozilla/CNAME#CVERS.
495492
*
496-
* - Define CMETA as all the non-name, non-vers exported meta tags in the
497-
* crate (in sorted order).
493+
* - Define CMH as SHA1(pkgid).
498494
*
499-
* - Define CMH as hash(CMETA + hashes of dependent crates).
495+
* - Define CMH8 as the first 8 characters of CMH.
500496
*
501-
* - Compile our crate to lib CNAME-CMH-CVERS.so
497+
* - Compile our crate to lib CNAME-CMH8-CVERS.so
502498
*
503-
* - Define STH(sym) as hash(CNAME, CMH, type_str(sym))
499+
* - Define STH(sym) as SHA1(CMH, type_str(sym))
504500
*
505501
* - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
506502
* name, non-name metadata, and type sense, and versioned in the way
507503
* system linkers understand.
508-
*
509504
*/
510505

511506
pub fn build_link_meta(sess: Session,
512507
c: &ast::Crate,
513508
output: &Path,
514-
symbol_hasher: &mut hash::State)
509+
symbol_hasher: &mut Sha1)
515510
-> LinkMeta {
516511
struct ProvidedMetas {
517512
name: Option<@str>,
518-
vers: Option<@str>,
519-
pkg_id: Option<@str>,
520-
cmh_items: ~[@ast::MetaItem]
513+
vers: @str,
514+
pkg_id: @str,
521515
}
522516

523-
fn provided_link_metas(sess: Session, c: &ast::Crate) ->
524-
ProvidedMetas {
525-
let mut name = None;
526-
let mut vers = None;
527-
let mut pkg_id = None;
528-
let mut cmh_items = ~[];
529-
let linkage_metas = attr::find_linkage_metas(c.attrs);
530-
attr::require_unique_names(sess.diagnostic(), linkage_metas);
531-
for meta in linkage_metas.iter() {
532-
match meta.name_str_pair() {
533-
Some((n, value)) if "name" == n => name = Some(value),
534-
Some((n, value)) if "vers" == n => vers = Some(value),
535-
Some((n, value)) if "package_id" == n => pkg_id = Some(value),
536-
_ => cmh_items.push(*meta)
517+
fn provided_link_metas(sess: Session, output: &Path, c: &ast::Crate) -> ProvidedMetas {
518+
let pkg_id = attr::first_attr_value_str_by_name(c.attrs, "pkgid");
519+
let pkg_id = match pkg_id {
520+
None => {
521+
session::expect(sess,
522+
output.filestem_str(),
523+
|| format!("output file name '{}' doesn't appear to have a stem",
524+
output.display())).to_managed()
537525
}
526+
Some(id) => id
527+
};
528+
let path = Path::new(pkg_id);
529+
if !path.is_relative() {
530+
sess.fatal("pkgid must not be absolute");
531+
}
532+
if path.filename().is_none() {
533+
sess.fatal("empty or missing pkgid");
538534
}
535+
let name = crate_meta_name(sess, pkg_id);
536+
let version = crate_meta_version(sess, pkg_id);
539537

540538
ProvidedMetas {
541539
name: name,
542-
vers: vers,
540+
vers: version,
543541
pkg_id: pkg_id,
544-
cmh_items: cmh_items
545542
}
546543
}
547544

548-
// This calculates CMH as defined above
549-
fn crate_meta_extras_hash(symbol_hasher: &mut hash::State,
550-
cmh_items: ~[@ast::MetaItem],
551-
dep_hashes: ~[@str],
552-
pkg_id: Option<@str>) -> @str {
553-
fn len_and_str(s: &str) -> ~str {
554-
format!("{}_{}", s.len(), s)
555-
}
556-
557-
fn len_and_str_lit(l: ast::lit) -> ~str {
558-
len_and_str(pprust::lit_to_str(@l))
559-
}
560-
561-
let cmh_items = attr::sort_meta_items(cmh_items);
562-
563-
fn hash(symbol_hasher: &mut hash::State, m: &@ast::MetaItem) {
564-
match m.node {
565-
ast::MetaNameValue(key, value) => {
566-
write_string(symbol_hasher, len_and_str(key));
567-
write_string(symbol_hasher, len_and_str_lit(value));
568-
}
569-
ast::MetaWord(name) => {
570-
write_string(symbol_hasher, len_and_str(name));
571-
}
572-
ast::MetaList(name, ref mis) => {
573-
write_string(symbol_hasher, len_and_str(name));
574-
for m_ in mis.iter() {
575-
hash(symbol_hasher, m_);
576-
}
577-
}
578-
}
579-
}
580-
581-
symbol_hasher.reset();
582-
for m in cmh_items.iter() {
583-
hash(symbol_hasher, m);
584-
}
585-
586-
for dh in dep_hashes.iter() {
587-
write_string(symbol_hasher, len_and_str(*dh));
588-
}
589-
590-
for p in pkg_id.iter() {
591-
write_string(symbol_hasher, len_and_str(*p));
545+
fn crate_meta_name(sess: Session, pkg_id: @str) -> Option<@str> {
546+
let hash_idx = match pkg_id.find('#') {
547+
None => pkg_id.len(),
548+
Some(idx) => idx,
549+
};
550+
let prefix = pkg_id.slice_to(hash_idx);
551+
let last_slash_idx = match prefix.rfind('/') {
552+
None => -1,
553+
Some(idx) => idx,
554+
};
555+
let name = prefix.slice_from(last_slash_idx + 1);
556+
if name.len() <= 0 {
557+
sess.fatal("pkgid is missing name");
592558
}
593-
594-
return truncated_hash_result(symbol_hasher).to_managed();
595-
}
596-
597-
fn warn_missing(sess: Session, name: &str, default: &str) {
598-
if !*sess.building_library { return; }
599-
sess.warn(format!("missing crate link meta `{}`, using `{}` as default",
600-
name, default));
559+
Some(name.to_managed())
601560
}
602561

603-
fn crate_meta_name(sess: Session, output: &Path, opt_name: Option<@str>)
604-
-> @str {
605-
match opt_name {
606-
Some(v) if !v.is_empty() => v,
607-
_ => {
608-
// to_managed could go away if there was a version of
609-
// filestem that returned an @str
610-
// FIXME (#9639): Non-utf8 filenames will give a misleading error
611-
let name = session::expect(sess,
612-
output.filestem_str(),
613-
|| format!("output file name `{}` doesn't\
614-
appear to have a stem",
615-
output.display())).to_managed();
616-
if name.is_empty() {
617-
sess.fatal("missing crate link meta `name`, and the \
618-
inferred name is blank");
562+
fn crate_meta_version(_sess: Session, pkg_id: @str) -> @str {
563+
match pkg_id.find('#') {
564+
None => @"0.0",
565+
Some(idx) => {
566+
if idx >= pkg_id.len() {
567+
@"0.0"
568+
} else {
569+
pkg_id.slice_from(idx + 1).to_managed()
619570
}
620-
warn_missing(sess, "name", name);
621-
name
622-
}
623-
}
624-
}
625-
626-
fn crate_meta_vers(sess: Session, opt_vers: Option<@str>) -> @str {
627-
match opt_vers {
628-
Some(v) if !v.is_empty() => v,
629-
_ => {
630-
let vers = @"0.0";
631-
warn_missing(sess, "vers", vers);
632-
vers
633571
}
634572
}
635573
}
636574

637-
fn crate_meta_pkgid(sess: Session, name: @str, opt_pkg_id: Option<@str>)
638-
-> @str {
639-
match opt_pkg_id {
640-
Some(v) if !v.is_empty() => v,
641-
_ => {
642-
let pkg_id = name.clone();
643-
warn_missing(sess, "package_id", pkg_id);
644-
pkg_id
645-
}
646-
}
575+
// This calculates CMH as defined above
576+
fn crate_meta_hash(symbol_hasher: &mut Sha1, pkg_id: @str) -> @str {
577+
symbol_hasher.reset();
578+
symbol_hasher.input_str(pkg_id);
579+
truncated_hash_result(symbol_hasher).to_managed()
647580
}
648581

649582
let ProvidedMetas {
650-
name: opt_name,
651-
vers: opt_vers,
652-
pkg_id: opt_pkg_id,
653-
cmh_items: cmh_items
654-
} = provided_link_metas(sess, c);
655-
let name = crate_meta_name(sess, output, opt_name);
656-
let vers = crate_meta_vers(sess, opt_vers);
657-
let pkg_id = crate_meta_pkgid(sess, name, opt_pkg_id);
658-
let dep_hashes = cstore::get_dep_hashes(sess.cstore);
659-
let extras_hash =
660-
crate_meta_extras_hash(symbol_hasher, cmh_items,
661-
dep_hashes, Some(pkg_id));
583+
name: name,
584+
vers: vers,
585+
pkg_id: pkg_id
586+
} = provided_link_metas(sess, output, c);
587+
let hash = crate_meta_hash(symbol_hasher, pkg_id);
662588

663589
LinkMeta {
664-
name: name,
590+
name: name.unwrap(),
665591
vers: vers,
666-
package_id: Some(pkg_id),
667-
extras_hash: extras_hash
592+
package_id: pkg_id,
593+
extras_hash: hash
668594
}
669595
}
670596

671-
pub fn truncated_hash_result(symbol_hasher: &mut hash::State) -> ~str {
597+
pub fn truncated_hash_result(symbol_hasher: &mut Sha1) -> ~str {
672598
symbol_hasher.result_str()
673599
}
674600

675601

676602
// This calculates STH for a symbol, as defined above
677603
pub fn symbol_hash(tcx: ty::ctxt,
678-
symbol_hasher: &mut hash::State,
604+
symbol_hasher: &mut Sha1,
679605
t: ty::t,
680606
link_meta: LinkMeta) -> @str {
681607
// NB: do *not* use abbrevs here as we want the symbol names
682608
// to be independent of one another in the crate.
683609

684610
symbol_hasher.reset();
685-
write_string(symbol_hasher, link_meta.name);
686-
write_string(symbol_hasher, "-");
687-
write_string(symbol_hasher, link_meta.extras_hash);
688-
write_string(symbol_hasher, "-");
689-
write_string(symbol_hasher, encoder::encoded_ty(tcx, t));
611+
symbol_hasher.input_str(link_meta.name);
612+
symbol_hasher.input_str("-");
613+
symbol_hasher.input_str(link_meta.extras_hash);
614+
symbol_hasher.input_str("-");
615+
symbol_hasher.input_str(encoder::encoded_ty(tcx, t));
690616
let mut hash = truncated_hash_result(symbol_hasher);
691617
// Prefix with 'h' so that it never blends into adjacent digits
692618
hash.unshift_char('h');
@@ -885,7 +811,8 @@ pub fn output_dll_filename(os: abi::Os, lm: LinkMeta) -> ~str {
885811
abi::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
886812
abi::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
887813
};
888-
format!("{}{}-{}-{}{}", dll_prefix, lm.name, lm.extras_hash, lm.vers, dll_suffix)
814+
format!("{}{}-{}-{}{}", dll_prefix, lm.name, lm.extras_hash.slice_chars(0, 8),
815+
lm.vers, dll_suffix)
889816
}
890817

891818
pub fn get_cc_prog(sess: Session) -> ~str {

src/librustc/lib.rs

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

11+
#[pkgid="rustc#0.9-pre"];
12+
// NOTE: remove after the next snapshot
1113
#[link(name = "rustc",
1214
package_id = "rustc",
1315
vers = "0.9-pre",
@@ -107,6 +109,7 @@ pub mod driver;
107109
pub mod util {
108110
pub mod common;
109111
pub mod ppaux;
112+
pub mod sha1;
110113
}
111114

112115
pub mod lib {

src/librustc/metadata/common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,6 @@ pub struct LinkMeta {
202202
name: @str,
203203
vers: @str,
204204
// Optional package ID
205-
package_id: Option<@str>, // non-None if this was a URL-like package ID
205+
package_id: @str, // non-None if this was a URL-like package ID
206206
extras_hash: @str
207207
}

src/librustc/metadata/encoder.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -1518,15 +1518,8 @@ fn synthesize_crate_attrs(ecx: &EncodeContext,
15181518
attr::mk_name_value_item_str(@"vers",
15191519
ecx.link_meta.vers);
15201520

1521-
let pkgid_item = match ecx.link_meta.package_id {
1522-
Some(pkg_id) => attr::mk_name_value_item_str(@"package_id",
1523-
pkg_id),
1524-
// uses package_id equal to name;
1525-
// this should never happen here but package_id is an Option
1526-
// FIXME (#10370): change package_id in LinkMeta to @str instead of Option<@str>
1527-
_ => attr::mk_name_value_item_str(@"package_id",
1528-
ecx.link_meta.name)
1529-
};
1521+
let pkgid_item = attr::mk_name_value_item_str(@"package_id",
1522+
ecx.link_meta.package_id);
15301523

15311524
let mut meta_items = ~[name_item, vers_item, pkgid_item];
15321525

0 commit comments

Comments
 (0)