From b349036e5f4f5f42e34ae9dd7859f3dc7a79de94 Mon Sep 17 00:00:00 2001
From: Jack Moffitt <jack@metajack.im>
Date: Mon, 9 Dec 2013 14:56:53 -0700
Subject: [PATCH 1/2] 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.
---
 src/driver/driver.rs                          |   8 +-
 src/etc/combine-tests.py                      |   1 +
 src/libextra/lib.rs                           |   2 +
 src/librustc/back/link.rs                     | 237 ++-----
 src/librustc/driver/driver.rs                 |  19 +-
 src/librustc/lib.rs                           |   3 +
 src/librustc/metadata/common.rs               |  10 +-
 src/librustc/metadata/creader.rs              | 111 ++-
 src/librustc/metadata/decoder.rs              |   8 +-
 src/librustc/metadata/encoder.rs              |  67 +-
 src/librustc/metadata/loader.rs               |  83 +--
 src/librustc/middle/lint.rs                   |   2 +-
 src/librustc/middle/trans/base.rs             |  17 +-
 src/librustc/middle/trans/context.rs          |  10 +-
 src/librustc/middle/trans/debuginfo.rs        |   2 +-
 src/librustc/middle/trans/expr.rs             |   2 +-
 src/librustc/middle/trans/intrinsic.rs        |   2 +-
 src/librustc/util/sha2.rs                     | 670 ++++++++++++++++++
 src/librustdoc/lib.rs                         |   2 +
 src/librustpkg/lib.rs                         |   9 +-
 src/librustpkg/tests.rs                       |  92 +--
 .../testsuite/pass/src/c-dependencies/pkg.rs  |   2 +-
 src/librustpkg/util.rs                        |  49 +-
 src/librustpkg/version.rs                     |   4 +-
 src/librustpkg/workcache_support.rs           |   6 +-
 src/librustuv/lib.rs                          |   2 +
 src/libstd/lib.rs                             |   8 +-
 src/libstd/rt/sched.rs                        |   2 -
 src/libsyntax/attr.rs                         |   8 +
 src/libsyntax/lib.rs                          |   3 +
 src/libsyntax/pkgid.rs                        | 160 +++++
 .../anon-extern-mod-cross-crate-1.rs          |   6 +-
 src/test/auxiliary/cci_impl_lib.rs            |   2 +
 src/test/auxiliary/cci_iter_lib.rs            |   2 +
 src/test/auxiliary/cci_no_inline_lib.rs       |   2 +
 .../crate-method-reexport-grrrrrrr2.rs        |   2 +
 src/test/auxiliary/crateresolve1-1.rs         |   2 +
 src/test/auxiliary/crateresolve1-2.rs         |   2 +
 src/test/auxiliary/crateresolve1-3.rs         |   2 +
 src/test/auxiliary/crateresolve2-1.rs         |   2 +
 src/test/auxiliary/crateresolve2-2.rs         |   2 +
 src/test/auxiliary/crateresolve2-3.rs         |   2 +
 src/test/auxiliary/crateresolve3-1.rs         |   2 +
 src/test/auxiliary/crateresolve3-2.rs         |   2 +
 src/test/auxiliary/crateresolve4a-1.rs        |   2 +
 src/test/auxiliary/crateresolve4a-2.rs        |   2 +
 src/test/auxiliary/crateresolve4b-1.rs        |   4 +-
 src/test/auxiliary/crateresolve4b-2.rs        |   4 +-
 src/test/auxiliary/crateresolve5-1.rs         |   2 +
 src/test/auxiliary/crateresolve5-2.rs         |   2 +
 src/test/auxiliary/crateresolve8-1.rs         |   2 +
 src/test/auxiliary/crateresolve_calories-1.rs |   2 +
 src/test/auxiliary/crateresolve_calories-2.rs |   2 +
 .../auxiliary/extern-crosscrate-source.rs     |   2 +
 src/test/auxiliary/foreign_lib.rs             |   2 +
 src/test/auxiliary/inline_dtor.rs             |   2 +
 src/test/auxiliary/iss.rs                     |   2 +
 src/test/auxiliary/issue-2380.rs              |   2 +
 src/test/auxiliary/issue-2414-a.rs            |   2 +
 src/test/auxiliary/issue-2414-b.rs            |   2 +
 src/test/auxiliary/issue-2526.rs              |   2 +
 src/test/auxiliary/issue-2631-a.rs            |   2 +
 src/test/auxiliary/issue-3012-1.rs            |   2 +
 src/test/auxiliary/issue-4208-cc.rs           |   2 +
 src/test/auxiliary/issue_2242_a.rs            |   2 +
 src/test/auxiliary/issue_2242_c.rs            |   2 +
 src/test/auxiliary/issue_3979_traits.rs       |   2 +
 src/test/auxiliary/lint_stability.rs          |   2 +
 .../auxiliary/static-function-pointer-aux.rs  |   2 +
 src/test/auxiliary/static-methods-crate.rs    |   2 +
 src/test/auxiliary/struct_variant_xc_aux.rs   |   2 +
 .../auxiliary/trait_default_method_xc_aux.rs  |   1 +
 .../trait_default_method_xc_aux_2.rs          |   2 +-
 src/test/compile-fail/crateresolve2.rs        |   4 +-
 src/test/compile-fail/crateresolve5.rs        |   4 +-
 src/test/compile-fail/dup-link-name.rs        |  15 -
 src/test/compile-fail/use-meta-dup.rs         |  15 -
 src/test/compile-fail/use-meta-mismatch.rs    |   2 +-
 src/test/compile-fail/use-meta.rc             |   3 +-
 .../bootstrap-from-c-with-uvio/lib.rs         |   4 +-
 src/test/run-pass/crateresolve1.rs            |   2 +-
 src/test/run-pass/crateresolve2.rs            |   6 +-
 src/test/run-pass/crateresolve3.rs            |   4 +-
 src/test/run-pass/crateresolve4.rs            |   4 +-
 src/test/run-pass/crateresolve5.rs            |   4 +-
 src/test/run-pass/crateresolve6.rs            |  23 -
 src/test/run-pass/crateresolve7.rs            |  21 -
 src/test/run-pass/crateresolve8.rs            |   4 +-
 src/test/run-pass/issue-1251.rs               |   2 +
 src/test/run-pass/issue-4545.rs               |   2 +-
 src/test/run-pass/issue-6919.rs               |   6 +-
 src/test/run-pass/issue-8044.rs               |   2 +-
 src/test/run-pass/issue-9906.rs               |   2 +-
 src/test/run-pass/issue-9968.rs               |   2 +-
 src/test/run-pass/item-attributes.rs          |   2 +
 src/test/run-pass/linkage-visibility.rs       |   2 +-
 src/test/run-pass/priv-impl-prim-ty.rs        |   2 +-
 .../run-pass/reexport-should-still-link.rs    |   2 +-
 src/test/run-pass/static-fn-inline-xc.rs      |   2 +-
 src/test/run-pass/static-fn-trait-xc.rs       |   2 +-
 .../run-pass/static-function-pointer-xc.rs    |   2 +-
 .../run-pass/trait-default-method-xc-2.rs     |   4 +-
 src/test/run-pass/trait-default-method-xc.rs  |   2 +-
 .../run-pass/trait-inheritance-auto-xc-2.rs   |   2 +-
 .../run-pass/trait-inheritance-auto-xc.rs     |   2 +-
 .../trait-inheritance-cross-trait-call-xc.rs  |   2 +-
 src/test/run-pass/typeid-intrinsic.rs         |   4 +-
 src/test/run-pass/use-crate-name-alias.rs     |   2 +-
 src/test/run-pass/use.rs                      |   4 +-
 .../run-pass/xcrate-address-insignificant.rs  |   2 +-
 .../run-pass/xcrate-trait-lifetime-param.rs   |   2 +-
 111 files changed, 1260 insertions(+), 603 deletions(-)
 create mode 100644 src/librustc/util/sha2.rs
 create mode 100644 src/libsyntax/pkgid.rs
 delete mode 100644 src/test/compile-fail/dup-link-name.rs
 delete mode 100644 src/test/compile-fail/use-meta-dup.rs
 delete mode 100644 src/test/run-pass/crateresolve6.rs
 delete mode 100644 src/test/run-pass/crateresolve7.rs

diff --git a/src/driver/driver.rs b/src/driver/driver.rs
index bfd623a28da..9402578d552 100644
--- a/src/driver/driver.rs
+++ b/src/driver/driver.rs
@@ -9,15 +9,15 @@
 // except according to those terms.
 
 #[cfg(rustpkg)]
-extern mod this(name = "rustpkg");
+extern mod this = "rustpkg";
 
 #[cfg(rustdoc)]
-extern mod this(name = "rustdoc");
+extern mod this = "rustdoc";
 
 #[cfg(rustc)]
-extern mod this(name = "rustc");
+extern mod this = "rustc";
 
 #[cfg(rustdoc_ng)]
-extern mod this(name = "rustdoc_ng");
+extern mod this = "rustdoc_ng";
 
 fn main() { this::main() }
diff --git a/src/etc/combine-tests.py b/src/etc/combine-tests.py
index 5815e5448ae..afecf0875c0 100755
--- a/src/etc/combine-tests.py
+++ b/src/etc/combine-tests.py
@@ -42,6 +42,7 @@ stage2_tests.sort()
 c = open("tmp/run_pass_stage2.rc", "w")
 i = 0
 c.write("// AUTO-GENERATED FILE: DO NOT EDIT\n")
+c.write("#[pkgid=\"run_pass_stage2#0.1\"];\n")
 c.write("#[link(name=\"run_pass_stage2\", vers=\"0.1\")];\n")
 c.write("#[feature(globs, macro_rules, struct_variant, managed_boxes)];\n")
 c.write("#[allow(attribute_usage)];\n")
diff --git a/src/libextra/lib.rs b/src/libextra/lib.rs
index ca2b76b620a..6532ae3dc5c 100644
--- a/src/libextra/lib.rs
+++ b/src/libextra/lib.rs
@@ -20,6 +20,8 @@ Rust extras are part of the standard Rust distribution.
 
 */
 
+#[pkgid="extra#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "extra",
        package_id = "extra",
        vers = "0.9-pre",
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 451213ab694..786b463c37e 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -24,11 +24,10 @@ use middle::trans::common::gensym_name;
 use middle::ty;
 use util::common::time;
 use util::ppaux;
+use util::sha2::{Digest, Sha256};
 
 use std::c_str::ToCStr;
 use std::char;
-use std::hash::Streaming;
-use std::hash;
 use std::os::consts::{macos, freebsd, linux, android, win32};
 use std::ptr;
 use std::run;
@@ -39,8 +38,8 @@ use syntax::abi;
 use syntax::ast;
 use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
 use syntax::attr;
-use syntax::attr::{AttrMetaMethods};
-use syntax::print::pprust;
+use syntax::attr::AttrMetaMethods;
+use syntax::pkgid::PkgId;
 
 #[deriving(Clone, Eq)]
 pub enum output_type {
@@ -52,10 +51,6 @@ pub enum output_type {
     output_type_exe,
 }
 
-fn write_string<W:Writer>(writer: &mut W, string: &str) {
-    writer.write(string.as_bytes());
-}
-
 pub fn llvm_err(sess: Session, msg: ~str) -> ! {
     unsafe {
         let cstr = llvm::LLVMRustGetLastError();
@@ -413,7 +408,7 @@ pub mod write {
  *  - Symbols with the same name but different types need to get different
  *    linkage-names. We do this by hashing a string-encoding of the type into
  *    a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
- *    we use SHA1) to "prevent collisions". This is not airtight but 16 hex
+ *    we use SHA256) to "prevent collisions". This is not airtight but 16 hex
  *    digits on uniform probability means you're going to need 2**32 same-name
  *    symbols in the same process before you're even hitting birthday-paradox
  *    collision probability.
@@ -421,209 +416,82 @@ pub mod write {
  *  - Symbols in different crates but with same names "within" the crate need
  *    to get different linkage-names.
  *
+ *  - The hash shown in the filename needs to be predictable and stable for
+ *    build tooling integration. It also needs to be using a hash function
+ *    which is easy to use from Python, make, etc.
+ *
  * So here is what we do:
  *
- *  - Separate the meta tags into two sets: exported and local. Only work with
- *    the exported ones when considering linkage.
+ *  - Consider the package id; every crate has one (specified with pkgid
+ *    attribute).  If a package id isn't provided explicitly, we infer a
+ *    versionless one from the output name. The version will end up being 0.0
+ *    in this case. CNAME and CVERS are taken from this package id. For
+ *    example, github.com/mozilla/CNAME#CVERS.
  *
- *  - Consider two exported tags as special (and mandatory): name and vers.
- *    Every crate gets them; if it doesn't name them explicitly we infer them
- *    as basename(crate) and "0.1", respectively. Call these CNAME, CVERS.
+ *  - Define CMH as SHA256(pkgid).
  *
- *  - Define CMETA as all the non-name, non-vers exported meta tags in the
- *    crate (in sorted order).
+ *  - Define CMH8 as the first 8 characters of CMH.
  *
- *  - Define CMH as hash(CMETA + hashes of dependent crates).
+ *  - Compile our crate to lib CNAME-CMH8-CVERS.so
  *
- *  - Compile our crate to lib CNAME-CMH-CVERS.so
- *
- *  - Define STH(sym) as hash(CNAME, CMH, type_str(sym))
+ *  - Define STH(sym) as SHA256(CMH, type_str(sym))
  *
  *  - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
  *    name, non-name metadata, and type sense, and versioned in the way
  *    system linkers understand.
- *
  */
 
 pub fn build_link_meta(sess: Session,
                        c: &ast::Crate,
                        output: &Path,
-                       symbol_hasher: &mut hash::State)
+                       symbol_hasher: &mut Sha256)
                        -> LinkMeta {
-    struct ProvidedMetas {
-        name: Option<@str>,
-        vers: Option<@str>,
-        pkg_id: Option<@str>,
-        cmh_items: ~[@ast::MetaItem]
-    }
-
-    fn provided_link_metas(sess: Session, c: &ast::Crate) ->
-       ProvidedMetas {
-        let mut name = None;
-        let mut vers = None;
-        let mut pkg_id = None;
-        let mut cmh_items = ~[];
-        let linkage_metas = attr::find_linkage_metas(c.attrs);
-        attr::require_unique_names(sess.diagnostic(), linkage_metas);
-        for meta in linkage_metas.iter() {
-            match meta.name_str_pair() {
-                Some((n, value)) if "name" == n => name = Some(value),
-                Some((n, value)) if "vers" == n => vers = Some(value),
-                Some((n, value)) if "package_id" == n => pkg_id = Some(value),
-                _ => cmh_items.push(*meta)
-            }
-        }
-
-        ProvidedMetas {
-            name: name,
-            vers: vers,
-            pkg_id: pkg_id,
-            cmh_items: cmh_items
-        }
-    }
-
     // This calculates CMH as defined above
-    fn crate_meta_extras_hash(symbol_hasher: &mut hash::State,
-                              cmh_items: ~[@ast::MetaItem],
-                              dep_hashes: ~[@str],
-                              pkg_id: Option<@str>) -> @str {
-        fn len_and_str(s: &str) -> ~str {
-            format!("{}_{}", s.len(), s)
-        }
-
-        fn len_and_str_lit(l: ast::lit) -> ~str {
-            len_and_str(pprust::lit_to_str(&l))
-        }
-
-        let cmh_items = attr::sort_meta_items(cmh_items);
-
-        fn hash(symbol_hasher: &mut hash::State, m: &@ast::MetaItem) {
-            match m.node {
-              ast::MetaNameValue(key, value) => {
-                write_string(symbol_hasher, len_and_str(key));
-                write_string(symbol_hasher, len_and_str_lit(value));
-              }
-              ast::MetaWord(name) => {
-                write_string(symbol_hasher, len_and_str(name));
-              }
-              ast::MetaList(name, ref mis) => {
-                write_string(symbol_hasher, len_and_str(name));
-                for m_ in mis.iter() {
-                    hash(symbol_hasher, m_);
-                }
-              }
-            }
-        }
-
+    fn crate_hash(symbol_hasher: &mut Sha256, pkgid: &PkgId) -> @str {
         symbol_hasher.reset();
-        for m in cmh_items.iter() {
-            hash(symbol_hasher, m);
-        }
-
-        for dh in dep_hashes.iter() {
-            write_string(symbol_hasher, len_and_str(*dh));
-        }
-
-        for p in pkg_id.iter() {
-            write_string(symbol_hasher, len_and_str(*p));
-        }
-
-        return truncated_hash_result(symbol_hasher).to_managed();
+        symbol_hasher.input_str(pkgid.to_str());
+        truncated_hash_result(symbol_hasher).to_managed()
     }
 
-    fn warn_missing(sess: Session, name: &str, default: &str) {
-        if !*sess.building_library { return; }
-        sess.warn(format!("missing crate link meta `{}`, using `{}` as default",
-                       name, default));
-    }
-
-    fn crate_meta_name(sess: Session, output: &Path, opt_name: Option<@str>)
-        -> @str {
-        match opt_name {
-            Some(v) if !v.is_empty() => v,
-            _ => {
-                // to_managed could go away if there was a version of
-                // filestem that returned an @str
-                // FIXME (#9639): Non-utf8 filenames will give a misleading error
-                let name = session::expect(sess,
-                                           output.filestem_str(),
-                                           || format!("output file name `{}` doesn't\
-                                                    appear to have a stem",
-                                                   output.display())).to_managed();
-                if name.is_empty() {
-                    sess.fatal("missing crate link meta `name`, and the \
-                                inferred name is blank");
-                }
-                warn_missing(sess, "name", name);
-                name
-            }
+    let pkgid = match attr::find_pkgid(c.attrs) {
+        None => {
+            let stem = session::expect(
+                sess,
+                output.filestem_str(),
+                || format!("output file name '{}' doesn't appear to have a stem",
+                           output.display()));
+            from_str(stem).unwrap()
         }
-    }
+        Some(s) => s,
+    };
 
-    fn crate_meta_vers(sess: Session, opt_vers: Option<@str>) -> @str {
-        match opt_vers {
-            Some(v) if !v.is_empty() => v,
-            _ => {
-                let vers = @"0.0";
-                warn_missing(sess, "vers", vers);
-                vers
-            }
-        }
-    }
-
-    fn crate_meta_pkgid(sess: Session, name: @str, opt_pkg_id: Option<@str>)
-        -> @str {
-        match opt_pkg_id {
-            Some(v) if !v.is_empty() => v,
-            _ => {
-                let pkg_id = name.clone();
-                warn_missing(sess, "package_id", pkg_id);
-                pkg_id
-            }
-        }
-    }
-
-    let ProvidedMetas {
-        name: opt_name,
-        vers: opt_vers,
-        pkg_id: opt_pkg_id,
-        cmh_items: cmh_items
-    } = provided_link_metas(sess, c);
-    let name = crate_meta_name(sess, output, opt_name);
-    let vers = crate_meta_vers(sess, opt_vers);
-    let pkg_id = crate_meta_pkgid(sess, name, opt_pkg_id);
-    let dep_hashes = cstore::get_dep_hashes(sess.cstore);
-    let extras_hash =
-        crate_meta_extras_hash(symbol_hasher, cmh_items,
-                               dep_hashes, Some(pkg_id));
+    let hash = crate_hash(symbol_hasher, &pkgid);
 
     LinkMeta {
-        name: name,
-        vers: vers,
-        package_id: Some(pkg_id),
-        extras_hash: extras_hash
+        pkgid: pkgid,
+        crate_hash: hash,
     }
 }
 
-pub fn truncated_hash_result(symbol_hasher: &mut hash::State) -> ~str {
+pub fn truncated_hash_result(symbol_hasher: &mut Sha256) -> ~str {
     symbol_hasher.result_str()
 }
 
 
 // This calculates STH for a symbol, as defined above
 pub fn symbol_hash(tcx: ty::ctxt,
-                   symbol_hasher: &mut hash::State,
+                   symbol_hasher: &mut Sha256,
                    t: ty::t,
-                   link_meta: LinkMeta) -> @str {
+                   link_meta: &LinkMeta) -> @str {
     // NB: do *not* use abbrevs here as we want the symbol names
     // to be independent of one another in the crate.
 
     symbol_hasher.reset();
-    write_string(symbol_hasher, link_meta.name);
-    write_string(symbol_hasher, "-");
-    write_string(symbol_hasher, link_meta.extras_hash);
-    write_string(symbol_hasher, "-");
-    write_string(symbol_hasher, encoder::encoded_ty(tcx, t));
+    symbol_hasher.input_str(link_meta.pkgid.name);
+    symbol_hasher.input_str("-");
+    symbol_hasher.input_str(link_meta.crate_hash);
+    symbol_hasher.input_str("-");
+    symbol_hasher.input_str(encoder::encoded_ty(tcx, t));
     let mut hash = truncated_hash_result(symbol_hasher);
     // Prefix with 'h' so that it never blends into adjacent digits
     hash.unshift_char('h');
@@ -635,7 +503,7 @@ pub fn get_symbol_hash(ccx: &mut CrateContext, t: ty::t) -> @str {
     match ccx.type_hashcodes.find(&t) {
       Some(&h) => h,
       None => {
-        let hash = symbol_hash(ccx.tcx, &mut ccx.symbol_hasher, t, ccx.link_meta);
+        let hash = symbol_hash(ccx.tcx, &mut ccx.symbol_hasher, t, &ccx.link_meta);
         ccx.type_hashcodes.insert(t, hash);
         hash
       }
@@ -774,7 +642,7 @@ pub fn mangle_exported_name(ccx: &mut CrateContext,
     let hash = get_symbol_hash(ccx, t);
     return exported_name(ccx.sess, path,
                          hash,
-                         ccx.link_meta.vers);
+                         ccx.link_meta.pkgid.version_or_default());
 }
 
 pub fn mangle_internal_name_by_type_only(ccx: &mut CrateContext,
@@ -813,8 +681,11 @@ pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str
     mangle(ccx.sess, path, None, None)
 }
 
-pub fn output_lib_filename(lm: LinkMeta) -> ~str {
-    format!("{}-{}-{}", lm.name, lm.extras_hash, lm.vers)
+pub fn output_lib_filename(lm: &LinkMeta) -> ~str {
+    format!("{}-{}-{}",
+            lm.pkgid.name,
+            lm.crate_hash.slice_chars(0, 8),
+            lm.pkgid.version_or_default())
 }
 
 pub fn get_cc_prog(sess: Session) -> ~str {
@@ -848,7 +719,8 @@ pub fn get_cc_prog(sess: Session) -> ~str {
 pub fn link_binary(sess: Session,
                    trans: &CrateTranslation,
                    obj_filename: &Path,
-                   out_filename: &Path) {
+                   out_filename: &Path,
+                   lm: &LinkMeta) {
     // If we're generating a test executable, then ignore all other output
     // styles at all other locations
     let outputs = if sess.opts.test {
@@ -858,7 +730,7 @@ pub fn link_binary(sess: Session,
     };
 
     for output in outputs.move_iter() {
-        link_binary_output(sess, trans, output, obj_filename, out_filename);
+        link_binary_output(sess, trans, output, obj_filename, out_filename, lm);
     }
 
     // Remove the temporary object file and metadata if we aren't saving temps
@@ -881,8 +753,9 @@ fn link_binary_output(sess: Session,
                       trans: &CrateTranslation,
                       output: session::OutputStyle,
                       obj_filename: &Path,
-                      out_filename: &Path) {
-    let libname = output_lib_filename(trans.link);
+                      out_filename: &Path,
+                      lm: &LinkMeta) {
+    let libname = output_lib_filename(lm);
     let out_filename = match output {
         session::OutputRlib => {
             out_filename.with_filename(format!("lib{}.rlib", libname))
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index c0ee53da970..ec042499407 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -389,7 +389,8 @@ pub fn phase_6_link_output(sess: Session,
          link::link_binary(sess,
                            trans,
                            &outputs.obj_filename,
-                           &outputs.out_filename));
+                           &outputs.out_filename,
+                           &trans.link));
 }
 
 pub fn stop_after_phase_3(sess: Session) -> bool {
@@ -977,17 +978,13 @@ pub fn build_output_filenames(input: &input,
               str_input(_) => @"rust_out"
           };
 
-          // If a linkage name meta is present, we use it as the link name
-          let linkage_metas = attr::find_linkage_metas(attrs);
-          if !linkage_metas.is_empty() {
-              // But if a linkage meta is present, that overrides
-              let maybe_name = linkage_metas.iter().find(|m| "name" == m.name());
-              match maybe_name.and_then(|m| m.value_str()) {
-                  Some(s) => stem = s,
-                  _ => ()
+          // If a pkgid is present, we use it as the link name
+          let pkgid = attr::find_pkgid(attrs);
+          match pkgid {
+              None => {}
+              Some(pkgid) => {
+                  stem = pkgid.name.to_managed()
               }
-              // If the name is missing, we just default to the filename
-              // version
           }
 
           if *sess.building_library {
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 3de33e13ecc..090329bc4a0 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="rustc#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "rustc",
        package_id = "rustc",
        vers = "0.9-pre",
@@ -109,6 +111,7 @@ pub mod driver;
 pub mod util {
     pub mod common;
     pub mod ppaux;
+    pub mod sha2;
 }
 
 pub mod lib {
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index f6eadfcc916..bd6794b1d9f 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -7,7 +7,9 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+
 use std::cast;
+use syntax::pkgid::PkgId;
 
 // EBML enum definitions and utils shared by the encoder and decoder
 
@@ -202,10 +204,8 @@ pub static tag_native_libraries_lib: uint = 0x104;
 pub static tag_native_libraries_name: uint = 0x105;
 pub static tag_native_libraries_kind: uint = 0x106;
 
+#[deriving(Clone)]
 pub struct LinkMeta {
-    name: @str,
-    vers: @str,
-    // Optional package ID
-    package_id: Option<@str>, // non-None if this was a URL-like package ID
-    extras_hash: @str
+    pkgid: PkgId,
+    crate_hash: @str,
 }
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index 67b684a2c51..955ba54a404 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -17,7 +17,6 @@ use metadata::loader;
 
 use std::hashmap::HashMap;
 use syntax::ast;
-use std::vec;
 use syntax::abi;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
@@ -25,6 +24,7 @@ use syntax::codemap::{Span, dummy_sp};
 use syntax::diagnostic::span_handler;
 use syntax::parse::token;
 use syntax::parse::token::ident_interner;
+use syntax::pkgid::PkgId;
 use syntax::visit;
 
 // Traverses an AST, reading all the information about use'd crates and extern
@@ -64,7 +64,7 @@ struct cache_entry {
     cnum: ast::CrateNum,
     span: Span,
     hash: @str,
-    metas: @~[@ast::MetaItem]
+    pkgid: PkgId,
 }
 
 fn dump_crates(crate_cache: &[cache_entry]) {
@@ -80,12 +80,10 @@ fn warn_if_multiple_versions(e: @mut Env,
                              diag: @mut span_handler,
                              crate_cache: &[cache_entry]) {
     if crate_cache.len() != 0u {
-        let name = loader::crate_name_from_metas(
-            *crate_cache[crate_cache.len() - 1].metas
-        );
+        let name = crate_cache[crate_cache.len() - 1].pkgid.name.clone();
 
         let (matches, non_matches) = crate_cache.partitioned(|entry|
-            name == loader::crate_name_from_metas(*entry.metas));
+            name == entry.pkgid.name);
 
         assert!(!matches.is_empty());
 
@@ -94,11 +92,7 @@ fn warn_if_multiple_versions(e: @mut Env,
                 format!("using multiple versions of crate `{}`", name));
             for match_ in matches.iter() {
                 diag.span_note(match_.span, "used here");
-                let attrs = ~[
-                    attr::mk_attr(attr::mk_list_item(@"link",
-                                                     (*match_.metas).clone()))
-                ];
-                loader::note_linkage_attrs(e.intr, diag, attrs);
+                loader::note_pkgid_attr(diag, &match_.pkgid);
             }
         }
 
@@ -129,28 +123,30 @@ fn visit_crate(e: &Env, c: &ast::Crate) {
 
 fn visit_view_item(e: @mut Env, i: &ast::view_item) {
     match i.node {
-      ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
+      ast::view_item_extern_mod(ident, path_opt, _, id) => {
           let ident = token::ident_to_str(&ident);
-          let meta_items = match path_opt {
-              None => meta_items.clone(),
-              Some((p, _path_str_style)) => {
-                  let p_path = Path::new(p);
-                  match p_path.filestem_str() {
-                      None|Some("") =>
-                          e.sess.span_bug(i.span, "Bad package path in `extern mod` item"),
-                      Some(s) =>
-                          vec::append(
-                              ~[attr::mk_name_value_item_str(@"package_id", p),
-                               attr::mk_name_value_item_str(@"name", s.to_managed())],
-                              *meta_items)
+          debug!("resolving extern mod stmt. ident: {:?} path_opt: {:?}",
+                 ident, path_opt);
+          let (name, version) = match path_opt {
+              Some((path_str, _)) => {
+                  let pkgid: Option<PkgId> = from_str(path_str);
+                  match pkgid {
+                      None => (@"", @""),
+                      Some(pkgid) => {
+                          let version = match pkgid.version {
+                              None => @"",
+                              Some(ref ver) => ver.to_managed(),
+                          };
+                          (pkgid.name.to_managed(), version)
+                      }
                   }
-            }
+              }
+              None => (ident, @""),
           };
-          debug!("resolving extern mod stmt. ident: {:?}, meta: {:?}",
-                 ident, meta_items);
           let cnum = resolve_crate(e,
                                    ident,
-                                   meta_items,
+                                   name,
+                                   version,
                                    @"",
                                    i.span);
           cstore::add_extern_mod_stmt_cnum(e.sess.cstore, id, cnum);
@@ -233,46 +229,36 @@ fn visit_item(e: &Env, i: @ast::item) {
     }
 }
 
-fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
-    -> ~[@ast::MetaItem] {
-    // Check if key isn't there yet.
-    if !attr::contains_name(metas, key) {
-        metas.push(attr::mk_name_value_item_str(key, ident));
-    }
-    metas
-}
-
-fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
-    -> ~[@ast::MetaItem] {
-    metas_with(ident, @"name", metas)
-}
-
-fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
-               -> Option<ast::CrateNum> {
+fn existing_match(e: &Env, name: @str, version: @str, hash: &str) -> Option<ast::CrateNum> {
     for c in e.crate_cache.iter() {
-        if loader::metadata_matches(*c.metas, metas)
-            && (hash.is_empty() || c.hash.as_slice() == hash) {
+        let pkgid_version = match c.pkgid.version {
+            None => @"0.0",
+            Some(ref ver) => ver.to_managed(),
+        };
+        if (name.is_empty() || c.pkgid.name.to_managed() == name) &&
+            (version.is_empty() || pkgid_version == version) &&
+            (hash.is_empty() || c.hash.as_slice() == hash) {
             return Some(c.cnum);
         }
     }
-    return None;
+    None
 }
 
 fn resolve_crate(e: @mut Env,
                  ident: @str,
-                 metas: ~[@ast::MetaItem],
+                 name: @str,
+                 version: @str,
                  hash: @str,
                  span: Span)
               -> ast::CrateNum {
-    let metas = metas_with_ident(ident, metas);
-
-    match existing_match(e, metas, hash) {
+    match existing_match(e, name, version, hash) {
       None => {
         let load_ctxt = loader::Context {
             sess: e.sess,
             span: span,
             ident: ident,
-            metas: metas,
+            name: name,
+            version: version,
             hash: hash,
             os: e.os,
             intr: e.intr
@@ -282,7 +268,7 @@ fn resolve_crate(e: @mut Env,
         } = load_ctxt.load_library_crate();
 
         let attrs = decoder::get_crate_attributes(metadata);
-        let linkage_metas = attr::find_linkage_metas(attrs);
+        let pkgid = attr::find_pkgid(attrs).unwrap();
         let hash = decoder::get_crate_hash(metadata);
 
         // Claim this crate number and cache it
@@ -291,21 +277,15 @@ fn resolve_crate(e: @mut Env,
             cnum: cnum,
             span: span,
             hash: hash,
-            metas: @linkage_metas
+            pkgid: pkgid,
         });
         e.next_crate_num += 1;
 
         // Now resolve the crates referenced by this crate
         let cnum_map = resolve_crate_deps(e, metadata);
 
-        let cname =
-            match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
-                                                         "name") {
-                Some(v) => v,
-                None => ident
-            };
         let cmeta = @cstore::crate_metadata {
-            name: cname,
+            name: name,
             data: metadata,
             cnum_map: cnum_map,
             cnum: cnum
@@ -336,12 +316,9 @@ fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
     for dep in r.iter() {
         let extrn_cnum = dep.cnum;
         let cname_str = token::ident_to_str(&dep.name);
-        let cmetas = metas_with(dep.vers, @"vers", ~[]);
         debug!("resolving dep crate {} ver: {} hash: {}",
                cname_str, dep.vers, dep.hash);
-        match existing_match(e,
-                             metas_with_ident(cname_str, cmetas.clone()),
-                             dep.hash) {
+        match existing_match(e, cname_str, dep.vers, dep.hash) {
           Some(local_cnum) => {
             debug!("already have it");
             // We've already seen this crate
@@ -353,8 +330,8 @@ fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
             // FIXME (#2404): Need better error reporting than just a bogus
             // span.
             let fake_span = dummy_sp();
-            let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash,
-                                           fake_span);
+            let local_cnum = resolve_crate(e, cname_str, cname_str, dep.vers,
+                                           dep.hash, fake_span);
             cnum_map.insert(extrn_cnum, local_cnum);
           }
         }
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index c6a1da655aa..dd4df7481d4 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -1171,11 +1171,9 @@ pub fn get_crate_hash(data: @~[u8]) -> @str {
 
 pub fn get_crate_vers(data: @~[u8]) -> @str {
     let attrs = decoder::get_crate_attributes(data);
-    let linkage_attrs = attr::find_linkage_metas(attrs);
-
-    match attr::last_meta_item_value_str_by_name(linkage_attrs, "vers") {
-        Some(ver) => ver,
-        None => @"0.0"
+    match attr::find_pkgid(attrs) {
+        None => @"0.0",
+        Some(pkgid) => pkgid.version_or_default().to_managed(),
     }
 }
 
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index bf50da3d789..209ed5e2d18 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1482,67 +1482,30 @@ fn encode_attributes(ebml_w: &mut writer::Encoder, attrs: &[Attribute]) {
     ebml_w.end_tag();
 }
 
-// So there's a special crate attribute called 'link' which defines the
-// metadata that Rust cares about for linking crates. This attribute requires
-// 'name', 'vers' and 'package_id' items, so if the user didn't provide them we
-// will throw them in anyway with default values.
+// So there's a special crate attribute called 'pkgid' which defines the
+// metadata that Rust cares about for linking crates. If the user didn't
+// provide it we will throw it in anyway with a default value.
 fn synthesize_crate_attrs(ecx: &EncodeContext,
                           crate: &Crate) -> ~[Attribute] {
 
-    fn synthesize_link_attr(ecx: &EncodeContext, items: ~[@MetaItem]) ->
-       Attribute {
+    fn synthesize_pkgid_attr(ecx: &EncodeContext) -> Attribute {
+        assert!(!ecx.link_meta.pkgid.name.is_empty());
 
-        assert!(!ecx.link_meta.name.is_empty());
-        assert!(!ecx.link_meta.vers.is_empty());
-
-        let name_item =
-            attr::mk_name_value_item_str(@"name",
-                                         ecx.link_meta.name);
-        let vers_item =
-            attr::mk_name_value_item_str(@"vers",
-                                         ecx.link_meta.vers);
-
-        let pkgid_item = match ecx.link_meta.package_id {
-                Some(pkg_id) =>  attr::mk_name_value_item_str(@"package_id",
-                                                              pkg_id),
-                // uses package_id equal to name;
-                // this should never happen here but package_id is an Option
-                // FIXME (#10370): change package_id in LinkMeta to @str instead of Option<@str>
-                _ => attr::mk_name_value_item_str(@"package_id",
-                                                  ecx.link_meta.name)
-        };
-
-        let mut meta_items = ~[name_item, vers_item, pkgid_item];
-
-        for &mi in items.iter().filter(|mi| "name" != mi.name() && "vers" != mi.name() &&
-                                            "package_id" != mi.name()) {
-            meta_items.push(mi);
-        }
-        let link_item = attr::mk_list_item(@"link", meta_items);
-
-        return attr::mk_attr(link_item);
+        attr::mk_attr(
+            attr::mk_name_value_item_str(
+                @"pkgid",
+                ecx.link_meta.pkgid.to_str().to_managed()))
     }
 
     let mut attrs = ~[];
-    let mut found_link_attr = false;
     for attr in crate.attrs.iter() {
-        attrs.push(
-            if "link" != attr.name()  {
-                *attr
-            } else {
-                match attr.meta_item_list() {
-                  Some(l) => {
-                    found_link_attr = true;;
-                    synthesize_link_attr(ecx, l.to_owned())
-                  }
-                  _ => *attr
-                }
-            });
+        if "pkgid" != attr.name()  {
+            attrs.push(*attr);
+        }
     }
+    attrs.push(synthesize_pkgid_attr(ecx));
 
-    if !found_link_attr { attrs.push(synthesize_link_attr(ecx, ~[])); }
-
-    return attrs;
+    attrs
 }
 
 fn encode_crate_deps(ecx: &EncodeContext,
@@ -1800,7 +1763,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
 
     let mut ebml_w = writer::Encoder(wr);
 
-    encode_hash(&mut ebml_w, ecx.link_meta.extras_hash);
+    encode_hash(&mut ebml_w, ecx.link_meta.crate_hash);
 
     let mut i = wr.tell();
     let crate_attrs = synthesize_crate_attrs(&ecx, crate);
diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs
index 5b1385c7579..dd5d082ddb7 100644
--- a/src/librustc/metadata/loader.rs
+++ b/src/librustc/metadata/loader.rs
@@ -20,8 +20,8 @@ use metadata::filesearch;
 use syntax::codemap::Span;
 use syntax::diagnostic::span_handler;
 use syntax::parse::token::ident_interner;
-use syntax::print::pprust;
-use syntax::{ast, attr};
+use syntax::pkgid::PkgId;
+use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 
 use std::c_str::ToCStr;
@@ -47,7 +47,8 @@ pub struct Context {
     sess: Session,
     span: Span,
     ident: @str,
-    metas: ~[@ast::MetaItem],
+    name: @str,
+    version: @str,
     hash: @str,
     os: Os,
     intr: @ident_interner
@@ -72,9 +73,8 @@ impl Context {
     }
 
     fn find_library_crate(&self) -> Option<Library> {
-        attr::require_unique_names(self.sess.diagnostic(), self.metas);
         let filesearch = self.sess.filesearch;
-        let crate_name = crate_name_from_metas(self.metas);
+        let crate_name = self.name;
         let (dyprefix, dysuffix) = self.dylibname();
 
         // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
@@ -103,9 +103,8 @@ impl Context {
                     } else if candidate {
                         match get_metadata_section(self.sess, self.os, path) {
                             Some(cvec) =>
-                                if crate_matches(cvec, self.metas, self.hash) {
-                                    debug!("found {} with matching metadata",
-                                           path.display());
+                                if crate_matches(cvec, self.name, self.version, self.hash) {
+                                    debug!("found {} with matching pkgid", path.display());
                                     let (rlib, dylib) = if file.ends_with(".rlib") {
                                         (Some(path.clone()), None)
                                     } else {
@@ -118,7 +117,7 @@ impl Context {
                                     });
                                     FileMatches
                                 } else {
-                                    debug!("skipping {}, metadata doesn't match",
+                                    debug!("skipping {}, pkgid doesn't match",
                                            path.display());
                                     FileDoesntMatch
                                 },
@@ -156,7 +155,12 @@ impl Context {
                         None => {}
                     }
                     let attrs = decoder::get_crate_attributes(lib.metadata);
-                    note_linkage_attrs(self.intr, self.sess.diagnostic(), attrs);
+                    match attr::find_pkgid(attrs) {
+                        None => {}
+                        Some(pkgid) => {
+                            note_pkgid_attr(self.sess.diagnostic(), &pkgid);
+                        }
+                    }
                 }
                 self.sess.abort_if_errors();
                 None
@@ -217,56 +221,27 @@ impl Context {
     }
 }
 
-pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str {
-    for m in metas.iter() {
-        match m.name_str_pair() {
-            Some((name, s)) if "name" == name => { return s; }
-            _ => {}
-        }
-    }
-    fail!("expected to find the crate name")
-}
-
-pub fn package_id_from_metas(metas: &[@ast::MetaItem]) -> Option<@str> {
-    for m in metas.iter() {
-        match m.name_str_pair() {
-            Some((name, s)) if "package_id" == name => { return Some(s); }
-            _ => {}
-        }
-    }
-    None
-}
-
-pub fn note_linkage_attrs(intr: @ident_interner,
-                          diag: @mut span_handler,
-                          attrs: ~[ast::Attribute]) {
-    let r = attr::find_linkage_metas(attrs);
-    for mi in r.iter() {
-        diag.handler().note(format!("meta: {}", pprust::meta_item_to_str(*mi,intr)));
-    }
+pub fn note_pkgid_attr(diag: @mut span_handler,
+                       pkgid: &PkgId) {
+    diag.handler().note(format!("pkgid: {}", pkgid.to_str()));
 }
 
 fn crate_matches(crate_data: @~[u8],
-                 metas: &[@ast::MetaItem],
+                 name: @str,
+                 version: @str,
                  hash: @str) -> bool {
     let attrs = decoder::get_crate_attributes(crate_data);
-    let linkage_metas = attr::find_linkage_metas(attrs);
-    if !hash.is_empty() {
-        let chash = decoder::get_crate_hash(crate_data);
-        if chash != hash { return false; }
+    match attr::find_pkgid(attrs) {
+        None => false,
+        Some(pkgid) => {
+            if !hash.is_empty() {
+                let chash = decoder::get_crate_hash(crate_data);
+                if chash != hash { return false; }
+            }
+            name == pkgid.name.to_managed() &&
+                (version.is_empty() || version == pkgid.version_or_default().to_managed())
+        }
     }
-    metadata_matches(linkage_metas, metas)
-}
-
-pub fn metadata_matches(extern_metas: &[@ast::MetaItem],
-                        local_metas: &[@ast::MetaItem]) -> bool {
-
-// extern_metas: metas we read from the crate
-// local_metas: metas we're looking for
-    debug!("matching {} metadata requirements against {} items",
-           local_metas.len(), extern_metas.len());
-
-    local_metas.iter().all(|needed| attr::contains(extern_metas, *needed))
 }
 
 fn get_metadata_section(sess: Session, os: Os, filename: &Path) -> Option<@~[u8]> {
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 2a69b8226ae..c3fbc83f0a7 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -816,7 +816,7 @@ fn check_heap_item(cx: &Context, it: &ast::item) {
 }
 
 static crate_attrs: &'static [&'static str] = &[
-    "crate_type", "feature", "no_uv", "no_main", "no_std",
+    "crate_type", "feature", "no_uv", "no_main", "no_std", "pkgid",
     "desc", "comment", "license", "copyright", // not used in rustc now
 ];
 
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 8a89cd35d0e..b95f4affc84 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -64,11 +64,10 @@ use middle::trans::value::Value;
 use middle::ty;
 use util::common::indenter;
 use util::ppaux::{Repr, ty_to_str};
-
+use util::sha2::Sha256;
 use middle::trans::type_::Type;
 
 use std::c_str::ToCStr;
-use std::hash;
 use std::hashmap::HashMap;
 use std::libc::c_uint;
 use std::vec;
@@ -2939,8 +2938,8 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
     let sym_name = if is_top {
         ~"_rust_crate_map_toplevel"
     } else {
-        symname(sess, "_rust_crate_map_" + mapmeta.name, mapmeta.extras_hash,
-                mapmeta.vers)
+        symname(sess, "_rust_crate_map_" + mapmeta.pkgid.name, mapmeta.crate_hash,
+                mapmeta.pkgid.version_or_default())
     };
 
     let slicetype = Type::struct_([int_type, int_type], false);
@@ -3059,8 +3058,8 @@ pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) -> ~[u8] {
                         flate::deflate_bytes(metadata);
     let llmeta = C_bytes(compressed);
     let llconst = C_struct([llmeta], false);
-    let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.name,
-                       cx.link_meta.vers, cx.link_meta.extras_hash);
+    let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.pkgid.name,
+                       cx.link_meta.pkgid.version_or_default(), cx.link_meta.crate_hash);
     let llglobal = name.with_c_str(|buf| {
         unsafe {
             llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
@@ -3084,7 +3083,7 @@ pub fn trans_crate(sess: session::Session,
         sess.bug("couldn't enable multi-threaded LLVM");
     }
 
-    let mut symbol_hasher = hash::default_state();
+    let mut symbol_hasher = Sha256::new();
     let link_meta = link::build_link_meta(sess, &crate, output,
                                           &mut symbol_hasher);
 
@@ -3096,7 +3095,7 @@ 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.name.to_owned() + ".rc";
+    let llmod_id = link_meta.pkgid.name.clone() + ".rc";
 
     let ccx = @mut CrateContext::new(sess,
                                      llmod_id,
@@ -3171,7 +3170,7 @@ pub fn trans_crate(sess: session::Session,
     }
 
     let llcx = ccx.llcx;
-    let link_meta = ccx.link_meta;
+    let link_meta = ccx.link_meta.clone();
     let llmod = ccx.llmod;
     let mut reachable = ccx.reachable.iter().filter_map(|id| {
         ccx.item_symbols.find(id).map(|s| s.to_owned())
diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs
index c483a0f48f8..3dbe70ecf69 100644
--- a/src/librustc/middle/trans/context.rs
+++ b/src/librustc/middle/trans/context.rs
@@ -26,8 +26,9 @@ use middle::ty;
 
 use middle::trans::type_::Type;
 
+use util::sha2::Sha256;
+
 use std::c_str::ToCStr;
-use std::hash;
 use std::hashmap::{HashMap, HashSet};
 use std::local_data;
 use std::vec;
@@ -98,7 +99,7 @@ pub struct CrateContext {
      lltypes: HashMap<ty::t, Type>,
      llsizingtypes: HashMap<ty::t, Type>,
      adt_reprs: HashMap<ty::t, @adt::Repr>,
-     symbol_hasher: hash::State,
+     symbol_hasher: Sha256,
      type_hashcodes: HashMap<ty::t, @str>,
      type_short_names: HashMap<ty::t, ~str>,
      all_llvm_symbols: HashSet<@str>,
@@ -126,7 +127,7 @@ impl CrateContext {
                tcx: ty::ctxt,
                emap2: resolve::ExportMap2,
                maps: astencode::Maps,
-               symbol_hasher: hash::State,
+               symbol_hasher: Sha256,
                link_meta: LinkMeta,
                reachable: @mut HashSet<ast::NodeId>)
                -> CrateContext {
@@ -168,8 +169,7 @@ impl CrateContext {
             tn.associate_type("tydesc", &tydesc_type);
             tn.associate_type("str_slice", &str_slice_ty);
 
-            let (crate_map_name, crate_map) = decl_crate_map(sess, link_meta,
-                                                             llmod);
+            let (crate_map_name, crate_map) = decl_crate_map(sess, link_meta.clone(), llmod);
             let dbg_cx = if sess.opts.debuginfo {
                 Some(debuginfo::CrateDebugContext::new(llmod, name.to_owned()))
             } else {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 37346715d28..8346228e881 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -2728,7 +2728,7 @@ fn namespace_for_item(cx: &mut CrateContext,
 
         if def_id.crate == ast::LOCAL_CRATE {
             // prepend crate name if not already present
-            let crate_namespace_ident = token::str_to_ident(cx.link_meta.name);
+            let crate_namespace_ident = token::str_to_ident(cx.link_meta.pkgid.name);
             item_path.insert(0, ast_map::path_mod(crate_namespace_ident));
         }
 
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index a66e6f90ac7..d5b70284f11 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -1795,7 +1795,7 @@ pub fn trans_log_level(bcx: @mut Block) -> DatumBlock {
             Some(&src) => {
                 cstore::get_crate_data(ccx.sess.cstore, src.crate).name
             }
-            None => ccx.link_meta.name,
+            None => ccx.link_meta.pkgid.name.to_managed(),
         };
         let mut modpath = ~[path_mod(ccx.sess.ident_of(srccrate))];
         for e in bcx.fcx.path.iter() {
diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs
index 110e964bdbc..f83bdd79959 100644
--- a/src/librustc/middle/trans/intrinsic.rs
+++ b/src/librustc/middle/trans/intrinsic.rs
@@ -286,7 +286,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
         }
         "type_id" => {
             let hash = ty::hash_crate_independent(ccx.tcx, substs.tys[0],
-                                                  ccx.link_meta.extras_hash);
+                                                  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/util/sha2.rs b/src/librustc/util/sha2.rs
new file mode 100644
index 00000000000..1ac5b731921
--- /dev/null
+++ b/src/librustc/util/sha2.rs
@@ -0,0 +1,670 @@
+// Copyright 2012-2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module implements only the Sha256 function since that is all that is needed for internal
+//! use. This implementation is not intended for external use or for any use where security is
+//! important.
+
+use std::iter::range_step;
+use std::num::Zero;
+use std::vec;
+use std::vec::bytes::{MutableByteVector, copy_memory};
+use extra::hex::ToHex;
+
+/// Write a u32 into a vector, which must be 4 bytes long. The value is written in big-endian
+/// format.
+fn write_u32_be(dst: &mut[u8], input: u32) {
+    use std::cast::transmute;
+    use std::unstable::intrinsics::to_be32;
+    assert!(dst.len() == 4);
+    unsafe {
+        let x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
+        *x = to_be32(input as i32);
+    }
+}
+
+/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format.
+fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
+    use std::cast::transmute;
+    use std::unstable::intrinsics::to_be32;
+    assert!(dst.len() * 4 == input.len());
+    unsafe {
+        let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
+        let mut y: *i32 = transmute(input.unsafe_ref(0));
+        dst.len().times(|| {
+            *x = to_be32(*y);
+            x = x.offset(1);
+            y = y.offset(1);
+        });
+    }
+}
+
+trait ToBits {
+    /// Convert the value in bytes to the number of bits, a tuple where the 1st item is the
+    /// high-order value and the 2nd item is the low order value.
+    fn to_bits(self) -> (Self, Self);
+}
+
+impl ToBits for u64 {
+    fn to_bits(self) -> (u64, u64) {
+        return (self >> 61, self << 3);
+    }
+}
+
+/// Adds the specified number of bytes to the bit count. fail!() if this would cause numeric
+/// overflow.
+fn add_bytes_to_bits<T: Int + CheckedAdd + ToBits>(bits: T, bytes: T) -> T {
+    let (new_high_bits, new_low_bits) = bytes.to_bits();
+
+    if new_high_bits > Zero::zero() {
+        fail!("Numeric overflow occured.")
+    }
+
+    match bits.checked_add(&new_low_bits) {
+        Some(x) => return x,
+        None => fail!("Numeric overflow occured.")
+    }
+}
+
+/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
+/// must be processed. The input() method takes care of processing and then clearing the buffer
+/// automatically. However, other methods do not and require the caller to process the buffer. Any
+/// method that modifies the buffer directory or provides the caller with bytes that can be modified
+/// results in those bytes being marked as used by the buffer.
+trait FixedBuffer {
+    /// Input a vector of bytes. If the buffer becomes full, process it with the provided
+    /// function and then clear the buffer.
+    fn input(&mut self, input: &[u8], func: |&[u8]|);
+
+    /// Reset the buffer.
+    fn reset(&mut self);
+
+    /// Zero the buffer up until the specified index. The buffer position currently must not be
+    /// greater than that index.
+    fn zero_until(&mut self, idx: uint);
+
+    /// Get a slice of the buffer of the specified size. There must be at least that many bytes
+    /// remaining in the buffer.
+    fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8];
+
+    /// Get the current buffer. The buffer must already be full. This clears the buffer as well.
+    fn full_buffer<'s>(&'s mut self) -> &'s [u8];
+
+    /// Get the current position of the buffer.
+    fn position(&self) -> uint;
+
+    /// Get the number of bytes remaining in the buffer until it is full.
+    fn remaining(&self) -> uint;
+
+    /// Get the size of the buffer
+    fn size(&self) -> uint;
+}
+
+/// A FixedBuffer of 64 bytes useful for implementing Sha256 which has a 64 byte blocksize.
+struct FixedBuffer64 {
+    priv buffer: [u8, ..64],
+    priv buffer_idx: uint,
+}
+
+impl FixedBuffer64 {
+    /// Create a new FixedBuffer64
+    fn new() -> FixedBuffer64 {
+        return FixedBuffer64 {
+            buffer: [0u8, ..64],
+            buffer_idx: 0
+        };
+    }
+}
+
+impl FixedBuffer for FixedBuffer64 {
+    fn input(&mut self, input: &[u8], func: |&[u8]|) {
+        let mut i = 0;
+
+        let size = self.size();
+
+        // If there is already data in the buffer, copy as much as we can into it and process
+        // the data if the buffer becomes full.
+        if self.buffer_idx != 0 {
+            let buffer_remaining = size - self.buffer_idx;
+            if input.len() >= buffer_remaining {
+                    copy_memory(
+                        self.buffer.mut_slice(self.buffer_idx, size),
+                        input.slice_to(buffer_remaining),
+                        buffer_remaining);
+                self.buffer_idx = 0;
+                func(self.buffer);
+                i += buffer_remaining;
+            } else {
+                copy_memory(
+                    self.buffer.mut_slice(self.buffer_idx, self.buffer_idx + input.len()),
+                    input,
+                    input.len());
+                self.buffer_idx += input.len();
+                return;
+            }
+        }
+
+        // While we have at least a full buffer size chunks's worth of data, process that data
+        // without copying it into the buffer
+        while input.len() - i >= size {
+            func(input.slice(i, i + size));
+            i += size;
+        }
+
+        // Copy any input data into the buffer. At this point in the method, the ammount of
+        // data left in the input vector will be less than the buffer size and the buffer will
+        // be empty.
+        let input_remaining = input.len() - i;
+        copy_memory(
+            self.buffer.mut_slice(0, input_remaining),
+            input.slice_from(i),
+            input.len() - i);
+        self.buffer_idx += input_remaining;
+    }
+
+    fn reset(&mut self) {
+        self.buffer_idx = 0;
+    }
+
+    fn zero_until(&mut self, idx: uint) {
+        assert!(idx >= self.buffer_idx);
+        self.buffer.mut_slice(self.buffer_idx, idx).set_memory(0);
+        self.buffer_idx = idx;
+    }
+
+    fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8] {
+        self.buffer_idx += len;
+        return self.buffer.mut_slice(self.buffer_idx - len, self.buffer_idx);
+    }
+
+    fn full_buffer<'s>(&'s mut self) -> &'s [u8] {
+        assert!(self.buffer_idx == 64);
+        self.buffer_idx = 0;
+        return self.buffer.slice_to(64);
+    }
+
+    fn position(&self) -> uint { self.buffer_idx }
+
+    fn remaining(&self) -> uint { 64 - self.buffer_idx }
+
+    fn size(&self) -> uint { 64 }
+}
+
+/// The StandardPadding trait adds a method useful for Sha256 to a FixedBuffer struct.
+trait StandardPadding {
+    /// Add padding to the buffer. The buffer must not be full when this method is called and is
+    /// guaranteed to have exactly rem remaining bytes when it returns. If there are not at least
+    /// rem bytes available, the buffer will be zero padded, processed, cleared, and then filled
+    /// with zeros again until only rem bytes are remaining.
+    fn standard_padding(&mut self, rem: uint, func: |&[u8]|);
+}
+
+impl <T: FixedBuffer> StandardPadding for T {
+    fn standard_padding(&mut self, rem: uint, func: |&[u8]|) {
+        let size = self.size();
+
+        self.next(1)[0] = 128;
+
+        if self.remaining() < rem {
+            self.zero_until(size);
+            func(self.full_buffer());
+        }
+
+        self.zero_until(size - rem);
+    }
+}
+
+/// The Digest trait specifies an interface common to digest functions, such as SHA-1 and the SHA-2
+/// family of digest functions.
+pub trait Digest {
+    /// Provide message data.
+    ///
+    /// # Arguments
+    ///
+    /// * input - A vector of message data
+    fn input(&mut self, input: &[u8]);
+
+    /// Retrieve the digest result. This method may be called multiple times.
+    ///
+    /// # Arguments
+    ///
+    /// * out - the vector to hold the result. Must be large enough to contain output_bits().
+    fn result(&mut self, out: &mut [u8]);
+
+    /// Reset the digest. This method must be called after result() and before supplying more
+    /// data.
+    fn reset(&mut self);
+
+    /// Get the output size in bits.
+    fn output_bits(&self) -> uint;
+
+    /// Convenience function that feeds a string into a digest.
+    ///
+    /// # Arguments
+    ///
+    /// * `input` The string to feed into the digest
+    fn input_str(&mut self, input: &str) {
+        self.input(input.as_bytes());
+    }
+
+    /// Convenience function that retrieves the result of a digest as a
+    /// newly allocated vec of bytes.
+    fn result_bytes(&mut self) -> ~[u8] {
+        let mut buf = vec::from_elem((self.output_bits()+7)/8, 0u8);
+        self.result(buf);
+        buf
+    }
+
+    /// Convenience function that retrieves the result of a digest as a
+    /// ~str in hexadecimal format.
+    fn result_str(&mut self) -> ~str {
+        self.result_bytes().to_hex()
+    }
+}
+
+// A structure that represents that state of a digest computation for the SHA-2 512 family of digest
+// functions
+struct Engine256State {
+    H0: u32,
+    H1: u32,
+    H2: u32,
+    H3: u32,
+    H4: u32,
+    H5: u32,
+    H6: u32,
+    H7: u32,
+}
+
+impl Engine256State {
+    fn new(h: &[u32, ..8]) -> Engine256State {
+        return Engine256State {
+            H0: h[0],
+            H1: h[1],
+            H2: h[2],
+            H3: h[3],
+            H4: h[4],
+            H5: h[5],
+            H6: h[6],
+            H7: h[7]
+        };
+    }
+
+    fn reset(&mut self, h: &[u32, ..8]) {
+        self.H0 = h[0];
+        self.H1 = h[1];
+        self.H2 = h[2];
+        self.H3 = h[3];
+        self.H4 = h[4];
+        self.H5 = h[5];
+        self.H6 = h[6];
+        self.H7 = h[7];
+    }
+
+    fn process_block(&mut self, data: &[u8]) {
+        fn ch(x: u32, y: u32, z: u32) -> u32 {
+            ((x & y) ^ ((!x) & z))
+        }
+
+        fn maj(x: u32, y: u32, z: u32) -> u32 {
+            ((x & y) ^ (x & z) ^ (y & z))
+        }
+
+        fn sum0(x: u32) -> u32 {
+            ((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))
+        }
+
+        fn sum1(x: u32) -> u32 {
+            ((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))
+        }
+
+        fn sigma0(x: u32) -> u32 {
+            ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3)
+        }
+
+        fn sigma1(x: u32) -> u32 {
+            ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10)
+        }
+
+        let mut a = self.H0;
+        let mut b = self.H1;
+        let mut c = self.H2;
+        let mut d = self.H3;
+        let mut e = self.H4;
+        let mut f = self.H5;
+        let mut g = self.H6;
+        let mut h = self.H7;
+
+        let mut W = [0u32, ..64];
+
+        // Sha-512 and Sha-256 use basically the same calculations which are implemented
+        // by these macros. Inlining the calculations seems to result in better generated code.
+        macro_rules! schedule_round( ($t:expr) => (
+                W[$t] = sigma1(W[$t - 2]) + W[$t - 7] + sigma0(W[$t - 15]) + W[$t - 16];
+                )
+        )
+
+        macro_rules! sha2_round(
+            ($A:ident, $B:ident, $C:ident, $D:ident,
+             $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => (
+                {
+                    $H += sum1($E) + ch($E, $F, $G) + $K[$t] + W[$t];
+                    $D += $H;
+                    $H += sum0($A) + maj($A, $B, $C);
+                }
+             )
+        )
+
+        read_u32v_be(W.mut_slice(0, 16), data);
+
+        // Putting the message schedule inside the same loop as the round calculations allows for
+        // the compiler to generate better code.
+        for t in range_step(0u, 48, 8) {
+            schedule_round!(t + 16);
+            schedule_round!(t + 17);
+            schedule_round!(t + 18);
+            schedule_round!(t + 19);
+            schedule_round!(t + 20);
+            schedule_round!(t + 21);
+            schedule_round!(t + 22);
+            schedule_round!(t + 23);
+
+            sha2_round!(a, b, c, d, e, f, g, h, K32, t);
+            sha2_round!(h, a, b, c, d, e, f, g, K32, t + 1);
+            sha2_round!(g, h, a, b, c, d, e, f, K32, t + 2);
+            sha2_round!(f, g, h, a, b, c, d, e, K32, t + 3);
+            sha2_round!(e, f, g, h, a, b, c, d, K32, t + 4);
+            sha2_round!(d, e, f, g, h, a, b, c, K32, t + 5);
+            sha2_round!(c, d, e, f, g, h, a, b, K32, t + 6);
+            sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7);
+        }
+
+        for t in range_step(48u, 64, 8) {
+            sha2_round!(a, b, c, d, e, f, g, h, K32, t);
+            sha2_round!(h, a, b, c, d, e, f, g, K32, t + 1);
+            sha2_round!(g, h, a, b, c, d, e, f, K32, t + 2);
+            sha2_round!(f, g, h, a, b, c, d, e, K32, t + 3);
+            sha2_round!(e, f, g, h, a, b, c, d, K32, t + 4);
+            sha2_round!(d, e, f, g, h, a, b, c, K32, t + 5);
+            sha2_round!(c, d, e, f, g, h, a, b, K32, t + 6);
+            sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7);
+        }
+
+        self.H0 += a;
+        self.H1 += b;
+        self.H2 += c;
+        self.H3 += d;
+        self.H4 += e;
+        self.H5 += f;
+        self.H6 += g;
+        self.H7 += h;
+    }
+}
+
+static K32: [u32, ..64] = [
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+];
+
+// A structure that keeps track of the state of the Sha-256 operation and contains the logic
+// necessary to perform the final calculations.
+struct Engine256 {
+    length_bits: u64,
+    buffer: FixedBuffer64,
+    state: Engine256State,
+    finished: bool,
+}
+
+impl Engine256 {
+    fn new(h: &[u32, ..8]) -> Engine256 {
+        return Engine256 {
+            length_bits: 0,
+            buffer: FixedBuffer64::new(),
+            state: Engine256State::new(h),
+            finished: false
+        }
+    }
+
+    fn reset(&mut self, h: &[u32, ..8]) {
+        self.length_bits = 0;
+        self.buffer.reset();
+        self.state.reset(h);
+        self.finished = false;
+    }
+
+    fn input(&mut self, input: &[u8]) {
+        assert!(!self.finished)
+        // Assumes that input.len() can be converted to u64 without overflow
+        self.length_bits = add_bytes_to_bits(self.length_bits, input.len() as u64);
+        self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) });
+    }
+
+    fn finish(&mut self) {
+        if self.finished {
+            return;
+        }
+
+        self.buffer.standard_padding(8, |input: &[u8]| { self.state.process_block(input) });
+        write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 );
+        write_u32_be(self.buffer.next(4), self.length_bits as u32);
+        self.state.process_block(self.buffer.full_buffer());
+
+        self.finished = true;
+    }
+}
+
+/// The SHA-256 hash algorithm
+pub struct Sha256 {
+    priv engine: Engine256
+}
+
+impl Sha256 {
+    /// Construct an new instance of a SHA-256 digest.
+    pub fn new() -> Sha256 {
+        Sha256 {
+            engine: Engine256::new(&H256)
+        }
+    }
+}
+
+impl Digest for Sha256 {
+    fn input(&mut self, d: &[u8]) {
+        self.engine.input(d);
+    }
+
+    fn result(&mut self, out: &mut [u8]) {
+        self.engine.finish();
+
+        write_u32_be(out.mut_slice(0, 4), self.engine.state.H0);
+        write_u32_be(out.mut_slice(4, 8), self.engine.state.H1);
+        write_u32_be(out.mut_slice(8, 12), self.engine.state.H2);
+        write_u32_be(out.mut_slice(12, 16), self.engine.state.H3);
+        write_u32_be(out.mut_slice(16, 20), self.engine.state.H4);
+        write_u32_be(out.mut_slice(20, 24), self.engine.state.H5);
+        write_u32_be(out.mut_slice(24, 28), self.engine.state.H6);
+        write_u32_be(out.mut_slice(28, 32), self.engine.state.H7);
+    }
+
+    fn reset(&mut self) {
+        self.engine.reset(&H256);
+    }
+
+    fn output_bits(&self) -> uint { 256 }
+}
+
+static H256: [u32, ..8] = [
+    0x6a09e667,
+    0xbb67ae85,
+    0x3c6ef372,
+    0xa54ff53a,
+    0x510e527f,
+    0x9b05688c,
+    0x1f83d9ab,
+    0x5be0cd19
+];
+
+#[cfg(test)]
+mod tests {
+    use super::{Digest, Sha256};
+    use std::vec;
+    use std::rand::isaac::IsaacRng;
+    use std::rand::Rng;
+    use extra::hex::FromHex;
+
+    // A normal addition - no overflow occurs
+    #[test]
+    fn test_add_bytes_to_bits_ok() {
+        assert!(super::add_bytes_to_bits::<u64>(100, 10) == 180);
+    }
+
+    // A simple failure case - adding 1 to the max value
+    #[test]
+    #[should_fail]
+    fn test_add_bytes_to_bits_overflow() {
+        super::add_bytes_to_bits::<u64>(Bounded::max_value(), 1);
+    }
+
+    struct Test {
+        input: ~str,
+        output_str: ~str,
+    }
+
+    fn test_hash<D: Digest>(sh: &mut D, tests: &[Test]) {
+        // Test that it works when accepting the message all at once
+        for t in tests.iter() {
+            sh.reset();
+            sh.input_str(t.input);
+            let out_str = sh.result_str();
+            assert!(out_str == t.output_str);
+        }
+
+        // Test that it works when accepting the message in pieces
+        for t in tests.iter() {
+            sh.reset();
+            let len = t.input.len();
+            let mut left = len;
+            while left > 0u {
+                let take = (left + 1u) / 2u;
+                sh.input_str(t.input.slice(len - left, take + len - left));
+                left = left - take;
+            }
+            let out_str = sh.result_str();
+            assert!(out_str == t.output_str);
+        }
+    }
+
+    #[test]
+    fn test_sha256() {
+        // Examples from wikipedia
+        let wikipedia_tests = ~[
+            Test {
+                input: ~"",
+                output_str: ~"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+            },
+            Test {
+                input: ~"The quick brown fox jumps over the lazy dog",
+                output_str: ~"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
+            },
+            Test {
+                input: ~"The quick brown fox jumps over the lazy dog.",
+                output_str: ~"ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"
+            },
+        ];
+
+        let tests = wikipedia_tests;
+
+        let mut sh = ~Sha256::new();
+
+        test_hash(sh, tests);
+    }
+
+    /// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is
+    /// correct.
+    fn test_digest_1million_random<D: Digest>(digest: &mut D, blocksize: uint, expected: &str) {
+        let total_size = 1000000;
+        let buffer = vec::from_elem(blocksize * 2, 'a' as u8);
+        let mut rng = IsaacRng::new_unseeded();
+        let mut count = 0;
+
+        digest.reset();
+
+        while count < total_size {
+            let next: uint = rng.gen_range(0, 2 * blocksize + 1);
+            let remaining = total_size - count;
+            let size = if next > remaining { remaining } else { next };
+            digest.input(buffer.slice_to(size));
+            count += size;
+        }
+
+        let result_str = digest.result_str();
+        let result_bytes = digest.result_bytes();
+
+        assert_eq!(expected, result_str.as_slice());
+        assert_eq!(expected.from_hex().unwrap(), result_bytes);
+    }
+
+    #[test]
+    fn test_1million_random_sha256() {
+        let mut sh = Sha256::new();
+        test_digest_1million_random(
+            &mut sh,
+            64,
+            "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+    }
+}
+
+#[cfg(test)]
+mod bench {
+    use extra::test::BenchHarness;
+    use super::Sha256;
+
+    #[bench]
+    pub fn sha256_10(bh: &mut BenchHarness) {
+        let mut sh = Sha256::new();
+        let bytes = [1u8, ..10];
+        bh.iter(|| {
+            sh.input(bytes);
+        });
+        bh.bytes = bytes.len() as u64;
+    }
+
+    #[bench]
+    pub fn sha256_1k(bh: &mut BenchHarness) {
+        let mut sh = Sha256::new();
+        let bytes = [1u8, ..1024];
+        bh.iter(|| {
+            sh.input(bytes);
+        });
+        bh.bytes = bytes.len() as u64;
+    }
+
+    #[bench]
+    pub fn sha256_64k(bh: &mut BenchHarness) {
+        let mut sh = Sha256::new();
+        let bytes = [1u8, ..65536];
+        bh.iter(|| {
+            sh.input(bytes);
+        });
+        bh.bytes = bytes.len() as u64;
+    }
+}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 39e2b908b50..f8b86da03c1 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="rustdoc#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "rustdoc",
        package_id = "rustdoc",
        vers = "0.9-pre",
diff --git a/src/librustpkg/lib.rs b/src/librustpkg/lib.rs
index 7801742bc91..0526e7dd434 100644
--- a/src/librustpkg/lib.rs
+++ b/src/librustpkg/lib.rs
@@ -10,6 +10,8 @@
 
 // rustpkg - a package manager and build system for Rust
 
+#[pkgid="rustpkg#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "rustpkg",
        package_id = "rustpkg",
        vers = "0.9-pre",
@@ -36,6 +38,7 @@ use extra::workcache;
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
 use rustc::metadata::filesearch::rust_path;
+use rustc::util::sha2;
 use extra::{getopts};
 use syntax::{ast, diagnostic};
 use messages::{error, warn, note};
@@ -52,7 +55,7 @@ use context::{Context, BuildContext,
 use package_id::PkgId;
 use package_source::PkgSrc;
 use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench};
-use target::{Tests, MaybeCustom, Inferred, JustOne};
+use target::{Main, Tests, MaybeCustom, Inferred, JustOne};
 use workcache_support::digest_only_date;
 use exit_codes::{COPY_FAILED_CODE, BAD_FLAG_CODE};
 
@@ -66,7 +69,6 @@ mod messages;
 pub mod package_id;
 pub mod package_source;
 mod path_util;
-mod sha1;
 mod source_control;
 mod target;
 #[cfg(not(windows), test)] // FIXME test failure on windows: #10471
@@ -151,7 +153,8 @@ impl<'self> PkgScript<'self> {
                                        Nothing,
                                        &self.build_dir,
                                        sess,
-                                       crate);
+                                       crate,
+                                       Main);
         // Discover the output
         // FIXME (#9639): This needs to handle non-utf8 paths
         // Discover the output
diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs
index 135ba9e9702..59fdf20941e 100644
--- a/src/librustpkg/tests.rs
+++ b/src/librustpkg/tests.rs
@@ -15,8 +15,6 @@ use std::{os, run, str, task};
 use std::io;
 use std::io::fs;
 use std::io::File;
-use std::io::process;
-use std::io::process::ProcessExit;
 use extra::arc::Arc;
 use extra::arc::RWArc;
 use extra::tempfile::TempDir;
@@ -739,8 +737,8 @@ fn test_package_ids_must_be_relative_path_like() {
 
     let whatever = PkgId::new("foo");
 
-    assert_eq!(~"foo-0.1", whatever.to_str());
-    assert!("github.com/catamorphism/test-pkg-0.1" ==
+    assert_eq!(~"foo-0.0", whatever.to_str());
+    assert!("github.com/catamorphism/test-pkg-0.0" ==
             PkgId::new("github.com/catamorphism/test-pkg").to_str());
 
     cond.trap(|(p, e)| {
@@ -749,7 +747,7 @@ fn test_package_ids_must_be_relative_path_like() {
         whatever.clone()
     }).inside(|| {
         let x = PkgId::new("");
-        assert_eq!(~"foo-0.1", x.to_str());
+        assert_eq!(~"foo-0.0", x.to_str());
     });
 
     cond.trap(|(p, e)| {
@@ -761,9 +759,8 @@ fn test_package_ids_must_be_relative_path_like() {
         let zp = os::make_absolute(&Path::new("foo/bar/quux"));
         // FIXME (#9639): This needs to handle non-utf8 paths
         let z = PkgId::new(zp.as_str().unwrap());
-        assert_eq!(~"foo-0.1", z.to_str());
+        assert_eq!(~"foo-0.0", z.to_str());
     })
-
 }
 
 #[test]
@@ -896,7 +893,7 @@ fn package_script_with_default_build() {
     let source = Path::new(file!()).dir_path().join_many(
         [~"testsuite", ~"pass", ~"src", ~"fancy-lib", ~"pkg.rs"]);
     debug!("package_script_with_default_build: {}", source.display());
-    fs::copy(&source, &dir.join_many(["src", "fancy-lib-0.1", "pkg.rs"]));
+    fs::copy(&source, &dir.join_many(["src", "fancy-lib-0.0", "pkg.rs"]));
     command_line_test([~"install", ~"fancy-lib"], dir);
     assert_lib_exists(dir, &Path::new("fancy-lib"), NoVersion);
     assert!(target_build_dir(dir).join_many([~"fancy-lib", ~"generated.rs"]).exists());
@@ -1349,7 +1346,7 @@ fn test_import_rustpkg() {
     let p_id = PkgId::new("foo");
     let workspace = create_local_package(&p_id);
     let workspace = workspace.path();
-    writeFile(&workspace.join_many(["src", "foo-0.1", "pkg.rs"]),
+    writeFile(&workspace.join_many(["src", "foo-0.0", "pkg.rs"]),
               "extern mod rustpkg; fn main() {}");
     command_line_test([~"build", ~"foo"], workspace);
     debug!("workspace = {}", workspace.display());
@@ -1362,7 +1359,7 @@ fn test_macro_pkg_script() {
     let p_id = PkgId::new("foo");
     let workspace = create_local_package(&p_id);
     let workspace = workspace.path();
-    writeFile(&workspace.join_many(["src", "foo-0.1", "pkg.rs"]),
+    writeFile(&workspace.join_many(["src", "foo-0.0", "pkg.rs"]),
               "extern mod rustpkg; fn main() { debug!(\"Hi\"); }");
     command_line_test([~"build", ~"foo"], workspace);
     debug!("workspace = {}", workspace.display());
@@ -1404,7 +1401,7 @@ fn rust_path_hack_test(hack_flag: bool) {
    let workspace = workspace.path();
    let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace");
    let dest_workspace = dest_workspace.path();
-   let foo_path = workspace.join_many(["src", "foo-0.1"]);
+   let foo_path = workspace.join_many(["src", "foo-0.0"]);
    let rust_path = Some(~[(~"RUST_PATH",
        format!("{}:{}",
                dest_workspace.as_str().unwrap(),
@@ -1534,9 +1531,9 @@ fn rust_path_hack_build_with_dependency() {
     let dep_workspace = dep_workspace.path();
     let dest_workspace = mk_emptier_workspace("dep");
     let dest_workspace = dest_workspace.path();
-    let source_dir = work_dir.join_many(["src", "foo-0.1"]);
+    let source_dir = work_dir.join_many(["src", "foo-0.0"]);
     writeFile(&source_dir.join("lib.rs"), "extern mod dep; pub fn f() { }");
-    let dep_dir = dep_workspace.join_many(["src", "dep-0.1"]);
+    let dep_dir = dep_workspace.join_many(["src", "dep-0.0"]);
     let rust_path = Some(~[(~"RUST_PATH",
                           format!("{}:{}",
                                   dest_workspace.display(),
@@ -1706,7 +1703,7 @@ fn test_cfg_build() {
     let workspace = create_local_package(&p_id);
     let workspace = workspace.path();
     // If the cfg flag gets messed up, this won't compile
-    writeFile(&workspace.join_many(["src", "foo-0.1", "main.rs"]),
+    writeFile(&workspace.join_many(["src", "foo-0.0", "main.rs"]),
                "#[cfg(quux)] fn main() {}");
     let test_sys = test_sysroot();
     // FIXME (#9639): This needs to handle non-utf8 paths
@@ -1724,7 +1721,7 @@ fn test_cfg_fail() {
     let p_id = PkgId::new("foo");
     let workspace = create_local_package(&p_id);
     let workspace = workspace.path();
-    writeFile(&workspace.join_many(["src", "foo-0.1", "main.rs"]),
+    writeFile(&workspace.join_many(["src", "foo-0.0", "main.rs"]),
                "#[cfg(quux)] fn main() {}");
     let test_sys = test_sysroot();
     // FIXME (#9639): This needs to handle non-utf8 paths
@@ -1892,11 +1889,13 @@ fn pkgid_pointing_to_subdir() {
                                        "extras", "bar"]);
     fs::mkdir_recursive(&foo_dir, io::UserRWX);
     fs::mkdir_recursive(&bar_dir, io::UserRWX);
-    writeFile(&foo_dir.join("lib.rs"), "pub fn f() {}");
-    writeFile(&bar_dir.join("lib.rs"), "pub fn g() {}");
+    writeFile(&foo_dir.join("lib.rs"),
+              "#[pkgid=\"mockgithub.com/mozilla/some_repo/extras/foo\"]; pub fn f() {}");
+    writeFile(&bar_dir.join("lib.rs"),
+              "#[pkgid=\"mockgithub.com/mozilla/some_repo/extras/bar\"]; pub fn g() {}");
 
     debug!("Creating a file in {}", workspace.display());
-    let testpkg_dir = workspace.join_many(["src", "testpkg-0.1"]);
+    let testpkg_dir = workspace.join_many(["src", "testpkg-0.0"]);
     fs::mkdir_recursive(&testpkg_dir, io::UserRWX);
 
     writeFile(&testpkg_dir.join("main.rs"),
@@ -1916,13 +1915,13 @@ fn test_recursive_deps() {
     let c_id = PkgId::new("c");
     let b_workspace = create_local_package_with_dep(&b_id, &c_id);
     let b_workspace = b_workspace.path();
-    writeFile(&b_workspace.join_many(["src", "c-0.1", "lib.rs"]),
+    writeFile(&b_workspace.join_many(["src", "c-0.0", "lib.rs"]),
                "pub fn g() {}");
     let a_workspace = create_local_package(&a_id);
     let a_workspace = a_workspace.path();
-    writeFile(&a_workspace.join_many(["src", "a-0.1", "main.rs"]),
+    writeFile(&a_workspace.join_many(["src", "a-0.0", "main.rs"]),
                "extern mod b; use b::f; fn main() { f(); }");
-    writeFile(&b_workspace.join_many(["src", "b-0.1", "lib.rs"]),
+    writeFile(&b_workspace.join_many(["src", "b-0.0", "lib.rs"]),
                "extern mod c; use c::g; pub fn f() { g(); }");
     // FIXME (#9639): This needs to handle non-utf8 paths
     let environment = Some(~[(~"RUST_PATH", b_workspace.as_str().unwrap().to_owned())]);
@@ -1999,7 +1998,7 @@ fn test_dependencies_terminate() {
     let b_id = PkgId::new("b");
     let workspace = create_local_package(&b_id);
     let workspace = workspace.path();
-    let b_dir = workspace.join_many(["src", "b-0.1"]);
+    let b_dir = workspace.join_many(["src", "b-0.0"]);
     let b_subdir = b_dir.join("test");
     fs::mkdir_recursive(&b_subdir, io::UserRWX);
     writeFile(&b_subdir.join("test.rs"),
@@ -2066,10 +2065,10 @@ fn correct_package_name_with_rust_path_hack() {
     let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace");
     let dest_workspace = dest_workspace.path();
 
-    writeFile(&dest_workspace.join_many(["src", "bar-0.1", "main.rs"]),
+    writeFile(&dest_workspace.join_many(["src", "bar-0.0", "main.rs"]),
               "extern mod blat; fn main() { let _x = (); }");
 
-    let foo_path = foo_workspace.join_many(["src", "foo-0.1"]);
+    let foo_path = foo_workspace.join_many(["src", "foo-0.0"]);
     // FIXME (#9639): This needs to handle non-utf8 paths
     let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dest_workspace.as_str().unwrap(),
                                                   foo_path.as_str().unwrap()))]);
@@ -2092,7 +2091,7 @@ fn test_rustpkg_test_creates_exec() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    writeFile(&foo_workspace.join_many(["src", "foo-0.1", "test.rs"]),
+    writeFile(&foo_workspace.join_many(["src", "foo-0.0", "test.rs"]),
               "#[test] fn f() { assert!('a' == 'a'); }");
     command_line_test([~"test", ~"foo"], foo_workspace);
     assert!(test_executable_exists(foo_workspace, "foo"));
@@ -2115,7 +2114,7 @@ fn test_rustpkg_test_failure_exit_status() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    writeFile(&foo_workspace.join_many(["src", "foo-0.1", "test.rs"]),
+    writeFile(&foo_workspace.join_many(["src", "foo-0.0", "test.rs"]),
               "#[test] fn f() { assert!('a' != 'a'); }");
     let res = command_line_test_partial([~"test", ~"foo"], foo_workspace);
     match res {
@@ -2129,7 +2128,7 @@ fn test_rustpkg_test_cfg() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    writeFile(&foo_workspace.join_many(["src", "foo-0.1", "test.rs"]),
+    writeFile(&foo_workspace.join_many(["src", "foo-0.0", "test.rs"]),
               "#[test] #[cfg(not(foobar))] fn f() { assert!('a' != 'a'); }");
     let output = command_line_test([~"test", ~"--cfg", ~"foobar", ~"foo"],
                                    foo_workspace);
@@ -2142,7 +2141,7 @@ fn test_rebuild_when_needed() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    let test_crate = foo_workspace.join_many(["src", "foo-0.1", "test.rs"]);
+    let test_crate = foo_workspace.join_many(["src", "foo-0.0", "test.rs"]);
     writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }");
     command_line_test([~"test", ~"foo"], foo_workspace);
     assert!(test_executable_exists(foo_workspace, "foo"));
@@ -2163,7 +2162,7 @@ fn test_no_rebuilding() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    let test_crate = foo_workspace.join_many(["src", "foo-0.1", "test.rs"]);
+    let test_crate = foo_workspace.join_many(["src", "foo-0.0", "test.rs"]);
     writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }");
     command_line_test([~"test", ~"foo"], foo_workspace);
     assert!(test_executable_exists(foo_workspace, "foo"));
@@ -2182,7 +2181,7 @@ fn test_no_rebuilding() {
 fn test_installed_read_only() {
     // Install sources from a "remote" (actually a local github repo)
     // Check that afterward, sources are read-only and installed under build/
-    let temp_pkg_id = git_repo_pkg();
+    let mut temp_pkg_id = git_repo_pkg();
     let repo = init_git_repo(&temp_pkg_id.path);
     let repo = repo.path();
     debug!("repo = {}", repo.display());
@@ -2194,6 +2193,8 @@ fn test_installed_read_only() {
     writeFile(&repo_subdir.join("lib.rs"),
               "pub fn f() { let _x = (); }");
     add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files
+    // update pkgid to what will be auto-detected
+    temp_pkg_id.version = ExactRevision(~"0.1");
 
     // FIXME (#9639): This needs to handle non-utf8 paths
     command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo);
@@ -2247,7 +2248,7 @@ fn test_installed_local_changes() {
     let target_dir = hacking_workspace.join_many(["src",
                                                   "mockgithub.com",
                                                   "catamorphism",
-                                                  "test-pkg-0.1"]);
+                                                  "test-pkg-0.0"]);
     debug!("---- git clone {} {}", repo_subdir.display(), target_dir.display());
 
     let c_res = safe_git_clone(&repo_subdir, &NoVersion, &target_dir);
@@ -2294,7 +2295,7 @@ fn test_compile_error() {
     let foo_id = PkgId::new("foo");
     let foo_workspace = create_local_package(&foo_id);
     let foo_workspace = foo_workspace.path();
-    let main_crate = foo_workspace.join_many(["src", "foo-0.1", "main.rs"]);
+    let main_crate = foo_workspace.join_many(["src", "foo-0.0", "main.rs"]);
     // Write something bogus
     writeFile(&main_crate, "pub fn main() { if 42 != ~\"the answer\" { fail!(); } }");
     let result = command_line_test_partial([~"build", ~"foo"], foo_workspace);
@@ -2327,15 +2328,15 @@ fn test_c_dependency_ok() {
 
     let dir = create_local_package(&PkgId::new("cdep"));
     let dir = dir.path();
-    writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]),
+    writeFile(&dir.join_many(["src", "cdep-0.0", "main.rs"]),
               "#[link_args = \"-lfoo\"]\nextern { fn f(); } \
               \nfn main() { unsafe { f(); } }");
-    writeFile(&dir.join_many(["src", "cdep-0.1", "foo.c"]), "void f() {}");
+    writeFile(&dir.join_many(["src", "cdep-0.0", "foo.c"]), "void f() {}");
 
     debug!("dir = {}", dir.display());
     let source = Path::new(file!()).dir_path().join_many(
         [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]);
-    fs::copy(&source, &dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"]));
+    fs::copy(&source, &dir.join_many([~"src", ~"cdep-0.0", ~"pkg.rs"]));
     command_line_test([~"build", ~"cdep"], dir);
     assert_executable_exists(dir, "cdep");
     let out_dir = target_build_dir(dir).join("cdep");
@@ -2344,20 +2345,21 @@ fn test_c_dependency_ok() {
     assert!(c_library_path.exists());
 }
 
+#[ignore(reason="rustpkg is not reentrant")]
 #[test]
 #[ignore(reason="busted")]
 fn test_c_dependency_no_rebuilding() {
     let dir = create_local_package(&PkgId::new("cdep"));
     let dir = dir.path();
-    writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]),
+    writeFile(&dir.join_many(["src", "cdep-0.0", "main.rs"]),
               "#[link_args = \"-lfoo\"]\nextern { fn f(); } \
               \nfn main() { unsafe { f(); } }");
-    writeFile(&dir.join_many(["src", "cdep-0.1", "foo.c"]), "void f() {}");
+    writeFile(&dir.join_many(["src", "cdep-0.0", "foo.c"]), "void f() {}");
 
     debug!("dir = {}", dir.display());
     let source = Path::new(file!()).dir_path().join_many(
         [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]);
-    fs::copy(&source, &dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"]));
+    fs::copy(&source, &dir.join_many([~"src", ~"cdep-0.0", ~"pkg.rs"]));
     command_line_test([~"build", ~"cdep"], dir);
     assert_executable_exists(dir, "cdep");
     let out_dir = target_build_dir(dir).join("cdep");
@@ -2383,15 +2385,15 @@ fn test_c_dependency_no_rebuilding() {
 fn test_c_dependency_yes_rebuilding() {
     let dir = create_local_package(&PkgId::new("cdep"));
     let dir = dir.path();
-    writeFile(&dir.join_many(["src", "cdep-0.1", "main.rs"]),
+    writeFile(&dir.join_many(["src", "cdep-0.0", "main.rs"]),
               "#[link_args = \"-lfoo\"]\nextern { fn f(); } \
               \nfn main() { unsafe { f(); } }");
-    let c_file_name = dir.join_many(["src", "cdep-0.1", "foo.c"]);
+    let c_file_name = dir.join_many(["src", "cdep-0.0", "foo.c"]);
     writeFile(&c_file_name, "void f() {}");
 
     let source = Path::new(file!()).dir_path().join_many(
         [~"testsuite", ~"pass", ~"src", ~"c-dependencies", ~"pkg.rs"]);
-    let target = dir.join_many([~"src", ~"cdep-0.1", ~"pkg.rs"]);
+    let target = dir.join_many([~"src", ~"cdep-0.0", ~"pkg.rs"]);
     debug!("Copying {} -> {}", source.display(), target.display());
     fs::copy(&source, &target);
     command_line_test([~"build", ~"cdep"], dir);
@@ -2420,13 +2422,13 @@ fn test_c_dependency_yes_rebuilding() {
 fn correct_error_dependency() {
     // Supposing a package we're trying to install via a dependency doesn't
     // exist, we should throw a condition, and not ICE
-    let dir = create_local_package(&PkgId::new("badpkg"));
+    let workspace_dir = create_local_package(&PkgId::new("badpkg"));
 
-    let dir = dir.path();
-    writeFile(&dir.join_many(["src", "badpkg-0.1", "main.rs"]),
+    let dir = workspace_dir.path();
+    let main_rs = dir.join_many(["src", "badpkg-0.0", "main.rs"]);
+    writeFile(&main_rs,
               "extern mod p = \"some_package_that_doesnt_exist\";
                fn main() {}");
-
     match command_line_test_partial([~"build", ~"badpkg"], dir) {
         Fail(ProcessOutput{ error: error, output: output, .. }) => {
             assert!(str::is_utf8(error));
diff --git a/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs b/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs
index 122f80a52f1..e5386aa701b 100644
--- a/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs
+++ b/src/librustpkg/testsuite/pass/src/c-dependencies/pkg.rs
@@ -42,7 +42,7 @@ pub fn main() {
 
     let mut context = api::default_context(sysroot, path_for_db);
     let my_workspace = api::my_workspace(&context.context, "cdep");
-    let foo_c_name = my_workspace.join_many(["src", "cdep-0.1", "foo.c"]);
+    let foo_c_name = my_workspace.join_many(["src", "cdep-0.0", "foo.c"]);
 
     let out_lib_path = context.workcache_context.with_prep("foo.c", |prep| {
         let sub_cx = context.context.clone();
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 3f6d5b55066..97fba807c0e 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -290,23 +290,17 @@ pub fn compile_input(context: &BuildContext,
                                       addl_lib_search_paths.insert(p);
                                   });
 
-    // Inject the link attributes so we get the right package name and version
-    if attr::find_linkage_metas(crate.attrs).is_empty() {
-        let name_to_use = match what {
-            Test  => format!("{}test", pkg_id.short_name).to_managed(),
-            Bench => format!("{}bench", pkg_id.short_name).to_managed(),
-            _     => pkg_id.short_name.to_managed()
-        };
-        debug!("Injecting link name: {}", name_to_use);
+    // Inject the pkgid attribute so we get the right package name and version
+    if !attr::contains_name(crate.attrs, "pkgid") {
         // FIXME (#9639): This needs to handle non-utf8 paths
-        let link_options =
-            ~[attr::mk_name_value_item_str(@"name", name_to_use),
-              attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())] +
-            ~[attr::mk_name_value_item_str(@"package_id",
-                                           pkg_id.path.as_str().unwrap().to_managed())];
+        let pkgid_attr =
+            attr::mk_name_value_item_str(@"pkgid",
+                                         format!("{}\\#{}",
+                                                 pkg_id.path.as_str().unwrap(),
+                                                 pkg_id.version.to_str()).to_managed());
 
-        debug!("link options: {:?}", link_options);
-        crate.attrs = ~[attr::mk_attr(attr::mk_list_item(@"link", link_options))];
+        debug!("pkgid attr: {:?}", pkgid_attr);
+        crate.attrs = ~[attr::mk_attr(pkgid_attr)];
     }
 
     debug!("calling compile_crate_from_input, workspace = {},
@@ -316,7 +310,8 @@ pub fn compile_input(context: &BuildContext,
                                           context.compile_upto(),
                                           &out_dir,
                                           sess,
-                                          crate);
+                                          crate,
+                                          what);
     // Discover the output
     let discovered_output = if what == Lib  {
         built_library_in_workspace(pkg_id, workspace) // Huh???
@@ -351,15 +346,29 @@ pub fn compile_crate_from_input(input: &Path,
                                 sess: session::Session,
 // Returns None if one of the flags that suppresses compilation output was
 // given
-                                crate: ast::Crate) -> Option<Path> {
+                                crate: ast::Crate,
+                                what: OutputType) -> Option<Path> {
     debug!("Calling build_output_filenames with {}, building library? {:?}",
            out_dir.display(), sess.building_library);
 
     // bad copy
     debug!("out_dir = {}", out_dir.display());
-    let outputs = driver::build_output_filenames(&driver::file_input(input.clone()),
-                                                 &Some(out_dir.clone()), &None,
-                                                 crate.attrs, sess);
+    let mut outputs = driver::build_output_filenames(&driver::file_input(input.clone()),
+                                                     &Some(out_dir.clone()), &None,
+                                                     crate.attrs, sess);
+    match what {
+        Lib | Main => {}
+        Test => {
+            let mut ofile = outputs.out_filename.filename_str().unwrap().to_owned();
+            ofile.push_str("test");
+            outputs.out_filename.set_filename(ofile);
+        }
+        Bench => {
+            let mut ofile = outputs.out_filename.filename_str().unwrap().to_owned();
+            ofile.push_str("bench");
+            outputs.out_filename.set_filename(ofile);
+        }
+    };
 
     debug!("Outputs are out_filename: {} and obj_filename: {} and output type = {:?}",
            outputs.out_filename.display(),
diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs
index eced433868f..ba6cf5f513b 100644
--- a/src/librustpkg/version.rs
+++ b/src/librustpkg/version.rs
@@ -24,7 +24,7 @@ pub enum Version {
     SemanticVersion(semver::Version),
     Tagged(~str), // String that can't be parsed as a version.
                   // Requirements get interpreted exactly
-    NoVersion // user didn't specify a version -- prints as 0.1
+    NoVersion // user didn't specify a version -- prints as 0.0
 }
 
 // Equality on versions is non-symmetric: if self is NoVersion, it's equal to
@@ -81,7 +81,7 @@ impl ToStr for Version {
         match *self {
             ExactRevision(ref n) | Tagged(ref n) => format!("{}", n.to_str()),
             SemanticVersion(ref v) => format!("{}", v.to_str()),
-            NoVersion => ~"0.1"
+            NoVersion => ~"0.0"
         }
     }
 }
diff --git a/src/librustpkg/workcache_support.rs b/src/librustpkg/workcache_support.rs
index 42f0aec6b74..824ba5341d4 100644
--- a/src/librustpkg/workcache_support.rs
+++ b/src/librustpkg/workcache_support.rs
@@ -11,7 +11,7 @@
 use std::io;
 use std::io::File;
 use extra::workcache;
-use sha1::{Digest, Sha1};
+use sha2::{Digest, Sha256};
 
 /// Hashes the file contents along with the last-modified time
 pub fn digest_file_with_date(path: &Path) -> ~str {
@@ -19,7 +19,7 @@ pub fn digest_file_with_date(path: &Path) -> ~str {
 
     match io::result(|| File::open(path).read_to_end()) {
         Ok(bytes) => {
-            let mut sha = Sha1::new();
+            let mut sha = Sha256::new();
             sha.input(bytes);
             let st = path.stat();
             sha.input_str(st.modified.to_str());
@@ -34,7 +34,7 @@ pub fn digest_file_with_date(path: &Path) -> ~str {
 
 /// Hashes only the last-modified time
 pub fn digest_only_date(path: &Path) -> ~str {
-    let mut sha = Sha1::new();
+    let mut sha = Sha256::new();
     let st = path.stat();
     sha.input_str(st.modified.to_str());
     sha.result_str()
diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs
index ad1c53e9739..d8b7c4db69c 100644
--- a/src/librustuv/lib.rs
+++ b/src/librustuv/lib.rs
@@ -34,6 +34,8 @@ via `close` and `delete` methods.
 
 */
 
+#[pkgid="rustuv#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "rustuv",
        package_id = "rustuv",
        vers = "0.9-pre",
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 6fe757976f0..e2674006215 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -43,6 +43,8 @@
 //!
 //!     use std::prelude::*;
 
+#[pkgid="std#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "std",
        package_id = "std",
        vers = "0.9-pre",
@@ -69,13 +71,13 @@
 // When testing libstd, bring in libuv as the I/O backend so tests can print
 // things and all of the std::io tests have an I/O interface to run on top
 // of
-#[cfg(test)] extern mod rustuv(vers = "0.9-pre");
+#[cfg(test)] extern mod rustuv = "rustuv#0.9-pre";
 
 // Make extra accessible for benchmarking
-#[cfg(test)] extern mod extra(vers = "0.9-pre");
+#[cfg(test)] extern mod extra = "extra#0.9-pre";
 
 // Make std testable by not duplicating lang items. See #2912
-#[cfg(test)] extern mod realstd(name = "std");
+#[cfg(test)] extern mod realstd = "std#0.9-pre";
 #[cfg(test)] pub use kinds = realstd::kinds;
 #[cfg(test)] pub use ops = realstd::ops;
 #[cfg(test)] pub use cmp = realstd::cmp;
diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs
index 5ad1d92fb15..eb5b2df4ed9 100644
--- a/src/libstd/rt/sched.rs
+++ b/src/libstd/rt/sched.rs
@@ -917,8 +917,6 @@ fn new_sched_rng() -> XorShiftRng {
 
 #[cfg(test)]
 mod test {
-    extern mod extra;
-
     use prelude::*;
     use rt::test::*;
     use unstable::run_in_bare_thread;
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 05a65de16b8..ac5254f1aba 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -18,6 +18,7 @@ use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use diagnostic::span_handler;
 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
+use pkgid::PkgId;
 
 use std::hashmap::HashSet;
 
@@ -235,6 +236,13 @@ pub fn find_linkage_metas(attrs: &[Attribute]) -> ~[@MetaItem] {
     result
 }
 
+pub fn find_pkgid(attrs: &[Attribute]) -> Option<PkgId> {
+    match first_attr_value_str_by_name(attrs, "pkgid") {
+        None => None,
+        Some(id) => from_str::<PkgId>(id),
+    }
+}
+
 #[deriving(Eq)]
 pub enum InlineAttr {
     InlineNone,
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index c9b3ba9d460..4382e6d67b8 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -13,6 +13,8 @@
  *  macros.
  */
 
+#[pkgid="syntax#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "syntax",
        package_id = "syntax",
        vers = "0.9-pre",
@@ -51,6 +53,7 @@ pub mod fold;
 
 
 pub mod parse;
+pub mod pkgid;
 
 pub mod print {
     pub mod pp;
diff --git a/src/libsyntax/pkgid.rs b/src/libsyntax/pkgid.rs
new file mode 100644
index 00000000000..1e840ca700b
--- /dev/null
+++ b/src/libsyntax/pkgid.rs
@@ -0,0 +1,160 @@
+// Copyright 2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[deriving(Clone, Eq)]
+pub struct PkgId {
+    path: ~str,
+    name: ~str,
+    version: Option<~str>,
+}
+
+impl ToStr for PkgId {
+    fn to_str(&self) -> ~str {
+        let version = match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        };
+        if self.path.is_empty() {
+            format!("{}\\#{}", self.name, version)
+        } else {
+            format!("{}/{}\\#{}", self.path, self.name, version)
+        }
+    }
+}
+
+impl FromStr for PkgId {
+    fn from_str(s: &str) -> Option<PkgId> {
+        let hash_idx = match s.find('#') {
+            None => s.len(),
+            Some(idx) => idx,
+        };
+        let prefix = s.slice_to(hash_idx);
+        let name_idx = match prefix.rfind('/') {
+            None => 0,
+            Some(idx) => idx + 1,
+        };
+        if name_idx >= prefix.len() {
+            return None;
+        }
+        let name = prefix.slice_from(name_idx);
+        if name.len() <= 0 {
+            return None;
+        }
+
+        let path = if name_idx == 0 {
+            ""
+        } else {
+            prefix.slice_to(name_idx - 1)
+        };
+        let check_path = Path::new(path);
+        if !check_path.is_relative() {
+            return None;
+        }
+
+        let version = match s.find('#') {
+            None => None,
+            Some(idx) => {
+                if idx >= s.len() {
+                    None
+                } else {
+                    let v = s.slice_from(idx + 1);
+                    if v.is_empty() {
+                        None
+                    } else {
+                        Some(v.to_owned())
+                    }
+                }
+            }
+        };
+
+        Some(PkgId{
+            path: path.to_owned(),
+            name: name.to_owned(),
+            version: version,
+        })
+    }
+}
+
+impl PkgId {
+    pub fn version_or_default<'a>(&'a self) -> &'a str {
+        match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        }
+    }
+}
+
+#[test]
+fn bare_name() {
+    let pkgid: PkgId = from_str("foo").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn bare_name_single_char() {
+    let pkgid: PkgId = from_str("f").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"f");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn empty_pkgid() {
+    let pkgid: Option<PkgId> = from_str("");
+    assert!(pkgid.is_none());
+}
+
+#[test]
+fn simple_path() {
+    let pkgid: PkgId = from_str("example.com/foo/bar").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"example.com/foo");
+}
+
+#[test]
+fn simple_version() {
+    let pkgid: PkgId = from_str("foo#1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn absolute_path() {
+    let pkgid: Option<PkgId> = from_str("/foo/bar");
+    assert!(pkgid.is_none());
+}
+
+#[test]
+fn path_and_version() {
+    let pkgid: PkgId = from_str("example.com/foo/bar#1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"example.com/foo");
+}
+
+#[test]
+fn single_chars() {
+    let pkgid: PkgId = from_str("a/b#1").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"b");
+    assert_eq!(pkgid.version, Some(~"1"));
+    assert_eq!(pkgid.path, ~"a");
+}
+
+#[test]
+fn missing_version() {
+    let pkgid: PkgId = from_str("foo#").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
\ No newline at end of file
diff --git a/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs b/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
index cce7df56504..0c1906500c0 100644
--- a/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
+++ b/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
@@ -8,13 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[link(name = "anonexternmod", vers = "0.1")];
-
-#[crate_type = "lib"];
+#[pkgid="anonexternmod#0.1"];
 
 use std::libc;
 
-#[link(name = "rustrt")]
+#[link(name="rustrt")]
 extern {
     pub fn rust_get_test_int() -> libc::intptr_t;
 }
diff --git a/src/test/auxiliary/cci_impl_lib.rs b/src/test/auxiliary/cci_impl_lib.rs
index f038acf67a8..16c03fa8e3b 100644
--- a/src/test/auxiliary/cci_impl_lib.rs
+++ b/src/test/auxiliary/cci_impl_lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="cci_impl_lib"];
+// NOTE: remove after the next snapshot
 #[link(name="cci_impl_lib", vers="0.0")];
 
 trait uint_helpers {
diff --git a/src/test/auxiliary/cci_iter_lib.rs b/src/test/auxiliary/cci_iter_lib.rs
index e08a2f7fcbb..903563055b7 100644
--- a/src/test/auxiliary/cci_iter_lib.rs
+++ b/src/test/auxiliary/cci_iter_lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="cci_iter_lib"];
+// NOTE: remove after the next snapshot
 #[link(name="cci_iter_lib", vers="0.0")];
 
 #[inline]
diff --git a/src/test/auxiliary/cci_no_inline_lib.rs b/src/test/auxiliary/cci_no_inline_lib.rs
index 99d13e91752..ea06cbf793a 100644
--- a/src/test/auxiliary/cci_no_inline_lib.rs
+++ b/src/test/auxiliary/cci_no_inline_lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="cci_no_inline_lib"];
+// NOTE: remove after the next snapshot
 #[link(name="cci_no_inline_lib", vers="0.0")];
 
 // same as cci_iter_lib, more-or-less, but not marked inline
diff --git a/src/test/auxiliary/crate-method-reexport-grrrrrrr2.rs b/src/test/auxiliary/crate-method-reexport-grrrrrrr2.rs
index 700f95f3d70..921082440d8 100644
--- a/src/test/auxiliary/crate-method-reexport-grrrrrrr2.rs
+++ b/src/test/auxiliary/crate-method-reexport-grrrrrrr2.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 #[feature(managed_boxes)];
+#[pkgid="crate_method_reexport_grrrrrrr2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crate_method_reexport_grrrrrrr2")];
 
 pub use name_pool::add;
diff --git a/src/test/auxiliary/crateresolve1-1.rs b/src/test/auxiliary/crateresolve1-1.rs
index 818d4da52ed..d7600f95ecd 100644
--- a/src/test/auxiliary/crateresolve1-1.rs
+++ b/src/test/auxiliary/crateresolve1-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve1#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve1",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/crateresolve1-2.rs b/src/test/auxiliary/crateresolve1-2.rs
index 6edbcbfdb85..239d11b1985 100644
--- a/src/test/auxiliary/crateresolve1-2.rs
+++ b/src/test/auxiliary/crateresolve1-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve1#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve1",
        vers = "0.2")];
 
diff --git a/src/test/auxiliary/crateresolve1-3.rs b/src/test/auxiliary/crateresolve1-3.rs
index ce23867c2c7..7acb3804fd3 100644
--- a/src/test/auxiliary/crateresolve1-3.rs
+++ b/src/test/auxiliary/crateresolve1-3.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve1#0.3"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve1",
        vers = "0.3")];
 
diff --git a/src/test/auxiliary/crateresolve2-1.rs b/src/test/auxiliary/crateresolve2-1.rs
index 4d0694f473b..149314e5a07 100644
--- a/src/test/auxiliary/crateresolve2-1.rs
+++ b/src/test/auxiliary/crateresolve2-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve2#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve2",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/crateresolve2-2.rs b/src/test/auxiliary/crateresolve2-2.rs
index 4ae0ce0109f..9286b68586a 100644
--- a/src/test/auxiliary/crateresolve2-2.rs
+++ b/src/test/auxiliary/crateresolve2-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve2#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve2",
        vers = "0.2")];
 
diff --git a/src/test/auxiliary/crateresolve2-3.rs b/src/test/auxiliary/crateresolve2-3.rs
index 6d401b50f8c..37ccbbd40d7 100644
--- a/src/test/auxiliary/crateresolve2-3.rs
+++ b/src/test/auxiliary/crateresolve2-3.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve2#0.3"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve2",
        vers = "0.3")];
 
diff --git a/src/test/auxiliary/crateresolve3-1.rs b/src/test/auxiliary/crateresolve3-1.rs
index b890508f744..e2c46a9a88d 100644
--- a/src/test/auxiliary/crateresolve3-1.rs
+++ b/src/test/auxiliary/crateresolve3-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve3#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve3",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/crateresolve3-2.rs b/src/test/auxiliary/crateresolve3-2.rs
index f11dc8eb3a0..cda986f21cb 100644
--- a/src/test/auxiliary/crateresolve3-2.rs
+++ b/src/test/auxiliary/crateresolve3-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve3#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve3",
        vers = "0.2")];
 
diff --git a/src/test/auxiliary/crateresolve4a-1.rs b/src/test/auxiliary/crateresolve4a-1.rs
index fcb167de453..eeb946b1da7 100644
--- a/src/test/auxiliary/crateresolve4a-1.rs
+++ b/src/test/auxiliary/crateresolve4a-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve4a#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve4a", vers = "0.1")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/crateresolve4a-2.rs b/src/test/auxiliary/crateresolve4a-2.rs
index 6b933631c19..9c089fd2141 100644
--- a/src/test/auxiliary/crateresolve4a-2.rs
+++ b/src/test/auxiliary/crateresolve4a-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve4a#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve4a", vers= "0.2")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/crateresolve4b-1.rs b/src/test/auxiliary/crateresolve4b-1.rs
index 50de9c89625..9fbfdcf1687 100644
--- a/src/test/auxiliary/crateresolve4b-1.rs
+++ b/src/test/auxiliary/crateresolve4b-1.rs
@@ -10,9 +10,11 @@
 
 // aux-build:crateresolve4a-1.rs
 // aux-build:crateresolve4a-2.rs
+#[pkgid="crateresolve4b#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve4b", vers = "0.1")];
 #[crate_type = "lib"];
 
-extern mod crateresolve4a(vers="0.2");
+extern mod crateresolve4a = "crateresolve4a#0.2";
 
 pub fn f() -> int { crateresolve4a::g() }
diff --git a/src/test/auxiliary/crateresolve4b-2.rs b/src/test/auxiliary/crateresolve4b-2.rs
index af02498ae7c..73484ab2232 100644
--- a/src/test/auxiliary/crateresolve4b-2.rs
+++ b/src/test/auxiliary/crateresolve4b-2.rs
@@ -10,9 +10,11 @@
 
 // aux-build:crateresolve4a-1.rs
 // aux-build:crateresolve4a-2.rs
+#[pkgid="crateresolve4b#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve4b", vers = "0.2")];
 #[crate_type = "lib"];
 
-extern mod crateresolve4a(vers="0.1");
+extern mod crateresolve4a = "crateresolve4a#0.1";
 
 pub fn g() -> int { crateresolve4a::f() }
diff --git a/src/test/auxiliary/crateresolve5-1.rs b/src/test/auxiliary/crateresolve5-1.rs
index f20a143b658..726b3919f7f 100644
--- a/src/test/auxiliary/crateresolve5-1.rs
+++ b/src/test/auxiliary/crateresolve5-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve5#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve5",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/crateresolve5-2.rs b/src/test/auxiliary/crateresolve5-2.rs
index 10adc338159..05a760dea73 100644
--- a/src/test/auxiliary/crateresolve5-2.rs
+++ b/src/test/auxiliary/crateresolve5-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve5#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve5",
        vers = "0.2")];
 
diff --git a/src/test/auxiliary/crateresolve8-1.rs b/src/test/auxiliary/crateresolve8-1.rs
index 22ccde01c4a..b0990d2e303 100644
--- a/src/test/auxiliary/crateresolve8-1.rs
+++ b/src/test/auxiliary/crateresolve8-1.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 // default link meta for 'package_id' will be equal to filestem
+#[pkgid="crateresolve8#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve8",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/crateresolve_calories-1.rs b/src/test/auxiliary/crateresolve_calories-1.rs
index 827517bf276..0f000716e4d 100644
--- a/src/test/auxiliary/crateresolve_calories-1.rs
+++ b/src/test/auxiliary/crateresolve_calories-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve_calories#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve_calories",
        vers = "0.1",
        calories = "100")];
diff --git a/src/test/auxiliary/crateresolve_calories-2.rs b/src/test/auxiliary/crateresolve_calories-2.rs
index 8dc5182c984..4d3ff1e2071 100644
--- a/src/test/auxiliary/crateresolve_calories-2.rs
+++ b/src/test/auxiliary/crateresolve_calories-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="crateresolve_calories#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "crateresolve_calories",
        vers = "0.1",
        calories = "200")];
diff --git a/src/test/auxiliary/extern-crosscrate-source.rs b/src/test/auxiliary/extern-crosscrate-source.rs
index 89317a45171..d6bf2dda9ce 100644
--- a/src/test/auxiliary/extern-crosscrate-source.rs
+++ b/src/test/auxiliary/extern-crosscrate-source.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="externcallback#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "externcallback",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/foreign_lib.rs b/src/test/auxiliary/foreign_lib.rs
index 4705d13cd96..4a92a8dd566 100644
--- a/src/test/auxiliary/foreign_lib.rs
+++ b/src/test/auxiliary/foreign_lib.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="foreign_lib"];
+// NOTE: remove after the next snapshot
 #[link(name="foreign_lib", vers="0.0")];
 
 pub mod rustrt {
diff --git a/src/test/auxiliary/inline_dtor.rs b/src/test/auxiliary/inline_dtor.rs
index 1e93cd63591..67db37234cf 100644
--- a/src/test/auxiliary/inline_dtor.rs
+++ b/src/test/auxiliary/inline_dtor.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="inline_dtor#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name="inline_dtor", vers="0.1")];
 
 pub struct Foo;
diff --git a/src/test/auxiliary/iss.rs b/src/test/auxiliary/iss.rs
index 3855a348f60..83574fb218a 100644
--- a/src/test/auxiliary/iss.rs
+++ b/src/test/auxiliary/iss.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="issue6919_3#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name="iss6919_3", vers="0.1")];
 
 // part of issue-6919.rs
diff --git a/src/test/auxiliary/issue-2380.rs b/src/test/auxiliary/issue-2380.rs
index 1ec88ba5735..a1534aa25e4 100644
--- a/src/test/auxiliary/issue-2380.rs
+++ b/src/test/auxiliary/issue-2380.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 #[feature(managed_boxes)];
+#[pkgid="a"];
+// NOTE: remove after the next snapshot
 #[link(name = "a", vers = "0.0")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue-2414-a.rs b/src/test/auxiliary/issue-2414-a.rs
index 54bb39fd2df..9caf89deb49 100644
--- a/src/test/auxiliary/issue-2414-a.rs
+++ b/src/test/auxiliary/issue-2414-a.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="a#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "a", vers = "0.1")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue-2414-b.rs b/src/test/auxiliary/issue-2414-b.rs
index f4ef02a2b7f..726f8cc29ab 100644
--- a/src/test/auxiliary/issue-2414-b.rs
+++ b/src/test/auxiliary/issue-2414-b.rs
@@ -10,6 +10,8 @@
 
 // xfail-fast
 
+#[pkgid="b#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "b", vers = "0.1")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue-2526.rs b/src/test/auxiliary/issue-2526.rs
index 8ed65628f54..cdbb081f902 100644
--- a/src/test/auxiliary/issue-2526.rs
+++ b/src/test/auxiliary/issue-2526.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="issue_2526#0.2"];
+// NOTE: remove after the next snapshot
 #[link(name = "issue_2526",
        vers = "0.2",
        uuid = "54cc1bc9-02b8-447c-a227-75ebc923bc29")];
diff --git a/src/test/auxiliary/issue-2631-a.rs b/src/test/auxiliary/issue-2631-a.rs
index fe68141e6dd..451fde61ce7 100644
--- a/src/test/auxiliary/issue-2631-a.rs
+++ b/src/test/auxiliary/issue-2631-a.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 #[feature(managed_boxes)];
+#[pkgid="req"];
+// NOTE: remove after the next snapshot
 #[link(name = "req")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue-3012-1.rs b/src/test/auxiliary/issue-3012-1.rs
index f9bbfa7ecce..f206911a7c0 100644
--- a/src/test/auxiliary/issue-3012-1.rs
+++ b/src/test/auxiliary/issue-3012-1.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="socketlib"];
+// NOTE: remove after the next snapshot
 #[link(name="socketlib", vers="0.0")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue-4208-cc.rs b/src/test/auxiliary/issue-4208-cc.rs
index 26db69fb9e1..35b19c6558c 100644
--- a/src/test/auxiliary/issue-4208-cc.rs
+++ b/src/test/auxiliary/issue-4208-cc.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="numeric#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "numeric",
        vers = "0.1")];
 #[crate_type = "lib"];
diff --git a/src/test/auxiliary/issue_2242_a.rs b/src/test/auxiliary/issue_2242_a.rs
index 9f504db8f2a..cfa75be5b0e 100644
--- a/src/test/auxiliary/issue_2242_a.rs
+++ b/src/test/auxiliary/issue_2242_a.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="a#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "a", vers = "0.1")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue_2242_c.rs b/src/test/auxiliary/issue_2242_c.rs
index 40a2bcc114a..331d2535893 100644
--- a/src/test/auxiliary/issue_2242_c.rs
+++ b/src/test/auxiliary/issue_2242_c.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="c#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "c", vers = "0.1")];
 #[crate_type = "lib"];
 
diff --git a/src/test/auxiliary/issue_3979_traits.rs b/src/test/auxiliary/issue_3979_traits.rs
index eb10553f19c..e800e59b997 100644
--- a/src/test/auxiliary/issue_3979_traits.rs
+++ b/src/test/auxiliary/issue_3979_traits.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="issue_3979_traits#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "issue_3979_traits",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/lint_stability.rs b/src/test/auxiliary/lint_stability.rs
index af00a6876c2..398d0268bc5 100644
--- a/src/test/auxiliary/lint_stability.rs
+++ b/src/test/auxiliary/lint_stability.rs
@@ -7,6 +7,8 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+#[pkgid="lint_stability#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "lint_stability",
        vers = "0.1")];
 #[crate_type = "lib"];
diff --git a/src/test/auxiliary/static-function-pointer-aux.rs b/src/test/auxiliary/static-function-pointer-aux.rs
index 85f01666a3f..b80ac427dc2 100644
--- a/src/test/auxiliary/static-function-pointer-aux.rs
+++ b/src/test/auxiliary/static-function-pointer-aux.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="static-function-pointer-aux"];
+
 pub fn f(x: int) -> int { -x }
 
 pub static F: extern fn(int) -> int = f;
diff --git a/src/test/auxiliary/static-methods-crate.rs b/src/test/auxiliary/static-methods-crate.rs
index 6978b9209d8..c6f27ebb971 100644
--- a/src/test/auxiliary/static-methods-crate.rs
+++ b/src/test/auxiliary/static-methods-crate.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="static_methods_crate#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "static_methods_crate",
        vers = "0.1")];
 
diff --git a/src/test/auxiliary/struct_variant_xc_aux.rs b/src/test/auxiliary/struct_variant_xc_aux.rs
index f797669195b..a090b052016 100644
--- a/src/test/auxiliary/struct_variant_xc_aux.rs
+++ b/src/test/auxiliary/struct_variant_xc_aux.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="struct_variant_xc_aux#0.1"];
+// NOTE: remove after the next snapshot
 #[link(name = "struct_variant_xc_aux",
        vers = "0.1")];
 #[crate_type = "lib"];
diff --git a/src/test/auxiliary/trait_default_method_xc_aux.rs b/src/test/auxiliary/trait_default_method_xc_aux.rs
index 123ad24e7a9..a0b50f860dd 100644
--- a/src/test/auxiliary/trait_default_method_xc_aux.rs
+++ b/src/test/auxiliary/trait_default_method_xc_aux.rs
@@ -1,3 +1,4 @@
+#[pkgid="trait_default_method_xc_aux"];
 
 pub struct Something { x: int }
 
diff --git a/src/test/auxiliary/trait_default_method_xc_aux_2.rs b/src/test/auxiliary/trait_default_method_xc_aux_2.rs
index 2d4f539f82b..849709dfd22 100644
--- a/src/test/auxiliary/trait_default_method_xc_aux_2.rs
+++ b/src/test/auxiliary/trait_default_method_xc_aux_2.rs
@@ -1,6 +1,6 @@
 // aux-build:trait_default_method_xc_aux.rs
 
-extern mod aux(name = "trait_default_method_xc_aux");
+extern mod aux = "trait_default_method_xc_aux";
 use aux::A;
 
 pub struct a_struct { x: int }
diff --git a/src/test/compile-fail/crateresolve2.rs b/src/test/compile-fail/crateresolve2.rs
index 130954d3a66..b1565d4b3a1 100644
--- a/src/test/compile-fail/crateresolve2.rs
+++ b/src/test/compile-fail/crateresolve2.rs
@@ -13,10 +13,10 @@
 // aux-build:crateresolve2-3.rs
 // error-pattern:using multiple versions of crate `crateresolve2`
 
-extern mod crateresolve2(vers = "0.1");
+extern mod crateresolve2 = "crateresolve2#0.1";
 
 mod m {
-    pub extern mod crateresolve2(vers = "0.2");
+    pub extern mod crateresolve2 = "crateresolve2#0.2";
 }
 
 fn main() {
diff --git a/src/test/compile-fail/crateresolve5.rs b/src/test/compile-fail/crateresolve5.rs
index 1ad190827da..8bf47d664ed 100644
--- a/src/test/compile-fail/crateresolve5.rs
+++ b/src/test/compile-fail/crateresolve5.rs
@@ -12,8 +12,8 @@
 // aux-build:crateresolve5-1.rs
 // aux-build:crateresolve5-2.rs
 
-extern mod cr5_1 (name = "crateresolve5", vers = "0.1");
-extern mod cr5_2 (name = "crateresolve5", vers = "0.2");
+extern mod cr5_1 = "crateresolve5#0.1";
+extern mod cr5_2 = "crateresolve5#0.2";
 
 
 fn main() {
diff --git a/src/test/compile-fail/dup-link-name.rs b/src/test/compile-fail/dup-link-name.rs
deleted file mode 100644
index f8ccdf118bb..00000000000
--- a/src/test/compile-fail/dup-link-name.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2012 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// error-pattern:duplicate meta item `name`
-
-#[link(name = "test", name)];
-
-fn main() { }
diff --git a/src/test/compile-fail/use-meta-dup.rs b/src/test/compile-fail/use-meta-dup.rs
deleted file mode 100644
index dd57382afbb..00000000000
--- a/src/test/compile-fail/use-meta-dup.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2012 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// error-pattern:duplicate meta item `name`
-
-extern mod extra(name = "extra", name = "nonstd");
-
-fn main() { }
diff --git a/src/test/compile-fail/use-meta-mismatch.rs b/src/test/compile-fail/use-meta-mismatch.rs
index 118fce8a891..dc8250d4134 100644
--- a/src/test/compile-fail/use-meta-mismatch.rs
+++ b/src/test/compile-fail/use-meta-mismatch.rs
@@ -10,6 +10,6 @@
 
 // error-pattern:can't find crate for `extra`
 
-extern mod extra(complex(meta(item)));
+extern mod extra = "fake-crate";
 
 fn main() { }
diff --git a/src/test/compile-fail/use-meta.rc b/src/test/compile-fail/use-meta.rc
index 3be2b03575d..61d18a4a2f7 100644
--- a/src/test/compile-fail/use-meta.rc
+++ b/src/test/compile-fail/use-meta.rc
@@ -10,5 +10,4 @@
 
 // error-pattern:can't find crate for `std`
 
-extern mod std (name = "std",
-         vers = "bogus");
\ No newline at end of file
+extern mod std = "std#bogus";
diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs
index 85941ec74a8..e42382fa84c 100644
--- a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs
+++ b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[link(package_id = "boot", name = "boot", vers = "0.1")];
-#[crate_type = "lib"];
+#[pkgid="boot#0.1"];
+#[crate_type="lib"];
 
 extern mod rustuv; // pull in uvio
 
diff --git a/src/test/run-pass/crateresolve1.rs b/src/test/run-pass/crateresolve1.rs
index 737a60470ad..563a036fecd 100644
--- a/src/test/run-pass/crateresolve1.rs
+++ b/src/test/run-pass/crateresolve1.rs
@@ -13,7 +13,7 @@
 // aux-build:crateresolve1-2.rs
 // aux-build:crateresolve1-3.rs
 
-extern mod crateresolve1(vers = "0.2");
+extern mod crateresolve1 = "crateresolve1#0.2";
 
 pub fn main() {
     assert_eq!(crateresolve1::f(), 20);
diff --git a/src/test/run-pass/crateresolve2.rs b/src/test/run-pass/crateresolve2.rs
index 1ffbf8320e5..d78f8cf66ba 100644
--- a/src/test/run-pass/crateresolve2.rs
+++ b/src/test/run-pass/crateresolve2.rs
@@ -14,17 +14,17 @@
 // aux-build:crateresolve2-3.rs
 
 mod a {
-    extern mod crateresolve2(vers = "0.1");
+    extern mod crateresolve2 = "crateresolve2#0.1";
     pub fn f() { assert!(crateresolve2::f() == 10); }
 }
 
 mod b {
-    extern mod crateresolve2(vers = "0.2");
+    extern mod crateresolve2 = "crateresolve2#0.2";
     pub fn f() { assert!(crateresolve2::f() == 20); }
 }
 
 mod c {
-    extern mod crateresolve2(vers = "0.3");
+    extern mod crateresolve2 = "crateresolve2#0.3";
     pub fn f() { assert!(crateresolve2::f() == 30); }
 }
 
diff --git a/src/test/run-pass/crateresolve3.rs b/src/test/run-pass/crateresolve3.rs
index 519e67acdd6..3114ee0fae0 100644
--- a/src/test/run-pass/crateresolve3.rs
+++ b/src/test/run-pass/crateresolve3.rs
@@ -16,12 +16,12 @@
 // as long as no name collision on invoked functions.
 
 mod a {
-    extern mod crateresolve3(vers = "0.1");
+    extern mod crateresolve3 = "crateresolve3#0.1";
     pub fn f() { assert!(crateresolve3::f() == 10); }
 }
 
 mod b {
-    extern mod crateresolve3(vers = "0.2");
+    extern mod crateresolve3 = "crateresolve3#0.2";
     pub fn f() { assert!(crateresolve3::g() == 20); }
 }
 
diff --git a/src/test/run-pass/crateresolve4.rs b/src/test/run-pass/crateresolve4.rs
index ead2a2131b0..d913d487c0f 100644
--- a/src/test/run-pass/crateresolve4.rs
+++ b/src/test/run-pass/crateresolve4.rs
@@ -15,12 +15,12 @@
 // aux-build:crateresolve4b-2.rs
 
 pub mod a {
-    extern mod crateresolve4b(vers = "0.1");
+    extern mod crateresolve4b = "crateresolve4b#0.1";
     pub fn f() { assert!(crateresolve4b::f() == 20); }
 }
 
 pub mod b {
-    extern mod crateresolve4b(vers = "0.2");
+    extern mod crateresolve4b = "crateresolve4b#0.2";
     pub fn f() { assert!(crateresolve4b::g() == 10); }
 }
 
diff --git a/src/test/run-pass/crateresolve5.rs b/src/test/run-pass/crateresolve5.rs
index 688853dd9ae..00c8cd90f84 100644
--- a/src/test/run-pass/crateresolve5.rs
+++ b/src/test/run-pass/crateresolve5.rs
@@ -12,8 +12,8 @@
 // aux-build:crateresolve5-1.rs
 // aux-build:crateresolve5-2.rs
 
-extern mod cr5_1 (name = "crateresolve5", vers = "0.1");
-extern mod cr5_2 (name = "crateresolve5", vers = "0.2");
+extern mod cr5_1 = "crateresolve5#0.1";
+extern mod cr5_2 = "crateresolve5#0.2";
 
 pub fn main() {
     // Structural types can be used between two versions of the same crate
diff --git a/src/test/run-pass/crateresolve6.rs b/src/test/run-pass/crateresolve6.rs
deleted file mode 100644
index 883f48656bc..00000000000
--- a/src/test/run-pass/crateresolve6.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2012 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// xfail-fast
-// aux-build:crateresolve_calories-1.rs
-// aux-build:crateresolve_calories-2.rs
-// error-pattern:mismatched types
-
-// These both have the same version but differ in other metadata
-extern mod cr6_1 (name = "crateresolve_calories", vers = "0.1", calories="100");
-extern mod cr6_2 (name = "crateresolve_calories", vers = "0.1", calories="200");
-
-pub fn main() {
-    assert_eq!(cr6_1::f(), 100);
-    assert_eq!(cr6_2::f(), 200);
-}
diff --git a/src/test/run-pass/crateresolve7.rs b/src/test/run-pass/crateresolve7.rs
deleted file mode 100644
index 86fc72aa489..00000000000
--- a/src/test/run-pass/crateresolve7.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2012 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// xfail-fast
-// aux-build:crateresolve_calories-1.rs
-// aux-build:crateresolve_calories-2.rs
-// aux-build:crateresolve7x.rs
-
-extern mod crateresolve7x;
-
-pub fn main() {
-    assert_eq!(crateresolve7x::a::f(), 100);
-    assert_eq!(crateresolve7x::b::f(), 200);
-}
diff --git a/src/test/run-pass/crateresolve8.rs b/src/test/run-pass/crateresolve8.rs
index 8ade79a4a84..ad716edd5ef 100644
--- a/src/test/run-pass/crateresolve8.rs
+++ b/src/test/run-pass/crateresolve8.rs
@@ -11,7 +11,9 @@
 // xfail-fast
 // aux-build:crateresolve8-1.rs
 
-extern mod crateresolve8(vers = "0.1", package_id="crateresolve8");
+#[pkgid="crateresolve8#0.1"];
+
+extern mod crateresolve8(vers = "0.1", package_id="crateresolve8#0.1");
 //extern mod crateresolve8(vers = "0.1");
 
 pub fn main() {
diff --git a/src/test/run-pass/issue-1251.rs b/src/test/run-pass/issue-1251.rs
index a78a10e20e8..7fb897f9f20 100644
--- a/src/test/run-pass/issue-1251.rs
+++ b/src/test/run-pass/issue-1251.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[pkgid="rust_get_test_int"];
+// NOTE: remove after the next snapshot
 #[link(name = "rust_get_test_int")];
 
 mod rustrt {
diff --git a/src/test/run-pass/issue-4545.rs b/src/test/run-pass/issue-4545.rs
index 4b13563726e..dfee437ba35 100644
--- a/src/test/run-pass/issue-4545.rs
+++ b/src/test/run-pass/issue-4545.rs
@@ -11,5 +11,5 @@
 // xfail-fast check-fast doesn't like aux-build
 // aux-build:issue-4545.rs
 
-extern mod somelib(name = "issue-4545");
+extern mod somelib = "issue-4545";
 fn main() { somelib::mk::<int>(); }
diff --git a/src/test/run-pass/issue-6919.rs b/src/test/run-pass/issue-6919.rs
index f63811fa82b..041c0609dcd 100644
--- a/src/test/run-pass/issue-6919.rs
+++ b/src/test/run-pass/issue-6919.rs
@@ -11,9 +11,11 @@
 // aux-build:iss.rs
 // xfail-fast
 
-extern mod iss ( name = "iss6919_3" );
+#[pkgid="issue-6919"];
+
+extern mod issue6919_3;
 
 pub fn main() {
-    iss::D.k;
+    issue6919_3::D.k;
 }
 
diff --git a/src/test/run-pass/issue-8044.rs b/src/test/run-pass/issue-8044.rs
index 300f54aa106..15d490e98d6 100644
--- a/src/test/run-pass/issue-8044.rs
+++ b/src/test/run-pass/issue-8044.rs
@@ -11,7 +11,7 @@
 // xfail-fast check-fast doesn't like aux-build
 // aux-build:issue-8044.rs
 
-extern mod minimal(name= "issue-8044");
+extern mod minimal = "issue-8044";
 use minimal::{BTree, leaf};
 
 fn main() {
diff --git a/src/test/run-pass/issue-9906.rs b/src/test/run-pass/issue-9906.rs
index ac15fef3622..e44374cb23a 100644
--- a/src/test/run-pass/issue-9906.rs
+++ b/src/test/run-pass/issue-9906.rs
@@ -11,7 +11,7 @@
 // xfail-fast check-fast doesn't like extern mod
 // aux-build:issue-9906.rs
 
-extern mod testmod(name = "issue-9906");
+extern mod testmod = "issue-9906";
 
 fn main() {
     testmod::foo();
diff --git a/src/test/run-pass/issue-9968.rs b/src/test/run-pass/issue-9968.rs
index ebe268cce1c..4ea5aba91da 100644
--- a/src/test/run-pass/issue-9968.rs
+++ b/src/test/run-pass/issue-9968.rs
@@ -11,7 +11,7 @@
 // xfail-fast check-fast doesn't like extern mod
 // aux-build:issue-9968.rs
 
-extern mod lib(name = "issue-9968");
+extern mod lib = "issue-9968";
 
 use lib::{Trait, Struct};
 
diff --git a/src/test/run-pass/item-attributes.rs b/src/test/run-pass/item-attributes.rs
index 310dbea50d1..99ada9cc650 100644
--- a/src/test/run-pass/item-attributes.rs
+++ b/src/test/run-pass/item-attributes.rs
@@ -16,6 +16,8 @@
 #[attr3];
 #[attr4(attr5)];
 
+#[pkgid="extra#0.1"];
+// NOTE: remove after the next snapshot
 // Special linkage attributes for the crate
 #[link(name = "extra",
        vers = "0.1",
diff --git a/src/test/run-pass/linkage-visibility.rs b/src/test/run-pass/linkage-visibility.rs
index 645be40250a..438fd21e810 100644
--- a/src/test/run-pass/linkage-visibility.rs
+++ b/src/test/run-pass/linkage-visibility.rs
@@ -13,7 +13,7 @@
 // xfail-android: FIXME(#10379)
 // xfail-win32: std::unstable::dynamic_lib does not work on win32 well
 
-extern mod foo(name = "linkage-visibility");
+extern mod foo = "linkage-visibility";
 
 fn main() {
     foo::test();
diff --git a/src/test/run-pass/priv-impl-prim-ty.rs b/src/test/run-pass/priv-impl-prim-ty.rs
index 4439da4f6f5..0c722e1ede5 100644
--- a/src/test/run-pass/priv-impl-prim-ty.rs
+++ b/src/test/run-pass/priv-impl-prim-ty.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:priv-impl-prim-ty.rs
 
-extern mod bar(name = "priv-impl-prim-ty");
+extern mod bar = "priv-impl-prim-ty";
 
 fn main() {
     bar::frob(1i);
diff --git a/src/test/run-pass/reexport-should-still-link.rs b/src/test/run-pass/reexport-should-still-link.rs
index f2d90a2374a..fcfcc30c988 100644
--- a/src/test/run-pass/reexport-should-still-link.rs
+++ b/src/test/run-pass/reexport-should-still-link.rs
@@ -11,7 +11,7 @@
 // aux-build:reexport-should-still-link.rs
 // xfail-fast check-fast doesn't like extern mod
 
-extern mod foo(name = "reexport-should-still-link");
+extern mod foo = "reexport-should-still-link";
 
 fn main() {
     foo::bar();
diff --git a/src/test/run-pass/static-fn-inline-xc.rs b/src/test/run-pass/static-fn-inline-xc.rs
index e3441661d5b..e772488c872 100644
--- a/src/test/run-pass/static-fn-inline-xc.rs
+++ b/src/test/run-pass/static-fn-inline-xc.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:static_fn_inline_xc_aux.rs
 
-extern mod mycore(name ="static_fn_inline_xc_aux");
+extern mod mycore = "static_fn_inline_xc_aux";
 
 use mycore::num;
 
diff --git a/src/test/run-pass/static-fn-trait-xc.rs b/src/test/run-pass/static-fn-trait-xc.rs
index 41665871451..ef40d2789e4 100644
--- a/src/test/run-pass/static-fn-trait-xc.rs
+++ b/src/test/run-pass/static-fn-trait-xc.rs
@@ -1,7 +1,7 @@
 // aux-build:static_fn_trait_xc_aux.rs
 // xfail-fast
 
-extern mod mycore(name ="static_fn_trait_xc_aux");
+extern mod mycore = "static_fn_trait_xc_aux";
 
 use mycore::num;
 
diff --git a/src/test/run-pass/static-function-pointer-xc.rs b/src/test/run-pass/static-function-pointer-xc.rs
index 0ba47320a67..61790e93584 100644
--- a/src/test/run-pass/static-function-pointer-xc.rs
+++ b/src/test/run-pass/static-function-pointer-xc.rs
@@ -10,7 +10,7 @@
 
 // xfail-fast
 // aux-build:static-function-pointer-aux.rs
-extern mod aux(name = "static-function-pointer-aux");
+extern mod aux = "static-function-pointer-aux";
 
 fn f(x: int) -> int { x }
 
diff --git a/src/test/run-pass/trait-default-method-xc-2.rs b/src/test/run-pass/trait-default-method-xc-2.rs
index 1dad5d23b88..171971e73e0 100644
--- a/src/test/run-pass/trait-default-method-xc-2.rs
+++ b/src/test/run-pass/trait-default-method-xc-2.rs
@@ -3,8 +3,8 @@
 // aux-build:trait_default_method_xc_aux_2.rs
 
 
-extern mod aux(name = "trait_default_method_xc_aux");
-extern mod aux2(name = "trait_default_method_xc_aux_2");
+extern mod aux = "trait_default_method_xc_aux";
+extern mod aux2 = "trait_default_method_xc_aux_2";
 use aux::A;
 use aux2::{a_struct, welp};
 
diff --git a/src/test/run-pass/trait-default-method-xc.rs b/src/test/run-pass/trait-default-method-xc.rs
index fe0194d6f8c..9813864724c 100644
--- a/src/test/run-pass/trait-default-method-xc.rs
+++ b/src/test/run-pass/trait-default-method-xc.rs
@@ -4,7 +4,7 @@
 // aux-build:trait_default_method_xc_aux.rs
 
 
-extern mod aux(name = "trait_default_method_xc_aux");
+extern mod aux = "trait_default_method_xc_aux";
 use aux::{A, TestEquality, Something};
 use aux::B;
 
diff --git a/src/test/run-pass/trait-inheritance-auto-xc-2.rs b/src/test/run-pass/trait-inheritance-auto-xc-2.rs
index 3f8d5368884..6793db96509 100644
--- a/src/test/run-pass/trait-inheritance-auto-xc-2.rs
+++ b/src/test/run-pass/trait-inheritance-auto-xc-2.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:trait_inheritance_auto_xc_2_aux.rs
 
-extern mod aux(name = "trait_inheritance_auto_xc_2_aux");
+extern mod aux = "trait_inheritance_auto_xc_2_aux";
 
 // aux defines impls of Foo, Bar and Baz for A
 use aux::{Foo, Bar, Baz, A};
diff --git a/src/test/run-pass/trait-inheritance-auto-xc.rs b/src/test/run-pass/trait-inheritance-auto-xc.rs
index 2e8883f0267..d4f718608a9 100644
--- a/src/test/run-pass/trait-inheritance-auto-xc.rs
+++ b/src/test/run-pass/trait-inheritance-auto-xc.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:trait_inheritance_auto_xc_aux.rs
 
-extern mod aux(name = "trait_inheritance_auto_xc_aux");
+extern mod aux = "trait_inheritance_auto_xc_aux";
 
 use aux::{Foo, Bar, Baz, Quux};
 
diff --git a/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs
index eddec87472c..0cdc1d0e27f 100644
--- a/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs
+++ b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:trait_inheritance_cross_trait_call_xc_aux.rs
 
-extern mod aux(name = "trait_inheritance_cross_trait_call_xc_aux");
+extern mod aux = "trait_inheritance_cross_trait_call_xc_aux";
 
 use aux::Foo;
 
diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs
index 1999043c6cd..a500e167a8b 100644
--- a/src/test/run-pass/typeid-intrinsic.rs
+++ b/src/test/run-pass/typeid-intrinsic.rs
@@ -12,8 +12,8 @@
 // aux-build:typeid-intrinsic.rs
 // aux-build:typeid-intrinsic2.rs
 
-extern mod other1(name = "typeid-intrinsic");
-extern mod other2(name = "typeid-intrinsic2");
+extern mod other1 = "typeid-intrinsic";
+extern mod other2 = "typeid-intrinsic2";
 
 use std::unstable::intrinsics;
 use std::unstable::intrinsics::TypeId;
diff --git a/src/test/run-pass/use-crate-name-alias.rs b/src/test/run-pass/use-crate-name-alias.rs
index 4954de3919f..d9f40e5f1f3 100644
--- a/src/test/run-pass/use-crate-name-alias.rs
+++ b/src/test/run-pass/use-crate-name-alias.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 // Issue #1706
-extern mod stdlib(name="extra");
+extern mod stdlib = "extra";
 
 pub fn main() {}
diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs
index 8ae8dafc23e..ddd4b10fd5c 100644
--- a/src/test/run-pass/use.rs
+++ b/src/test/run-pass/use.rs
@@ -14,8 +14,8 @@
 
 #[no_std];
 extern mod std;
-extern mod zed(name = "std");
-extern mod bar(name = "std", vers = "0.9-pre");
+extern mod zed = "std";
+extern mod bar = "std#0.9-pre";
 
 
 use std::str;
diff --git a/src/test/run-pass/xcrate-address-insignificant.rs b/src/test/run-pass/xcrate-address-insignificant.rs
index 91b8c99ca19..8a5a4a0cfe5 100644
--- a/src/test/run-pass/xcrate-address-insignificant.rs
+++ b/src/test/run-pass/xcrate-address-insignificant.rs
@@ -11,7 +11,7 @@
 // xfail-fast check-fast doesn't like aux-build
 // aux-build:xcrate_address_insignificant.rs
 
-extern mod foo(name = "xcrate_address_insignificant");
+extern mod foo = "xcrate_address_insignificant";
 
 fn main() {
     assert_eq!(foo::foo::<f64>(), foo::bar());
diff --git a/src/test/run-pass/xcrate-trait-lifetime-param.rs b/src/test/run-pass/xcrate-trait-lifetime-param.rs
index 51ba05f1faf..941efdb24a8 100644
--- a/src/test/run-pass/xcrate-trait-lifetime-param.rs
+++ b/src/test/run-pass/xcrate-trait-lifetime-param.rs
@@ -11,7 +11,7 @@
 // xfail-fast
 // aux-build:xcrate-trait-lifetime-param.rs
 
-extern mod other(name = "xcrate-trait-lifetime-param");
+extern mod other = "xcrate-trait-lifetime-param";
 
 struct Reader<'a> {
     b : &'a [u8]

From a16753c18844ad37d63ee00fe0292303afc990ac Mon Sep 17 00:00:00 2001
From: Jack Moffitt <jack@metajack.im>
Date: Tue, 10 Dec 2013 17:04:17 -0700
Subject: [PATCH 2/2] Add missing sundown dependency to rustdoc tests.

---
 mk/tests.mk | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mk/tests.mk b/mk/tests.mk
index 6776345ec2b..97c47178feb 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -394,7 +394,8 @@ $(3)/stage$(1)/test/rustdoctest-$(2)$$(X_$(2)):					\
 		$$(RUSTDOC_LIB) $$(RUSTDOC_INPUTS)		\
 		$$(SREQ$(1)_T_$(2)_H_$(3)) \
 		$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX_$(2)) \
-		$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2))
+		$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2)) \
+		$$(SUNDOWN_LIB_$(2))
 	@$$(call E, compile_and_link: $$@)
 	$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test \
 		-L $$(SUNDOWN_DIR_$(2))