about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/rust.md30
-rw-r--r--mk/install.mk2
-rw-r--r--mk/tests.mk6
-rw-r--r--src/libextra/time.rs3
-rw-r--r--src/librustc/back/link.rs23
-rw-r--r--src/librustc/back/rpath.rs126
-rw-r--r--src/librustc/driver/driver.rs19
-rw-r--r--src/librustc/front/std_inject.rs2
-rw-r--r--src/librustc/front/test.rs2
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/metadata/creader.rs45
-rw-r--r--src/librustc/metadata/decoder.rs12
-rw-r--r--src/librustc/metadata/encoder.rs43
-rw-r--r--src/librustc/metadata/filesearch.rs151
-rw-r--r--src/librustc/metadata/loader.rs78
-rw-r--r--src/librustc/middle/resolve.rs11
-rw-r--r--src/librustc/middle/trans/_match.rs139
-rw-r--r--src/librustc/rustc.rs11
-rw-r--r--src/librustpkg/api.rs24
-rw-r--r--src/librustpkg/context.rs24
-rw-r--r--src/librustpkg/installed_packages.rs42
-rw-r--r--src/librustpkg/package_id.rs76
-rw-r--r--src/librustpkg/package_path.rs68
-rw-r--r--src/librustpkg/package_source.rs59
-rw-r--r--src/librustpkg/path_util.rs137
-rw-r--r--src/librustpkg/rustpkg.rs89
-rw-r--r--src/librustpkg/source_control.rs37
-rw-r--r--src/librustpkg/tests.rs342
-rw-r--r--src/librustpkg/util.rs106
-rw-r--r--src/librustpkg/version.rs32
-rw-r--r--src/librustpkg/workspace.rs8
-rw-r--r--src/libstd/bool.rs6
-rw-r--r--src/libstd/char.rs6
-rw-r--r--src/libstd/cmp.rs9
-rw-r--r--src/libstd/nil.rs6
-rw-r--r--src/libstd/num/int_macros.rs6
-rw-r--r--src/libstd/num/uint_macros.rs6
-rw-r--r--src/libstd/os.rs4
-rw-r--r--src/libstd/path.rs165
-rw-r--r--src/libstd/rt/comm.rs4
-rw-r--r--src/libstd/rt/io/net/ip.rs374
-rw-r--r--src/libstd/rt/local_heap.rs22
-rw-r--r--src/libstd/rt/mod.rs51
-rw-r--r--src/libstd/rt/uv/net.rs72
-rw-r--r--src/libstd/run.rs8
-rw-r--r--src/libstd/str.rs18
-rw-r--r--src/libstd/sys.rs4
-rw-r--r--src/libstd/task/mod.rs15
-rw-r--r--src/libstd/task/spawn.rs17
-rw-r--r--src/libstd/unstable/lang.rs17
-rw-r--r--src/libstd/unstable/sync.rs26
-rw-r--r--src/libsyntax/ast.rs6
-rw-r--r--src/libsyntax/ast_util.rs2
-rw-r--r--src/libsyntax/ext/asm.rs4
-rw-r--r--src/libsyntax/ext/base.rs14
-rw-r--r--src/libsyntax/ext/env.rs32
-rw-r--r--src/libsyntax/ext/fmt.rs4
-rw-r--r--src/libsyntax/parse/parser.rs23
-rw-r--r--src/libsyntax/print/pprust.rs6
-rw-r--r--src/test/compile-fail/extenv-arg-2-not-string-literal.rs (renamed from src/test/run-pass/extern-mod-url.rs)7
-rw-r--r--src/test/compile-fail/extenv-no-args.rs6
-rw-r--r--src/test/compile-fail/extenv-not-defined-custom.rs11
-rw-r--r--src/test/compile-fail/extenv-not-defined-default.rs11
-rw-r--r--src/test/compile-fail/extenv-not-string-literal.rs6
-rw-r--r--src/test/compile-fail/extenv-too-many-args.rs4
-rw-r--r--src/test/compile-fail/extoption_env-no-args.rs11
-rw-r--r--src/test/compile-fail/extoption_env-not-string-literal.rs11
-rw-r--r--src/test/compile-fail/extoption_env-too-many-args.rs11
-rw-r--r--src/test/run-pass/extoption_env-not-defined.rs14
-rw-r--r--src/test/run-pass/issue-5530.rs2
-rw-r--r--src/test/run-pass/match-enum-struct-0.rs24
-rw-r--r--src/test/run-pass/match-enum-struct-1.rs26
-rw-r--r--src/test/run-pass/match-struct-0.rs29
73 files changed, 1740 insertions, 1109 deletions
diff --git a/doc/rust.md b/doc/rust.md
index 2f5c310ec83..d285253ffe2 100644
--- a/doc/rust.md
+++ b/doc/rust.md
@@ -744,7 +744,7 @@ There are several kinds of view item:
 ##### Extern mod declarations
 
 ~~~~~~~~ {.ebnf .gram}
-extern_mod_decl : "extern" "mod" ident [ '(' link_attrs ')' ] ? ;
+extern_mod_decl : "extern" "mod" ident [ '(' link_attrs ')' ] ? [ '=' string_lit ] ? ;
 link_attrs : link_attr [ ',' link_attrs ] + ;
 link_attr : ident '=' literal ;
 ~~~~~~~~
@@ -755,13 +755,25 @@ as the `ident` provided in the `extern_mod_decl`.
 
 The external crate is resolved to a specific `soname` at compile time,
 and a runtime linkage requirement to that `soname` is passed to the linker for
-loading at runtime. The `soname` is resolved at compile time by scanning the
-compiler's library path and matching the `link_attrs` provided in the
-`use_decl` against any `#link` attributes that were declared on the external
-crate when it was compiled. If no `link_attrs` are provided, a default `name`
-attribute is assumed, equal to the `ident` given in the `use_decl`.
-
-Three examples of `extern mod` declarations:
+loading at runtime.
+The `soname` is resolved at compile time by scanning the compiler's library path
+and matching the `link_attrs` provided in the `use_decl` against any `#link` attributes that
+were declared on the external crate when it was compiled.
+If no `link_attrs` are provided,
+a default `name` attribute is assumed,
+equal to the `ident` given in the `use_decl`.
+
+Optionally, an identifier in an `extern mod` declaration may be followed by an equals sign,
+then a string literal denoting a relative path on the filesystem.
+This path should exist in one of the directories in the Rust path,
+which by default contains the `.rust` subdirectory of the current directory and each of its parents,
+as well as any directories in the colon-separated (or semicolon-separated on Windows)
+list of paths that is the `RUST_PATH` environment variable.
+The meaning of `extern mod a = "b/c/d";`, supposing that `/a` is in the RUST_PATH,
+is that the name `a` should be taken as a reference to the crate whose absolute location is
+`/a/b/c/d`.
+
+Four examples of `extern mod` declarations:
 
 ~~~~~~~~{.xfail-test}
 extern mod pcre (uuid = "54aba0f8-a7b1-4beb-92f1-4cf625264841");
@@ -769,6 +781,8 @@ extern mod pcre (uuid = "54aba0f8-a7b1-4beb-92f1-4cf625264841");
 extern mod extra; // equivalent to: extern mod extra ( name = "extra" );
 
 extern mod rustextra (name = "extra"); // linking to 'extra' under another name
+
+extern mod complicated_mod = "some-file/in/the-rust/path";
 ~~~~~~~~
 
 ##### Use declarations
diff --git a/mk/install.mk b/mk/install.mk
index 07cb21217ae..4b50c5aa796 100644
--- a/mk/install.mk
+++ b/mk/install.mk
@@ -199,7 +199,7 @@ endef
 $(foreach target,$(CFG_TARGET_TRIPLES), \
   $(if $(findstring $(target),"arm-linux-androideabi"), \
     $(if $(findstring adb,$(CFG_ADB)), \
-      $(if $(findstring device,$(shell adb devices 2>/dev/null | grep -E '^[_A-Za-z0-9-]+[[:blank:]]+device')), \
+      $(if $(findstring device,$(shell $(CFG_ADB) devices 2>/dev/null | grep -E '^[_A-Za-z0-9-]+[[:blank:]]+device')), \
         $(info install: install-runtime-target for $(target) enabled \
           $(info install: android device attached) \
           $(eval $(call DEF_ADB_DEVICE_STATUS, true))), \
diff --git a/mk/tests.mk b/mk/tests.mk
index 682ea3e5f69..349ffc63d97 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -123,7 +123,7 @@ endef
 $(foreach target,$(CFG_TARGET_TRIPLES), \
   $(if $(findstring $(target),"arm-linux-androideabi"), \
     $(if $(findstring adb,$(CFG_ADB)), \
-      $(if $(findstring device,$(shell adb devices 2>/dev/null | grep -E '^[_A-Za-z0-9-]+[[:blank:]]+device')), \
+      $(if $(findstring device,$(shell $(CFG_ADB) devices 2>/dev/null | grep -E '^[_A-Za-z0-9-]+[[:blank:]]+device')), \
         $(info check: $(target) test enabled \
           $(info check: android device attached) \
           $(eval $(call DEF_ADB_DEVICE_STATUS, true))), \
@@ -348,7 +348,9 @@ $(3)/stage$(1)/test/rustpkgtest-$(2)$$(X_$(2)):					\
 		$$(RUSTPKG_LIB) $$(RUSTPKG_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)) \
+		$$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$$(X_$(2)) \
+		$$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(2))
 	@$$(call E, compile_and_link: $$@)
 	$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
 
diff --git a/src/libextra/time.rs b/src/libextra/time.rs
index b7033196b8a..f79e01b6f28 100644
--- a/src/libextra/time.rs
+++ b/src/libextra/time.rs
@@ -57,9 +57,6 @@ impl Ord for Timespec {
         self.sec < other.sec ||
             (self.sec == other.sec && self.nsec < other.nsec)
     }
-    fn le(&self, other: &Timespec) -> bool { !other.lt(self) }
-    fn ge(&self, other: &Timespec) -> bool { !self.lt(other) }
-    fn gt(&self, other: &Timespec) -> bool { !self.le(other) }
 }
 
 /**
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 3dc02db8e41..fbe17fb0d1c 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -16,7 +16,7 @@ use lib::llvm::llvm;
 use lib::llvm::ModuleRef;
 use lib;
 use metadata::common::LinkMeta;
-use metadata::{encoder, csearch, cstore};
+use metadata::{encoder, csearch, cstore, filesearch};
 use middle::trans::context::CrateContext;
 use middle::trans::common::gensym_name;
 use middle::ty;
@@ -500,6 +500,7 @@ pub fn build_link_meta(sess: Session,
     struct ProvidedMetas {
         name: Option<@str>,
         vers: Option<@str>,
+        pkg_id: Option<@str>,
         cmh_items: ~[@ast::MetaItem]
     }
 
@@ -507,6 +508,7 @@ pub fn build_link_meta(sess: Session,
        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);
@@ -514,6 +516,7 @@ pub fn build_link_meta(sess: Session,
             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)
             }
         }
@@ -521,6 +524,7 @@ pub fn build_link_meta(sess: Session,
         ProvidedMetas {
             name: name,
             vers: vers,
+            pkg_id: pkg_id,
             cmh_items: cmh_items
         }
     }
@@ -528,7 +532,8 @@ pub fn build_link_meta(sess: Session,
     // This calculates CMH as defined above
     fn crate_meta_extras_hash(symbol_hasher: &mut hash::State,
                               cmh_items: ~[@ast::MetaItem],
-                              dep_hashes: ~[@str]) -> @str {
+                              dep_hashes: ~[@str],
+                              pkg_id: Option<@str>) -> @str {
         fn len_and_str(s: &str) -> ~str {
             fmt!("%u_%s", s.len(), s)
         }
@@ -566,7 +571,10 @@ pub fn build_link_meta(sess: Session,
             write_string(symbol_hasher, len_and_str(*dh));
         }
 
-    // tjc: allocation is unfortunate; need to change std::hash
+        for p in pkg_id.iter() {
+            write_string(symbol_hasher, len_and_str(*p));
+        }
+
         return truncated_hash_result(symbol_hasher).to_managed();
     }
 
@@ -608,6 +616,7 @@ pub fn build_link_meta(sess: Session,
     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);
@@ -615,11 +624,12 @@ pub fn build_link_meta(sess: Session,
     let dep_hashes = cstore::get_dep_hashes(sess.cstore);
     let extras_hash =
         crate_meta_extras_hash(symbol_hasher, cmh_items,
-                               dep_hashes);
+                               dep_hashes, opt_pkg_id);
 
     LinkMeta {
         name: name,
         vers: vers,
+        package_id: opt_pkg_id,
         extras_hash: extras_hash
     }
 }
@@ -942,6 +952,11 @@ pub fn link_args(sess: Session,
         args.push(~"-L" + path.to_str());
     }
 
+    let rustpath = filesearch::rust_path();
+    for path in rustpath.iter() {
+        args.push(~"-L" + path.to_str());
+    }
+
     // The names of the extern libraries
     let used_libs = cstore::get_used_libraries(cstore);
     for l in used_libs.iter() { args.push(~"-l" + *l); }
diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs
index 571721ed40c..5dc92dbc5e6 100644
--- a/src/librustc/back/rpath.rs
+++ b/src/librustc/back/rpath.rs
@@ -14,10 +14,7 @@ use metadata::cstore;
 use metadata::filesearch;
 
 use std::hashmap::HashSet;
-use std::num;
-use std::os;
-use std::util;
-use std::vec;
+use std::{num, os, path, uint, util, vec};
 
 fn not_win32(os: session::os) -> bool {
   os != session::os_win32
@@ -122,42 +119,7 @@ pub fn get_rpath_relative_to_output(os: session::os,
         session::os_win32 => util::unreachable()
     };
 
-    Path(prefix).push_rel(&get_relative_to(&os::make_absolute(output),
-                                           &os::make_absolute(lib)))
-}
-
-// Find the relative path from one file to another
-pub fn get_relative_to(abs1: &Path, abs2: &Path) -> Path {
-    assert!(abs1.is_absolute);
-    assert!(abs2.is_absolute);
-    let abs1 = abs1.normalize();
-    let abs2 = abs2.normalize();
-    debug!("finding relative path from %s to %s",
-           abs1.to_str(), abs2.to_str());
-    let split1: &[~str] = abs1.components;
-    let split2: &[~str] = abs2.components;
-    let len1 = split1.len();
-    let len2 = split2.len();
-    assert!(len1 > 0);
-    assert!(len2 > 0);
-
-    let max_common_path = num::min(len1, len2) - 1;
-    let mut start_idx = 0;
-    while start_idx < max_common_path
-        && split1[start_idx] == split2[start_idx] {
-        start_idx += 1;
-    }
-
-    let mut path = ~[];
-    for _ in range(start_idx, len1 - 1) { path.push(~".."); };
-
-    path.push_all(split2.slice(start_idx, len2 - 1));
-
-    return if !path.is_empty() {
-        Path("").push_many(path)
-    } else {
-        Path(".")
-    }
+    Path(prefix).push_rel(&os::make_absolute(output).get_relative_to(&os::make_absolute(lib)))
 }
 
 fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] {
@@ -168,6 +130,7 @@ pub fn get_absolute_rpath(lib: &Path) -> Path {
     os::make_absolute(lib).dir_path()
 }
 
+#[cfg(stage0)]
 pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
     let install_prefix = env!("CFG_PREFIX");
 
@@ -179,6 +142,14 @@ pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
     os::make_absolute(&Path(install_prefix).push_rel(&tlib))
 }
 
+#[cfg(not(stage0))]
+pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
+    let install_prefix = env!("CFG_PREFIX");
+
+    let tlib = filesearch::relative_target_lib_path(target_triple);
+    os::make_absolute(&Path(install_prefix).push_rel(&tlib))
+}
+
 pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] {
     let mut set = HashSet::new();
     let mut minimized = ~[];
@@ -199,8 +170,7 @@ mod test {
     #[cfg(test)]
     #[cfg(test)]
     use back::rpath::{get_absolute_rpath, get_install_prefix_rpath};
-    use back::rpath::{get_relative_to, get_rpath_relative_to_output};
-    use back::rpath::{minimize_rpaths, rpaths_to_flags};
+    use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
     use driver::session;
 
     #[test]
@@ -245,77 +215,8 @@ mod test {
     }
 
     #[test]
-    fn test_relative_to1() {
-        let p1 = Path("/usr/bin/rustc");
-        let p2 = Path("/usr/lib/mylib");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("../lib"));
-    }
-
-    #[test]
-    fn test_relative_to2() {
-        let p1 = Path("/usr/bin/rustc");
-        let p2 = Path("/usr/bin/../lib/mylib");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("../lib"));
-    }
-
-    #[test]
-    fn test_relative_to3() {
-        let p1 = Path("/usr/bin/whatever/rustc");
-        let p2 = Path("/usr/lib/whatever/mylib");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("../../lib/whatever"));
-    }
-
-    #[test]
-    fn test_relative_to4() {
-        let p1 = Path("/usr/bin/whatever/../rustc");
-        let p2 = Path("/usr/lib/whatever/mylib");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("../lib/whatever"));
-    }
-
-    #[test]
-    fn test_relative_to5() {
-        let p1 = Path("/usr/bin/whatever/../rustc");
-        let p2 = Path("/usr/lib/whatever/../mylib");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("../lib"));
-    }
-
-    #[test]
-    fn test_relative_to6() {
-        let p1 = Path("/1");
-        let p2 = Path("/2/3");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path("2"));
-    }
-
-    #[test]
-    fn test_relative_to7() {
-        let p1 = Path("/1/2");
-        let p2 = Path("/3");
-        let res = get_relative_to(&p1, &p2);
-        assert_eq!(res, Path(".."));
-    }
-
-    #[test]
-    fn test_relative_to8() {
-        let p1 = Path("/home/brian/Dev/rust/build/").push_rel(
-            &Path("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
-        let p2 = Path("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
-            &Path("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
-        let res = get_relative_to(&p1, &p2);
-        debug!("test_relative_tu8: %s vs. %s",
-               res.to_str(),
-               Path(".").to_str());
-        assert_eq!(res, Path("."));
-    }
-
-    #[test]
     #[cfg(target_os = "linux")]
-    #[cfg(target_os = "andorid")]
+    #[cfg(target_os = "android")]
     fn test_rpath_relative() {
       let o = session::os_linux;
       let res = get_rpath_relative_to_output(o,
@@ -335,7 +236,6 @@ mod test {
     #[test]
     #[cfg(target_os = "macos")]
     fn test_rpath_relative() {
-        // this is why refinements would be nice
         let o = session::os_macos;
         let res = get_rpath_relative_to_output(o,
                                                &Path("bin/rustc"),
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 61ab826e9ee..e349502d143 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -578,6 +578,7 @@ pub fn build_target_config(sopts: @session::options,
     return target_cfg;
 }
 
+#[cfg(stage0)]
 pub fn host_triple() -> ~str {
     // Get the host triple out of the build environment. This ensures that our
     // idea of the host triple is the same as for the set of libraries we've
@@ -595,6 +596,19 @@ pub fn host_triple() -> ~str {
         };
 }
 
+#[cfg(not(stage0))]
+pub fn host_triple() -> ~str {
+    // Get the host triple out of the build environment. This ensures that our
+    // idea of the host triple is the same as for the set of libraries we've
+    // actually built.  We can't just take LLVM's host triple because they
+    // normalize all ix86 architectures to i386.
+    //
+    // Instead of grabbing the host triple (for the current host), we grab (at
+    // compile time) the target triple that this rustc is built with and
+    // calling that (at runtime) the host triple.
+    (env!("CFG_COMPILER_TRIPLE")).to_owned()
+}
+
 pub fn build_session_options(binary: @str,
                              matches: &getopts::Matches,
                              demitter: diagnostic::Emitter)
@@ -821,7 +835,8 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] {
   optmulti("", "cfg", "Configure the compilation
                           environment", "SPEC"),
   optflag("",  "emit-llvm",
-                        "Produce an LLVM bitcode file"),
+                        "Produce an LLVM assembly file if used with -S option;
+                         produce an LLVM bitcode file otherwise"),
   optflag("h", "help","Display this message"),
   optmulti("L", "",   "Add a directory to the library search path",
                               "PATH"),
diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs
index 3a1129b1dd9..2a61ea28e0c 100644
--- a/src/librustc/front/std_inject.rs
+++ b/src/librustc/front/std_inject.rs
@@ -47,7 +47,7 @@ fn inject_libstd_ref(sess: Session, crate: &ast::Crate) -> @ast::Crate {
             let n1 = sess.next_node_id();
             let vi1 = ast::view_item {
                 node: ast::view_item_extern_mod(
-                        sess.ident_of("std"), ~[], n1),
+                        sess.ident_of("std"), None, ~[], n1),
                 attrs: ~[
                     attr::mk_attr(
                         attr::mk_name_value_item_str(@"vers", STD_VERSION.to_managed()))
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index d2d2a8b4be9..c6b1bdbe51b 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -282,7 +282,7 @@ fn mk_std(cx: &TestCtxt) -> ast::view_item {
                                             cx.sess.next_node_id()))])
     } else {
         let mi = attr::mk_name_value_item_str(@"vers", @"0.8-pre");
-        ast::view_item_extern_mod(id_extra, ~[mi], cx.sess.next_node_id())
+        ast::view_item_extern_mod(id_extra, None, ~[mi], cx.sess.next_node_id())
     };
     ast::view_item {
         node: vi,
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 1c5d202d4d9..f2d8b68faa6 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -185,5 +185,7 @@ pub static tag_item_impl_vtables: uint = 0x82;
 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
 }
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index d8f14228824..0a9e8490f22 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -18,6 +18,7 @@ use metadata::loader;
 
 use std::hashmap::HashMap;
 use syntax::ast;
+use std::vec;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::{span, dummy_sp};
@@ -137,18 +138,33 @@ 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, ref meta_items, id) => {
-        debug!("resolving extern mod stmt. ident: %?, meta: %?",
-               ident, *meta_items);
-        let cnum = resolve_crate(e,
-                                 ident,
-                                 (*meta_items).clone(),
-                                 @"",
-                                 i.span);
-        cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
+      ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
+          let ident = token::ident_to_str(&ident);
+          let meta_items = match path_opt {
+              None => meta_items.clone(),
+              Some(p) => {
+                  let p_path = Path(p);
+                  match p_path.filestem() {
+                      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),
+                      None => e.diag.span_bug(i.span, "Bad package path in `extern mod` item")
+                  }
+            }
+          };
+          debug!("resolving extern mod stmt. ident: %?, meta: %?",
+                 ident, meta_items);
+          let cnum = resolve_crate(e,
+                                   ident,
+                                   meta_items,
+                                   @"",
+                                   i.span);
+          cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
       }
       _ => ()
-    }
+  }
 }
 
 fn visit_item(e: &Env, i: @ast::item) {
@@ -233,12 +249,12 @@ fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
 }
 
 fn resolve_crate(e: @mut Env,
-                 ident: ast::ident,
+                 ident: @str,
                  metas: ~[@ast::MetaItem],
                  hash: @str,
                  span: span)
               -> ast::CrateNum {
-    let metas = metas_with_ident(token::ident_to_str(&ident), metas);
+    let metas = metas_with_ident(ident, metas);
 
     match existing_match(e, metas, hash) {
       None => {
@@ -279,7 +295,7 @@ fn resolve_crate(e: @mut Env,
             match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
                                                          "name") {
                 Some(v) => v,
-                None => token::ident_to_str(&ident),
+                None => ident
             };
         let cmeta = @cstore::crate_metadata {
             name: cname,
@@ -308,7 +324,6 @@ fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
     let r = decoder::get_crate_deps(cdata);
     for dep in r.iter() {
         let extrn_cnum = dep.cnum;
-        let cname = dep.name;
         let cname_str = token::ident_to_str(&dep.name);
         let cmetas = metas_with(dep.vers, @"vers", ~[]);
         debug!("resolving dep crate %s ver: %s hash: %s",
@@ -327,7 +342,7 @@ 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, cmetas, dep.hash,
+            let local_cnum = resolve_crate(e, cname_str, cmetas, 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 8d357126018..81a2e863bde 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -51,20 +51,20 @@ type cmd = @crate_metadata;
 // what crate that's in and give us a def_id that makes sense for the current
 // build.
 
-fn lookup_hash(d: ebml::Doc, eq_fn: &fn(x:&[u8]) -> bool, hash: uint) ->
+fn lookup_hash(d: ebml::Doc, eq_fn: &fn(x:&[u8]) -> bool, hash: u64) ->
    Option<ebml::Doc> {
     let index = reader::get_doc(d, tag_index);
     let table = reader::get_doc(index, tag_index_table);
-    let hash_pos = table.start + hash % 256u * 4u;
-    let pos = io::u64_from_be_bytes(*d.data, hash_pos, 4u) as uint;
+    let hash_pos = table.start + (hash % 256 * 4) as uint;
+    let pos = io::u64_from_be_bytes(*d.data, hash_pos, 4) as uint;
     let tagged_doc = reader::doc_at(d.data, pos);
 
     let belt = tag_index_buckets_bucket_elt;
 
     let mut ret = None;
     do reader::tagged_docs(tagged_doc.doc, belt) |elt| {
-        let pos = io::u64_from_be_bytes(*elt.data, elt.start, 4u) as uint;
-        if eq_fn(elt.data.slice(elt.start + 4u, elt.end)) {
+        let pos = io::u64_from_be_bytes(*elt.data, elt.start, 4) as uint;
+        if eq_fn(elt.data.slice(elt.start + 4, elt.end)) {
             ret = Some(reader::doc_at(d.data, pos).doc);
             false
         } else {
@@ -84,7 +84,7 @@ pub fn maybe_find_item(item_id: int, items: ebml::Doc) -> Option<ebml::Doc> {
     }
     lookup_hash(items,
                 |a| eq_item(a, item_id),
-                item_id.hash() as uint)
+                (item_id as i64).hash())
 }
 
 fn find_item(item_id: int, items: ebml::Doc) -> ebml::Doc {
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index d847d851126..c4919e7f263 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -319,7 +319,7 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
                             id: NodeId,
                             variants: &[variant],
                             path: &[ast_map::path_elt],
-                            index: @mut ~[entry<int>],
+                            index: @mut ~[entry<i64>],
                             generics: &ast::Generics) {
     debug!("encode_enum_variant_info(id=%?)", id);
 
@@ -329,7 +329,8 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
                                ast::def_id { crate: LOCAL_CRATE, node: id });
     for variant in variants.iter() {
         let def_id = local_def(variant.node.id);
-        index.push(entry {val: variant.node.id, pos: ebml_w.writer.tell()});
+        index.push(entry {val: variant.node.id as i64,
+                          pos: ebml_w.writer.tell()});
         ebml_w.start_tag(tag_items_data_item);
         encode_def_id(ebml_w, def_id);
         encode_family(ebml_w, 'v');
@@ -677,8 +678,8 @@ fn encode_info_for_struct(ecx: &EncodeContext,
                           ebml_w: &mut writer::Encoder,
                           path: &[ast_map::path_elt],
                           fields: &[@struct_field],
-                          global_index: @mut ~[entry<int>])
-                          -> ~[entry<int>] {
+                          global_index: @mut ~[entry<i64>])
+                          -> ~[entry<i64>] {
     /* Each class has its own index, since different classes
        may have fields with the same name */
     let mut index = ~[];
@@ -692,8 +693,8 @@ fn encode_info_for_struct(ecx: &EncodeContext,
         };
 
         let id = field.node.id;
-        index.push(entry {val: id, pos: ebml_w.writer.tell()});
-        global_index.push(entry {val: id, pos: ebml_w.writer.tell()});
+        index.push(entry {val: id as i64, pos: ebml_w.writer.tell()});
+        global_index.push(entry {val: id as i64, pos: ebml_w.writer.tell()});
         ebml_w.start_tag(tag_items_data_item);
         debug!("encode_info_for_struct: doing %s %d",
                tcx.sess.str_of(nm), id);
@@ -712,8 +713,8 @@ fn encode_info_for_struct_ctor(ecx: &EncodeContext,
                                path: &[ast_map::path_elt],
                                name: ast::ident,
                                ctor_id: NodeId,
-                               index: @mut ~[entry<int>]) {
-    index.push(entry { val: ctor_id, pos: ebml_w.writer.tell() });
+                               index: @mut ~[entry<i64>]) {
+    index.push(entry { val: ctor_id as i64, pos: ebml_w.writer.tell() });
 
     ebml_w.start_tag(tag_items_data_item);
     encode_def_id(ebml_w, local_def(ctor_id));
@@ -815,13 +816,13 @@ fn should_inline(attrs: &[Attribute]) -> bool {
 fn encode_info_for_item(ecx: &EncodeContext,
                         ebml_w: &mut writer::Encoder,
                         item: @item,
-                        index: @mut ~[entry<int>],
+                        index: @mut ~[entry<i64>],
                         path: &[ast_map::path_elt]) {
     let tcx = ecx.tcx;
 
     fn add_to_index_(item: @item, ebml_w: &writer::Encoder,
-                     index: @mut ~[entry<int>]) {
-        index.push(entry { val: item.id, pos: ebml_w.writer.tell() });
+                     index: @mut ~[entry<i64>]) {
+        index.push(entry { val: item.id as i64, pos: ebml_w.writer.tell() });
     }
     let add_to_index: &fn() = || add_to_index_(item, ebml_w, index);
 
@@ -969,7 +970,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
 
         /* Each class has its own index -- encode it */
         let bkts = create_index(idx);
-        encode_index(ebml_w, bkts, write_int);
+        encode_index(ebml_w, bkts, write_i64);
         ebml_w.end_tag();
 
         // If this is a tuple- or enum-like struct, encode the type of the
@@ -1040,7 +1041,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
                 Some(ast_methods[i])
             } else { None };
 
-            index.push(entry {val: m.def_id.node, pos: ebml_w.writer.tell()});
+            index.push(entry {val: m.def_id.node as i64,
+                              pos: ebml_w.writer.tell()});
             encode_info_for_method(ecx,
                                    ebml_w,
                                    *m,
@@ -1086,7 +1088,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
 
             let method_ty = ty::method(tcx, method_def_id);
 
-            index.push(entry {val: method_def_id.node, pos: ebml_w.writer.tell()});
+            index.push(entry {val: method_def_id.node as i64,
+                              pos: ebml_w.writer.tell()});
 
             ebml_w.start_tag(tag_items_data_item);
 
@@ -1145,10 +1148,10 @@ fn encode_info_for_item(ecx: &EncodeContext,
 fn encode_info_for_foreign_item(ecx: &EncodeContext,
                                 ebml_w: &mut writer::Encoder,
                                 nitem: @foreign_item,
-                                index: @mut ~[entry<int>],
+                                index: @mut ~[entry<i64>],
                                 path: &ast_map::path,
                                 abi: AbiSet) {
-    index.push(entry { val: nitem.id, pos: ebml_w.writer.tell() });
+    index.push(entry { val: nitem.id as i64, pos: ebml_w.writer.tell() });
 
     ebml_w.start_tag(tag_items_data_item);
     match nitem.node {
@@ -1184,10 +1187,10 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext,
 fn encode_info_for_items(ecx: &EncodeContext,
                          ebml_w: &mut writer::Encoder,
                          crate: &Crate)
-                         -> ~[entry<int>] {
+                         -> ~[entry<i64>] {
     let index = @mut ~[];
     ebml_w.start_tag(tag_items_data);
-    index.push(entry { val: CRATE_NODE_ID, pos: ebml_w.writer.tell() });
+    index.push(entry { val: CRATE_NODE_ID as i64, pos: ebml_w.writer.tell() });
     encode_info_for_mod(ecx,
                         ebml_w,
                         &crate.module,
@@ -1304,7 +1307,7 @@ fn write_str(writer: @io::Writer, s: ~str) {
     writer.write_str(s);
 }
 
-fn write_int(writer: @io::Writer, &n: &int) {
+fn write_i64(writer: @io::Writer, &n: &i64) {
     assert!(n < 0x7fff_ffff);
     writer.write_be_u32(n as u32);
 }
@@ -1623,7 +1626,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
 
     i = *wr.pos;
     let items_buckets = create_index(items_index);
-    encode_index(&mut ebml_w, items_buckets, write_int);
+    encode_index(&mut ebml_w, items_buckets, write_i64);
     ecx.stats.index_bytes = *wr.pos - i;
     ebml_w.end_tag();
 
diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs
index 2422be3960b..2b44d793a9d 100644
--- a/src/librustc/metadata/filesearch.rs
+++ b/src/librustc/metadata/filesearch.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -11,12 +11,15 @@
 
 use std::option;
 use std::os;
-use std::result;
+use std::{result, str};
+use std::hashmap::HashSet;
 
 // A module for searching for libraries
 // FIXME (#2658): I'm not happy how this module turned out. Should
 // probably just be folded into cstore.
 
+/// Functions with type `pick` take a parent directory as well as
+/// a file found in that directory.
 pub type pick<'self, T> = &'self fn(path: &Path) -> Option<T>;
 
 pub fn pick_file(file: Path, path: &Path) -> Option<Path> {
@@ -46,28 +49,33 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>,
     impl FileSearch for FileSearchImpl {
         fn sysroot(&self) -> @Path { self.sysroot }
         fn for_each_lib_search_path(&self, f: &fn(&Path) -> bool) -> bool {
+            let mut visited_dirs = HashSet::new();
+
             debug!("filesearch: searching additional lib search paths [%?]",
                    self.addl_lib_search_paths.len());
-            // a little weird
-            self.addl_lib_search_paths.iter().advance(|path| f(path));
+            for path in self.addl_lib_search_paths.iter() {
+                f(path);
+                visited_dirs.insert(path.to_str());
+            }
 
             debug!("filesearch: searching target lib path");
-            if !f(&make_target_lib_path(self.sysroot,
-                                        self.target_triple)) {
-                return false;
-            }
-            debug!("filesearch: searching rustpkg lib path nearest");
-            if match get_rustpkg_lib_path_nearest() {
-                    result::Ok(ref p) => f(p),
-                    result::Err(_) => true
-                } {
-                    return true;
+            let tlib_path = make_target_lib_path(self.sysroot,
+                                        self.target_triple);
+            if !visited_dirs.contains(&tlib_path.to_str()) {
+                if !f(&tlib_path) {
+                    return false;
                 }
-           debug!("filesearch: searching rustpkg lib path");
-           match get_rustpkg_lib_path() {
-              result::Ok(ref p) => f(p),
-              result::Err(_) => true
-           }
+            }
+            visited_dirs.insert(tlib_path.to_str());
+            // Try RUST_PATH
+            let rustpath = rust_path();
+            for path in rustpath.iter() {
+                    if !visited_dirs.contains(&path.push("lib").to_str()) {
+                        f(&path.push("lib"));
+                        visited_dirs.insert(path.push("lib").to_str());
+                    }
+            }
+            true
         }
         fn get_target_lib_path(&self) -> Path {
             make_target_lib_path(self.sysroot, self.target_triple)
@@ -94,12 +102,15 @@ pub fn search<T>(filesearch: @FileSearch, pick: pick<T>) -> Option<T> {
         for path in r.iter() {
             debug!("testing %s", path.to_str());
             let maybe_picked = pick(path);
-            if maybe_picked.is_some() {
-                debug!("picked %s", path.to_str());
-                rslt = maybe_picked;
-                break;
-            } else {
-                debug!("rejected %s", path.to_str());
+            match maybe_picked {
+                Some(_) => {
+                    debug!("picked %s", path.to_str());
+                    rslt = maybe_picked;
+                    break;
+                }
+                None => {
+                    debug!("rejected %s", path.to_str());
+                }
             }
         }
         rslt.is_none()
@@ -132,60 +143,65 @@ fn get_sysroot(maybe_sysroot: &Option<@Path>) -> @Path {
     }
 }
 
-pub fn get_rustpkg_sysroot() -> Result<Path, ~str> {
-    result::Ok(get_or_default_sysroot().push_many([libdir(), ~"rustpkg"]))
+#[cfg(windows)]
+static PATH_ENTRY_SEPARATOR: &'static str = ";";
+#[cfg(not(windows))]
+static PATH_ENTRY_SEPARATOR: &'static str = ":";
+
+/// Returns RUST_PATH as a string, without default paths added
+pub fn get_rust_path() -> Option<~str> {
+    os::getenv("RUST_PATH")
 }
 
-pub fn get_rustpkg_root() -> Result<Path, ~str> {
-    match os::getenv("RUSTPKG_ROOT") {
-        Some(ref _p) => result::Ok(Path((*_p))),
-        None => match os::homedir() {
-          Some(ref _q) => result::Ok((*_q).push(".rustpkg")),
-          None => result::Err(~"no RUSTPKG_ROOT or home directory")
+/// Returns the value of RUST_PATH, as a list
+/// of Paths. Includes default entries for, if they exist:
+/// $HOME/.rust
+/// DIR/.rust for any DIR that's the current working directory
+/// or an ancestor of it
+pub fn rust_path() -> ~[Path] {
+    let mut env_rust_path: ~[Path] = match get_rust_path() {
+        Some(env_path) => {
+            let env_path_components: ~[&str] =
+                env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
+            env_path_components.map(|&s| Path(s))
         }
+        None => ~[]
+    };
+    let cwd = os::getcwd();
+    // now add in default entries
+    let cwd_dot_rust = cwd.push(".rust");
+    if !env_rust_path.contains(&cwd_dot_rust) {
+        env_rust_path.push(cwd_dot_rust);
     }
-}
-
-pub fn get_rustpkg_root_nearest() -> Result<Path, ~str> {
-    do get_rustpkg_root().chain |p| {
-        let cwd = os::getcwd();
-        let cwd_rustpkg = cwd.push(".rustpkg");
-        let rustpkg_is_non_root_file =
-            !os::path_is_dir(&cwd_rustpkg) && cwd_rustpkg != p;
-        let mut par_rustpkg = cwd.pop().push(".rustpkg");
-        let mut rslt = result::Ok(cwd_rustpkg);
-
-        if rustpkg_is_non_root_file {
-            while par_rustpkg != p {
-                if os::path_is_dir(&par_rustpkg) {
-                    rslt = result::Ok(par_rustpkg);
-                    break;
-                }
-                if par_rustpkg.components.len() == 1 {
-                    // We just checked /.rustpkg, stop now.
-                    break;
-                }
-                par_rustpkg = par_rustpkg.pop().pop().push(".rustpkg");
-            }
+    if !env_rust_path.contains(&cwd) {
+        env_rust_path.push(cwd.clone());
+    }
+    do cwd.each_parent() |p| {
+        if !env_rust_path.contains(&p.push(".rust")) {
+            push_if_exists(&mut env_rust_path, p);
         }
-        rslt
     }
-}
-
-fn get_rustpkg_lib_path() -> Result<Path, ~str> {
-    do get_rustpkg_root().chain |p| {
-        result::Ok(p.push(libdir()))
+    let h = os::homedir();
+    for h in h.iter() {
+        if !env_rust_path.contains(&h.push(".rust")) {
+            push_if_exists(&mut env_rust_path, h);
+        }
     }
+    env_rust_path
 }
 
-fn get_rustpkg_lib_path_nearest() -> Result<Path, ~str> {
-    do get_rustpkg_root_nearest().chain |p| {
-        result::Ok(p.push(libdir()))
+
+/// Adds p/.rust into vec, only if it exists
+fn push_if_exists(vec: &mut ~[Path], p: &Path) {
+    let maybe_dir = p.push(".rust");
+    if os::path_exists(&maybe_dir) {
+        vec.push(maybe_dir);
     }
 }
 
 // The name of the directory rustc expects libraries to be located.
 // On Unix should be "lib", on windows "bin"
+#[cfg(stage0)]
 pub fn libdir() -> ~str {
    let libdir = env!("CFG_LIBDIR");
    if libdir.is_empty() {
@@ -193,3 +209,8 @@ pub fn libdir() -> ~str {
    }
    libdir.to_owned()
 }
+
+#[cfg(not(stage0))]
+pub fn libdir() -> ~str {
+    (env!("CFG_LIBDIR")).to_owned()
+}
diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs
index 058be7cc558..704a22ec979 100644
--- a/src/librustc/metadata/loader.rs
+++ b/src/librustc/metadata/loader.rs
@@ -18,7 +18,6 @@ use metadata::filesearch::FileSearch;
 use metadata::filesearch;
 use syntax::codemap::span;
 use syntax::diagnostic::span_handler;
-use syntax::parse::token;
 use syntax::parse::token::ident_interner;
 use syntax::print::pprust;
 use syntax::{ast, attr};
@@ -47,7 +46,7 @@ pub struct Context {
     diag: @span_handler,
     filesearch: @FileSearch,
     span: span,
-    ident: ast::ident,
+    ident: @str,
     metas: ~[@ast::MetaItem],
     hash: @str,
     os: os,
@@ -61,7 +60,7 @@ pub fn load_library_crate(cx: &Context) -> (~str, @~[u8]) {
       None => {
         cx.diag.span_fatal(cx.span,
                            fmt!("can't find crate for `%s`",
-                                token::ident_to_str(&cx.ident)));
+                                cx.ident));
       }
     }
 }
@@ -90,37 +89,38 @@ fn find_library_crate_aux(
     filesearch: @filesearch::FileSearch
 ) -> Option<(~str, @~[u8])> {
     let crate_name = crate_name_from_metas(cx.metas);
-    let prefix = prefix + crate_name + "-";
-
+    // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
+    let prefix = fmt!("%s%s-", prefix, crate_name);
     let mut matches = ~[];
     filesearch::search(filesearch, |path| -> Option<()> {
-        debug!("inspecting file %s", path.to_str());
-        match path.filename() {
-            Some(ref f) if f.starts_with(prefix) && f.ends_with(suffix) => {
-                debug!("%s is a candidate", path.to_str());
-                match get_metadata_section(cx.os, path) {
-                    Some(cvec) =>
-                        if !crate_matches(cvec, cx.metas, cx.hash) {
-                            debug!("skipping %s, metadata doesn't match",
-                                   path.to_str());
-                            None
-                        } else {
-                            debug!("found %s with matching metadata", path.to_str());
-                            matches.push((path.to_str(), cvec));
-                            None
-                        },
-                    _ => {
-                        debug!("could not load metadata for %s", path.to_str());
-                        None
-                    }
-                }
-            }
-            _ => {
-                debug!("skipping %s, doesn't look like %s*%s", path.to_str(),
-                       prefix, suffix);
-                None
-            }
-        }});
+      let path_str = path.filename();
+      match path_str {
+          None => None,
+          Some(path_str) =>
+              if path_str.starts_with(prefix) && path_str.ends_with(suffix) {
+                  debug!("%s is a candidate", path.to_str());
+                  match get_metadata_section(cx.os, path) {
+                      Some(cvec) =>
+                          if !crate_matches(cvec, cx.metas, cx.hash) {
+                              debug!("skipping %s, metadata doesn't match",
+                                  path.to_str());
+                              None
+                          } else {
+                              debug!("found %s with matching metadata", path.to_str());
+                              matches.push((path.to_str(), cvec));
+                              None
+                          },
+                      _ => {
+                          debug!("could not load metadata for %s", path.to_str());
+                          None
+                      }
+                  }
+               }
+               else {
+                   None
+               }
+      }
+    });
 
     match matches.len() {
         0 => None,
@@ -138,8 +138,8 @@ fn find_library_crate_aux(
                 }
                 cx.diag.handler().abort_if_errors();
                 None
-            }
         }
+    }
 }
 
 pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str {
@@ -152,6 +152,16 @@ pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str {
     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: @span_handler,
                           attrs: ~[ast::Attribute]) {
@@ -176,6 +186,8 @@ fn crate_matches(crate_data: @~[u8],
 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 %u metadata requirements against %u items",
            local_metas.len(), extern_metas.len());
 
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index f55fdd22c9a..7d892d97676 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -1487,9 +1487,10 @@ impl Resolver {
                 }
             }
 
-            view_item_extern_mod(name, _, node_id) => {
+            view_item_extern_mod(name, _, _, node_id) => {
+                // n.b. we don't need to look at the path option here, because cstore already did
                 match find_extern_mod_stmt_cnum(self.session.cstore,
-                                                node_id) {
+                                                        node_id) {
                     Some(crate_id) => {
                         let def_id = def_id { crate: crate_id, node: 0 };
                         let parent_link = ModuleParentLink
@@ -4994,7 +4995,9 @@ impl Resolver {
                             if self.structs.contains(&class_id) => {
                         self.record_def(expr.id, definition);
                     }
-                    _ => {
+                    result => {
+                        debug!("(resolving expression) didn't find struct \
+                                def: %?", result);
                         self.session.span_err(
                             path.span,
                             fmt!("`%s` does not name a structure",
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 9a0dc5f036c..327d2e698c1 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -183,7 +183,7 @@ use syntax::codemap::{span, dummy_sp};
 
 // An option identifying a literal: either a unit-like struct or an
 // expression.
-pub enum Lit {
+enum Lit {
     UnitLikeStructLit(ast::NodeId),    // the node ID of the pattern
     ExprLit(@ast::expr),
     ConstLit(ast::def_id),              // the def ID of the constant
@@ -191,7 +191,7 @@ pub enum Lit {
 
 // An option identifying a branch (either a literal, a enum variant or a
 // range)
-pub enum Opt {
+enum Opt {
     lit(Lit),
     var(/* disr val */ uint, @adt::Repr),
     range(@ast::expr, @ast::expr),
@@ -199,7 +199,7 @@ pub enum Opt {
     vec_len_ge(uint, /* slice */uint)
 }
 
-pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
+fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
     match (a, b) {
         (&lit(a), &lit(b)) => {
             match (a, b) {
@@ -258,7 +258,7 @@ pub enum opt_result {
     lower_bound(Result),
     range_result(Result, Result),
 }
-pub fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
+fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
     let _icx = push_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
     let bcx = bcx;
@@ -292,7 +292,7 @@ pub fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
     }
 }
 
-pub fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
+fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
     -> Opt {
     let ccx = bcx.ccx();
     match ccx.tcx.def_map.get_copy(&pat_id) {
@@ -317,7 +317,7 @@ pub fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
 }
 
 #[deriving(Clone)]
-pub enum TransBindingMode {
+enum TransBindingMode {
     TrByValue(/*llbinding:*/ ValueRef),
     TrByRef,
 }
@@ -331,24 +331,24 @@ pub enum TransBindingMode {
  * - `id` is the node id of the binding
  * - `ty` is the Rust type of the binding */
  #[deriving(Clone)]
-pub struct BindingInfo {
+struct BindingInfo {
     llmatch: ValueRef,
     trmode: TransBindingMode,
     id: ast::NodeId,
     ty: ty::t,
 }
 
-pub type BindingsMap = HashMap<ident, BindingInfo>;
+type BindingsMap = HashMap<ident, BindingInfo>;
 
 #[deriving(Clone)]
-pub struct ArmData<'self> {
+struct ArmData<'self> {
     bodycx: @mut Block,
     arm: &'self ast::arm,
     bindings_map: @BindingsMap
 }
 
 #[deriving(Clone)]
-pub struct Match<'self> {
+struct Match<'self> {
     pats: ~[@ast::pat],
     data: ArmData<'self>
 }
@@ -364,7 +364,7 @@ impl<'self> Repr for Match<'self> {
     }
 }
 
-pub fn has_nested_bindings(m: &[Match], col: uint) -> bool {
+fn has_nested_bindings(m: &[Match], col: uint) -> bool {
     for br in m.iter() {
         match br.pats[col].node {
           ast::pat_ident(_, _, Some(_)) => return true,
@@ -374,7 +374,7 @@ pub fn has_nested_bindings(m: &[Match], col: uint) -> bool {
     return false;
 }
 
-pub fn expand_nested_bindings<'r>(bcx: @mut Block,
+fn expand_nested_bindings<'r>(bcx: @mut Block,
                                   m: &[Match<'r>],
                                   col: uint,
                                   val: ValueRef)
@@ -409,7 +409,7 @@ pub fn expand_nested_bindings<'r>(bcx: @mut Block,
     }
 }
 
-pub fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
+fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
     if !pat_is_binding_or_wild(bcx.tcx().def_map, p) {
         bcx.sess().span_bug(
             p.span,
@@ -418,9 +418,9 @@ pub fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
     }
 }
 
-pub type enter_pat<'self> = &'self fn(@ast::pat) -> Option<~[@ast::pat]>;
+type enter_pat<'self> = &'self fn(@ast::pat) -> Option<~[@ast::pat]>;
 
-pub fn enter_match<'r>(bcx: @mut Block,
+fn enter_match<'r>(bcx: @mut Block,
                        dm: DefMap,
                        m: &[Match<'r>],
                        col: uint,
@@ -470,7 +470,7 @@ pub fn enter_match<'r>(bcx: @mut Block,
     return result;
 }
 
-pub fn enter_default<'r>(bcx: @mut Block,
+fn enter_default<'r>(bcx: @mut Block,
                          dm: DefMap,
                          m: &[Match<'r>],
                          col: uint,
@@ -485,7 +485,7 @@ pub fn enter_default<'r>(bcx: @mut Block,
 
     do enter_match(bcx, dm, m, col, val) |p| {
         match p.node {
-          ast::pat_wild | ast::pat_tup(_) | ast::pat_struct(*) => Some(~[]),
+          ast::pat_wild | ast::pat_tup(_) => Some(~[]),
           ast::pat_ident(_, _, None) if pat_is_binding(dm, p) => Some(~[]),
           _ => None
         }
@@ -516,7 +516,7 @@ pub fn enter_default<'r>(bcx: @mut Block,
 // <nmatsakis> so all patterns must either be records (resp. tuples) or
 //             wildcards
 
-pub fn enter_opt<'r>(bcx: @mut Block,
+fn enter_opt<'r>(bcx: @mut Block,
                      m: &[Match<'r>],
                      opt: &Opt,
                      col: uint,
@@ -628,7 +628,7 @@ pub fn enter_opt<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_rec_or_struct<'r>(bcx: @mut Block,
+fn enter_rec_or_struct<'r>(bcx: @mut Block,
                                dm: DefMap,
                                m: &[Match<'r>],
                                col: uint,
@@ -663,7 +663,7 @@ pub fn enter_rec_or_struct<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_tup<'r>(bcx: @mut Block,
+fn enter_tup<'r>(bcx: @mut Block,
                      dm: DefMap,
                      m: &[Match<'r>],
                      col: uint,
@@ -689,7 +689,7 @@ pub fn enter_tup<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_tuple_struct<'r>(bcx: @mut Block,
+fn enter_tuple_struct<'r>(bcx: @mut Block,
                               dm: DefMap,
                               m: &[Match<'r>],
                               col: uint,
@@ -715,7 +715,7 @@ pub fn enter_tuple_struct<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_box<'r>(bcx: @mut Block,
+fn enter_box<'r>(bcx: @mut Block,
                      dm: DefMap,
                      m: &[Match<'r>],
                      col: uint,
@@ -742,7 +742,7 @@ pub fn enter_box<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_uniq<'r>(bcx: @mut Block,
+fn enter_uniq<'r>(bcx: @mut Block,
                       dm: DefMap,
                       m: &[Match<'r>],
                       col: uint,
@@ -769,7 +769,7 @@ pub fn enter_uniq<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_region<'r>(bcx: @mut Block,
+fn enter_region<'r>(bcx: @mut Block,
                         dm: DefMap,
                         m: &[Match<'r>],
                         col: uint,
@@ -799,7 +799,7 @@ pub fn enter_region<'r>(bcx: @mut Block,
 // Returns the options in one column of matches. An option is something that
 // needs to be conditionally matched at runtime; for example, the discriminant
 // on a set of enum variants or a literal.
-pub fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
+fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
     let ccx = bcx.ccx();
     fn add_to_set(tcx: ty::ctxt, set: &mut ~[Opt], val: Opt) {
         if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;}
@@ -865,12 +865,12 @@ pub fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
     return found;
 }
 
-pub struct ExtractedBlock {
+struct ExtractedBlock {
     vals: ~[ValueRef],
     bcx: @mut Block
 }
 
-pub fn extract_variant_args(bcx: @mut Block,
+fn extract_variant_args(bcx: @mut Block,
                             repr: &adt::Repr,
                             disr_val: uint,
                             val: ValueRef)
@@ -893,7 +893,7 @@ fn match_datum(bcx: @mut Block, val: ValueRef, pat_id: ast::NodeId) -> Datum {
 }
 
 
-pub fn extract_vec_elems(bcx: @mut Block,
+fn extract_vec_elems(bcx: @mut Block,
                          pat_span: span,
                          pat_id: ast::NodeId,
                          elem_count: uint,
@@ -947,24 +947,37 @@ pub fn extract_vec_elems(bcx: @mut Block,
     ExtractedBlock { vals: elems, bcx: bcx }
 }
 
-// NB: This function does not collect fields from struct-like enum variants.
-pub fn collect_record_or_struct_fields(bcx: @mut Block,
+/// Checks every pattern in `m` at `col` column.
+/// If there are a struct pattern among them function
+/// returns list of all fields that are matched in these patterns.
+/// Function returns None if there is no struct pattern.
+/// Function doesn't collect fields from struct-like enum variants.
+/// Function can return empty list if there is only wildcard struct pattern.
+fn collect_record_or_struct_fields(bcx: @mut Block,
                                        m: &[Match],
                                        col: uint)
-                                    -> ~[ast::ident] {
+                                    -> Option<~[ast::ident]> {
     let mut fields: ~[ast::ident] = ~[];
+    let mut found = false;
     for br in m.iter() {
         match br.pats[col].node {
           ast::pat_struct(_, ref fs, _) => {
             match ty::get(node_id_type(bcx, br.pats[col].id)).sty {
-              ty::ty_struct(*) => extend(&mut fields, *fs),
+              ty::ty_struct(*) => {
+                   extend(&mut fields, *fs);
+                   found = true;
+              }
               _ => ()
             }
           }
           _ => ()
         }
     }
-    return fields;
+    if found {
+        return Some(fields);
+    } else {
+        return None;
+    }
 
     fn extend(idents: &mut ~[ast::ident], field_pats: &[ast::field_pat]) {
         for field_pat in field_pats.iter() {
@@ -976,7 +989,7 @@ pub fn collect_record_or_struct_fields(bcx: @mut Block,
     }
 }
 
-pub fn pats_require_rooting(bcx: @mut Block,
+fn pats_require_rooting(bcx: @mut Block,
                             m: &[Match],
                             col: uint)
                          -> bool {
@@ -987,7 +1000,7 @@ pub fn pats_require_rooting(bcx: @mut Block,
     }
 }
 
-pub fn root_pats_as_necessary(mut bcx: @mut Block,
+fn root_pats_as_necessary(mut bcx: @mut Block,
                               m: &[Match],
                               col: uint,
                               val: ValueRef)
@@ -1018,23 +1031,23 @@ macro_rules! any_pat (
     )
 )
 
-pub fn any_box_pat(m: &[Match], col: uint) -> bool {
+fn any_box_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_box(_))
 }
 
-pub fn any_uniq_pat(m: &[Match], col: uint) -> bool {
+fn any_uniq_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_uniq(_))
 }
 
-pub fn any_region_pat(m: &[Match], col: uint) -> bool {
+fn any_region_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_region(_))
 }
 
-pub fn any_tup_pat(m: &[Match], col: uint) -> bool {
+fn any_tup_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_tup(_))
 }
 
-pub fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
+fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
     do m.iter().any |br| {
         let pat = br.pats[col];
         match pat.node {
@@ -1050,9 +1063,9 @@ pub fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
     }
 }
 
-pub type mk_fail = @fn() -> BasicBlockRef;
+type mk_fail = @fn() -> BasicBlockRef;
 
-pub fn pick_col(m: &[Match]) -> uint {
+fn pick_col(m: &[Match]) -> uint {
     fn score(p: &ast::pat) -> uint {
         match p.node {
           ast::pat_lit(_) | ast::pat_enum(_, _) | ast::pat_range(_, _) => 1u,
@@ -1088,7 +1101,7 @@ pub enum branch_kind { no_branch, single, switch, compare, compare_vec_len, }
 // Compiles a comparison between two things.
 //
 // NB: This must produce an i1, not a Rust bool (i8).
-pub fn compare_values(cx: @mut Block,
+fn compare_values(cx: @mut Block,
                       lhs: ValueRef,
                       rhs: ValueRef,
                       rhs_t: ty::t)
@@ -1204,7 +1217,7 @@ fn insert_lllocals(bcx: @mut Block,
     return bcx;
 }
 
-pub fn compile_guard(bcx: @mut Block,
+fn compile_guard(bcx: @mut Block,
                      guard_expr: @ast::expr,
                      data: &ArmData,
                      m: &[Match],
@@ -1261,7 +1274,7 @@ pub fn compile_guard(bcx: @mut Block,
     }
 }
 
-pub fn compile_submatch(bcx: @mut Block,
+fn compile_submatch(bcx: @mut Block,
                         m: &[Match],
                         vals: &[ValueRef],
                         chk: Option<mk_fail>) {
@@ -1336,22 +1349,24 @@ fn compile_submatch_continue(mut bcx: @mut Block,
     // required to root any values.
     assert!(any_box_pat(m, col) || !pats_require_rooting(bcx, m, col));
 
-    let rec_fields = collect_record_or_struct_fields(bcx, m, col);
-    if rec_fields.len() > 0 {
-        let pat_ty = node_id_type(bcx, pat_id);
-        let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
-        do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
-            let rec_vals = rec_fields.map(|field_name| {
-                let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
-                adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
-            });
-            compile_submatch(
-                bcx,
-                enter_rec_or_struct(bcx, dm, m, col, rec_fields, val),
-                vec::append(rec_vals, vals_left),
-                chk);
+    match collect_record_or_struct_fields(bcx, m, col) {
+        Some(ref rec_fields) => {
+            let pat_ty = node_id_type(bcx, pat_id);
+            let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
+            do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
+                let rec_vals = rec_fields.map(|field_name| {
+                        let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
+                        adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
+                        });
+                compile_submatch(
+                        bcx,
+                        enter_rec_or_struct(bcx, dm, m, col, *rec_fields, val),
+                        vec::append(rec_vals, vals_left),
+                        chk);
+            }
+            return;
         }
-        return;
+        None => {}
     }
 
     if any_tup_pat(m, col) {
@@ -1670,7 +1685,7 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap {
     return bindings_map;
 }
 
-pub fn trans_match_inner(scope_cx: @mut Block,
+fn trans_match_inner(scope_cx: @mut Block,
                          discr_expr: @ast::expr,
                          arms: &[ast::arm],
                          dest: Dest) -> @mut Block {
@@ -1752,7 +1767,7 @@ pub fn trans_match_inner(scope_cx: @mut Block,
     }
 }
 
-pub enum IrrefutablePatternBindingMode {
+enum IrrefutablePatternBindingMode {
     // Stores the association between node ID and LLVM value in `lllocals`.
     BindLocal,
     // Stores the association between node ID and LLVM value in `llargs`.
diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs
index 5bc22db0ca1..369045500ad 100644
--- a/src/librustc/rustc.rs
+++ b/src/librustc/rustc.rs
@@ -117,6 +117,7 @@ mod std {
 }
 */
 
+#[cfg(stage0)]
 pub fn version(argv0: &str) {
     let mut vers = ~"unknown version";
     let env_vers = env!("CFG_VERSION");
@@ -125,6 +126,16 @@ pub fn version(argv0: &str) {
     printfln!("host: %s", host_triple());
 }
 
+#[cfg(not(stage0))]
+pub fn version(argv0: &str) {
+    let vers = match option_env!("CFG_VERSION") {
+        Some(vers) => vers,
+        None => "unknown version"
+    };
+    printfln!("%s %s", argv0, vers);
+    printfln!("host: %s", host_triple());
+}
+
 pub fn usage(argv0: &str) {
     let message = fmt!("Usage: %s [OPTIONS] INPUT", argv0);
     printfln!("%s\
diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs
index bcda135cbb6..2da66baa412 100644
--- a/src/librustpkg/api.rs
+++ b/src/librustpkg/api.rs
@@ -23,33 +23,30 @@ fn default_ctxt(p: @Path) -> Ctx {
     Ctx { sysroot_opt: Some(p), json: false, dep_cache: @mut HashMap::new() }
 }
 
-pub fn build_lib(sysroot: @Path, root: Path, dest: Path, name: ~str, version: Version,
+pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version,
                  lib: Path) {
 
     let pkg_src = PkgSrc {
         root: root,
-        dst_dir: dest.clone(),
-        id: PkgId{ version: version, ..PkgId::new(name, &dest.pop())},
+        id: PkgId{ version: version, ..PkgId::new(name)},
         libs: ~[mk_crate(lib)],
         mains: ~[],
         tests: ~[],
         benchs: ~[]
     };
-    pkg_src.build(&default_ctxt(sysroot), pkg_src.dst_dir, ~[]);
+    pkg_src.build(&default_ctxt(sysroot), ~[]);
 }
 
-pub fn build_exe(sysroot: @Path, root: Path, dest: Path, name: ~str, version: Version,
-                 main: Path) {
+pub fn build_exe(sysroot: @Path, root: Path, name: ~str, version: Version, main: Path) {
     let pkg_src = PkgSrc {
         root: root,
-        dst_dir: dest.clone(),
-        id: PkgId{ version: version, ..PkgId::new(name, &dest.pop())},
+        id: PkgId{ version: version, ..PkgId::new(name)},
         libs: ~[],
         mains: ~[mk_crate(main)],
         tests: ~[],
         benchs: ~[]
     };
-    pkg_src.build(&default_ctxt(sysroot), pkg_src.dst_dir, ~[]);
+    pkg_src.build(&default_ctxt(sysroot), ~[]);
 
 }
 
@@ -62,12 +59,9 @@ pub fn install_lib(sysroot: @Path,
     debug!("sysroot = %s", sysroot.to_str());
     debug!("workspace = %s", workspace.to_str());
     // make a PkgSrc
-    let pkg_id = PkgId{ version: version, ..PkgId::new(name, &workspace)};
-    let build_dir = workspace.push("build");
-    let dst_dir = build_dir.push_rel(&*pkg_id.local_path);
+    let pkg_id = PkgId{ version: version, ..PkgId::new(name)};
     let pkg_src = PkgSrc {
         root: workspace.clone(),
-        dst_dir: dst_dir.clone(),
         id: pkg_id.clone(),
         libs: ~[mk_crate(lib_path)],
         mains: ~[],
@@ -75,13 +69,13 @@ pub fn install_lib(sysroot: @Path,
         benchs: ~[]
     };
     let cx = default_ctxt(sysroot);
-    pkg_src.build(&cx, dst_dir, ~[]);
+    pkg_src.build(&cx, ~[]);
     cx.install_no_build(&workspace, &pkg_id);
 }
 
 pub fn install_exe(sysroot: @Path, workspace: Path, name: ~str, version: Version) {
     default_ctxt(sysroot).install(&workspace, &PkgId{ version: version,
-                                            ..PkgId::new(name, &workspace)});
+                                            ..PkgId::new(name)});
 
 }
 
diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs
index 230a0f915ac..f051be25f26 100644
--- a/src/librustpkg/context.rs
+++ b/src/librustpkg/context.rs
@@ -12,6 +12,7 @@
 
 
 use std::hashmap::HashMap;
+use std::os;
 
 pub struct Ctx {
     // Sysroot -- if this is None, uses rustc filesearch's
@@ -23,3 +24,26 @@ pub struct Ctx {
     // though I'm not sure why the value is a bool
     dep_cache: @mut HashMap<~str, bool>,
 }
+
+impl Ctx {
+    /// Debugging
+    pub fn sysroot_opt_str(&self) -> ~str {
+        match self.sysroot_opt {
+            None => ~"[none]",
+            Some(p) => p.to_str()
+        }
+    }
+}
+
+/// We assume that if ../../rustc exists, then we're running
+/// rustpkg from a Rust target directory. This is part of a
+/// kludgy hack used to adjust the sysroot.
+pub fn in_target(sysroot_opt: Option<@Path>) -> bool {
+    match sysroot_opt {
+        None => false,
+        Some(p) => {
+            debug!("Checking whether %s is in target", p.to_str());
+            os::path_is_dir(&p.pop().pop().push("rustc"))
+        }
+    }
+}
diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs
index cec64f36947..7c3cde65517 100644
--- a/src/librustpkg/installed_packages.rs
+++ b/src/librustpkg/installed_packages.rs
@@ -10,6 +10,7 @@
 
 // Listing installed packages
 
+use rustc::metadata::filesearch::rust_path;
 use path_util::*;
 use std::os;
 
@@ -20,21 +21,46 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool  {
         for exec in binfiles.iter() {
             let exec_path = Path(*exec).filestem();
             do exec_path.iter().advance |s| {
-                f(&PkgId::new(*s, p))
+                f(&PkgId::new(*s))
             };
         }
         let libfiles = os::list_dir(&p.push("lib"));
         for lib in libfiles.iter() {
-            debug!("Full name: %s", *lib);
-            let lib_path = Path(*lib).filestem();
-            do lib_path.iter().advance |s| {
-                f(&PkgId::new(*s, p))
-            };
-        }
+            let lib = Path(*lib);
+            debug!("Full name: %s", lib.to_str());
+            match has_library(&lib) {
+                Some(basename) => {
+                    debug!("parent = %s, child = %s",
+                           p.push("lib").to_str(), lib.to_str());
+                    let rel_p = p.push("lib/").get_relative_to(&lib);
+                    debug!("Rel: %s", rel_p.to_str());
+                    let rel_path = rel_p.push(basename).to_str();
+                    debug!("Rel name: %s", rel_path);
+                    f(&PkgId::new(rel_path));
+                }
+                None => ()
+            }
+        };
     }
     true
 }
 
+pub fn has_library(p: &Path) -> Option<~str> {
+    let files = os::list_dir(p);
+    for q in files.iter() {
+        let as_path = Path(*q);
+        if as_path.filetype() == Some(os::consts::DLL_SUFFIX.to_owned()) {
+            let stuff : ~str = as_path.filestem().expect("has_library: weird path");
+            let mut stuff2 = stuff.split_str_iter(&"-");
+            let stuff3: ~[&str] = stuff2.collect();
+            // argh
+            let chars_to_drop = os::consts::DLL_PREFIX.len();
+            return Some(stuff3[0].slice(chars_to_drop, stuff3[0].len()).to_owned());
+        }
+    }
+    None
+}
+
 pub fn package_is_installed(p: &PkgId) -> bool {
     let mut is_installed = false;
     do list_installed_packages() |installed| {
@@ -44,4 +70,4 @@ pub fn package_is_installed(p: &PkgId) -> bool {
         false
     };
     is_installed
-}
\ No newline at end of file
+}
diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs
index e1cf5b1fd35..e3b3252587a 100644
--- a/src/librustpkg/package_id.rs
+++ b/src/librustpkg/package_id.rs
@@ -8,33 +8,37 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use package_path::{RemotePath, LocalPath, normalize, hash};
 use version::{try_getting_version, try_getting_local_version,
               Version, NoVersion, split_version};
+use std::rt::io::Writer;
+use std::hash::Streaming;
+use std::hash;
 
 /// Path-fragment identifier of a package such as
 /// 'github.com/graydon/test'; path must be a relative
 /// path with >=1 component.
 #[deriving(Clone)]
 pub struct PkgId {
-    /// Remote path: for example, github.com/mozilla/quux-whatever
-    remote_path: RemotePath,
-    /// Local path: for example, /home/quux/github.com/mozilla/quux_whatever
-    /// Note that '-' normalizes to '_' when mapping a remote path
-    /// onto a local path
-    /// Also, this will change when we implement #6407, though we'll still
-    /// need to keep track of separate local and remote paths
-    local_path: LocalPath,
-    /// Short name. This is the local path's filestem, but we store it
+    /// This is a path, on the local filesystem, referring to where the
+    /// files for this package live. For example:
+    /// github.com/mozilla/quux-whatever (it's assumed that if we're
+    /// working with a package ID of this form, rustpkg has already cloned
+    /// the sources into a local directory in the RUST_PATH).
+    path: Path,
+    /// Short name. This is the path's filestem, but we store it
     /// redundantly so as to not call get() everywhere (filestem() returns an
     /// option)
+    /// The short name does not need to be a valid Rust identifier.
+    /// Users can write: `extern mod foo = "...";` to get around the issue
+    /// of package IDs whose short names aren't valid Rust identifiers.
     short_name: ~str,
+    /// The requested package version.
     version: Version
 }
 
 impl Eq for PkgId {
     fn eq(&self, p: &PkgId) -> bool {
-        *p.local_path == *self.local_path && p.version == self.version
+        p.path == self.path && p.version == self.version
     }
     fn ne(&self, p: &PkgId) -> bool {
         !(self.eq(p))
@@ -42,10 +46,7 @@ impl Eq for PkgId {
 }
 
 impl PkgId {
-    // The PkgId constructor takes a Path argument so as
-    // to be able to infer the version if the path refers
-    // to a local git repository
-    pub fn new(s: &str, work_dir: &Path) -> PkgId {
+    pub fn new(s: &str) -> PkgId {
         use conditions::bad_pkg_id::cond;
 
         let mut given_version = None;
@@ -63,51 +64,64 @@ impl PkgId {
             }
         };
 
-        let p = Path(s);
-        if p.is_absolute {
-            return cond.raise((p, ~"absolute pkgid"));
+        let path = Path(s);
+        if path.is_absolute {
+            return cond.raise((path, ~"absolute pkgid"));
         }
-        if p.components.len() < 1 {
-            return cond.raise((p, ~"0-length pkgid"));
+        if path.components.len() < 1 {
+            return cond.raise((path, ~"0-length pkgid"));
         }
-        let remote_path = RemotePath(p);
-        let local_path = normalize(remote_path.clone());
-        let short_name = local_path.clone().filestem().expect(fmt!("Strange path! %s", s));
+        let short_name = path.clone().filestem().expect(fmt!("Strange path! %s", s));
 
         let version = match given_version {
             Some(v) => v,
-            None => match try_getting_local_version(&work_dir.push_rel(&*local_path)) {
+            None => match try_getting_local_version(&path) {
                 Some(v) => v,
-                None => match try_getting_version(&remote_path) {
+                None => match try_getting_version(&path) {
                     Some(v) => v,
                     None => NoVersion
                 }
             }
         };
 
-        debug!("local_path = %s, remote_path = %s", local_path.to_str(), remote_path.to_str());
+        debug!("path = %s", path.to_str());
         PkgId {
-            local_path: local_path,
-            remote_path: remote_path,
+            path: path,
             short_name: short_name,
             version: version
         }
     }
 
     pub fn hash(&self) -> ~str {
-        fmt!("%s-%s-%s", self.remote_path.to_str(),
-             hash(self.remote_path.to_str() + self.version.to_str()),
+        fmt!("%s-%s-%s", self.path.to_str(),
+             hash(self.path.to_str() + self.version.to_str()),
              self.version.to_str())
     }
 
     pub fn short_name_with_version(&self) -> ~str {
         fmt!("%s%s", self.short_name, self.version.to_str())
     }
+
+    /// True if the ID has multiple components
+    pub fn is_complex(&self) -> bool {
+        self.short_name != self.path.to_str()
+     }
 }
 
 impl ToStr for PkgId {
     fn to_str(&self) -> ~str {
         // should probably use the filestem and not the whole path
-        fmt!("%s-%s", self.local_path.to_str(), self.version.to_str())
+        fmt!("%s-%s", self.path.to_str(), self.version.to_str())
     }
 }
+
+
+pub fn write<W: Writer>(writer: &mut W, string: &str) {
+    writer.write(string.as_bytes());
+}
+
+pub fn hash(data: ~str) -> ~str {
+    let hasher = &mut hash::default_state();
+    write(hasher, data);
+    hasher.result_str()
+}
diff --git a/src/librustpkg/package_path.rs b/src/librustpkg/package_path.rs
deleted file mode 100644
index 4ba9c8066e4..00000000000
--- a/src/librustpkg/package_path.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-// rustpkg utilities having to do with local and remote paths
-
-use std::clone::Clone;
-use std::hash::Streaming;
-use std::hash;
-use std::option::Some;
-use std::path::Path;
-use std::rt::io::Writer;
-
-/// Wrappers to prevent local and remote paths from getting confused
-/// (These will go away after #6407)
-pub struct RemotePath (Path);
-
-impl Clone for RemotePath {
-    fn clone(&self) -> RemotePath {
-        RemotePath((**self).clone())
-    }
-}
-
-pub struct LocalPath (Path);
-
-impl Clone for LocalPath {
-    fn clone(&self) -> LocalPath {
-        LocalPath((**self).clone())
-    }
-}
-
-
-// normalize should be the only way to construct a LocalPath
-// (though this isn't enforced)
-/// Replace all occurrences of '-' in the stem part of path with '_'
-/// This is because we treat rust-foo-bar-quux and rust_foo_bar_quux
-/// as the same name
-pub fn normalize(p_: RemotePath) -> LocalPath {
-    let RemotePath(p) = p_;
-    match p.filestem() {
-        None => LocalPath(p),
-        Some(st) => {
-            let replaced = st.replace("-", "_");
-            if replaced != st {
-                LocalPath(p.with_filestem(replaced))
-            }
-            else {
-                LocalPath(p)
-            }
-        }
-    }
-}
-
-pub fn write<W: Writer>(writer: &mut W, string: &str) {
-    writer.write(string.as_bytes());
-}
-
-pub fn hash(data: ~str) -> ~str {
-    let hasher = &mut hash::default_state();
-    write(hasher, data);
-    hasher.result_str()
-}
diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs
index bbe35ee5004..9833e18e016 100644
--- a/src/librustpkg/package_source.rs
+++ b/src/librustpkg/package_source.rs
@@ -11,7 +11,7 @@
 use target::*;
 use package_id::PkgId;
 use std::path::Path;
-use std::{os, str};
+use std::os;
 use context::*;
 use crate::Crate;
 use messages::*;
@@ -23,7 +23,6 @@ use util::compile_crate;
 // This contains a list of files found in the source workspace.
 pub struct PkgSrc {
     root: Path, // root of where the package source code lives
-    dst_dir: Path, // directory where we will put the compiled output
     id: PkgId,
     libs: ~[Crate],
     mains: ~[Crate],
@@ -37,11 +36,9 @@ condition! {
 
 impl PkgSrc {
 
-    pub fn new(src_dir: &Path, dst_dir: &Path,
-                  id: &PkgId) -> PkgSrc {
+    pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc {
         PkgSrc {
             root: (*src_dir).clone(),
-            dst_dir: (*dst_dir).clone(),
             id: (*id).clone(),
             libs: ~[],
             mains: ~[],
@@ -54,8 +51,7 @@ impl PkgSrc {
     fn check_dir(&self) -> Path {
         use conditions::nonexistent_package::cond;
 
-        debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(),
-               self.root.to_str());
+        debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str());
         let dir;
         let dirs = pkgid_src_in_workspace(&self.id, &self.root);
         debug!("Checking dirs: %?", dirs);
@@ -89,18 +85,18 @@ impl PkgSrc {
         os::remove_dir_recursive(&local);
 
         debug!("Checking whether %s exists locally. Cwd = %s, does it? %?",
-               self.id.local_path.to_str(),
+               self.id.path.to_str(),
                os::getcwd().to_str(),
-               os::path_exists(&*self.id.local_path));
+               os::path_exists(&self.id.path));
 
-        if os::path_exists(&*self.id.local_path) {
+        if os::path_exists(&self.id.path) {
             debug!("%s exists locally! Cloning it into %s",
-                   self.id.local_path.to_str(), local.to_str());
-            git_clone(&*self.id.local_path, &local, &self.id.version);
+                   self.id.path.to_str(), local.to_str());
+            git_clone(&self.id.path, &local, &self.id.version);
             return Some(local);
         }
 
-        let url = fmt!("https://%s", self.id.remote_path.to_str());
+        let url = fmt!("https://%s", self.id.path.to_str());
         note(fmt!("Fetching package: git clone %s %s [version=%s]",
                   url, local.to_str(), self.id.version.to_str()));
         if git_clone_general(url, &local, &self.id.version) {
@@ -125,25 +121,8 @@ impl PkgSrc {
     }
 
     /// True if the given path's stem is self's pkg ID's stem
-    /// or if the pkg ID's stem is <rust-foo> and the given path's
-    /// stem is foo
-    /// Requires that dashes in p have already been normalized to
-    /// underscores
     fn stem_matches(&self, p: &Path) -> bool {
-        let self_id = self.id.local_path.filestem();
-        if self_id == p.filestem() {
-            return true;
-        }
-        else {
-            for pth in self_id.iter() {
-                if pth.starts_with("rust_") // because p is already normalized
-                    && match p.filestem() {
-                           Some(s) => str::eq_slice(s, pth.slice(5, pth.len())),
-                           None => false
-                       } { return true; }
-            }
-        }
-        false
+        p.filestem().map_default(false, |p| { p == &self.id.short_name })
     }
 
     fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
@@ -164,7 +143,7 @@ impl PkgSrc {
         let dir = self.check_dir();
         debug!("Called check_dir, I'm in %s", dir.to_str());
         let prefix = dir.components.len();
-        debug!("Matching against %?", self.id.local_path.filestem());
+        debug!("Matching against %?", self.id.short_name);
         do os::walk_dir(&dir) |pth| {
             match pth.filename() {
                 Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs,
@@ -202,7 +181,6 @@ impl PkgSrc {
 
     fn build_crates(&self,
                     ctx: &Ctx,
-                    dst_dir: &Path,
                     src_dir: &Path,
                     crates: &[Crate],
                     cfgs: &[~str],
@@ -210,12 +188,13 @@ impl PkgSrc {
         for crate in crates.iter() {
             let path = &src_dir.push_rel(&crate.file).normalize();
             note(fmt!("build_crates: compiling %s", path.to_str()));
-            note(fmt!("build_crates: destination dir is %s", dst_dir.to_str()));
+            note(fmt!("build_crates: using as workspace %s", self.root.to_str()));
 
             let result = compile_crate(ctx,
                                        &self.id,
                                        path,
-                                       dst_dir,
+                                       // compile_crate wants the workspace
+                                       &self.root,
                                        crate.flags,
                                        crate.cfgs + cfgs,
                                        false,
@@ -229,15 +208,15 @@ impl PkgSrc {
         }
     }
 
-    pub fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) {
+    pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) {
         let dir = self.check_dir();
         debug!("Building libs in %s", dir.to_str());
-        self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib);
+        self.build_crates(ctx, &dir, self.libs, cfgs, Lib);
         debug!("Building mains");
-        self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main);
+        self.build_crates(ctx, &dir, self.mains, cfgs, Main);
         debug!("Building tests");
-        self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test);
+        self.build_crates(ctx, &dir, self.tests, cfgs, Test);
         debug!("Building benches");
-        self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench);
+        self.build_crates(ctx, &dir, self.benchs, cfgs, Bench);
     }
 }
diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs
index fdfa29b2f83..bbe84b2ecac 100644
--- a/src/librustpkg/path_util.rs
+++ b/src/librustpkg/path_util.rs
@@ -10,65 +10,17 @@
 
 // rustpkg utilities having to do with paths and directories
 
-pub use package_path::{RemotePath, LocalPath, normalize};
 pub use package_id::PkgId;
 pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
 pub use version::{Version, NoVersion, split_version_general};
+pub use rustc::metadata::filesearch::rust_path;
+
 use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
 use std::os::mkdir_recursive;
 use std::os;
-use std::iterator::IteratorUtil;
 use messages::*;
 use package_id::*;
 
-fn push_if_exists(vec: &mut ~[Path], p: &Path) {
-    let maybe_dir = p.push(".rust");
-    if os::path_exists(&maybe_dir) {
-        vec.push(maybe_dir);
-    }
-}
-
-#[cfg(windows)]
-static PATH_ENTRY_SEPARATOR: &'static str = ";";
-#[cfg(not(windows))]
-static PATH_ENTRY_SEPARATOR: &'static str = ":";
-
-/// Returns RUST_PATH as a string, without default paths added
-pub fn get_rust_path() -> Option<~str> {
-    os::getenv("RUST_PATH")
-}
-
-/// Returns the value of RUST_PATH, as a list
-/// of Paths. Includes default entries for, if they exist:
-/// $HOME/.rust
-/// DIR/.rust for any DIR that's the current working directory
-/// or an ancestor of it
-pub fn rust_path() -> ~[Path] {
-    let mut env_rust_path: ~[Path] = match get_rust_path() {
-        Some(env_path) => {
-            let env_path_components: ~[&str] =
-                env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
-            env_path_components.map(|&s| Path(s))
-        }
-        None => ~[]
-    };
-    debug!("RUST_PATH entries from environment: %?", env_rust_path);
-    let cwd = os::getcwd();
-    // now add in default entries
-    env_rust_path.push(cwd.clone());
-    do cwd.each_parent() |p| { push_if_exists(&mut env_rust_path, p) };
-    let h = os::homedir();
-    // Avoid adding duplicates
-    // could still add dups if someone puts one of these in the RUST_PATH
-    // manually, though...
-    for hdir in h.iter() {
-        if !(cwd.is_ancestor_of(hdir) || hdir.is_ancestor_of(&cwd)) {
-            push_if_exists(&mut env_rust_path, hdir);
-        }
-    }
-    env_rust_path
-}
-
 pub fn default_workspace() -> Path {
     let p = rust_path();
     if p.is_empty() {
@@ -99,39 +51,39 @@ pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
 /// pkgid's short name
 pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
     let src_dir = workspace.push("src");
-    let dirs = os::list_dir(&src_dir);
-    for p in dirs.iter() {
-        let p = Path((*p).clone());
+    let mut found = false;
+    do os::walk_dir(&src_dir) |p| {
         debug!("=> p = %s", p.to_str());
-        if !os::path_is_dir(&src_dir.push_rel(&p)) {
-            loop;
-        }
-        debug!("p = %s, remote_path = %s", p.to_str(), pkgid.remote_path.to_str());
+        if os::path_is_dir(p) {
+            debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(),
+            src_dir.push_rel(&pkgid.path).to_str());
 
-        if p == *pkgid.remote_path {
-            return true;
-        }
-        else {
-            let pf = p.filename();
-            for pf in pf.iter() {
-                let f_ = (*pf).clone();
-                let g = f_.to_str();
-                match split_version_general(g, '-') {
-                    Some((ref might_match, ref vers)) => {
-                        debug!("might_match = %s, vers = %s", *might_match,
+            if *p == src_dir.push_rel(&pkgid.path) {
+                found = true;
+            }
+            else {
+                let pf = p.filename();
+                for pf in pf.iter() {
+                    let f_ = (*pf).clone();
+                    let g = f_.to_str();
+                    match split_version_general(g, '-') {
+                        Some((ref might_match, ref vers)) => {
+                            debug!("might_match = %s, vers = %s", *might_match,
                                vers.to_str());
-                        if *might_match == pkgid.short_name
-                            && (*vers == pkgid.version || pkgid.version == NoVersion)
-                        {
-                            return true;
+                            if *might_match == pkgid.short_name
+                                 && (*vers == pkgid.version || pkgid.version == NoVersion)
+                            {
+                                  found = true;
+                            }
                         }
-                    }
-                    None => ()
+                        None => ()
+                     }
                 }
             }
         }
-    }
-    false
+        true
+    };
+    found
 }
 
 /// Returns a list of possible directories
@@ -141,9 +93,9 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
 pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] {
     let mut results = ~[];
     let result = workspace.push("src").push(fmt!("%s-%s",
-                     pkgid.local_path.to_str(), pkgid.version.to_str()));
+                     pkgid.path.to_str(), pkgid.version.to_str()));
     results.push(result);
-    results.push(workspace.push("src").push_rel(&*pkgid.remote_path));
+    results.push(workspace.push("src").push_rel(&pkgid.path));
     results
 }
 
@@ -163,7 +115,7 @@ pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<P
 pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
     let mut result = workspace.push("build");
     // should use a target-specific subdirectory
-    result = mk_output_path(Main, Build, pkgid, &result);
+    result = mk_output_path(Main, Build, pkgid, result);
     debug!("built_executable_in_workspace: checking whether %s exists",
            result.to_str());
     if os::path_exists(&result) {
@@ -191,7 +143,7 @@ pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path>
 fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option<Path> {
     let mut result = workspace.push("build");
     // should use a target-specific subdirectory
-    result = mk_output_path(what, Build, pkgid, &result);
+    result = mk_output_path(what, Build, pkgid, result);
     debug!("output_in_workspace: checking whether %s exists",
            result.to_str());
     if os::path_exists(&result) {
@@ -206,14 +158,12 @@ fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Opt
 /// Figure out what the library name for <pkgid> in <workspace>'s build
 /// directory is, and if the file exists, return it.
 pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
-    library_in_workspace(&pkgid.local_path, pkgid.short_name,
-                         Build, workspace, "build")
+    library_in_workspace(&pkgid.path, pkgid.short_name, Build, workspace, "build")
 }
 
 /// Does the actual searching stuff
 pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option<Path> {
-    library_in_workspace(&normalize(RemotePath(Path(short_name))),
-                         short_name, Install, workspace, "lib")
+    library_in_workspace(&Path(short_name), short_name, Install, workspace, "lib")
 }
 
 
@@ -221,7 +171,7 @@ pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Opt
 /// don't know the entire package ID.
 /// `workspace` is used to figure out the directory to search.
 /// `short_name` is taken as the link name of the library.
-pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target,
+pub fn library_in_workspace(path: &Path, short_name: &str, where: Target,
                         workspace: &Path, prefix: &str) -> Option<Path> {
     debug!("library_in_workspace: checking whether a library named %s exists",
            short_name);
@@ -233,7 +183,7 @@ pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target,
             prefix = %s", short_name, where, workspace.to_str(), prefix);
 
     let dir_to_search = match where {
-        Build => workspace.push(prefix).push_rel(&**path),
+        Build => workspace.push(prefix).push_rel(path),
         Install => workspace.push(prefix)
     };
     debug!("Listing directory %s", dir_to_search.to_str());
@@ -349,7 +299,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path,
     // Artifacts in the build directory live in a package-ID-specific subdirectory,
     // but installed ones don't.
     let result = match where {
-                Build => workspace.push(subdir).push_rel(&*pkgid.local_path),
+                Build => workspace.push(subdir).push_rel(&pkgid.path),
                 _     => workspace.push(subdir)
     };
     if !os::path_exists(&result) && !mkdir_recursive(&result, U_RWX) {
@@ -357,7 +307,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path,
             create the %s dir (pkgid=%s, workspace=%s, what=%?, where=%?",
             subdir, pkgid.to_str(), workspace.to_str(), what, where)));
     }
-    mk_output_path(what, where, pkgid, &result)
+    mk_output_path(what, where, pkgid, result)
 }
 
 /// Return the directory for <pkgid>'s build artifacts in <workspace>.
@@ -368,7 +318,7 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
     let mut result = workspace.push("build");
     // n.b. Should actually use a target-specific
     // subdirectory of build/
-    result = result.push_rel(&*pkgid.local_path);
+    result = result.push_rel(&pkgid.path);
     if os::path_exists(&result) || os::mkdir_recursive(&result, U_RWX) {
         result
     }
@@ -380,19 +330,16 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
 /// Return the output file for a given directory name,
 /// given whether we're building a library and whether we're building tests
 pub fn mk_output_path(what: OutputType, where: Target,
-                      pkg_id: &PkgId, workspace: &Path) -> Path {
+                      pkg_id: &PkgId, workspace: Path) -> Path {
     let short_name_with_version = fmt!("%s-%s", pkg_id.short_name,
                                        pkg_id.version.to_str());
     // Not local_path.dir_path()! For package foo/bar/blat/, we want
     // the executable blat-0.5 to live under blat/
     let dir = match where {
         // If we're installing, it just goes under <workspace>...
-        Install => {
-            // bad copy, but I just couldn't make the borrow checker happy
-            (*workspace).clone()
-        }
+        Install => workspace,
         // and if we're just building, it goes in a package-specific subdir
-        Build => workspace.push_rel(&*pkg_id.local_path)
+        Build => workspace.push_rel(&pkg_id.path)
     };
     debug!("[%?:%?] mk_output_path: short_name = %s, path = %s", what, where,
            if what == Lib { short_name_with_version.clone() } else { pkg_id.short_name.clone() },
diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs
index fa03a5bbfc2..26dab4120fd 100644
--- a/src/librustpkg/rustpkg.rs
+++ b/src/librustpkg/rustpkg.rs
@@ -33,12 +33,13 @@ use std::hashmap::HashMap;
 
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
+use rustc::metadata::filesearch::rust_path;
 use extra::{getopts};
 use syntax::{ast, diagnostic};
 use util::*;
 use messages::*;
 use path_util::{build_pkg_id_in_workspace, first_pkgid_src_in_workspace};
-use path_util::{U_RWX, rust_path, in_rust_path};
+use path_util::{U_RWX, in_rust_path};
 use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace};
 use path_util::{target_executable_in_workspace, target_library_in_workspace};
 use source_control::is_git_dir;
@@ -54,7 +55,6 @@ mod crate;
 mod installed_packages;
 mod messages;
 mod package_id;
-mod package_path;
 mod package_source;
 mod path_util;
 mod search;
@@ -138,35 +138,28 @@ impl<'self> PkgScript<'self> {
         let crate = util::ready_crate(sess, self.crate);
         debug!("Building output filenames with script name %s",
                driver::source_name(&self.input));
-        match filesearch::get_rustpkg_sysroot() {
-            Ok(r) => {
-                let root = r.pop().pop().pop().pop(); // :-\
-                debug!("Root is %s, calling compile_rest", root.to_str());
-                let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
-                util::compile_crate_from_input(&self.input,
-                                               &self.build_dir,
-                                               sess,
-                                               crate);
-                debug!("Running program: %s %s %s %s", exe.to_str(),
-                       sysroot.to_str(), root.to_str(), "install");
-                // FIXME #7401 should support commands besides `install`
-                let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]);
-                if status != 0 {
-                    return (~[], status);
-                }
-                else {
-                    debug!("Running program (configs): %s %s %s",
-                           exe.to_str(), root.to_str(), "configs");
-                    let output = run::process_output(exe.to_str(), [root.to_str(), ~"configs"]);
-                    // Run the configs() function to get the configs
-                    let cfgs = str::from_bytes_slice(output.output).word_iter()
-                        .transform(|w| w.to_owned()).collect();
-                    (cfgs, output.status)
-                }
-            }
-            Err(e) => {
-                fail!("Running package script, couldn't find rustpkg sysroot (%s)", e)
-            }
+        let root = filesearch::get_or_default_sysroot().pop().pop(); // :-\
+        debug!("Root is %s, calling compile_rest", root.to_str());
+        let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
+        util::compile_crate_from_input(&self.input,
+                                       &self.build_dir,
+                                       sess,
+                                       crate);
+        debug!("Running program: %s %s %s %s", exe.to_str(),
+               sysroot.to_str(), root.to_str(), "install");
+        // FIXME #7401 should support commands besides `install`
+        let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]);
+        if status != 0 {
+            return (~[], status);
+        }
+        else {
+            debug!("Running program (configs): %s %s %s",
+                   exe.to_str(), root.to_str(), "configs");
+            let output = run::process_output(exe.to_str(), [root.to_str(), ~"configs"]);
+            // Run the configs() function to get the configs
+            let cfgs = str::from_bytes_slice(output.output).word_iter()
+                .transform(|w| w.to_owned()).collect();
+            (cfgs, output.status)
         }
     }
 
@@ -205,7 +198,7 @@ impl CtxMethods for Ctx {
                 else {
                     // The package id is presumed to be the first command-line
                     // argument
-                    let pkgid = PkgId::new(args[0].clone(), &os::getcwd());
+                    let pkgid = PkgId::new(args[0].clone());
                     do each_pkg_parent_workspace(&pkgid) |workspace| {
                         debug!("found pkg %s in workspace %s, trying to build",
                                pkgid.to_str(), workspace.to_str());
@@ -228,7 +221,7 @@ impl CtxMethods for Ctx {
                 else {
                     // The package id is presumed to be the first command-line
                     // argument
-                    let pkgid = PkgId::new(args[0].clone(), &os::getcwd());
+                    let pkgid = PkgId::new(args[0].clone());
                     let cwd = os::getcwd();
                     self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd
                 }
@@ -254,13 +247,12 @@ impl CtxMethods for Ctx {
                 else {
                     // The package id is presumed to be the first command-line
                     // argument
-                    let pkgid = PkgId::new(args[0], &os::getcwd());
+                    let pkgid = PkgId::new(args[0]);
                     let workspaces = pkg_parent_workspaces(&pkgid);
                     if workspaces.is_empty() {
                         let rp = rust_path();
                         assert!(!rp.is_empty());
-                        let src = PkgSrc::new(&rp[0], &build_pkg_id_in_workspace(&pkgid, &rp[0]),
-                                              &pkgid);
+                        let src = PkgSrc::new(&rp[0], &pkgid);
                         src.fetch_git();
                         self.install(&rp[0], &pkgid);
                     }
@@ -275,7 +267,7 @@ impl CtxMethods for Ctx {
             "list" => {
                 io::println("Installed packages:");
                 do installed_packages::list_installed_packages |pkg_id| {
-                    println(pkg_id.local_path.to_str());
+                    println(pkg_id.path.to_str());
                     true
                 };
             }
@@ -294,7 +286,7 @@ impl CtxMethods for Ctx {
                     return usage::uninstall();
                 }
 
-                let pkgid = PkgId::new(args[0], &os::getcwd()); // ??
+                let pkgid = PkgId::new(args[0]);
                 if !installed_packages::package_is_installed(&pkgid) {
                     warn(fmt!("Package %s doesn't seem to be installed! Doing nothing.", args[0]));
                     return;
@@ -329,20 +321,18 @@ impl CtxMethods for Ctx {
     fn build(&self, workspace: &Path, pkgid: &PkgId) {
         debug!("build: workspace = %s (in Rust path? %? is git dir? %? \
                 pkgid = %s", workspace.to_str(),
-               in_rust_path(workspace), is_git_dir(&workspace.push_rel(&*pkgid.local_path)),
+               in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)),
                pkgid.to_str());
         let src_dir   = first_pkgid_src_in_workspace(pkgid, workspace);
-        let build_dir = build_pkg_id_in_workspace(pkgid, workspace);
-        debug!("Destination dir = %s", build_dir.to_str());
 
         // If workspace isn't in the RUST_PATH, and it's a git repo,
         // then clone it into the first entry in RUST_PATH, and repeat
         debug!("%? %? %s", in_rust_path(workspace),
-               is_git_dir(&workspace.push_rel(&*pkgid.local_path)),
+               is_git_dir(&workspace.push_rel(&pkgid.path)),
                workspace.to_str());
-        if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&*pkgid.local_path)) {
-            let out_dir = default_workspace().push("src").push_rel(&*pkgid.local_path);
-            source_control::git_clone(&workspace.push_rel(&*pkgid.local_path),
+        if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) {
+            let out_dir = default_workspace().push("src").push_rel(&pkgid.path);
+            source_control::git_clone(&workspace.push_rel(&pkgid.path),
                                       &out_dir, &pkgid.version);
             let default_ws = default_workspace();
             debug!("Calling build recursively with %? and %?", default_ws.to_str(),
@@ -351,7 +341,7 @@ impl CtxMethods for Ctx {
         }
 
         // Create the package source
-        let mut src = PkgSrc::new(workspace, &build_dir, pkgid);
+        let mut src = PkgSrc::new(workspace, pkgid);
         debug!("Package src = %?", src);
 
         // Is there custom build logic? If so, use it
@@ -385,7 +375,7 @@ impl CtxMethods for Ctx {
             // Find crates inside the workspace
             src.find_crates();
             // Build it!
-            src.build(self, build_dir, cfgs);
+            src.build(self, cfgs);
         }
     }
 
@@ -444,6 +434,7 @@ impl CtxMethods for Ctx {
         for lib in maybe_library.iter() {
             let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \
                                                 didn't install it!", lib.to_str()));
+            let target_lib = target_lib.pop().push(lib.filename().expect("weird target lib"));
             debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str());
             if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) &&
                  os::copy_file(lib, &target_lib)) {
@@ -518,9 +509,7 @@ pub fn main() {
         };
     }
 
-    let sroot = match filesearch::get_rustpkg_sysroot() {
-        Ok(r) => Some(@r.pop().pop()), Err(_) => None
-    };
+    let sroot = Some(@filesearch::get_or_default_sysroot());
     debug!("Using sysroot: %?", sroot);
     Ctx {
         sysroot_opt: sroot, // Currently, only tests override this
diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs
index e3b796a03bb..caa004a53b2 100644
--- a/src/librustpkg/source_control.rs
+++ b/src/librustpkg/source_control.rs
@@ -10,7 +10,7 @@
 
 // Utils for working with version control repositories. Just git right now.
 
-use std::{os, run, str};
+use std::{io, os, run, str};
 use std::run::{ProcessOutput, ProcessOptions, Process};
 use version::*;
 
@@ -19,14 +19,37 @@ pub fn git_clone(source: &Path, target: &Path, v: &Version) {
     assert!(os::path_is_dir(source));
     assert!(is_git_dir(source));
     if !os::path_exists(target) {
-        debug!("Running: git clone %s %s", source.to_str(),
-               target.to_str());
-        assert!(git_clone_general(source.to_str(), target, v));
+        debug!("Running: git clone %s %s", source.to_str(), target.to_str());
+        let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]);
+        if outp.status != 0 {
+            io::println(str::from_bytes_owned(outp.output.clone()));
+            io::println(str::from_bytes_owned(outp.error));
+            fail!("Couldn't `git clone` %s", source.to_str());
+        }
+        else {
+            match v {
+                &ExactRevision(ref s) => {
+                    debug!("`Running: git --work-tree=%s --git-dir=%s checkout %s",
+                           *s, target.to_str(), target.push(".git").to_str());
+                    let outp = run::process_output("git",
+                                   [fmt!("--work-tree=%s", target.to_str()),
+                                    fmt!("--git-dir=%s", target.push(".git").to_str()),
+                                    ~"checkout", fmt!("%s", *s)]);
+                    if outp.status != 0 {
+                        io::println(str::from_bytes_owned(outp.output.clone()));
+                        io::println(str::from_bytes_owned(outp.error));
+                        fail!("Couldn't `git checkout %s` in %s",
+                              *s, target.to_str());
+                    }
+                }
+                _ => ()
+            }
+        }
     }
     else {
-        // Pull changes
-        // Note that this ignores tags, which is probably wrong. There are no tests for
-        // it, though.
+        // Check that no version was specified. There's no reason to not handle the
+        // case where a version was requested, but I haven't implemented it.
+        assert!(*v == NoVersion);
         debug!("Running: git --work-tree=%s --git-dir=%s pull --no-edit %s",
                target.to_str(), target.push(".git").to_str(), source.to_str());
         let outp = run::process_output("git", [fmt!("--work-tree=%s", target.to_str()),
diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs
index 9fea8662129..121dcf40150 100644
--- a/src/librustpkg/tests.rs
+++ b/src/librustpkg/tests.rs
@@ -12,11 +12,10 @@
 
 use context::Ctx;
 use std::hashmap::HashMap;
-use std::{io, libc, os, result, run, str};
+use std::{io, libc, os, run, str};
 use extra::tempfile::mkdtemp;
 use std::run::ProcessOutput;
 use installed_packages::list_installed_packages;
-use package_path::*;
 use package_id::{PkgId};
 use version::{ExactRevision, NoVersion, Version, Tagged};
 use path_util::{target_executable_in_workspace, target_library_in_workspace,
@@ -24,7 +23,9 @@ use path_util::{target_executable_in_workspace, target_library_in_workspace,
                make_dir_rwx, U_RWX, library_in_workspace,
                built_bench_in_workspace, built_test_in_workspace,
                built_library_in_workspace, built_executable_in_workspace,
-                installed_library_in_workspace, rust_path};
+                installed_library_in_workspace};
+use rustc::metadata::filesearch::rust_path;
+use rustc::driver::driver::host_triple;
 use target::*;
 
 /// Returns the last-modified date as an Option
@@ -42,31 +43,25 @@ fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
 
 fn fake_pkg() -> PkgId {
     let sn = ~"bogus";
-    let remote = RemotePath(Path(sn));
     PkgId {
-        local_path: normalize(remote.clone()),
-        remote_path: remote,
+        path: Path(sn),
         short_name: sn,
         version: NoVersion
     }
 }
 
 fn git_repo_pkg() -> PkgId {
-    let remote = RemotePath(Path("mockgithub.com/catamorphism/test-pkg"));
     PkgId {
-        local_path: normalize(remote.clone()),
-        remote_path: remote,
-        short_name: ~"test_pkg",
+        path: Path("mockgithub.com/catamorphism/test-pkg"),
+        short_name: ~"test-pkg",
         version: NoVersion
     }
 }
 
 fn git_repo_pkg_with_tag(a_tag: ~str) -> PkgId {
-    let remote = RemotePath(Path("mockgithub.com/catamorphism/test-pkg"));
     PkgId {
-        local_path: normalize(remote.clone()),
-        remote_path: remote,
-        short_name: ~"test_pkg",
+        path: Path("mockgithub.com/catamorphism/test-pkg"),
+        short_name: ~"test-pkg",
         version: Tagged(a_tag)
     }
 }
@@ -76,13 +71,13 @@ fn writeFile(file_path: &Path, contents: &str) {
     out.write_line(contents);
 }
 
-fn mk_empty_workspace(short_name: &LocalPath, version: &Version) -> Path {
+fn mk_empty_workspace(short_name: &Path, version: &Version) -> Path {
     let workspace_dir = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
     mk_workspace(&workspace_dir, short_name, version);
     workspace_dir
 }
 
-fn mk_workspace(workspace: &Path, short_name: &LocalPath, version: &Version) -> Path {
+fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path {
     // include version number in directory name
     let package_dir = workspace.push("src").push(fmt!("%s-%s",
                                                       short_name.to_str(), version.to_str()));
@@ -90,7 +85,7 @@ fn mk_workspace(workspace: &Path, short_name: &LocalPath, version: &Version) ->
     package_dir
 }
 
-fn mk_temp_workspace(short_name: &LocalPath, version: &Version) -> Path {
+fn mk_temp_workspace(short_name: &Path, version: &Version) -> Path {
     let package_dir = mk_empty_workspace(short_name,
                                          version).push("src").push(fmt!("%s-%s",
                                                             short_name.to_str(),
@@ -116,6 +111,22 @@ fn mk_temp_workspace(short_name: &LocalPath, version: &Version) -> Path {
     package_dir
 }
 
+fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &str) {
+    let cwd = (*cwd).clone();
+    let mut prog = run::Process::new("git", args, run::ProcessOptions {
+        env: env.map(|v| v.slice(0, v.len())),
+        dir: Some(&cwd),
+        in_fd: None,
+        out_fd: None,
+        err_fd: None
+    });
+    let rslt = prog.finish_with_output();
+    if rslt.status != 0 {
+        fail!("%s [git returned %?, output = %s, error = %s]", err_msg,
+           rslt.status, str::from_bytes(rslt.output), str::from_bytes(rslt.error));
+    }
+}
+
 /// Should create an empty git repo in p, relative to the tmp dir, and return the new
 /// absolute path
 fn init_git_repo(p: &Path) -> Path {
@@ -125,37 +136,14 @@ fn init_git_repo(p: &Path) -> Path {
     let work_dir_for_opts = work_dir.clone();
     assert!(os::mkdir_recursive(&work_dir, U_RWX));
     debug!("Running: git init in %s", work_dir.to_str());
-    let opts = run::ProcessOptions {
-        env: None,
-        dir: Some(&work_dir_for_opts),
-        in_fd: None,
-        out_fd: None,
-        err_fd: None
-    };
-    let mut prog = run::Process::new("git", [~"init"], opts);
-    let mut output = prog.finish_with_output();
-    if output.status == 0 {
-        // Add stuff to the dir so that git tag succeeds
-        writeFile(&work_dir.push("README"), "");
-        prog = run::Process::new("git", [~"add", ~"README"], opts);
-        output = prog.finish_with_output();
-        if output.status == 0 {
-            prog = run::Process::new("git", [~"commit", ~"-m", ~"whatever"], opts);
-            output = prog.finish_with_output();
-            if output.status == 0 {
-                tmp
-            }
-            else {
-                fail!("Couldn't commit in %s", work_dir.to_str());
-            }
-        }
-        else {
-            fail!("Couldn't add in %s", work_dir.to_str());
-        }
-    }
-    else {
-        fail!("Couldn't initialize git repository in %s", work_dir.to_str())
-    }
+    let ws = work_dir.to_str();
+    run_git([~"init"], None, &work_dir_for_opts,
+        fmt!("Couldn't initialize git repository in %s", ws));
+    // Add stuff to the dir so that git tag succeeds
+    writeFile(&work_dir.push("README"), "");
+    run_git([~"add", ~"README"], None, &work_dir_for_opts, fmt!("Couldn't add in %s", ws));
+    git_commit(&work_dir_for_opts, ~"whatever");
+    tmp
 }
 
 fn add_all_and_commit(repo: &Path) {
@@ -164,51 +152,20 @@ fn add_all_and_commit(repo: &Path) {
 }
 
 fn git_commit(repo: &Path, msg: ~str) {
-    let mut prog = run::Process::new("git", [~"commit", ~"-m", msg],
-                                     run::ProcessOptions { env: None,
-                                                          dir: Some(repo),
-                                                          in_fd: None,
-                                                          out_fd: None,
-                                                          err_fd: None
-                                                         });
-    let output = prog.finish_with_output();
-    if output.status != 0 {
-        fail!("Couldn't commit in %s: output was %s", repo.to_str(),
-              str::from_bytes(output.output + output.error))
-    }
-
+    run_git([~"commit", ~"--author=tester <test@mozilla.com>", ~"-m", msg],
+            None, repo, fmt!("Couldn't commit in %s", repo.to_str()));
 }
 
 fn git_add_all(repo: &Path) {
-    let mut prog = run::Process::new("git", [~"add", ~"-A"],
-                                     run::ProcessOptions { env: None,
-                                                          dir: Some(repo),
-                                                          in_fd: None,
-                                                          out_fd: None,
-                                                          err_fd: None
-                                                         });
-    let output = prog.finish_with_output();
-    if output.status != 0 {
-        fail!("Couldn't add all files in %s: output was %s",
-              repo.to_str(), str::from_bytes(output.output + output.error))
-    }
+    run_git([~"add", ~"-A"], None, repo, fmt!("Couldn't add all files in %s", repo.to_str()));
 }
 
 fn add_git_tag(repo: &Path, tag: ~str) {
     assert!(repo.is_absolute());
     git_add_all(repo);
     git_commit(repo, ~"whatever");
-    let mut prog = run::Process::new("git", [~"tag", tag.clone()],
-                                     run::ProcessOptions { env: None,
-                                                          dir: Some(repo),
-                                                          in_fd: None,
-                                                          out_fd: None,
-                                                          err_fd: None
-                                                         });
-    let output = prog.finish_with_output();
-    if output.status != 0 {
-        fail!("Couldn't add git tag %s in %s", tag, repo.to_str())
-    }
+    run_git([~"tag", tag.clone()], None, repo,
+            fmt!("Couldn't add git tag %s in %s", tag, repo.to_str()));
 }
 
 fn is_rwx(p: &Path) -> bool {
@@ -231,6 +188,25 @@ fn test_sysroot() -> Path {
     self_path.pop()
 }
 
+// Returns the path to rustpkg
+fn rustpkg_exec() -> Path {
+    // Ugh
+    let first_try = test_sysroot().push("lib").push("rustc")
+        .push(host_triple()).push("bin").push("rustpkg");
+    if is_executable(&first_try) {
+        first_try
+    }
+    else {
+        let second_try = test_sysroot().push("bin").push("rustpkg");
+        if is_executable(&second_try) {
+            second_try
+        }
+        else {
+            fail!("in rustpkg test, can't find an installed rustpkg");
+        }
+    }
+}
+
 fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
     command_line_test_with_env(args, cwd, None)
 }
@@ -240,8 +216,9 @@ fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
 /// Returns the process's output.
 fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>)
     -> ProcessOutput {
-    let cmd = test_sysroot().push("bin").push("rustpkg").to_str();
-    debug!("About to run command: %? %? in %s", cmd, args, cwd.to_str());
+    let cmd = rustpkg_exec().to_str();
+    debug!("cd %s; %s %s",
+           cwd.to_str(), cmd, args.connect(" "));
     assert!(os::path_is_dir(&*cwd));
     let cwd = (*cwd).clone();
     let mut prog = run::Process::new(cmd, args, run::ProcessOptions {
@@ -263,14 +240,15 @@ So tests that use this need to check the existence of a file
 to make sure the command succeeded
 */
     if output.status != 0 {
-        fail!("Command %s %? failed with exit code %?",
-              cmd, args, output.status);
+        fail!("Command %s %? failed with exit code %?; its output was {{{ %s }}}",
+              cmd, args, output.status,
+              str::from_bytes(output.output) + str::from_bytes(output.error));
     }
     output
 }
 
 fn create_local_package(pkgid: &PkgId) -> Path {
-    let parent_dir = mk_temp_workspace(&pkgid.local_path, &pkgid.version);
+    let parent_dir = mk_temp_workspace(&pkgid.path, &pkgid.version);
     debug!("Created empty package dir for %s, returning %s", pkgid.to_str(), parent_dir.to_str());
     parent_dir.pop().pop()
 }
@@ -327,26 +305,26 @@ fn create_local_package_with_custom_build_hook(pkgid: &PkgId,
 
 }
 
-fn assert_lib_exists(repo: &Path, short_name: &str, v: Version) {
+fn assert_lib_exists(repo: &Path, short_name: &str, _v: Version) { // ??? version?
     debug!("assert_lib_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
-    let lib = target_library_in_workspace(&(PkgId {
-        version: v, ..PkgId::new(short_name, repo)}
-                                           ), repo);
-    debug!("assert_lib_exists: checking whether %s exists", lib.to_str());
-    assert!(os::path_exists(&lib));
-    assert!(is_rwx(&lib));
+    let lib = installed_library_in_workspace(short_name, repo);
+    debug!("assert_lib_exists: checking whether %? exists", lib);
+    assert!(lib.is_some());
+    let libname = lib.get_ref();
+    assert!(os::path_exists(libname));
+    assert!(is_rwx(libname));
 }
 
 fn assert_executable_exists(repo: &Path, short_name: &str) {
     debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
-    let exec = target_executable_in_workspace(&PkgId::new(short_name, repo), repo);
+    let exec = target_executable_in_workspace(&PkgId::new(short_name), repo);
     assert!(os::path_exists(&exec));
     assert!(is_rwx(&exec));
 }
 
 fn assert_built_executable_exists(repo: &Path, short_name: &str) {
     debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
-    let exec = built_executable_in_workspace(&PkgId::new(short_name, repo),
+    let exec = built_executable_in_workspace(&PkgId::new(short_name),
                                              repo).expect("assert_built_executable_exists failed");
     assert!(os::path_exists(&exec));
     assert!(is_rwx(&exec));
@@ -372,11 +350,11 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~
     result
 }
 
-// assumes short_name and local_path are one and the same -- I should fix
+// assumes short_name and path are one and the same -- I should fix
 fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path {
     debug!("lib_output_file_name: given %s and parent %s and short name %s",
            workspace.to_str(), parent, short_name);
-    library_in_workspace(&normalize(RemotePath(Path(short_name))),
+    library_in_workspace(&Path(short_name),
                          short_name,
                          Build,
                          workspace,
@@ -451,7 +429,7 @@ fn test_install_valid() {
     debug!("sysroot = %s", sysroot.to_str());
     let ctxt = fake_ctxt(Some(@sysroot));
     let temp_pkg_id = fake_pkg();
-    let temp_workspace = mk_temp_workspace(&temp_pkg_id.local_path, &NoVersion).pop().pop();
+    let temp_workspace = mk_temp_workspace(&temp_pkg_id.path, &NoVersion).pop().pop();
     debug!("temp_workspace = %s", temp_workspace.to_str());
     // should have test, bench, lib, and main
     ctxt.install(&temp_workspace, &temp_pkg_id);
@@ -504,7 +482,7 @@ fn test_install_git() {
     let sysroot = test_sysroot();
     debug!("sysroot = %s", sysroot.to_str());
     let temp_pkg_id = git_repo_pkg();
-    let repo = init_git_repo(&Path(temp_pkg_id.local_path.to_str()));
+    let repo = init_git_repo(&temp_pkg_id.path);
     let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test_pkg");
     writeFile(&repo_subdir.push("main.rs"),
               "fn main() { let _x = (); }");
@@ -517,9 +495,9 @@ fn test_install_git() {
     add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files
 
     debug!("test_install_git: calling rustpkg install %s in %s",
-           temp_pkg_id.local_path.to_str(), repo.to_str());
+           temp_pkg_id.path.to_str(), repo.to_str());
     // should have test, bench, lib, and main
-    command_line_test([~"install", temp_pkg_id.local_path.to_str()], &repo);
+    command_line_test([~"install", temp_pkg_id.path.to_str()], &repo);
     // Check that all files exist
     debug!("Checking for files in %s", repo.to_str());
     let exec = target_executable_in_workspace(&temp_pkg_id, &repo);
@@ -563,18 +541,18 @@ fn test_package_ids_must_be_relative_path_like() {
 
     */
 
-    let whatever = PkgId::new("foo", &os::getcwd());
+    let whatever = PkgId::new("foo");
 
     assert_eq!(~"foo-0.1", whatever.to_str());
-    assert!("github.com/catamorphism/test_pkg-0.1" ==
-            PkgId::new("github.com/catamorphism/test-pkg", &os::getcwd()).to_str());
+    assert!("github.com/catamorphism/test-pkg-0.1" ==
+            PkgId::new("github.com/catamorphism/test-pkg").to_str());
 
     do cond.trap(|(p, e)| {
         assert!("" == p.to_str());
         assert!("0-length pkgid" == e);
         whatever.clone()
     }).inside {
-        let x = PkgId::new("", &os::getcwd());
+        let x = PkgId::new("");
         assert_eq!(~"foo-0.1", x.to_str());
     }
 
@@ -583,8 +561,7 @@ fn test_package_ids_must_be_relative_path_like() {
         assert!("absolute pkgid" == e);
         whatever.clone()
     }).inside {
-        let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str(),
-                           &os::getcwd());
+        let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str());
         assert_eq!(~"foo-0.1", z.to_str());
     }
 
@@ -607,7 +584,7 @@ fn test_package_version() {
               "#[bench] pub fn f() { (); }");
     add_git_tag(&repo_subdir, ~"0.4");
 
-    let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version", &repo);
+    let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version");
     match temp_pkg_id.version {
         ExactRevision(~"0.4") => (),
         _ => fail!(fmt!("test_package_version: package version was %?, expected Some(0.4)",
@@ -656,7 +633,7 @@ fn test_package_request_version() {
         }
         None    => false
     });
-    let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3", &repo);
+    let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3");
     assert!(target_executable_in_workspace(&temp_pkg_id, &repo.push(".rust"))
             == repo.push(".rust").push("bin").push("test_pkg_version"));
 
@@ -696,12 +673,12 @@ fn rustpkg_library_target() {
 
     add_git_tag(&package_dir, ~"1.0");
     command_line_test([~"install", ~"foo"], &foo_repo);
-    assert_lib_exists(&foo_repo, "foo", ExactRevision(~"1.0"));
+    assert_lib_exists(&foo_repo.push(".rust"), "foo", ExactRevision(~"1.0"));
 }
 
 #[test]
 fn rustpkg_local_pkg() {
-    let dir = create_local_package(&PkgId::new("foo", &os::getcwd()));
+    let dir = create_local_package(&PkgId::new("foo"));
     command_line_test([~"install", ~"foo"], &dir);
     assert_executable_exists(&dir, "foo");
 }
@@ -711,7 +688,7 @@ fn rustpkg_local_pkg() {
 #[test]
 #[ignore]
 fn package_script_with_default_build() {
-    let dir = create_local_package(&PkgId::new("fancy-lib", &os::getcwd()));
+    let dir = create_local_package(&PkgId::new("fancy-lib"));
     debug!("dir = %s", dir.to_str());
     let source = test_sysroot().pop().pop().pop().push("src").push("librustpkg").
         push("testsuite").push("pass").push("src").push("fancy-lib").push("pkg.rs");
@@ -763,7 +740,7 @@ fn rustpkg_clean_no_arg() {
     command_line_test([~"build"], &package_dir);
     assert_built_executable_exists(&tmp, "foo");
     command_line_test([~"clean"], &package_dir);
-    assert!(!built_executable_in_workspace(&PkgId::new("foo", &package_dir),
+    assert!(!built_executable_in_workspace(&PkgId::new("foo"),
                 &tmp).map_default(false, |m| { os::path_exists(m) }));
 }
 
@@ -771,14 +748,13 @@ fn rustpkg_clean_no_arg() {
 #[ignore (reason = "Specifying env doesn't work -- see #8028")]
 fn rust_path_test() {
     let dir_for_path = mkdtemp(&os::tmpdir(), "more_rust").expect("rust_path_test failed");
-    let dir = mk_workspace(&dir_for_path, &normalize(RemotePath(Path("foo"))), &NoVersion);
+    let dir = mk_workspace(&dir_for_path, &Path("foo"), &NoVersion);
     debug!("dir = %s", dir.to_str());
     writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }");
 
     let cwd = os::getcwd();
     debug!("cwd = %s", cwd.to_str());
-    debug!("Running command: cd %s; RUST_LOG=rustpkg RUST_PATH=%s rustpkg install foo",
-           cwd.to_str(), dir_for_path.to_str());
+                                     // use command_line_test_with_env
     let mut prog = run::Process::new("rustpkg",
                                      [~"install", ~"foo"],
                                      run::ProcessOptions { env: Some(&[(~"RUST_LOG",
@@ -830,39 +806,38 @@ fn rust_path_parse() {
 #[test]
 fn test_list() {
     let dir = mkdtemp(&os::tmpdir(), "test_list").expect("test_list failed");
-    let foo = PkgId::new("foo", &dir);
+    let foo = PkgId::new("foo");
     create_local_package_in(&foo, &dir);
-    let bar = PkgId::new("bar", &dir);
+    let bar = PkgId::new("bar");
     create_local_package_in(&bar, &dir);
-    let quux = PkgId::new("quux", &dir);
+    let quux = PkgId::new("quux");
     create_local_package_in(&quux, &dir);
 
-// NOTE Not really great output, though...
-// NOTE do any tests need to be unignored?
+// list doesn't output very much right now...
     command_line_test([~"install", ~"foo"], &dir);
     let env_arg = ~[(~"RUST_PATH", dir.to_str())];
     debug!("RUST_PATH = %s", dir.to_str());
     let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
-    assert!(list_output.iter().any(|x| x.starts_with("libfoo_")));
+    assert!(list_output.iter().any(|x| x.starts_with("foo")));
 
     command_line_test([~"install", ~"bar"], &dir);
     let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
-    assert!(list_output.iter().any(|x| x.starts_with("libfoo_")));
-    assert!(list_output.iter().any(|x| x.starts_with("libbar_")));
+    assert!(list_output.iter().any(|x| x.starts_with("foo")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar")));
 
     command_line_test([~"install", ~"quux"], &dir);
     let list_output = command_line_test_output_with_env([~"list"], env_arg);
-    assert!(list_output.iter().any(|x| x.starts_with("libfoo_")));
-    assert!(list_output.iter().any(|x| x.starts_with("libbar_")));
-    assert!(list_output.iter().any(|x| x.starts_with("libquux_")));
+    assert!(list_output.iter().any(|x| x.starts_with("foo")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar")));
+    assert!(list_output.iter().any(|x| x.starts_with("quux")));
 }
 
 #[test]
 fn install_remove() {
     let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove");
-    let foo = PkgId::new("foo", &dir);
-    let bar = PkgId::new("bar", &dir);
-    let quux = PkgId::new("quux", &dir);
+    let foo = PkgId::new("foo");
+    let bar = PkgId::new("bar");
+    let quux = PkgId::new("quux");
     create_local_package_in(&foo, &dir);
     create_local_package_in(&bar, &dir);
     create_local_package_in(&quux, &dir);
@@ -887,7 +862,7 @@ fn install_check_duplicates() {
     // ("Is already installed -- doing nothing")
     // check invariant that there are no dups in the pkg database
     let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove");
-    let foo = PkgId::new("foo", &dir);
+    let foo = PkgId::new("foo");
     create_local_package_in(&foo, &dir);
 
     command_line_test([~"install", ~"foo"], &dir);
@@ -895,7 +870,7 @@ fn install_check_duplicates() {
     let mut contents = ~[];
     let check_dups = |p: &PkgId| {
         if contents.contains(p) {
-            fail!("package %s appears in `list` output more than once", p.local_path.to_str());
+            fail!("package %s appears in `list` output more than once", p.path.to_str());
         }
         else {
             contents.push((*p).clone());
@@ -908,7 +883,7 @@ fn install_check_duplicates() {
 #[test]
 #[ignore(reason = "Workcache not yet implemented -- see #7075")]
 fn no_rebuilding() {
-    let p_id = PkgId::new("foo", &os::getcwd());
+    let p_id = PkgId::new("foo");
     let workspace = create_local_package(&p_id);
     command_line_test([~"build", ~"foo"], &workspace);
     let date = datestamp(&built_library_in_workspace(&p_id,
@@ -922,8 +897,8 @@ fn no_rebuilding() {
 #[test]
 #[ignore(reason = "Workcache not yet implemented -- see #7075")]
 fn no_rebuilding_dep() {
-    let p_id = PkgId::new("foo", &os::getcwd());
-    let dep_id = PkgId::new("bar", &os::getcwd());
+    let p_id = PkgId::new("foo");
+    let dep_id = PkgId::new("bar");
     let workspace = create_local_package_with_dep(&p_id, &dep_id);
     command_line_test([~"build", ~"foo"], &workspace);
     let bar_date = datestamp(&lib_output_file_name(&workspace,
@@ -935,8 +910,8 @@ fn no_rebuilding_dep() {
 
 #[test]
 fn do_rebuild_dep_dates_change() {
-    let p_id = PkgId::new("foo", &os::getcwd());
-    let dep_id = PkgId::new("bar", &os::getcwd());
+    let p_id = PkgId::new("foo");
+    let dep_id = PkgId::new("bar");
     let workspace = create_local_package_with_dep(&p_id, &dep_id);
     command_line_test([~"build", ~"foo"], &workspace);
     let bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar"));
@@ -948,8 +923,8 @@ fn do_rebuild_dep_dates_change() {
 
 #[test]
 fn do_rebuild_dep_only_contents_change() {
-    let p_id = PkgId::new("foo", &os::getcwd());
-    let dep_id = PkgId::new("bar", &os::getcwd());
+    let p_id = PkgId::new("foo");
+    let dep_id = PkgId::new("bar");
     let workspace = create_local_package_with_dep(&p_id, &dep_id);
     command_line_test([~"build", ~"foo"], &workspace);
     let bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar"));
@@ -962,8 +937,8 @@ fn do_rebuild_dep_only_contents_change() {
 
 #[test]
 fn test_versions() {
-    let workspace = create_local_package(&PkgId::new("foo#0.1", &os::getcwd()));
-    create_local_package(&PkgId::new("foo#0.2", &os::getcwd()));
+    let workspace = create_local_package(&PkgId::new("foo#0.1"));
+    create_local_package(&PkgId::new("foo#0.2"));
     command_line_test([~"install", ~"foo#0.1"], &workspace);
     let output = command_line_test_output([~"list"]);
     // make sure output includes versions
@@ -973,7 +948,7 @@ fn test_versions() {
 #[test]
 #[ignore(reason = "do not yet implemented")]
 fn test_build_hooks() {
-    let workspace = create_local_package_with_custom_build_hook(&PkgId::new("foo", &os::getcwd()),
+    let workspace = create_local_package_with_custom_build_hook(&PkgId::new("foo"),
                                                                 "frob");
     command_line_test([~"do", ~"foo", ~"frob"], &workspace);
 }
@@ -983,7 +958,7 @@ fn test_build_hooks() {
 #[ignore(reason = "info not yet implemented")]
 fn test_info() {
     let expected_info = ~"package foo"; // fill in
-    let workspace = create_local_package(&PkgId::new("foo", &os::getcwd()));
+    let workspace = create_local_package(&PkgId::new("foo"));
     let output = command_line_test([~"info", ~"foo"], &workspace);
     assert_eq!(str::from_bytes(output.output), expected_info);
 }
@@ -992,7 +967,7 @@ fn test_info() {
 #[ignore(reason = "test not yet implemented")]
 fn test_rustpkg_test() {
     let expected_results = ~"1 out of 1 tests passed"; // fill in
-    let workspace = create_local_package_with_test(&PkgId::new("foo", &os::getcwd()));
+    let workspace = create_local_package_with_test(&PkgId::new("foo"));
     let output = command_line_test([~"test", ~"foo"], &workspace);
     assert_eq!(str::from_bytes(output.output), expected_results);
 }
@@ -1000,7 +975,7 @@ fn test_rustpkg_test() {
 #[test]
 #[ignore(reason = "test not yet implemented")]
 fn test_uninstall() {
-    let workspace = create_local_package(&PkgId::new("foo", &os::getcwd()));
+    let workspace = create_local_package(&PkgId::new("foo"));
     let _output = command_line_test([~"info", ~"foo"], &workspace);
     command_line_test([~"uninstall", ~"foo"], &workspace);
     let output = command_line_test([~"list"], &workspace);
@@ -1010,8 +985,8 @@ fn test_uninstall() {
 #[test]
 fn test_non_numeric_tag() {
     let temp_pkg_id = git_repo_pkg();
-    let repo = init_git_repo(&Path(temp_pkg_id.local_path.to_str()));
-    let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test_pkg");
+    let repo = init_git_repo(&temp_pkg_id.path);
+    let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test-pkg");
     writeFile(&repo_subdir.push("foo"), "foo");
     writeFile(&repo_subdir.push("lib.rs"),
               "pub fn f() { let _x = (); }");
@@ -1021,13 +996,70 @@ fn test_non_numeric_tag() {
     writeFile(&repo_subdir.push("not_on_testbranch_only"), "bye bye");
     add_all_and_commit(&repo_subdir);
 
-
-    command_line_test([~"install", fmt!("%s#testbranch", temp_pkg_id.remote_path.to_str())],
-                      &repo);
+    command_line_test([~"install", fmt!("%s#testbranch", temp_pkg_id.path.to_str())], &repo);
     let file1 = repo.push_many(["mockgithub.com", "catamorphism",
-                                "test_pkg", "testbranch_only"]);
-    let file2 = repo.push_many(["mockgithub.com", "catamorphism", "test_pkg",
+                                "test-pkg", "testbranch_only"]);
+    let file2 = repo.push_many(["mockgithub.com", "catamorphism", "test-pkg",
                                 "master_only"]);
     assert!(os::path_exists(&file1));
     assert!(!os::path_exists(&file2));
 }
+
+#[test]
+fn test_extern_mod() {
+    let dir = mkdtemp(&os::tmpdir(), "test_extern_mod").expect("test_extern_mod");
+    let main_file = dir.push("main.rs");
+    let lib_depend_dir = mkdtemp(&os::tmpdir(), "foo").expect("test_extern_mod");
+    let aux_dir = lib_depend_dir.push_many(["src", "mockgithub.com", "catamorphism", "test_pkg"]);
+    assert!(os::mkdir_recursive(&aux_dir, U_RWX));
+    let aux_pkg_file = aux_dir.push("lib.rs");
+
+    writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() {  assert!(true); } }\n");
+    assert!(os::path_exists(&aux_pkg_file));
+
+    writeFile(&main_file,
+              "extern mod test = \"mockgithub.com/catamorphism/test_pkg\";\nuse test::bar;\
+               fn main() { bar::assert_true(); }\n");
+
+    command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg"], &lib_depend_dir);
+
+    let exec_file = dir.push("out");
+    // Be sure to extend the existing environment
+    let env = Some([(~"RUST_PATH", lib_depend_dir.to_str())] + os::env());
+    let rustpkg_exec = rustpkg_exec();
+    let rustc = rustpkg_exec.with_filename("rustc");
+    debug!("RUST_PATH=%s %s %s \n --sysroot %s -o %s",
+                     lib_depend_dir.to_str(),
+                     rustc.to_str(),
+                     main_file.to_str(),
+                     test_sysroot().to_str(),
+                     exec_file.to_str());
+
+    let mut prog = run::Process::new(rustc.to_str(), [main_file.to_str(),
+                                                      ~"--sysroot", test_sysroot().to_str(),
+                                               ~"-o", exec_file.to_str()],
+                                     run::ProcessOptions {
+        env: env.map(|v| v.slice(0, v.len())),
+        dir: Some(&dir),
+        in_fd: None,
+        out_fd: None,
+        err_fd: None
+    });
+    let outp = prog.finish_with_output();
+    if outp.status != 0 {
+        fail!("output was %s, error was %s",
+              str::from_bytes(outp.output),
+              str::from_bytes(outp.error));
+    }
+    assert!(os::path_exists(&exec_file) && is_executable(&exec_file));
+}
+
+/// Returns true if p exists and is executable
+fn is_executable(p: &Path) -> bool {
+    use std::libc::consts::os::posix88::{S_IXUSR};
+
+    match p.get_mode() {
+        None => false,
+        Some(mode) => mode & S_IXUSR as uint == S_IXUSR as uint
+    }
+}
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 3a0954c62d7..afac9423fba 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -8,9 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::{os, result};
+use std::os;
 use rustc::driver::{driver, session};
-use rustc::metadata::filesearch;
 use extra::getopts::groups::getopts;
 use syntax::ast_util::*;
 use syntax::codemap::{dummy_sp, spanned};
@@ -19,10 +18,10 @@ use syntax::{ast, attr, codemap, diagnostic, fold};
 use syntax::attr::AttrMetaMethods;
 use rustc::back::link::output_type_exe;
 use rustc::driver::session::{lib_crate, bin_crate};
-use context::Ctx;
+use context::{Ctx, in_target};
 use package_id::PkgId;
 use search::find_library_in_search_path;
-use path_util::target_library_in_workspace;
+use path_util::{target_library_in_workspace, U_RWX};
 pub use target::{OutputType, Main, Lib, Bench, Test};
 
 // It would be nice to have the list of commands in just one place -- for example,
@@ -47,13 +46,6 @@ impl ToStr for Pkg {
     }
 }
 
-pub fn root() -> Path {
-    match filesearch::get_rustpkg_root() {
-        result::Ok(path) => path,
-        result::Err(err) => fail!(err)
-    }
-}
-
 pub fn is_cmd(cmd: &str) -> bool {
     COMMANDS.iter().any(|&c| c == cmd)
 }
@@ -162,25 +154,25 @@ pub fn ready_crate(sess: session::Session,
 pub fn compile_input(ctxt: &Ctx,
                      pkg_id: &PkgId,
                      in_file: &Path,
-                     out_dir: &Path,
+                     workspace: &Path,
                      flags: &[~str],
                      cfgs: &[~str],
                      opt: bool,
                      what: OutputType) -> bool {
 
-    let workspace = out_dir.pop().pop();
-
     assert!(in_file.components.len() > 1);
     let input = driver::file_input((*in_file).clone());
     debug!("compile_input: %s / %?", in_file.to_str(), what);
     // tjc: by default, use the package ID name as the link name
     // not sure if we should support anything else
 
+    let out_dir = workspace.push("build").push_rel(&pkg_id.path);
+
     let binary = os::args()[0].to_managed();
 
     debug!("flags: %s", flags.connect(" "));
     debug!("cfgs: %s", cfgs.connect(" "));
-    debug!("compile_input's sysroot = %?", ctxt.sysroot_opt);
+    debug!("out_dir = %s", out_dir.to_str());
 
     let crate_type = match what {
         Lib => lib_crate,
@@ -196,12 +188,22 @@ pub fn compile_input(ctxt: &Ctx,
                           + flags
                           + cfgs.flat_map(|c| { ~[~"--cfg", (*c).clone()] }),
                           driver::optgroups()).unwrap();
+    // Hack so that rustpkg can run either out of a rustc target dir,
+    // or the host dir
+    let sysroot_to_use = if !in_target(ctxt.sysroot_opt) {
+        ctxt.sysroot_opt
+    }
+    else {
+        ctxt.sysroot_opt.map(|p| { @p.pop().pop().pop() })
+    };
+    debug!("compile_input's sysroot = %?", ctxt.sysroot_opt_str());
+    debug!("sysroot_to_use = %?", sysroot_to_use);
     let options = @session::options {
         crate_type: crate_type,
         optimize: if opt { session::Aggressive } else { session::No },
         test: what == Test || what == Bench,
-        maybe_sysroot: ctxt.sysroot_opt,
-        addl_lib_search_paths: @mut (~[(*out_dir).clone()]),
+        maybe_sysroot: sysroot_to_use,
+        addl_lib_search_paths: @mut (~[out_dir.clone()]),
         // output_type should be conditional
         output_type: output_type_exe, // Use this to get a library? That's weird
         .. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone()
@@ -211,7 +213,12 @@ pub fn compile_input(ctxt: &Ctx,
     // Make sure all the library directories actually exist, since the linker will complain
     // otherwise
     for p in addl_lib_search_paths.iter() {
-        assert!(os::path_is_dir(p));
+        if os::path_exists(p) {
+            assert!(os::path_is_dir(p));
+        }
+        else {
+            assert!(os::mkdir_recursive(p, U_RWX));
+        }
     }
 
     let sess = driver::build_session(options, diagnostic::emit);
@@ -224,35 +231,44 @@ pub fn compile_input(ctxt: &Ctx,
 
     // Not really right. Should search other workspaces too, and the installed
     // database (which doesn't exist yet)
-    find_and_install_dependencies(ctxt, sess, &workspace, crate,
+    find_and_install_dependencies(ctxt, sess, workspace, crate,
                                   |p| {
                                       debug!("a dependency: %s", p.to_str());
                                       // Pass the directory containing a dependency
                                       // as an additional lib search path
-                                      addl_lib_search_paths.push(p);
+                                      if !addl_lib_search_paths.contains(&p) {
+                                          // Might be inefficient, but this set probably
+                                          // won't get too large -- tjc
+                                          addl_lib_search_paths.push(p);
+                                      }
                                   });
 
     // Inject the link attributes so we get the right package name and version
     if attr::find_linkage_metas(crate.attrs).is_empty() {
-        let short_name_to_use = match what {
-            Test  => fmt!("%stest", pkg_id.short_name),
-            Bench => fmt!("%sbench", pkg_id.short_name),
-            _     => pkg_id.short_name.clone()
+        let name_to_use = match what {
+            Test  => fmt!("%stest", pkg_id.short_name).to_managed(),
+            Bench => fmt!("%sbench", pkg_id.short_name).to_managed(),
+            _     => pkg_id.short_name.to_managed()
         };
-        debug!("Injecting link name: %s", short_name_to_use);
+        debug!("Injecting link name: %s", name_to_use);
         let link_options =
-            ~[attr::mk_name_value_item_str(@"name", short_name_to_use.to_managed()),
-              attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())];
-
+            ~[attr::mk_name_value_item_str(@"name", name_to_use),
+              attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())] +
+                        if pkg_id.is_complex() {
+                        ~[attr::mk_name_value_item_str(@"package_id",
+                                                       pkg_id.path.to_str().to_managed())]
+                } else { ~[] };
+
+        debug!("link options: %?", link_options);
         crate = @ast::Crate {
             attrs: ~[attr::mk_attr(attr::mk_list_item(@"link", link_options))],
             .. (*crate).clone()
-        };
+        }
     }
 
-    debug!("calling compile_crate_from_input, out_dir = %s,
+    debug!("calling compile_crate_from_input, workspace = %s,
            building_library = %?", out_dir.to_str(), sess.building_library);
-    compile_crate_from_input(&input, out_dir, sess, crate);
+    compile_crate_from_input(&input, &out_dir, sess, crate);
     true
 }
 
@@ -262,17 +278,22 @@ pub fn compile_input(ctxt: &Ctx,
 // call compile_upto and return the crate
 // also, too many arguments
 pub fn compile_crate_from_input(input: &driver::input,
-                                build_dir: &Path,
+ // should be of the form <workspace>/build/<pkg id's path>
+                                out_dir: &Path,
                                 sess: session::Session,
                                 crate: @ast::Crate) {
     debug!("Calling build_output_filenames with %s, building library? %?",
-           build_dir.to_str(), sess.building_library);
+           out_dir.to_str(), sess.building_library);
 
     // bad copy
-    let outputs = driver::build_output_filenames(input, &Some((*build_dir).clone()), &None,
+    debug!("out_dir = %s", out_dir.to_str());
+    let outputs = driver::build_output_filenames(input, &Some(out_dir.clone()), &None,
                                                  crate.attrs, sess);
 
-    debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type);
+    debug!("Outputs are out_filename: %s and obj_filename: %s and output type = %?",
+           outputs.out_filename.to_str(),
+           outputs.obj_filename.to_str(),
+           sess.opts.output_type);
     debug!("additional libraries:");
     for lib in sess.opts.addl_lib_search_paths.iter() {
         debug!("an additional library: %s", lib.to_str());
@@ -298,15 +319,15 @@ pub fn exe_suffix() -> ~str { ~"" }
 // Called by build_crates
 // FIXME (#4432): Use workcache to only compile when needed
 pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId,
-                     crate: &Path, dir: &Path,
+                     crate: &Path, workspace: &Path,
                      flags: &[~str], cfgs: &[~str], opt: bool,
                      what: OutputType) -> bool {
-    debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str());
+    debug!("compile_crate: crate=%s, workspace=%s", crate.to_str(), workspace.to_str());
     debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
     for fl in flags.iter() {
         debug!("+++ %s", *fl);
     }
-    compile_input(ctxt, pkg_id, crate, dir, flags, cfgs, opt, what)
+    compile_input(ctxt, pkg_id, crate, workspace, flags, cfgs, opt, what)
 }
 
 
@@ -327,19 +348,20 @@ pub fn find_and_install_dependencies(ctxt: &Ctx,
         debug!("A view item!");
         match vi.node {
             // ignore metadata, I guess
-            ast::view_item_extern_mod(lib_ident, _, _) => {
+            ast::view_item_extern_mod(lib_ident, path_opt, _, _) => {
                 match my_ctxt.sysroot_opt {
-                    Some(ref x) => debug!("sysroot: %s", x.to_str()),
+                    Some(ref x) => debug!("*** sysroot: %s", x.to_str()),
                     None => debug!("No sysroot given")
                 };
-                let lib_name = sess.str_of(lib_ident);
+                let lib_name = match path_opt { // ???
+                    Some(p) => p, None => sess.str_of(lib_ident) };
                 match find_library_in_search_path(my_ctxt.sysroot_opt, lib_name) {
                     Some(installed_path) => {
                         debug!("It exists: %s", installed_path.to_str());
                     }
                     None => {
                         // Try to install it
-                        let pkg_id = PkgId::new(lib_name, &os::getcwd());
+                        let pkg_id = PkgId::new(lib_name);
                         my_ctxt.install(&my_workspace, &pkg_id);
                         // Also, add an additional search path
                         debug!("let installed_path...")
diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs
index 88391850891..ab4f47ba69a 100644
--- a/src/librustpkg/version.rs
+++ b/src/librustpkg/version.rs
@@ -15,8 +15,8 @@ extern mod std;
 
 use extra::semver;
 use std::{char, os, result, run, str};
-use package_path::RemotePath;
 use extra::tempfile::mkdtemp;
+use path_util::rust_path;
 
 #[deriving(Clone)]
 pub enum Version {
@@ -92,19 +92,22 @@ pub fn parse_vers(vers: ~str) -> result::Result<semver::Version, ~str> {
     }
 }
 
-/// If `local_path` is a git repo, and the most recent tag in that repo denotes a version,
-/// return it; otherwise, `None`
+/// If `local_path` is a git repo in the RUST_PATH, and the most recent tag
+/// in that repo denotes a version, return it; otherwise, `None`
 pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
-    debug!("in try_getting_local_version");
-    let outp = run::process_output("git",
+    let rustpath = rust_path();
+    for rp in rustpath.iter() {
+        let local_path = rp.push_rel(local_path);
+        debug!("in try_getting_local_version");
+        let outp = run::process_output("git",
                                    [fmt!("--git-dir=%s", local_path.push(".git").to_str()),
                                     ~"tag", ~"-l"]);
 
-    debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status);
+        debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status);
 
-    if outp.status != 0 {
-        return None;
-    }
+        if outp.status != 0 {
+            loop;
+        }
 
     let mut output = None;
     let output_text = str::from_bytes(outp.output);
@@ -112,14 +115,19 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
         if !l.is_whitespace() {
             output = Some(l);
         }
+        match output.chain(try_parsing_version) {
+            Some(v) => return Some(v),
+            None    => ()
+        }
     }
-    output.chain(try_parsing_version)
+  }
+  None
 }
 
 /// If `remote_path` refers to a git repo that can be downloaded,
 /// and the most recent tag in that repo denotes a version, return it;
 /// otherwise, `None`
-pub fn try_getting_version(remote_path: &RemotePath) -> Option<Version> {
+pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
     debug!("try_getting_version: %s", remote_path.to_str());
     if is_url_like(remote_path) {
         debug!("Trying to fetch its sources..");
@@ -190,7 +198,7 @@ fn try_parsing_version(s: &str) -> Option<Version> {
 }
 
 /// Just an approximation
-fn is_url_like(p: &RemotePath) -> bool {
+fn is_url_like(p: &Path) -> bool {
     let str = p.to_str();
     str.split_iter('/').len_() > 2
 }
diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs
index d877b4ff489..3e0e08dfe2d 100644
--- a/src/librustpkg/workspace.rs
+++ b/src/librustpkg/workspace.rs
@@ -12,9 +12,11 @@
 
 use std::os;
 use std::path::Path;
-use path_util::{rust_path, workspace_contains_package_id};
+use path_util::workspace_contains_package_id;
 use package_id::PkgId;
 
+use rustc::metadata::filesearch::rust_path;
+
 pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {
     // Using the RUST_PATH, find workspaces that contain
     // this package ID
@@ -23,7 +25,7 @@ pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> b
         // tjc: make this a condition
         fail!("Package %s not found in any of \
                     the following workspaces: %s",
-                   pkgid.remote_path.to_str(),
+                   pkgid.path.to_str(),
                    rust_path().to_str());
     }
     for ws in workspaces.iter() {
@@ -58,5 +60,5 @@ pub fn cwd_to_workspace() -> (Path, PkgId) {
     let ws = cwd.pop().pop();
     let cwd_ = cwd.clone();
     let pkgid = cwd_.components.last().to_str();
-    (ws, PkgId::new(pkgid, &cwd))
+    (ws, PkgId::new(pkgid))
 }
diff --git a/src/libstd/bool.rs b/src/libstd/bool.rs
index eeaea6a2cff..598e8080618 100644
--- a/src/libstd/bool.rs
+++ b/src/libstd/bool.rs
@@ -284,12 +284,6 @@ impl Not<bool> for bool {
 impl Ord for bool {
     #[inline]
     fn lt(&self, other: &bool) -> bool { to_bit(*self) < to_bit(*other) }
-    #[inline]
-    fn le(&self, other: &bool) -> bool { to_bit(*self) <= to_bit(*other) }
-    #[inline]
-    fn gt(&self, other: &bool) -> bool { to_bit(*self) > to_bit(*other) }
-    #[inline]
-    fn ge(&self, other: &bool) -> bool { to_bit(*self) >= to_bit(*other) }
 }
 
 #[cfg(not(test))]
diff --git a/src/libstd/char.rs b/src/libstd/char.rs
index 85053855432..9c55e22b1f8 100644
--- a/src/libstd/char.rs
+++ b/src/libstd/char.rs
@@ -322,12 +322,6 @@ impl Eq for char {
 impl Ord for char {
     #[inline]
     fn lt(&self, other: &char) -> bool { *self < *other }
-    #[inline]
-    fn le(&self, other: &char) -> bool { *self <= *other }
-    #[inline]
-    fn gt(&self, other: &char) -> bool { *self > *other }
-    #[inline]
-    fn ge(&self, other: &char) -> bool { *self >= *other }
 }
 
 #[cfg(not(test))]
diff --git a/src/libstd/cmp.rs b/src/libstd/cmp.rs
index b66f89e8341..28d45abb688 100644
--- a/src/libstd/cmp.rs
+++ b/src/libstd/cmp.rs
@@ -101,12 +101,6 @@ impl TotalOrd for Ordering {
 impl Ord for Ordering {
     #[inline]
     fn lt(&self, other: &Ordering) -> bool { (*self as int) < (*other as int) }
-    #[inline]
-    fn le(&self, other: &Ordering) -> bool { (*self as int) <= (*other as int) }
-    #[inline]
-    fn gt(&self, other: &Ordering) -> bool { (*self as int) > (*other as int) }
-    #[inline]
-    fn ge(&self, other: &Ordering) -> bool { (*self as int) >= (*other as int) }
 }
 
 macro_rules! totalord_impl(
@@ -174,8 +168,11 @@ pub fn lexical_ordering(o1: Ordering, o2: Ordering) -> Ordering {
 #[lang="ord"]
 pub trait Ord {
     fn lt(&self, other: &Self) -> bool;
+    #[inline]
     fn le(&self, other: &Self) -> bool { !other.lt(self) }
+    #[inline]
     fn gt(&self, other: &Self) -> bool {  other.lt(self) }
+    #[inline]
     fn ge(&self, other: &Self) -> bool { !self.lt(other) }
 }
 
diff --git a/src/libstd/nil.rs b/src/libstd/nil.rs
index 81b7662e581..3507dc9d2b2 100644
--- a/src/libstd/nil.rs
+++ b/src/libstd/nil.rs
@@ -33,12 +33,6 @@ impl Eq for () {
 impl Ord for () {
     #[inline]
     fn lt(&self, _other: &()) -> bool { false }
-    #[inline]
-    fn le(&self, _other: &()) -> bool { true }
-    #[inline]
-    fn ge(&self, _other: &()) -> bool { true }
-    #[inline]
-    fn gt(&self, _other: &()) -> bool { false }
 }
 
 #[cfg(not(test))]
diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs
index b692bedebfd..41da9a6ccbe 100644
--- a/src/libstd/num/int_macros.rs
+++ b/src/libstd/num/int_macros.rs
@@ -130,12 +130,6 @@ impl Num for $T {}
 impl Ord for $T {
     #[inline]
     fn lt(&self, other: &$T) -> bool { return (*self) < (*other); }
-    #[inline]
-    fn le(&self, other: &$T) -> bool { return (*self) <= (*other); }
-    #[inline]
-    fn ge(&self, other: &$T) -> bool { return (*self) >= (*other); }
-    #[inline]
-    fn gt(&self, other: &$T) -> bool { return (*self) > (*other); }
 }
 
 #[cfg(not(test))]
diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs
index 29b8f29d87d..86b5b4ddfc0 100644
--- a/src/libstd/num/uint_macros.rs
+++ b/src/libstd/num/uint_macros.rs
@@ -131,12 +131,6 @@ impl Num for $T {}
 impl Ord for $T {
     #[inline]
     fn lt(&self, other: &$T) -> bool { (*self) < (*other) }
-    #[inline]
-    fn le(&self, other: &$T) -> bool { (*self) <= (*other) }
-    #[inline]
-    fn ge(&self, other: &$T) -> bool { (*self) >= (*other) }
-    #[inline]
-    fn gt(&self, other: &$T) -> bool { (*self) > (*other) }
 }
 
 #[cfg(not(test))]
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index 365ddeb4f64..7def75907f8 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -1152,9 +1152,9 @@ pub fn real_args() -> ~[~str] {
 #[cfg(target_os = "freebsd")]
 pub fn real_args() -> ~[~str] {
     use rt;
-    use rt::TaskContext;
+    use rt::NewRtContext;
 
-    if rt::context() == TaskContext {
+    if rt::context() == NewRtContext {
         match rt::args::clone() {
             Some(args) => args,
             None => fail!("process arguments not initialized")
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index de34ec4bed5..14844e24006 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -21,13 +21,14 @@ use c_str;
 use clone::Clone;
 use cmp::Eq;
 use container::Container;
-use iterator::{Iterator, IteratorUtil};
+use iterator::{Iterator, IteratorUtil, range};
 use libc;
+use num;
 use option::{None, Option, Some};
 use str::{OwnedStr, Str, StrSlice, StrVector};
 use to_str::ToStr;
 use ascii::{AsciiCast, AsciiStr};
-use vec::{OwnedVector, ImmutableVector};
+use vec::{OwnedVector, ImmutableVector, OwnedCopyableVector};
 
 #[cfg(windows)]
 pub use Path = self::WindowsPath;
@@ -126,6 +127,43 @@ pub trait GenericPath {
 
     /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples
     fn is_ancestor_of(&self, (&Self)) -> bool;
+
+    /// Find the relative path from one file to another
+    fn get_relative_to(&self, abs2: (&Self)) -> Self {
+        assert!(self.is_absolute());
+        assert!(abs2.is_absolute());
+        let abs1 = self.normalize();
+        let abs2 = abs2.normalize();
+
+        let split1: &[~str] = abs1.components();
+        let split2: &[~str] = abs2.components();
+        let len1 = split1.len();
+        let len2 = split2.len();
+        assert!(len1 > 0);
+        assert!(len2 > 0);
+
+        let max_common_path = num::min(len1, len2) - 1;
+        let mut start_idx = 0;
+        while start_idx < max_common_path
+            && split1[start_idx] == split2[start_idx] {
+            start_idx += 1;
+        }
+
+        let mut path: ~[~str] = ~[];
+        for _ in range(start_idx, len1 - 1) { path.push(~".."); };
+
+        path.push_all(split2.slice(start_idx, len2 - 1));
+
+        let mut result: Self = GenericPath::from_str(".");
+        if !path.is_empty() {
+            // Without this type hint, the typechecker doesn't seem to like it
+            let p: Self = GenericPath::from_str("");
+            result = p.push_many(path);
+        };
+        result
+    }
+
+    fn components(self) -> ~[~str];
 }
 
 #[cfg(target_os = "linux")]
@@ -711,6 +749,7 @@ impl GenericPath for PosixPath {
              self.is_ancestor_of(&other.pop()))
     }
 
+   fn components(self) -> ~[~str] { self.components }
 }
 
 
@@ -998,6 +1037,8 @@ impl GenericPath for WindowsPath {
             (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
              self.is_ancestor_of(&other.pop()))
     }
+
+   fn components(self) -> ~[~str] { self.components }
 }
 
 pub fn normalize(components: &[~str]) -> ~[~str] {
@@ -1354,4 +1395,124 @@ mod tests {
 
     }
 
+    #[test]
+    fn test_relative_to1() {
+        let p1 = PosixPath("/usr/bin/rustc");
+        let p2 = PosixPath("/usr/lib/mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("../lib"));
+
+        let p1 = WindowsPath("C:\\usr\\bin\\rustc");
+        let p2 = WindowsPath("C:\\usr\\lib\\mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("..\\lib"));
+
+    }
+
+    #[test]
+    fn test_relative_to2() {
+        let p1 = PosixPath("/usr/bin/rustc");
+        let p2 = PosixPath("/usr/bin/../lib/mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("../lib"));
+
+        let p1 = WindowsPath("C:\\usr\\bin\\rustc");
+        let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("..\\lib"));
+    }
+
+    #[test]
+    fn test_relative_to3() {
+        let p1 = PosixPath("/usr/bin/whatever/rustc");
+        let p2 = PosixPath("/usr/lib/whatever/mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("../../lib/whatever"));
+
+        let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc");
+        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("..\\..\\lib\\whatever"));
+
+    }
+
+    #[test]
+    fn test_relative_to4() {
+        let p1 = PosixPath("/usr/bin/whatever/../rustc");
+        let p2 = PosixPath("/usr/lib/whatever/mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("../lib/whatever"));
+
+        let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc");
+        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("..\\lib\\whatever"));
+
+    }
+
+    #[test]
+    fn test_relative_to5() {
+        let p1 = PosixPath("/usr/bin/whatever/../rustc");
+        let p2 = PosixPath("/usr/lib/whatever/../mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("../lib"));
+
+        let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc");
+        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("..\\lib"));
+    }
+
+    #[test]
+    fn test_relative_to6() {
+        let p1 = PosixPath("/1");
+        let p2 = PosixPath("/2/3");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath("2"));
+
+        let p1 = WindowsPath("C:\\1");
+        let p2 = WindowsPath("C:\\2\\3");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath("2"));
+
+    }
+
+    #[test]
+    fn test_relative_to7() {
+        let p1 = PosixPath("/1/2");
+        let p2 = PosixPath("/3");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, PosixPath(".."));
+
+        let p1 = WindowsPath("C:\\1\\2");
+        let p2 = WindowsPath("C:\\3");
+        let res = p1.get_relative_to(&p2);
+        assert_eq!(res, WindowsPath(".."));
+
+    }
+
+    #[test]
+    fn test_relative_to8() {
+        let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel(
+            &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
+        let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
+            &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
+        let res = p1.get_relative_to(&p2);
+        debug!("test_relative_to8: %s vs. %s",
+               res.to_str(),
+               PosixPath(".").to_str());
+        assert_eq!(res, PosixPath("."));
+
+        let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel(
+            &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so"));
+        let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel(
+            &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so"));
+        let res = p1.get_relative_to(&p2);
+        debug!("test_relative_to8: %s vs. %s",
+               res.to_str(),
+               WindowsPath(".").to_str());
+        assert_eq!(res, WindowsPath("."));
+
+    }
+
 }
diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs
index 936a6526508..793e244bec7 100644
--- a/src/libstd/rt/comm.rs
+++ b/src/libstd/rt/comm.rs
@@ -15,6 +15,7 @@ use cast;
 use ops::Drop;
 use rt::kill::BlockedTask;
 use kinds::Send;
+use rt;
 use rt::sched::Scheduler;
 use rt::local::Local;
 use rt::select::{Select, SelectPort};
@@ -24,7 +25,6 @@ use util::Void;
 use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable};
 use cell::Cell;
 use clone::Clone;
-use rt::{context, SchedulerContext};
 use tuple::ImmutableTuple;
 
 /// A combined refcount / BlockedTask-as-uint pointer.
@@ -113,7 +113,7 @@ impl<T> ChanOne<T> {
     // 'do_resched' configures whether the scheduler immediately switches to
     // the receiving task, or leaves the sending task still running.
     fn try_send_inner(self, val: T, do_resched: bool) -> bool {
-        rtassert!(context() != SchedulerContext);
+        rtassert!(!rt::in_sched_context());
 
         let mut this = self;
         let mut recvr_active = true;
diff --git a/src/libstd/rt/io/net/ip.rs b/src/libstd/rt/io/net/ip.rs
index 815ec9b5c61..77176088801 100644
--- a/src/libstd/rt/io/net/ip.rs
+++ b/src/libstd/rt/io/net/ip.rs
@@ -9,7 +9,11 @@
 // except according to those terms.
 
 use num::FromStrRadix;
+use vec::MutableCloneableVector;
 use to_str::ToStr;
+use from_str::FromStr;
+use option::{Option, None, Some};
+
 
 type Port = u16;
 
@@ -39,7 +43,7 @@ impl ToStr for IpAddr {
             }
 
             // Ipv4-Mapped address
-            Ipv6Addr(0, 0, 0, 0, 0, 1, g, h) => {
+            Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => {
                 let a = fmt!("%04x", g as uint);
                 let b = FromStrRadix::from_str_radix(a.slice(2, 4), 16).unwrap();
                 let a = FromStrRadix::from_str_radix(a.slice(0, 2), 16).unwrap();
@@ -73,3 +77,371 @@ impl ToStr for SocketAddr {
         }
     }
 }
+
+struct Parser<'self> {
+    // parsing as ASCII, so can use byte array
+    s: &'self [u8],
+    pos: uint,
+}
+
+impl<'self> Parser<'self> {
+    fn new(s: &'self str) -> Parser<'self> {
+        Parser {
+            s: s.as_bytes(),
+            pos: 0,
+        }
+    }
+
+    fn is_eof(&self) -> bool {
+        self.pos == self.s.len()
+    }
+
+    // Commit only if parser returns Some
+    fn read_atomically<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
+        let pos = self.pos;
+        let r = cb(self);
+        if r.is_none() {
+            self.pos = pos;
+        }
+        r
+    }
+
+    // Commit only if parser read till EOF
+    fn read_till_eof<T>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> {
+        do self.read_atomically |p| {
+            cb(p).filtered(|_| p.is_eof())
+        }
+    }
+
+    // Return result of first successful parser
+    fn read_or<T>(&mut self, parsers: &[&fn(&mut Parser) -> Option<T>]) -> Option<T> {
+        for pf in parsers.iter() {
+            match self.read_atomically(|p: &mut Parser| (*pf)(p)) {
+                Some(r) => return Some(r),
+                None => {}
+            }
+        }
+        None
+    }
+
+    // Apply 3 parsers sequentially
+    fn read_seq_3<A, B, C>(&mut self,
+            pa: &fn(&mut Parser) -> Option<A>,
+            pb: &fn(&mut Parser) -> Option<B>,
+            pc: &fn(&mut Parser) -> Option<C>
+        ) -> Option<(A, B, C)>
+    {
+        do self.read_atomically |p| {
+            let a = pa(p);
+            let b = if a.is_some() { pb(p) } else { None };
+            let c = if b.is_some() { pc(p) } else { None };
+            match (a, b, c) {
+                (Some(a), Some(b), Some(c)) => Some((a, b, c)),
+                _ => None
+            }
+        }
+    }
+
+    // Read next char
+    fn read_char(&mut self) -> Option<char> {
+        if self.is_eof() {
+            None
+        } else {
+            let r = self.s[self.pos] as char;
+            self.pos += 1;
+            Some(r)
+        }
+    }
+
+    // Return char and advance iff next char is equal to requested
+    fn read_given_char(&mut self, c: char) -> Option<char> {
+        do self.read_atomically |p| {
+            p.read_char().filtered(|&next| next == c)
+        }
+    }
+
+    // Read digit
+    fn read_digit(&mut self, radix: u8) -> Option<u8> {
+        fn parse_digit(c: char, radix: u8) -> Option<u8> {
+            // assuming radix is either 10 or 16
+            if c >= '0' && c <= '9' {
+                Some((c - '0') as u8)
+            } else if radix > 10 && c >= 'a' && c < 'a' + (radix - 10) as char {
+                Some((c - 'a' + (10 as char)) as u8)
+            } else if radix > 10 && c >= 'A' && c < 'A' + (radix - 10) as char {
+                Some((c - 'A' + (10 as char)) as u8)
+            } else {
+                None
+            }
+        }
+
+        do self.read_atomically |p| {
+            p.read_char().chain(|c| parse_digit(c, radix))
+        }
+    }
+
+    fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        let mut r = 0u32;
+        let mut digit_count = 0;
+        loop {
+            match self.read_digit(radix) {
+                Some(d) => {
+                    r = r * (radix as u32) + (d as u32);
+                    digit_count += 1;
+                    if digit_count > max_digits || r >= upto {
+                        return None
+                    }
+                }
+                None => {
+                    if digit_count == 0 {
+                        return None
+                    } else {
+                        return Some(r)
+                    }
+                }
+            };
+        }
+    }
+
+    // Read number, failing if max_digits of number value exceeded
+    fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        do self.read_atomically |p| {
+            p.read_number_impl(radix, max_digits, upto)
+        }
+    }
+
+    fn read_ipv4_addr_impl(&mut self) -> Option<IpAddr> {
+        let mut bs = [0u8, ..4];
+        let mut i = 0;
+        while i < 4 {
+            if i != 0 && self.read_given_char('.').is_none() {
+                return None;
+            }
+
+            let octet = self.read_number(10, 3, 0x100).map(|&n| n as u8);
+            match octet {
+                Some(d) => bs[i] = d,
+                None => return None,
+            };
+            i += 1;
+        }
+        Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3]))
+    }
+
+    // Read IPv4 address
+    fn read_ipv4_addr(&mut self) -> Option<IpAddr> {
+        do self.read_atomically |p| {
+            p.read_ipv4_addr_impl()
+        }
+    }
+
+    fn read_ipv6_addr_impl(&mut self) -> Option<IpAddr> {
+        fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr {
+            assert!(head.len() + tail.len() <= 8);
+            let mut gs = [0u16, ..8];
+            gs.copy_from(head);
+            gs.mut_slice(8 - tail.len(), 8).copy_from(tail);
+            Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
+        }
+
+        fn read_groups(p: &mut Parser, groups: &mut [u16, ..8], limit: uint) -> (uint, bool) {
+            let mut i = 0;
+            while i < limit {
+                if i < limit - 1 {
+                    let ipv4 = do p.read_atomically |p| {
+                        if i == 0 || p.read_given_char(':').is_some() {
+                            p.read_ipv4_addr()
+                        } else {
+                            None
+                        }
+                    };
+                    match ipv4 {
+                        Some(Ipv4Addr(a, b, c, d)) => {
+                            groups[i + 0] = (a as u16 << 8) | (b as u16);
+                            groups[i + 1] = (c as u16 << 8) | (d as u16);
+                            return (i + 2, true);
+                        }
+                        _ => {}
+                    }
+                }
+
+                let group = do p.read_atomically |p| {
+                    if i == 0 || p.read_given_char(':').is_some() {
+                        p.read_number(16, 4, 0x10000).map(|&n| n as u16)
+                    } else {
+                        None
+                    }
+                };
+                match group {
+                    Some(g) => groups[i] = g,
+                    None => return (i, false)
+                }
+                i += 1;
+            }
+            (i, false)
+        }
+
+        let mut head = [0u16, ..8];
+        let (head_size, head_ipv4) = read_groups(self, &mut head, 8);
+
+        if head_size == 8 {
+            return Some(Ipv6Addr(
+                head[0], head[1], head[2], head[3],
+                head[4], head[5], head[6], head[7]))
+        }
+
+        // IPv4 part is not allowed before `::`
+        if head_ipv4 {
+            return None
+        }
+
+        // read `::` if previous code parsed less than 8 groups
+        if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() {
+            return None;
+        }
+
+        let mut tail = [0u16, ..8];
+        let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size);
+        Some(ipv6_addr_from_head_tail(head.slice(0, head_size), tail.slice(0, tail_size)))
+    }
+
+    fn read_ipv6_addr(&mut self) -> Option<IpAddr> {
+        do self.read_atomically |p| {
+            p.read_ipv6_addr_impl()
+        }
+    }
+
+    fn read_ip_addr(&mut self) -> Option<IpAddr> {
+        let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr();
+        let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr();
+        self.read_or([ipv4_addr, ipv6_addr])
+    }
+
+    fn read_socket_addr(&mut self) -> Option<SocketAddr> {
+        let ip_addr = |p: &mut Parser| {
+            let ipv4_p = |p: &mut Parser| p.read_ip_addr();
+            let ipv6_p = |p: &mut Parser| {
+                let open_br = |p: &mut Parser| p.read_given_char('[');
+                let ip_addr = |p: &mut Parser| p.read_ipv6_addr();
+                let clos_br = |p: &mut Parser| p.read_given_char(']');
+                p.read_seq_3::<char, IpAddr, char>(open_br, ip_addr, clos_br)
+                        .map(|&t| match t { (_, ip, _) => ip })
+            };
+            p.read_or([ipv4_p, ipv6_p])
+        };
+        let colon = |p: &mut Parser| p.read_given_char(':');
+        let port  = |p: &mut Parser| p.read_number(10, 5, 0x10000).map(|&n| n as u16);
+
+        // host, colon, port
+        self.read_seq_3::<IpAddr, char, u16>(ip_addr, colon, port)
+                .map(|&t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } })
+    }
+}
+
+impl FromStr for IpAddr {
+    fn from_str(s: &str) -> Option<IpAddr> {
+        do Parser::new(s).read_till_eof |p| {
+            p.read_ip_addr()
+        }
+    }
+}
+
+impl FromStr for SocketAddr {
+    fn from_str(s: &str) -> Option<SocketAddr> {
+        do Parser::new(s).read_till_eof |p| {
+            p.read_socket_addr()
+        }
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use from_str::FromStr;
+    use option::{Some, None};
+
+    #[test]
+    fn test_from_str_ipv4() {
+        assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1"));
+        assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255"));
+        assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0"));
+
+        // out of range
+        assert_eq!(None, FromStr::from_str::<IpAddr>("256.0.0.1"));
+        // too short
+        assert_eq!(None, FromStr::from_str::<IpAddr>("255.0.0"));
+        // too long
+        assert_eq!(None, FromStr::from_str::<IpAddr>("255.0.0.1.2"));
+        // no number between dots
+        assert_eq!(None, FromStr::from_str::<IpAddr>("255.0..1"));
+    }
+
+    #[test]
+    fn test_from_str_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1"));
+
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::"));
+
+        assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
+                FromStr::from_str("2a02:6b8::11:11"));
+
+        // too long group
+        assert_eq!(None, FromStr::from_str::<IpAddr>("::00000"));
+        // too short
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:3:4:5:6:7"));
+        // too long
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:3:4:5:6:7:8:9"));
+        // triple colon
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1:2:::6:7:8"));
+        // two double colons
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1:2::6::8"));
+    }
+
+    #[test]
+    fn test_from_str_ipv4_in_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
+                FromStr::from_str("::FFFF:192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("64:ff9b::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
+                FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33"));
+
+        // colon after v4
+        assert_eq!(None, FromStr::from_str::<IpAddr>("::127.0.0.1:"));
+        // not enought groups
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1.2.3.4.5:127.0.0.1"));
+        // too many groups
+        assert_eq!(None, FromStr::from_str::<IpAddr>("1.2.3.4.5:6:7:127.0.0.1"));
+    }
+
+    #[test]
+    fn test_from_str_socket_addr() {
+        assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }),
+                FromStr::from_str("77.88.21.11:80"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }),
+                FromStr::from_str("[2a02:6b8:0:1::1]:53"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }),
+                FromStr::from_str("[::127.0.0.1]:22"));
+
+        // without port
+        assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1"));
+        // without port
+        assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1:"));
+        // wrong brackets around v4
+        assert_eq!(None, FromStr::from_str::<SocketAddr>("[127.0.0.1]:22"));
+        // port out of range
+        assert_eq!(None, FromStr::from_str::<SocketAddr>("127.0.0.1:123456"));
+    }
+
+    #[test]
+    fn ipv6_addr_to_str() {
+        let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
+        assert!(a1.to_str() == ~"::ffff:192.0.2.128" || a1.to_str() == ~"::FFFF:192.0.2.128");
+    }
+
+}
diff --git a/src/libstd/rt/local_heap.rs b/src/libstd/rt/local_heap.rs
index e1e7ceacc38..8832597f40c 100644
--- a/src/libstd/rt/local_heap.rs
+++ b/src/libstd/rt/local_heap.rs
@@ -13,6 +13,7 @@
 use libc;
 use libc::{c_void, uintptr_t, size_t};
 use ops::Drop;
+use option::{Some, None};
 use rt;
 use rt::OldTaskContext;
 use rt::local::Local;
@@ -86,8 +87,12 @@ impl Drop for LocalHeap {
 
 // A little compatibility function
 pub unsafe fn local_free(ptr: *libc::c_char) {
-    match rt::context() {
-        OldTaskContext => {
+    // XXX: Unsafe borrow for speed. Lame.
+    match Local::try_unsafe_borrow::<Task>() {
+        Some(task) => {
+            (*task).heap.free(ptr as *libc::c_void);
+        }
+        None => {
             rust_upcall_free_noswitch(ptr);
 
             extern {
@@ -95,11 +100,6 @@ pub unsafe fn local_free(ptr: *libc::c_char) {
                 fn rust_upcall_free_noswitch(ptr: *libc::c_char);
             }
         }
-        _ => {
-            do Local::borrow::<Task,()> |task| {
-                task.heap.free(ptr as *libc::c_void);
-            }
-        }
     }
 }
 
@@ -119,20 +119,28 @@ pub fn live_allocs() -> *raw::Box<()> {
 }
 
 extern {
+    #[fast_ffi]
     fn rust_new_memory_region(synchronized: uintptr_t,
                                detailed_leaks: uintptr_t,
                                poison_on_free: uintptr_t) -> *MemoryRegion;
+    #[fast_ffi]
     fn rust_delete_memory_region(region: *MemoryRegion);
+    #[fast_ffi]
     fn rust_new_boxed_region(region: *MemoryRegion,
                              poison_on_free: uintptr_t) -> *BoxedRegion;
+    #[fast_ffi]
     fn rust_delete_boxed_region(region: *BoxedRegion);
+    #[fast_ffi]
     fn rust_boxed_region_malloc(region: *BoxedRegion,
                                 td: *TypeDesc,
                                 size: size_t) -> *OpaqueBox;
+    #[fast_ffi]
     fn rust_boxed_region_realloc(region: *BoxedRegion,
                                  ptr: *OpaqueBox,
                                  size: size_t) -> *OpaqueBox;
+    #[fast_ffi]
     fn rust_boxed_region_free(region: *BoxedRegion, box: *OpaqueBox);
+    #[fast_ffi]
     fn rust_current_boxed_region() -> *BoxedRegion;
 }
 
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index 01a52892f63..be71bc651df 100644
--- a/src/libstd/rt/mod.rs
+++ b/src/libstd/rt/mod.rs
@@ -407,14 +407,10 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
 /// or the old scheduler.
 #[deriving(Eq)]
 pub enum RuntimeContext {
-    // Only the exchange heap is available
-    GlobalContext,
-    // The scheduler may be accessed
-    SchedulerContext,
-    // Full task services, e.g. local heap, unwinding
-    TaskContext,
     // Running in an old-style task
-    OldTaskContext
+    OldTaskContext,
+    // Not old task context
+    NewRtContext
 }
 
 /// Determine the current RuntimeContext
@@ -424,19 +420,8 @@ pub fn context() -> RuntimeContext {
 
     if unsafe { rust_try_get_task().is_not_null() } {
         return OldTaskContext;
-    } else if Local::exists::<Task>() {
-        // In this case we know it is a new runtime context, but we
-        // need to check which one. Going to try borrowing task to
-        // check. Task should always be in TLS, so hopefully this
-        // doesn't conflict with other ops that borrow.
-        return do Local::borrow::<Task,RuntimeContext> |task| {
-            match task.task_type {
-                SchedTask => SchedulerContext,
-                GreenTask(_) => TaskContext
-            }
-        };
     } else {
-        return GlobalContext;
+        return NewRtContext;
     }
 
     extern {
@@ -444,3 +429,31 @@ pub fn context() -> RuntimeContext {
         pub fn rust_try_get_task() -> *rust_task;
     }
 }
+
+pub fn in_sched_context() -> bool {
+    unsafe {
+        match Local::try_unsafe_borrow::<Task>() {
+            Some(task) => {
+                match (*task).task_type {
+                    SchedTask => true,
+                    _ => false
+                }
+            }
+            None => false
+        }
+    }
+}
+
+pub fn in_green_task_context() -> bool {
+    unsafe {
+        match Local::try_unsafe_borrow::<Task>() {
+            Some(task) => {
+                match (*task).task_type {
+                    GreenTask(_) => true,
+                    _ => false
+                }
+            }
+            None => false
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libstd/rt/uv/net.rs b/src/libstd/rt/uv/net.rs
index fd3042899f6..c8b3d41a78d 100644
--- a/src/libstd/rt/uv/net.rs
+++ b/src/libstd/rt/uv/net.rs
@@ -20,7 +20,6 @@ use rt::uv::last_uv_error;
 use vec;
 use str;
 use from_str::{FromStr};
-use num;
 
 pub enum UvSocketAddr {
     UvIpv4SocketAddr(*sockaddr_in),
@@ -85,77 +84,10 @@ fn uv_socket_addr_as_socket_addr<T>(addr: UvSocketAddr, f: &fn(SocketAddr) -> T)
         port as u16
     };
     let ip_str = str::from_bytes_slice(ip_name).trim_right_chars(&'\x00');
-    let ip = match addr {
-        UvIpv4SocketAddr(*) => {
-            let ip: ~[u8] =
-                ip_str.split_iter('.')
-                      .transform(|s: &str| -> u8 { FromStr::from_str(s).unwrap() })
-                      .collect();
-            assert_eq!(ip.len(), 4);
-            SocketAddr {
-                ip: Ipv4Addr(ip[0], ip[1], ip[2], ip[3]),
-                port: ip_port
-            }
-        },
-        UvIpv6SocketAddr(*) => {
-            let ip: ~[u16] = {
-                let expand_shorthand_and_convert = |s: &str| -> ~[~[u16]] {
-                    let convert_each_segment = |s: &str| -> ~[u16] {
-                        let read_hex_segment = |s: &str| -> u16 {
-                            num::FromStrRadix::from_str_radix(s, 16u).unwrap()
-                        };
-                        match s {
-                            "" => ~[],
-                            // IPv4-Mapped/Compatible IPv6 Address?
-                            s if s.find('.').is_some() => {
-                                let i = s.rfind(':').unwrap_or_default(-1);
-
-                                let b = s.slice(i + 1, s.len()); // the ipv4 part
-
-                                let h = b.split_iter('.')
-                                   .transform(|s: &str| -> u8 { FromStr::from_str(s).unwrap() })
-                                   .transform(|s: u8| -> ~str { fmt!("%02x", s as uint) })
-                                   .collect::<~[~str]>();
-
-                                if i == -1 {
-                                    // Ipv4 Compatible Address (::x.x.x.x)
-                                    // first 96 bits are zero leaving 32 bits
-                                    // for the ipv4 part
-                                    // (i.e ::127.0.0.1 == ::7F00:1)
-                                    ~[num::FromStrRadix::from_str_radix(h[0] + h[1], 16).unwrap(),
-                                      num::FromStrRadix::from_str_radix(h[2] + h[3], 16).unwrap()]
-                                } else {
-                                    // Ipv4-Mapped Address (::FFFF:x.x.x.x)
-                                    // first 80 bits are zero, followed by all ones
-                                    // for the next 16 bits, leaving 32 bits for
-                                    // the ipv4 part
-                                    // (i.e ::FFFF:127.0.0.1 == ::FFFF:7F00:1)
-                                    ~[1,
-                                      num::FromStrRadix::from_str_radix(h[0] + h[1], 16).unwrap(),
-                                      num::FromStrRadix::from_str_radix(h[2] + h[3], 16).unwrap()]
-                                }
-                            },
-                            s => s.split_iter(':').transform(read_hex_segment).collect()
-                        }
-                    };
-                    s.split_str_iter("::").transform(convert_each_segment).collect()
-                };
-                match expand_shorthand_and_convert(ip_str) {
-                    [x] => x, // no shorthand found
-                    [l, r] => l + vec::from_elem(8 - l.len() - r.len(), 0u16) + r, // fill the gap
-                    _ => fail!(), // impossible. only one shorthand allowed.
-                }
-            };
-            assert_eq!(ip.len(), 8);
-            SocketAddr {
-                ip: Ipv6Addr(ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
-                port: ip_port
-            }
-        },
-    };
+    let ip_addr = FromStr::from_str(ip_str).unwrap();
 
     // finally run the closure
-    f(ip)
+    f(SocketAddr { ip: ip_addr, port: ip_port })
 }
 
 pub fn uv_socket_addr_to_socket_addr(addr: UvSocketAddr) -> SocketAddr {
diff --git a/src/libstd/run.rs b/src/libstd/run.rs
index 23ee52389a0..31e317604c7 100644
--- a/src/libstd/run.rs
+++ b/src/libstd/run.rs
@@ -1329,11 +1329,11 @@ mod tests {
         let output = str::from_bytes(prog.finish_with_output().output);
 
         let r = os::env();
-        for &(k, v) in r.iter() {
+        for &(ref k, ref v) in r.iter() {
             // don't check android RANDOM variables
-            if k != ~"RANDOM" {
-                assert!(output.contains(fmt!("%s=%s", k, v)) ||
-                        output.contains(fmt!("%s=\'%s\'", k, v)));
+            if *k != ~"RANDOM" {
+                assert!(output.contains(fmt!("%s=%s", *k, *v)) ||
+                        output.contains(fmt!("%s=\'%s\'", *k, *v)));
             }
         }
     }
diff --git a/src/libstd/str.rs b/src/libstd/str.rs
index 724fbcac891..b72e5a87c6d 100644
--- a/src/libstd/str.rs
+++ b/src/libstd/str.rs
@@ -1243,34 +1243,16 @@ pub mod traits {
     impl<'self> Ord for &'self str {
         #[inline]
         fn lt(&self, other: & &'self str) -> bool { self.cmp(other) == Less }
-        #[inline]
-        fn le(&self, other: & &'self str) -> bool { self.cmp(other) != Greater }
-        #[inline]
-        fn ge(&self, other: & &'self str) -> bool { self.cmp(other) != Less }
-        #[inline]
-        fn gt(&self, other: & &'self str) -> bool { self.cmp(other) == Greater }
     }
 
     impl Ord for ~str {
         #[inline]
         fn lt(&self, other: &~str) -> bool { self.cmp(other) == Less }
-        #[inline]
-        fn le(&self, other: &~str) -> bool { self.cmp(other) != Greater }
-        #[inline]
-        fn ge(&self, other: &~str) -> bool { self.cmp(other) != Less }
-        #[inline]
-        fn gt(&self, other: &~str) -> bool { self.cmp(other) == Greater }
     }
 
     impl Ord for @str {
         #[inline]
         fn lt(&self, other: &@str) -> bool { self.cmp(other) == Less }
-        #[inline]
-        fn le(&self, other: &@str) -> bool { self.cmp(other) != Greater }
-        #[inline]
-        fn ge(&self, other: &@str) -> bool { self.cmp(other) != Less }
-        #[inline]
-        fn gt(&self, other: &@str) -> bool { self.cmp(other) == Greater }
     }
 
     impl<'self, S: Str> Equiv<S> for &'self str {
diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs
index 83ee4cf07c6..3add87bbd1d 100644
--- a/src/libstd/sys.rs
+++ b/src/libstd/sys.rs
@@ -136,7 +136,7 @@ impl FailWithCause for &'static str {
 pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
     use either::Left;
     use option::{Some, None};
-    use rt::{context, OldTaskContext, TaskContext};
+    use rt::{context, OldTaskContext, in_green_task_context};
     use rt::task::Task;
     use rt::local::Local;
     use rt::logging::Logger;
@@ -158,7 +158,7 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
 
                 // XXX: Logging doesn't work correctly in non-task context because it
                 // invokes the local heap
-                if context == TaskContext {
+                if in_green_task_context() {
                     // XXX: Logging doesn't work here - the check to call the log
                     // function never passes - so calling the log function directly.
                     do Local::borrow::<Task, ()> |task| {
diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs
index 2e0c9c1d1ad..269c828a984 100644
--- a/src/libstd/task/mod.rs
+++ b/src/libstd/task/mod.rs
@@ -42,7 +42,7 @@ use cmp::Eq;
 use comm::{stream, Chan, GenericChan, GenericPort, Port};
 use result::Result;
 use result;
-use rt::{context, OldTaskContext, TaskContext};
+use rt::{context, OldTaskContext, in_green_task_context};
 use rt::local::Local;
 use unstable::finally::Finally;
 use util;
@@ -527,14 +527,15 @@ pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
 pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
     use rt::task::Task;
 
-    match context() {
-        TaskContext => do Local::borrow::<Task, U> |task| {
+    if in_green_task_context() {
+        do Local::borrow::<Task, U> |task| {
             match task.name {
                 Some(ref name) => blk(Some(name.as_slice())),
                 None => blk(None)
             }
-        },
-        _ => fail!("no task name exists in %?", context()),
+        }
+    } else {
+        fail!("no task name exists in %?", context())
     }
 }
 
@@ -614,7 +615,7 @@ pub fn unkillable<U>(f: &fn() -> U) -> U {
                     rt::rust_task_allow_kill(t);
                 }
             }
-            TaskContext => {
+            _ if in_green_task_context() => {
                 // The inhibits/allows might fail and need to borrow the task.
                 let t = Local::unsafe_borrow::<Task>();
                 do (|| {
@@ -645,7 +646,7 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
                 rt::rust_task_inhibit_kill(t);
             }
         }
-        TaskContext => {
+        _ if in_green_task_context() => {
             let t = Local::unsafe_borrow::<Task>();
             do (|| {
                 (*t).death.allow_kill((*t).unwinder.unwinding);
diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs
index 05a17f8539c..314377b8dc9 100644
--- a/src/libstd/task/spawn.rs
+++ b/src/libstd/task/spawn.rs
@@ -91,7 +91,7 @@ use to_bytes::IterBytes;
 use uint;
 use util;
 use unstable::sync::Exclusive;
-use rt::{OldTaskContext, TaskContext, SchedulerContext, GlobalContext, context};
+use rt::{OldTaskContext, NewRtContext, context, in_green_task_context};
 use rt::local::Local;
 use rt::task::{Task, Sched};
 use rt::kill::KillHandle;
@@ -526,7 +526,7 @@ impl RuntimeGlue {
                 let me = rt::rust_get_task();
                 blk(OldTask(me), rt::rust_task_is_unwinding(me))
             },
-            TaskContext => unsafe {
+            NewRtContext if in_green_task_context() => unsafe {
                 // Can't use safe borrow, because the taskgroup destructor needs to
                 // access the scheduler again to send kill signals to other tasks.
                 let me = Local::unsafe_borrow::<Task>();
@@ -535,7 +535,7 @@ impl RuntimeGlue {
                 blk(NewTask((*me).death.kill_handle.get_ref().clone()),
                     (*me).unwinder.unwinding)
             },
-            SchedulerContext | GlobalContext => rtabort!("task dying in bad context"),
+            NewRtContext => rtabort!("task dying in bad context"),
         }
     }
 
@@ -563,7 +563,7 @@ impl RuntimeGlue {
                     }
                 }
             },
-            TaskContext => unsafe {
+            NewRtContext if in_green_task_context() => unsafe {
                 // Can't use safe borrow, because creating new hashmaps for the
                 // tasksets requires an rng, which needs to borrow the sched.
                 let me = Local::unsafe_borrow::<Task>();
@@ -588,7 +588,7 @@ impl RuntimeGlue {
                     Some(ref group) => group,
                 })
             },
-            SchedulerContext | GlobalContext => rtabort!("spawning in bad context"),
+            NewRtContext => rtabort!("spawning in bad context"),
         }
     }
 }
@@ -666,10 +666,9 @@ fn enlist_many(child: TaskHandle, child_arc: &TaskGroupArc,
 
 pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
     match context() {
-        OldTaskContext   => spawn_raw_oldsched(opts, f),
-        TaskContext      => spawn_raw_newsched(opts, f),
-        SchedulerContext => fail!("can't spawn from scheduler context"),
-        GlobalContext    => fail!("can't spawn from global context"),
+        OldTaskContext => spawn_raw_oldsched(opts, f),
+        _ if in_green_task_context() => spawn_raw_newsched(opts, f),
+        _ => fail!("can't spawn from this context")
     }
 }
 
diff --git a/src/libstd/unstable/lang.rs b/src/libstd/unstable/lang.rs
index c41712eb80a..457f640033c 100644
--- a/src/libstd/unstable/lang.rs
+++ b/src/libstd/unstable/lang.rs
@@ -13,9 +13,9 @@
 use c_str::ToCStr;
 use cast::transmute;
 use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
+use option::{Some, None};
 use str;
 use sys;
-use rt::{context, OldTaskContext};
 use rt::task::Task;
 use rt::local::Local;
 use rt::borrowck;
@@ -57,16 +57,13 @@ pub fn fail_bounds_check(file: *c_char, line: size_t,
 
 #[lang="malloc"]
 pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
-    match context() {
-        OldTaskContext => {
-            return rustrt::rust_upcall_malloc_noswitch(td, size);
+    // XXX: Unsafe borrow for speed. Lame.
+    match Local::try_unsafe_borrow::<Task>() {
+        Some(task) => {
+            (*task).heap.alloc(td as *c_void, size as uint) as *c_char
         }
-        _ => {
-            let mut alloc = ::ptr::null();
-            do Local::borrow::<Task,()> |task| {
-                alloc = task.heap.alloc(td as *c_void, size as uint) as *c_char;
-            }
-            return alloc;
+        None => {
+            rustrt::rust_upcall_malloc_noswitch(td, size)
         }
     }
 }
diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs
index 225ac5c92ad..a8d942a46b3 100644
--- a/src/libstd/unstable/sync.rs
+++ b/src/libstd/unstable/sync.rs
@@ -282,7 +282,7 @@ pub unsafe fn atomically<U>(f: &fn() -> U) -> U {
     use rt::task::Task;
     use task::rt;
     use rt::local::Local;
-    use rt::{context, OldTaskContext, TaskContext};
+    use rt::{context, OldTaskContext};
 
     match context() {
         OldTaskContext => {
@@ -296,17 +296,23 @@ pub unsafe fn atomically<U>(f: &fn() -> U) -> U {
                 rt::rust_task_allow_kill(t);
             }
         }
-        TaskContext => {
-            let t = Local::unsafe_borrow::<Task>();
-            do (|| {
-                (*t).death.inhibit_yield();
-                f()
-            }).finally {
-                (*t).death.allow_yield();
+        _ => {
+            let t = Local::try_unsafe_borrow::<Task>();
+            match t {
+                Some(t) => {
+                    do (|| {
+                        (*t).death.inhibit_yield();
+                        f()
+                    }).finally {
+                        (*t).death.allow_yield();
+                    }
+                }
+                None => {
+                    // FIXME(#3095): As in unkillable().
+                    f()
+                }
             }
         }
-        // FIXME(#3095): As in unkillable().
-        _ => f()
     }
 }
 
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 435be3c71af..17247222c3f 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -951,7 +951,11 @@ pub struct view_item {
 
 #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
 pub enum view_item_ {
-    view_item_extern_mod(ident, ~[@MetaItem], NodeId),
+    // ident: name used to refer to this crate in the code
+    // optional @str: if present, this is a location (containing
+    // arbitrary characters) from which to fetch the crate sources
+    // For example, extern mod whatever = "github.com/mozilla/rust"
+    view_item_extern_mod(ident, Option<@str>, ~[@MetaItem], NodeId),
     view_item_use(~[@view_path]),
 }
 
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index ba167fe6714..9a8a3bc25d8 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -419,7 +419,7 @@ impl Visitor<()> for IdVisitor {
 
     fn visit_view_item(@mut self, view_item: &view_item, env: ()) {
         match view_item.node {
-            view_item_extern_mod(_, _, node_id) => {
+            view_item_extern_mod(_, _, _, node_id) => {
                 (self.visit_callback)(node_id)
             }
             view_item_use(ref view_paths) => {
diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs
index ee0ec664e1b..b5d97427baf 100644
--- a/src/libsyntax/ext/asm.rs
+++ b/src/libsyntax/ext/asm.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -59,7 +59,7 @@ pub fn expand_asm(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
         match state {
             Asm => {
                 asm = expr_to_str(cx, p.parse_expr(),
-                                  ~"inline assembly must be a string literal.");
+                                  "inline assembly must be a string literal.");
             }
             Outputs => {
                 while *p.token != token::EOF &&
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index dc20994b49f..1e696451701 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -148,7 +148,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
         intern(&"auto_decode"),
         @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
     syntax_expanders.insert(intern(&"env"),
-                            builtin_normal_tt(ext::env::expand_syntax_ext));
+                            builtin_normal_tt(ext::env::expand_env));
+    syntax_expanders.insert(intern(&"option_env"),
+                            builtin_normal_tt(ext::env::expand_option_env));
     syntax_expanders.insert(intern("bytes"),
                             builtin_normal_tt(ext::bytes::expand_syntax_ext));
     syntax_expanders.insert(intern("concat_idents"),
@@ -313,7 +315,7 @@ impl ExtCtxt {
     }
 }
 
-pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str {
+pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: &str) -> @str {
     match expr.node {
       ast::expr_lit(l) => match l.node {
         ast::lit_str(s) => s,
@@ -538,8 +540,8 @@ mod test {
         a.insert (@"abc",@15);
         let m = MapChain::new(~a);
         m.insert (@"def",@16);
-        // FIXME: #4492 (ICE)  assert_eq!(m.find(&@"abc"),Some(@15));
-        //  ....               assert_eq!(m.find(&@"def"),Some(@16));
+        assert_eq!(m.find(&@"abc"),Some(@15));
+        assert_eq!(m.find(&@"def"),Some(@16));
         assert_eq!(*(m.find(&@"abc").unwrap()),15);
         assert_eq!(*(m.find(&@"def").unwrap()),16);
         let n = m.push_frame();
@@ -551,8 +553,8 @@ mod test {
         assert_eq!(*(n.find(&@"abc").unwrap()),15);
         assert_eq!(*(n.find(&@"def").unwrap()),17);
         // ... but m still has the old ones
-        // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15));
-        // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16));
+        assert_eq!(m.find(&@"abc"),Some(@15));
+        assert_eq!(m.find(&@"def"),Some(@16));
         assert_eq!(*(m.find(&@"abc").unwrap()),15);
         assert_eq!(*(m.find(&@"def").unwrap()),16);
     }
diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs
index a6cb6155878..c9e01b0f0d5 100644
--- a/src/libsyntax/ext/env.rs
+++ b/src/libsyntax/ext/env.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -22,17 +22,35 @@ use ext::build::AstBuilder;
 
 use std::os;
 
-pub fn expand_syntax_ext(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
+pub fn expand_option_env(ext_cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
     -> base::MacResult {
+    let var = get_single_str_from_tts(ext_cx, sp, tts, "option_env!");
 
-    let var = get_single_str_from_tts(cx, sp, tts, "env!");
+    let e = match os::getenv(var) {
+      None => quote_expr!(::std::option::None),
+      Some(s) => quote_expr!(::std::option::Some($s))
+    };
+    MRExpr(e)
+}
 
-    // FIXME (#2248): if this was more thorough it would manufacture an
-    // Option<str> rather than just an maybe-empty string.
+pub fn expand_env(ext_cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
+    -> base::MacResult {
+    let exprs = get_exprs_from_tts(ext_cx, sp, tts);
+
+    if exprs.len() == 0 {
+        ext_cx.span_fatal(sp, "env! takes 1 or 2 arguments");
+    }
+
+    let var = expr_to_str(ext_cx, exprs[0], "expected string literal");
+    let msg = match exprs.len() {
+        1 => fmt!("Environment variable %s not defined", var).to_managed(),
+        2 => expr_to_str(ext_cx, exprs[1], "expected string literal"),
+        _ => ext_cx.span_fatal(sp, "env! takes 1 or 2 arguments")
+    };
 
     let e = match os::getenv(var) {
-      None => cx.expr_str(sp, @""),
-      Some(s) => cx.expr_str(sp, s.to_managed())
+        None => ext_cx.span_fatal(sp, msg),
+        Some(s) => ext_cx.expr_str(sp, s.to_managed())
     };
     MRExpr(e)
 }
diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs
index 2dbf6887a21..008545c9729 100644
--- a/src/libsyntax/ext/fmt.rs
+++ b/src/libsyntax/ext/fmt.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -32,7 +32,7 @@ pub fn expand_syntax_ext(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
     }
     let fmt =
         expr_to_str(cx, args[0],
-                    ~"first argument to fmt! must be a string literal.");
+                    "first argument to fmt! must be a string literal.");
     let fmtspan = args[0].span;
     debug!("Format string: %s", fmt);
     fn parse_fmt_err_(cx: @ExtCtxt, sp: span, msg: &str) -> ! {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 7d6dce22fb7..ddb0e3bfa68 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -4181,8 +4181,16 @@ impl Parser {
                                  self.this_token_to_str()));
         }
 
-        let (sort, ident) = match *self.token {
-            token::IDENT(*) => (ast::named, self.parse_ident()),
+        let (sort, maybe_path, ident) = match *self.token {
+            token::IDENT(*) => {
+                let the_ident = self.parse_ident();
+                let path = if *self.token == token::EQ {
+                    self.bump();
+                    Some(self.parse_str())
+                }
+                else { None };
+                (ast::named, path, the_ident)
+            }
             _ => {
                 if must_be_named_mod {
                     self.span_fatal(*self.span,
@@ -4191,7 +4199,7 @@ impl Parser {
                                          self.this_token_to_str()));
                 }
 
-                (ast::anonymous,
+                (ast::anonymous, None,
                  special_idents::clownshoes_foreign_mod)
             }
         };
@@ -4230,7 +4238,7 @@ impl Parser {
         let metadata = self.parse_optional_meta();
         self.expect(&token::SEMI);
         iovi_view_item(ast::view_item {
-            node: view_item_extern_mod(ident, metadata, self.get_id()),
+            node: view_item_extern_mod(ident, maybe_path, metadata, self.get_id()),
             attrs: attrs,
             vis: visibility,
             span: mk_sp(lo, self.last_span.hi)
@@ -4812,8 +4820,13 @@ impl Parser {
         } else if self.eat_keyword(keywords::Extern) {
             self.expect_keyword(keywords::Mod);
             let ident = self.parse_ident();
+            let path = if *self.token == token::EQ {
+                self.bump();
+                Some(self.parse_str())
+            }
+            else { None };
             let metadata = self.parse_optional_meta();
-            view_item_extern_mod(ident, metadata, self.get_id())
+            view_item_extern_mod(ident, path, metadata, self.get_id())
         } else {
             self.bug("expected view item");
         };
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index f517179f603..ffe9575a864 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1856,9 +1856,13 @@ pub fn print_view_item(s: @ps, item: &ast::view_item) {
     print_outer_attributes(s, item.attrs);
     print_visibility(s, item.vis);
     match item.node {
-        ast::view_item_extern_mod(id, ref mta, _) => {
+        ast::view_item_extern_mod(id, ref optional_path, ref mta, _) => {
             head(s, "extern mod");
             print_ident(s, id);
+            for p in optional_path.iter() {
+                word(s.s, "=");
+                print_string(s, *p);
+            }
             if !mta.is_empty() {
                 popen(s);
                 commasep(s, consistent, *mta, |p, &i| print_meta_item(p, i));
diff --git a/src/test/run-pass/extern-mod-url.rs b/src/test/compile-fail/extenv-arg-2-not-string-literal.rs
index 363c54f6812..c2362689721 100644
--- a/src/test/run-pass/extern-mod-url.rs
+++ b/src/test/compile-fail/extenv-arg-2-not-string-literal.rs
@@ -8,9 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Just a test that new-style extern mods parse
-
-// xfail-test FIXME #6407
-extern mod test = "github.com/catamorphism/test-pkg";
-
-fn main() {}
+fn main() { env!("one", 10); } //~ ERROR: expected string literal
diff --git a/src/test/compile-fail/extenv-no-args.rs b/src/test/compile-fail/extenv-no-args.rs
index 7ff1357dcf3..afa47dbe744 100644
--- a/src/test/compile-fail/extenv-no-args.rs
+++ b/src/test/compile-fail/extenv-no-args.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -8,6 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: env! takes 1 argument
-
-fn main() { env!(); }
+fn main() { env!(); } //~ ERROR: env! takes 1 or 2 arguments
diff --git a/src/test/compile-fail/extenv-not-defined-custom.rs b/src/test/compile-fail/extenv-not-defined-custom.rs
new file mode 100644
index 00000000000..485b6c09f0a
--- /dev/null
+++ b/src/test/compile-fail/extenv-not-defined-custom.rs
@@ -0,0 +1,11 @@
+// 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.
+
+fn main() { env!("__HOPEFULLY_NOT_DEFINED__", "my error message"); } //~ ERROR: my error message
diff --git a/src/test/compile-fail/extenv-not-defined-default.rs b/src/test/compile-fail/extenv-not-defined-default.rs
new file mode 100644
index 00000000000..d7a543c045a
--- /dev/null
+++ b/src/test/compile-fail/extenv-not-defined-default.rs
@@ -0,0 +1,11 @@
+// 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.
+
+fn main() { env!("__HOPEFULLY_NOT_DEFINED__"); } //~ ERROR: Environment variable __HOPEFULLY_NOT_DEFINED__ not defined
diff --git a/src/test/compile-fail/extenv-not-string-literal.rs b/src/test/compile-fail/extenv-not-string-literal.rs
index 3f09f81b85b..07ce47a14d8 100644
--- a/src/test/compile-fail/extenv-not-string-literal.rs
+++ b/src/test/compile-fail/extenv-not-string-literal.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -8,6 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:requires a string
-
-fn main() { env!(10); }
+fn main() { env!(10, "two"); } //~ ERROR: expected string literal
diff --git a/src/test/compile-fail/extenv-too-many-args.rs b/src/test/compile-fail/extenv-too-many-args.rs
index b1b2001abc3..c6c4f0ec6b8 100644
--- a/src/test/compile-fail/extenv-too-many-args.rs
+++ b/src/test/compile-fail/extenv-too-many-args.rs
@@ -8,6 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: env! takes 1 argument
-
-fn main() { env!("one", "two"); }
+fn main() { env!("one", "two", "three"); } //~ ERROR: env! takes 1 or 2 arguments
diff --git a/src/test/compile-fail/extoption_env-no-args.rs b/src/test/compile-fail/extoption_env-no-args.rs
new file mode 100644
index 00000000000..fd56756584a
--- /dev/null
+++ b/src/test/compile-fail/extoption_env-no-args.rs
@@ -0,0 +1,11 @@
+// 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.
+
+fn main() { option_env!(); } //~ ERROR: option_env! takes 1 argument
diff --git a/src/test/compile-fail/extoption_env-not-string-literal.rs b/src/test/compile-fail/extoption_env-not-string-literal.rs
new file mode 100644
index 00000000000..10f6c34980e
--- /dev/null
+++ b/src/test/compile-fail/extoption_env-not-string-literal.rs
@@ -0,0 +1,11 @@
+// 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.
+
+fn main() { option_env!(10); } //~ ERROR: requires a string
diff --git a/src/test/compile-fail/extoption_env-too-many-args.rs b/src/test/compile-fail/extoption_env-too-many-args.rs
new file mode 100644
index 00000000000..b31e857c14e
--- /dev/null
+++ b/src/test/compile-fail/extoption_env-too-many-args.rs
@@ -0,0 +1,11 @@
+// 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.
+
+fn main() { option_env!("one", "two"); } //~ ERROR: option_env! takes 1 argument
diff --git a/src/test/run-pass/extoption_env-not-defined.rs b/src/test/run-pass/extoption_env-not-defined.rs
new file mode 100644
index 00000000000..412efcc25a8
--- /dev/null
+++ b/src/test/run-pass/extoption_env-not-defined.rs
@@ -0,0 +1,14 @@
+// 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.
+
+fn main() {
+    let opt: Option<&'static str> = option_env!("__HOPEFULLY_DOESNT_EXIST__");
+    assert!(opt.is_none());
+}
diff --git a/src/test/run-pass/issue-5530.rs b/src/test/run-pass/issue-5530.rs
index 002435fcb36..8e55ad90c70 100644
--- a/src/test/run-pass/issue-5530.rs
+++ b/src/test/run-pass/issue-5530.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-test
-
 enum Enum {
     Foo { foo: uint },
     Bar { bar: uint }
diff --git a/src/test/run-pass/match-enum-struct-0.rs b/src/test/run-pass/match-enum-struct-0.rs
new file mode 100644
index 00000000000..365729ec860
--- /dev/null
+++ b/src/test/run-pass/match-enum-struct-0.rs
@@ -0,0 +1,24 @@
+// 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.
+
+// regression test for issue #5625
+
+enum E {
+    Foo{f : int},
+    Bar
+}
+
+pub fn main() {
+    let e = Bar;
+    match e {
+        Foo{f: _f} => fail!(),
+        _ => (),
+    }
+}
diff --git a/src/test/run-pass/match-enum-struct-1.rs b/src/test/run-pass/match-enum-struct-1.rs
new file mode 100644
index 00000000000..15d24c41a3d
--- /dev/null
+++ b/src/test/run-pass/match-enum-struct-1.rs
@@ -0,0 +1,26 @@
+// 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.
+
+enum E {
+    Foo{f : int},
+    Bar
+}
+
+pub fn main() {
+    let e = Foo{f: 1};
+    match e {
+        Foo{_} => (),
+        _ => fail!(),
+    }
+    match e {
+        Foo{f: _f} => (),
+        _ => fail!(),
+    }
+}
diff --git a/src/test/run-pass/match-struct-0.rs b/src/test/run-pass/match-struct-0.rs
new file mode 100644
index 00000000000..67e844c519e
--- /dev/null
+++ b/src/test/run-pass/match-struct-0.rs
@@ -0,0 +1,29 @@
+// 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.
+
+struct Foo{
+    f : int,
+}
+
+pub fn main() {
+    let f = Foo{f: 1};
+    match f {
+        Foo{f: 0} => fail!(),
+        Foo{_} => (),
+    }
+    match f {
+        Foo{f: 0} => fail!(),
+        Foo{f: _f} => (),
+    }
+    match f {
+        Foo{f: 0} => fail!(),
+        _ => (),
+    }
+}