about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-07-04 15:58:24 +0000
committerbors <bors@rust-lang.org>2017-07-04 15:58:24 +0000
commit2fbba5bdbadeef403a64e9e1568cdad225cbcec1 (patch)
tree5c28e75455be93fe83569947dfe602866a56e6ed /src
parentde7f0617681fc94ecd0090d2dd2e4d70fea8f3a2 (diff)
parent9cca462ca5faf348eb8a41d85193843fd6827434 (diff)
downloadrust-2fbba5bdbadeef403a64e9e1568cdad225cbcec1.tar.gz
rust-2fbba5bdbadeef403a64e9e1568cdad225cbcec1.zip
Auto merge of #43051 - Mark-Simulacrum:rollup, r=Mark-Simulacrum
Rollup of 8 pull requests

- Successful merges: #42227, #42836, #42975, #42994, #43041, #43042, #43043, #43045
- Failed merges:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/bin/rustc.rs15
-rw-r--r--src/bootstrap/cc.rs14
-rw-r--r--src/bootstrap/channel.rs10
-rw-r--r--src/bootstrap/check.rs77
-rw-r--r--src/bootstrap/compile.rs37
-rw-r--r--src/bootstrap/config.rs29
-rw-r--r--src/bootstrap/dist.rs67
-rw-r--r--src/bootstrap/doc.rs18
-rw-r--r--src/bootstrap/flags.rs54
-rw-r--r--src/bootstrap/install.rs2
-rw-r--r--src/bootstrap/lib.rs128
-rw-r--r--src/bootstrap/metadata.rs2
-rw-r--r--src/bootstrap/native.rs8
-rw-r--r--src/bootstrap/sanity.rs168
-rw-r--r--src/bootstrap/step.rs134
-rw-r--r--src/bootstrap/util.rs15
-rw-r--r--src/liballoc/string.rs8
-rw-r--r--src/liballoc/tests/vec.rs5
-rw-r--r--src/liballoc/vec.rs11
-rw-r--r--src/libcore/cmp.rs2
-rw-r--r--src/libcore/fmt/builders.rs139
-rw-r--r--src/libstd/ffi/c_str.rs8
-rw-r--r--src/libstd/ffi/os_str.rs8
-rw-r--r--src/libstd/path.rs8
-rw-r--r--src/libstd/sys/redox/fs.rs19
-rw-r--r--src/libstd/sys/redox/syscall/flag.rs2
26 files changed, 557 insertions, 431 deletions
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index 8c6eaee24f2..7232208b522 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -75,16 +75,11 @@ fn main() {
         Err(_) => 0,
     };
 
-    // Build scripts always use the snapshot compiler which is guaranteed to be
-    // able to produce an executable, whereas intermediate compilers may not
-    // have the standard library built yet and may not be able to produce an
-    // executable. Otherwise we just use the standard compiler we're
-    // bootstrapping with.
-    //
-    // Also note that cargo will detect the version of the compiler to trigger
-    // a rebuild when the compiler changes. If this happens, we want to make
-    // sure to use the actual compiler instead of the snapshot compiler becase
-    // that's the one that's actually changing.
+    // Use a different compiler for build scripts, since there may not yet be a
+    // libstd for the real compiler to use. However, if Cargo is attempting to
+    // determine the version of the compiler, the real compiler needs to be
+    // used. Currently, these two states are differentiated based on whether
+    // --target and -vV is/isn't passed.
     let (rustc, libdir) = if target.is_none() && version.is_none() {
         ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
     } else {
diff --git a/src/bootstrap/cc.rs b/src/bootstrap/cc.rs
index 54c8194678e..7c7161916ee 100644
--- a/src/bootstrap/cc.rs
+++ b/src/bootstrap/cc.rs
@@ -42,10 +42,13 @@ use config::Target;
 pub fn find(build: &mut Build) {
     // For all targets we're going to need a C compiler for building some shims
     // and such as well as for being a linker for Rust code.
-    for target in build.config.target.iter() {
+    //
+    // This includes targets that aren't necessarily passed on the commandline
+    // (FIXME: Perhaps it shouldn't?)
+    for target in &build.config.target {
         let mut cfg = gcc::Config::new();
         cfg.cargo_metadata(false).opt_level(0).debug(false)
-           .target(target).host(&build.config.build);
+           .target(target).host(&build.build);
 
         let config = build.config.target_config.get(target);
         if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
@@ -64,10 +67,13 @@ pub fn find(build: &mut Build) {
     }
 
     // For all host triples we need to find a C++ compiler as well
-    for host in build.config.host.iter() {
+    //
+    // This includes hosts that aren't necessarily passed on the commandline
+    // (FIXME: Perhaps it shouldn't?)
+    for host in &build.config.host {
         let mut cfg = gcc::Config::new();
         cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true)
-           .target(host).host(&build.config.build);
+           .target(host).host(&build.build);
         let config = build.config.target_config.get(host);
         if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
             cfg.compiler(cxx);
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs
index 4664b1f765e..1153acfa57d 100644
--- a/src/bootstrap/channel.rs
+++ b/src/bootstrap/channel.rs
@@ -23,12 +23,12 @@ use build_helper::output;
 use Build;
 
 // The version number
-pub const CFG_RELEASE_NUM: &'static str = "1.20.0";
+pub const CFG_RELEASE_NUM: &str = "1.20.0";
 
 // An optional number to put after the label, e.g. '.2' -> '-beta.2'
 // Be sure to make this starts with a dot to conform to semver pre-release
 // versions (section 9)
-pub const CFG_PRERELEASE_VERSION: &'static str = ".1";
+pub const CFG_PRERELEASE_VERSION: &str = ".1";
 
 pub struct GitInfo {
     inner: Option<Info>,
@@ -99,6 +99,10 @@ impl GitInfo {
             version.push_str(&inner.commit_date);
             version.push_str(")");
         }
-        return version
+        version
+    }
+
+    pub fn is_git(&self) -> bool {
+        self.inner.is_some()
     }
 }
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 277728b90b7..b3b5ae8d67d 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -13,23 +13,22 @@
 //! This file implements the various regression test suites that we execute on
 //! our CI.
 
-extern crate build_helper;
-
 use std::collections::HashSet;
 use std::env;
+use std::iter;
 use std::fmt;
 use std::fs::{self, File};
 use std::path::{PathBuf, Path};
 use std::process::Command;
 use std::io::Read;
 
-use build_helper::output;
+use build_helper::{self, output};
 
 use {Build, Compiler, Mode};
 use dist;
 use util::{self, dylib_path, dylib_path_var, exe};
 
-const ADB_TEST_DIR: &'static str = "/data/tmp/work";
+const ADB_TEST_DIR: &str = "/data/tmp/work";
 
 /// The two modes of the test runner; tests or benchmarks.
 #[derive(Copy, Clone)]
@@ -60,7 +59,7 @@ impl fmt::Display for TestKind {
 }
 
 fn try_run(build: &Build, cmd: &mut Command) {
-    if build.flags.cmd.no_fail_fast() {
+    if !build.fail_fast {
         if !build.try_run(cmd) {
             let failures = build.delayed_failures.get();
             build.delayed_failures.set(failures + 1);
@@ -71,7 +70,7 @@ fn try_run(build: &Build, cmd: &mut Command) {
 }
 
 fn try_run_quiet(build: &Build, cmd: &mut Command) {
-    if build.flags.cmd.no_fail_fast() {
+    if !build.fail_fast {
         if !build.try_run_quiet(cmd) {
             let failures = build.delayed_failures.get();
             build.delayed_failures.set(failures + 1);
@@ -99,7 +98,7 @@ pub fn linkcheck(build: &Build, host: &str) {
 /// This tool in `src/tools` will check out a few Rust projects and run `cargo
 /// test` to ensure that we don't regress the test suites there.
 pub fn cargotest(build: &Build, stage: u32, host: &str) {
-    let ref compiler = Compiler::new(stage, host);
+    let compiler = Compiler::new(stage, host);
 
     // Note that this is a short, cryptic, and not scoped directory name. This
     // is currently to minimize the length of path on Windows where we otherwise
@@ -109,11 +108,11 @@ pub fn cargotest(build: &Build, stage: u32, host: &str) {
 
     let _time = util::timeit();
     let mut cmd = Command::new(build.tool(&Compiler::new(0, host), "cargotest"));
-    build.prepare_tool_cmd(compiler, &mut cmd);
-    try_run(build, cmd.arg(&build.cargo)
+    build.prepare_tool_cmd(&compiler, &mut cmd);
+    try_run(build, cmd.arg(&build.initial_cargo)
                       .arg(&out_dir)
-                      .env("RUSTC", build.compiler_path(compiler))
-                      .env("RUSTDOC", build.rustdoc(compiler)));
+                      .env("RUSTC", build.compiler_path(&compiler))
+                      .env("RUSTDOC", build.rustdoc(&compiler)));
 }
 
 /// Runs `cargo test` for `cargo` packaged with Rust.
@@ -124,13 +123,12 @@ pub fn cargo(build: &Build, stage: u32, host: &str) {
     // and not RUSTC because the Cargo test suite has tests that will
     // fail if rustc is not spelled `rustc`.
     let path = build.sysroot(compiler).join("bin");
-    let old_path = ::std::env::var("PATH").expect("");
-    let sep = if cfg!(windows) { ";" } else {":" };
-    let ref newpath = format!("{}{}{}", path.display(), sep, old_path);
+    let old_path = env::var_os("PATH").unwrap_or_default();
+    let newpath = env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("");
 
     let mut cargo = build.cargo(compiler, Mode::Tool, host, "test");
     cargo.arg("--manifest-path").arg(build.src.join("src/tools/cargo/Cargo.toml"));
-    if build.flags.cmd.no_fail_fast() {
+    if !build.fail_fast {
         cargo.arg("--no-fail-fast");
     }
 
@@ -198,9 +196,9 @@ pub fn compiletest(build: &Build,
     cmd.arg("--mode").arg(mode);
     cmd.arg("--target").arg(target);
     cmd.arg("--host").arg(compiler.host);
-    cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.config.build));
+    cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.build));
 
-    if let Some(nodejs) = build.config.nodejs.as_ref() {
+    if let Some(ref nodejs) = build.config.nodejs {
         cmd.arg("--nodejs").arg(nodejs);
     }
 
@@ -224,7 +222,7 @@ pub fn compiletest(build: &Build,
 
     cmd.arg("--docck-python").arg(build.python());
 
-    if build.config.build.ends_with("apple-darwin") {
+    if build.build.ends_with("apple-darwin") {
         // Force /usr/bin/python on macOS for LLDB tests because we're loading the
         // LLDB plugin's compiled module which only works with the system python
         // (namely not Homebrew-installed python)
@@ -251,7 +249,7 @@ pub fn compiletest(build: &Build,
 
     cmd.args(&build.flags.cmd.test_args());
 
-    if build.config.verbose() || build.flags.verbose() {
+    if build.is_verbose() {
         cmd.arg("--verbose");
     }
 
@@ -279,7 +277,7 @@ pub fn compiletest(build: &Build,
 
     if build.remote_tested(target) {
         cmd.arg("--remote-test-client")
-           .arg(build.tool(&Compiler::new(0, &build.config.build),
+           .arg(build.tool(&Compiler::new(0, &build.build),
                            "remote-test-client"));
     }
 
@@ -368,7 +366,7 @@ pub fn error_index(build: &Build, compiler: &Compiler) {
                              "error_index_generator")
                    .arg("markdown")
                    .arg(&output)
-                   .env("CFG_BUILD", &build.config.build));
+                   .env("CFG_BUILD", &build.build));
 
     markdown_test(build, compiler, &output);
 }
@@ -450,7 +448,7 @@ pub fn krate(build: &Build,
     cargo.arg("--manifest-path")
          .arg(build.src.join(path).join("Cargo.toml"))
          .arg("--features").arg(features);
-    if test_kind.subcommand() == "test" && build.flags.cmd.no_fail_fast() {
+    if test_kind.subcommand() == "test" && !build.fail_fast {
         cargo.arg("--no-fail-fast");
     }
 
@@ -520,16 +518,14 @@ fn krate_emscripten(build: &Build,
                     compiler: &Compiler,
                     target: &str,
                     mode: Mode) {
-    let mut tests = Vec::new();
     let out_dir = build.cargo_out(compiler, mode, target);
-    find_tests(&out_dir.join("deps"), target, &mut tests);
+    let tests = find_tests(&out_dir.join("deps"), target);
 
+    let nodejs = build.config.nodejs.as_ref().expect("nodejs not configured");
     for test in tests {
-        let test_file_name = test.to_string_lossy().into_owned();
-        println!("running {}", test_file_name);
-        let nodejs = build.config.nodejs.as_ref().expect("nodejs not configured");
+        println!("running {}", test.display());
         let mut cmd = Command::new(nodejs);
-        cmd.arg(&test_file_name);
+        cmd.arg(&test);
         if build.config.quiet_tests {
             cmd.arg("--quiet");
         }
@@ -541,11 +537,10 @@ fn krate_remote(build: &Build,
                 compiler: &Compiler,
                 target: &str,
                 mode: Mode) {
-    let mut tests = Vec::new();
     let out_dir = build.cargo_out(compiler, mode, target);
-    find_tests(&out_dir.join("deps"), target, &mut tests);
+    let tests = find_tests(&out_dir.join("deps"), target);
 
-    let tool = build.tool(&Compiler::new(0, &build.config.build),
+    let tool = build.tool(&Compiler::new(0, &build.build),
                           "remote-test-client");
     for test in tests {
         let mut cmd = Command::new(&tool);
@@ -559,9 +554,8 @@ fn krate_remote(build: &Build,
     }
 }
 
-fn find_tests(dir: &Path,
-              target: &str,
-              dst: &mut Vec<PathBuf>) {
+fn find_tests(dir: &Path, target: &str) -> Vec<PathBuf> {
+    let mut dst = Vec::new();
     for e in t!(dir.read_dir()).map(|e| t!(e)) {
         let file_type = t!(e.file_type());
         if !file_type.is_file() {
@@ -576,6 +570,7 @@ fn find_tests(dir: &Path,
             dst.push(e.path());
         }
     }
+    dst
 }
 
 pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) {
@@ -590,7 +585,7 @@ pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) {
                       .join(exe("remote-test-server", target));
 
     // Spawn the emulator and wait for it to come online
-    let tool = build.tool(&Compiler::new(0, &build.config.build),
+    let tool = build.tool(&Compiler::new(0, &build.build),
                           "remote-test-client");
     let mut cmd = Command::new(&tool);
     cmd.arg("spawn-emulator")
@@ -616,7 +611,7 @@ pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) {
 
 /// Run "distcheck", a 'make check' from a tarball
 pub fn distcheck(build: &Build) {
-    if build.config.build != "x86_64-unknown-linux-gnu" {
+    if build.build != "x86_64-unknown-linux-gnu" {
         return
     }
     if !build.config.host.iter().any(|s| s == "x86_64-unknown-linux-gnu") {
@@ -641,7 +636,7 @@ pub fn distcheck(build: &Build) {
                      .args(&build.config.configure_args)
                      .arg("--enable-vendor")
                      .current_dir(&dir));
-    build.run(Command::new(build_helper::make(&build.config.build))
+    build.run(Command::new(build_helper::make(&build.build))
                      .arg("check")
                      .current_dir(&dir));
 
@@ -659,7 +654,7 @@ pub fn distcheck(build: &Build) {
     build.run(&mut cmd);
 
     let toml = dir.join("rust-src/lib/rustlib/src/rust/src/libstd/Cargo.toml");
-    build.run(Command::new(&build.cargo)
+    build.run(Command::new(&build.initial_cargo)
                      .arg("generate-lockfile")
                      .arg("--manifest-path")
                      .arg(&toml)
@@ -668,13 +663,13 @@ pub fn distcheck(build: &Build) {
 
 /// Test the build system itself
 pub fn bootstrap(build: &Build) {
-    let mut cmd = Command::new(&build.cargo);
+    let mut cmd = Command::new(&build.initial_cargo);
     cmd.arg("test")
        .current_dir(build.src.join("src/bootstrap"))
        .env("CARGO_TARGET_DIR", build.out.join("bootstrap"))
        .env("RUSTC_BOOTSTRAP", "1")
-       .env("RUSTC", &build.rustc);
-    if build.flags.cmd.no_fail_fast() {
+       .env("RUSTC", &build.initial_rustc);
+    if !build.fail_fast {
         cmd.arg("--no-fail-fast");
     }
     cmd.arg("--").args(&build.flags.cmd.test_args());
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index b2bd792e93b..5a3106c7d5e 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -50,7 +50,7 @@ pub fn std(build: &Build, target: &str, compiler: &Compiler) {
     let mut cargo = build.cargo(compiler, Mode::Libstd, target, "build");
     let mut features = build.std_features();
 
-    if let Ok(target) = env::var("MACOSX_STD_DEPLOYMENT_TARGET") {
+    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
         cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
     }
 
@@ -158,7 +158,7 @@ pub fn build_startup_objects(build: &Build, for_compiler: &Compiler, target: &st
         return
     }
 
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
     let compiler_path = build.compiler_path(&compiler);
     let src_dir = &build.src.join("src/rtstartup");
     let dst_dir = &build.native_dir(target).join("rtstartup");
@@ -199,7 +199,7 @@ pub fn test(build: &Build, target: &str, compiler: &Compiler) {
     let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
     build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
     let mut cargo = build.cargo(compiler, Mode::Libtest, target, "build");
-    if let Ok(target) = env::var("MACOSX_STD_DEPLOYMENT_TARGET") {
+    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
         cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
     }
     cargo.arg("--manifest-path")
@@ -247,7 +247,7 @@ pub fn rustc(build: &Build, target: &str, compiler: &Compiler) {
     cargo.env("CFG_RELEASE", build.rust_release())
          .env("CFG_RELEASE_CHANNEL", &build.config.channel)
          .env("CFG_VERSION", build.rust_version())
-         .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or(PathBuf::new()));
+         .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default());
 
     if compiler.stage == 0 {
         cargo.env("CFG_LIBDIR_RELATIVE", "lib");
@@ -351,7 +351,7 @@ pub fn create_sysroot(build: &Build, compiler: &Compiler) {
 /// Prepare a new compiler from the artifacts in `stage`
 ///
 /// This will assemble a compiler in `build/$host/stage$stage`. The compiler
-/// must have been previously produced by the `stage - 1` build.config.build
+/// must have been previously produced by the `stage - 1` build.build
 /// compiler.
 pub fn assemble_rustc(build: &Build, stage: u32, host: &str) {
     // nothing to do in stage0
@@ -365,7 +365,7 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) {
     let target_compiler = Compiler::new(stage, host);
 
     // The compiler that compiled the compiler we're assembling
-    let build_compiler = Compiler::new(stage - 1, &build.config.build);
+    let build_compiler = Compiler::new(stage - 1, &build.build);
 
     // Link in all dylibs to the libdir
     let sysroot = build.sysroot(&target_compiler);
@@ -385,7 +385,7 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) {
     let rustc = out_dir.join(exe("rustc", host));
     let bindir = sysroot.join("bin");
     t!(fs::create_dir_all(&bindir));
-    let compiler = build.compiler_path(&Compiler::new(stage, host));
+    let compiler = build.compiler_path(&target_compiler);
     let _ = fs::remove_file(&compiler);
     copy(&rustc, &compiler);
 
@@ -407,6 +407,8 @@ fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
     t!(fs::create_dir_all(&sysroot_dst));
     let mut contents = Vec::new();
     t!(t!(File::open(stamp)).read_to_end(&mut contents));
+    // This is the method we use for extracting paths from the stamp file passed to us. See
+    // run_cargo for more information (in this file).
     for part in contents.split(|b| *b == 0) {
         if part.is_empty() {
             continue
@@ -421,7 +423,7 @@ fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
 /// This will build the specified tool with the specified `host` compiler in
 /// `stage` into the normal cargo output directory.
 pub fn maybe_clean_tools(build: &Build, stage: u32, target: &str, mode: Mode) {
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
 
     let stamp = match mode {
         Mode::Libstd => libstd_stamp(build, &compiler, target),
@@ -441,7 +443,7 @@ pub fn tool(build: &Build, stage: u32, target: &str, tool: &str) {
     let _folder = build.fold_output(|| format!("stage{}-{}", stage, tool));
     println!("Building stage{} tool {} ({})", stage, tool, target);
 
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
 
     let mut cargo = build.cargo(&compiler, Mode::Tool, target, "build");
     let dir = build.src.join("src/tools").join(tool);
@@ -557,23 +559,24 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) {
             // If this was an output file in the "host dir" we don't actually
             // worry about it, it's not relevant for us.
             if filename.starts_with(&host_root_dir) {
-                continue
+                continue;
+            }
 
             // If this was output in the `deps` dir then this is a precise file
             // name (hash included) so we start tracking it.
-            } else if filename.starts_with(&target_deps_dir) {
+            if filename.starts_with(&target_deps_dir) {
                 deps.push(filename.to_path_buf());
+                continue;
+            }
 
             // Otherwise this was a "top level artifact" which right now doesn't
             // have a hash in the name, but there's a version of this file in
             // the `deps` folder which *does* have a hash in the name. That's
             // the one we'll want to we'll probe for it later.
-            } else {
-                toplevel.push((filename.file_stem().unwrap()
-                                       .to_str().unwrap().to_string(),
-                               filename.extension().unwrap().to_owned()
-                                       .to_str().unwrap().to_string()));
-            }
+            toplevel.push((filename.file_stem().unwrap()
+                                    .to_str().unwrap().to_string(),
+                            filename.extension().unwrap().to_owned()
+                                    .to_str().unwrap().to_string()));
         }
     }
 
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 3ada846e382..34628852ab3 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -81,8 +81,6 @@ pub struct Config {
     pub build: String,
     pub host: Vec<String>,
     pub target: Vec<String>,
-    pub rustc: Option<PathBuf>,
-    pub cargo: Option<PathBuf>,
     pub local_rebuild: bool,
 
     // dist misc
@@ -114,11 +112,18 @@ pub struct Config {
     pub python: Option<PathBuf>,
     pub configure_args: Vec<String>,
     pub openssl_static: bool,
+
+
+    // These are either the stage0 downloaded binaries or the locally installed ones.
+    pub initial_cargo: PathBuf,
+    pub initial_rustc: PathBuf,
+
 }
 
 /// Per-target configuration stored in the global configuration structure.
 #[derive(Default)]
 pub struct Target {
+    /// Some(path to llvm-config) if using an external LLVM.
     pub llvm_config: Option<PathBuf>,
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
@@ -307,8 +312,6 @@ impl Config {
                 config.target.push(target.clone());
             }
         }
-        config.rustc = build.rustc.map(PathBuf::from);
-        config.cargo = build.cargo.map(PathBuf::from);
         config.nodejs = build.nodejs.map(PathBuf::from);
         config.gdb = build.gdb.map(PathBuf::from);
         config.python = build.python.map(PathBuf::from);
@@ -410,13 +413,25 @@ impl Config {
             set(&mut config.rust_dist_src, t.src_tarball);
         }
 
+        let cwd = t!(env::current_dir());
+        let out = cwd.join("build");
+
+        let stage0_root = out.join(&config.build).join("stage0/bin");
+        config.initial_rustc = match build.rustc {
+            Some(s) => PathBuf::from(s),
+            None => stage0_root.join(exe("rustc", &config.build)),
+        };
+        config.initial_cargo = match build.cargo {
+            Some(s) => PathBuf::from(s),
+            None => stage0_root.join(exe("cargo", &config.build)),
+        };
 
         // compat with `./configure` while we're still using that
         if fs::metadata("config.mk").is_ok() {
             config.update_with_config_mk();
         }
 
-        return config
+        config
     }
 
     /// "Temporary" routine to parse `config.mk` into this configuration.
@@ -609,8 +624,8 @@ impl Config {
                 }
                 "CFG_LOCAL_RUST_ROOT" if value.len() > 0 => {
                     let path = parse_configure_path(value);
-                    self.rustc = Some(push_exe_path(path.clone(), &["bin", "rustc"]));
-                    self.cargo = Some(push_exe_path(path, &["bin", "cargo"]));
+                    self.initial_rustc = push_exe_path(path.clone(), &["bin", "rustc"]);
+                    self.initial_cargo = push_exe_path(path, &["bin", "cargo"]);
                 }
                 "CFG_PYTHON" if value.len() > 0 => {
                     let path = parse_configure_path(value);
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 753bd1df0d8..efc33bcee9b 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -50,7 +50,7 @@ pub fn tmpdir(build: &Build) -> PathBuf {
 }
 
 fn rust_installer(build: &Build) -> Command {
-    build.tool_cmd(&Compiler::new(0, &build.config.build), "rust-installer")
+    build.tool_cmd(&Compiler::new(0, &build.build), "rust-installer")
 }
 
 /// Builds the `rust-docs` installer component.
@@ -89,7 +89,7 @@ pub fn docs(build: &Build, stage: u32, host: &str) {
 
     // As part of this step, *also* copy the docs directory to a directory which
     // buildbot typically uploads.
-    if host == build.config.build {
+    if host == build.build {
         let dst = distdir(build).join("doc").join(build.rust_package_vers());
         t!(fs::create_dir_all(&dst));
         cp_r(&src, &dst);
@@ -97,7 +97,7 @@ pub fn docs(build: &Build, stage: u32, host: &str) {
 }
 
 fn find_files(files: &[&str], path: &[PathBuf]) -> Vec<PathBuf> {
-    let mut found = Vec::new();
+    let mut found = Vec::with_capacity(files.len());
 
     for file in files {
         let file_path =
@@ -119,17 +119,9 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
     //Ask gcc where it keeps its stuff
     let mut cmd = Command::new(build.cc(target_triple));
     cmd.arg("-print-search-dirs");
-    build.run_quiet(&mut cmd);
-    let gcc_out =
-        String::from_utf8(
-                cmd
-                .output()
-                .expect("failed to execute gcc")
-                .stdout).expect("gcc.exe output was not utf8");
-
-    let mut bin_path: Vec<_> =
-        env::split_paths(&env::var_os("PATH").unwrap_or_default())
-        .collect();
+    let gcc_out = output(&mut cmd);
+
+    let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect();
     let mut lib_path = Vec::new();
 
     for line in gcc_out.lines() {
@@ -140,7 +132,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
             line[(idx + 1)..]
                 .trim_left_matches(trim_chars)
                 .split(';')
-                .map(|s| PathBuf::from(s));
+                .map(PathBuf::from);
 
         if key == "programs" {
             bin_path.extend(value);
@@ -149,7 +141,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
         }
     }
 
-    let target_tools = vec!["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe", "libwinpthread-1.dll"];
+    let target_tools = ["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe", "libwinpthread-1.dll"];
     let mut rustc_dlls = vec!["libstdc++-6.dll", "libwinpthread-1.dll"];
     if target_triple.starts_with("i686-") {
         rustc_dlls.push("libgcc_s_dw2-1.dll");
@@ -157,7 +149,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
         rustc_dlls.push("libgcc_s_seh-1.dll");
     }
 
-    let target_libs = vec![ //MinGW libs
+    let target_libs = [ //MinGW libs
         "libgcc.a",
         "libgcc_eh.a",
         "libgcc_s.a",
@@ -203,7 +195,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
     let target_libs = find_files(&target_libs, &lib_path);
 
     fn copy_to_folder(src: &Path, dest_folder: &Path) {
-        let file_name = src.file_name().unwrap().to_os_string();
+        let file_name = src.file_name().unwrap();
         let dest = dest_folder.join(file_name);
         copy(src, &dest);
     }
@@ -234,8 +226,6 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build:
 ///
 /// This contains all the bits and pieces to run the MinGW Windows targets
 /// without any extra installed software (e.g. we bundle gcc, libraries, etc).
-/// Currently just shells out to a python script, but that should be rewritten
-/// in Rust.
 pub fn mingw(build: &Build, host: &str) {
     println!("Dist mingw ({})", host);
     let name = pkgname(build, "rust-mingw");
@@ -366,9 +356,9 @@ pub fn rustc(build: &Build, stage: u32, host: &str) {
 pub fn debugger_scripts(build: &Build,
                         sysroot: &Path,
                         host: &str) {
+    let dst = sysroot.join("lib/rustlib/etc");
+    t!(fs::create_dir_all(&dst));
     let cp_debugger_script = |file: &str| {
-        let dst = sysroot.join("lib/rustlib/etc");
-        t!(fs::create_dir_all(&dst));
         install(&build.src.join("src/etc/").join(file), &dst, 0o644);
     };
     if host.contains("windows-msvc") {
@@ -404,7 +394,7 @@ pub fn std(build: &Build, compiler: &Compiler, target: &str) {
 
     // The only true set of target libraries came from the build triple, so
     // let's reduce redundant work by only producing archives from that host.
-    if compiler.host != build.config.build {
+    if compiler.host != build.build {
         println!("\tskipping, not a build host");
         return
     }
@@ -450,7 +440,7 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) {
     assert!(build.config.extended);
     println!("Dist analysis");
 
-    if compiler.host != build.config.build {
+    if compiler.host != build.build {
         println!("\tskipping, not a build host");
         return;
     }
@@ -498,12 +488,11 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di
         if spath.ends_with("~") || spath.ends_with(".pyc") {
             return false
         }
-        if spath.contains("llvm/test") || spath.contains("llvm\\test") {
-            if spath.ends_with(".ll") ||
-               spath.ends_with(".td") ||
-               spath.ends_with(".s") {
-                return false
-            }
+        if (spath.contains("llvm/test") || spath.contains("llvm\\test")) &&
+            (spath.ends_with(".ll") ||
+             spath.ends_with(".td") ||
+             spath.ends_with(".s")) {
+            return false
         }
 
         let full_path = Path::new(dir).join(path);
@@ -595,7 +584,7 @@ pub fn rust_src(build: &Build) {
     t!(fs::remove_dir_all(&image));
 }
 
-const CARGO_VENDOR_VERSION: &'static str = "0.1.4";
+const CARGO_VENDOR_VERSION: &str = "0.1.4";
 
 /// Creates the plain source tarball
 pub fn plain_source_tarball(build: &Build) {
@@ -634,26 +623,26 @@ pub fn plain_source_tarball(build: &Build) {
     write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes());
 
     // If we're building from git sources, we need to vendor a complete distribution.
-    if build.src_is_git {
+    if build.rust_info.is_git() {
         // Get cargo-vendor installed, if it isn't already.
         let mut has_cargo_vendor = false;
-        let mut cmd = Command::new(&build.cargo);
+        let mut cmd = Command::new(&build.initial_cargo);
         for line in output(cmd.arg("install").arg("--list")).lines() {
             has_cargo_vendor |= line.starts_with("cargo-vendor ");
         }
         if !has_cargo_vendor {
-            let mut cmd = Command::new(&build.cargo);
+            let mut cmd = Command::new(&build.initial_cargo);
             cmd.arg("install")
                .arg("--force")
                .arg("--debug")
                .arg("--vers").arg(CARGO_VENDOR_VERSION)
                .arg("cargo-vendor")
-               .env("RUSTC", &build.rustc);
+               .env("RUSTC", &build.initial_rustc);
             build.run(&mut cmd);
         }
 
         // Vendor all Cargo dependencies
-        let mut cmd = Command::new(&build.cargo);
+        let mut cmd = Command::new(&build.initial_cargo);
         cmd.arg("vendor")
            .current_dir(&plain_dst_src.join("src"));
         build.run(&mut cmd);
@@ -716,7 +705,7 @@ fn write_file(path: &Path, data: &[u8]) {
 
 pub fn cargo(build: &Build, stage: u32, target: &str) {
     println!("Dist cargo stage{} ({})", stage, target);
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
 
     let src = build.src.join("src/tools/cargo");
     let etc = src.join("src/etc");
@@ -777,7 +766,7 @@ pub fn cargo(build: &Build, stage: u32, target: &str) {
 pub fn rls(build: &Build, stage: u32, target: &str) {
     assert!(build.config.extended);
     println!("Dist RLS stage{} ({})", stage, target);
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
 
     let src = build.src.join("src/tools/rls");
     let release_num = build.release_num("rls");
@@ -1209,7 +1198,7 @@ fn add_env(build: &Build, cmd: &mut Command, target: &str) {
 }
 
 pub fn hash_and_sign(build: &Build) {
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
     let mut cmd = build.tool_cmd(&compiler, "build-manifest");
     let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
         panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n")
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 30f631ca2df..7dbc3e55539 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -45,7 +45,7 @@ pub fn rustbook_src(build: &Build, target: &str, name: &str, src: &Path) {
     t!(fs::create_dir_all(&out));
 
     let out = out.join(name);
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
     let src = src.join(name);
     let index = out.join("index.html");
     let rustbook = build.tool(&compiler, "rustbook");
@@ -95,7 +95,7 @@ pub fn book(build: &Build, target: &str, name: &str) {
 fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
     let out = build.doc_out(target);
 
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
 
     let path = build.src.join("src/doc").join(markdown);
 
@@ -150,7 +150,7 @@ pub fn standalone(build: &Build, target: &str) {
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
 
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
 
     let favicon = build.src.join("src/doc/favicon.inc");
     let footer = build.src.join("src/doc/footer.inc");
@@ -217,7 +217,7 @@ pub fn std(build: &Build, stage: u32, target: &str) {
     println!("Documenting stage{} std ({})", stage, target);
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
     let compiler = if build.force_use_stage1(&compiler, target) {
         Compiler::new(1, compiler.host)
     } else {
@@ -276,7 +276,7 @@ pub fn test(build: &Build, stage: u32, target: &str) {
     println!("Documenting stage{} test ({})", stage, target);
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
     let compiler = if build.force_use_stage1(&compiler, target) {
         Compiler::new(1, compiler.host)
     } else {
@@ -306,7 +306,7 @@ pub fn rustc(build: &Build, stage: u32, target: &str) {
     println!("Documenting stage{} compiler ({})", stage, target);
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
-    let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = Compiler::new(stage, &build.build);
     let compiler = if build.force_use_stage1(&compiler, target) {
         Compiler::new(1, compiler.host)
     } else {
@@ -351,13 +351,13 @@ pub fn error_index(build: &Build, target: &str) {
     println!("Documenting error index ({})", target);
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
     let mut index = build.tool_cmd(&compiler, "error_index_generator");
     index.arg("html");
     index.arg(out.join("error-index.html"));
 
     // FIXME: shouldn't have to pass this env var
-    index.env("CFG_BUILD", &build.config.build);
+    index.env("CFG_BUILD", &build.build);
 
     build.run(&mut index);
 }
@@ -367,7 +367,7 @@ pub fn unstable_book_gen(build: &Build, target: &str) {
     let out = build.md_doc_out(target).join("unstable-book");
     t!(fs::create_dir_all(&out));
     t!(fs::remove_dir_all(&out));
-    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = Compiler::new(0, &build.build);
     let mut cmd = build.tool_cmd(&compiler, "unstable-book-gen");
     cmd.arg(build.src.join("src"));
     cmd.arg(out);
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index dc9dac73627..5804df34e8b 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -35,22 +35,12 @@ pub struct Flags {
     pub host: Vec<String>,
     pub target: Vec<String>,
     pub config: Option<PathBuf>,
-    pub src: Option<PathBuf>,
+    pub src: PathBuf,
     pub jobs: Option<u32>,
     pub cmd: Subcommand,
     pub incremental: bool,
 }
 
-impl Flags {
-    pub fn verbose(&self) -> bool {
-        self.verbose > 0
-    }
-
-    pub fn very_verbose(&self) -> bool {
-        self.verbose > 1
-    }
-}
-
 pub enum Subcommand {
     Build {
         paths: Vec<PathBuf>,
@@ -61,7 +51,7 @@ pub enum Subcommand {
     Test {
         paths: Vec<PathBuf>,
         test_args: Vec<String>,
-        no_fail_fast: bool,
+        fail_fast: bool,
     },
     Bench {
         paths: Vec<PathBuf>,
@@ -122,16 +112,15 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
         // the subcommand. Therefore we must manually identify the subcommand first, so that we can
         // complete the definition of the options.  Then we can use the getopt::Matches object from
         // there on out.
-        let mut possible_subcommands = args.iter().collect::<Vec<_>>();
-        possible_subcommands.retain(|&s|
-                                           (s == "build")
-                                        || (s == "test")
-                                        || (s == "bench")
-                                        || (s == "doc")
-                                        || (s == "clean")
-                                        || (s == "dist")
-                                        || (s == "install"));
-        let subcommand = match possible_subcommands.first() {
+        let subcommand = args.iter().find(|&s|
+            (s == "build")
+            || (s == "test")
+            || (s == "bench")
+            || (s == "doc")
+            || (s == "clean")
+            || (s == "dist")
+            || (s == "install"));
+        let subcommand = match subcommand {
             Some(s) => s,
             None => {
                 // No subcommand -- show the general usage and subcommand help
@@ -164,7 +153,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
         let mut pass_sanity_check = true;
         match matches.free.get(0) {
             Some(check_subcommand) => {
-                if &check_subcommand != subcommand {
+                if check_subcommand != subcommand {
                     pass_sanity_check = false;
                 }
             },
@@ -279,7 +268,7 @@ Arguments:
                 Subcommand::Test {
                     paths: paths,
                     test_args: matches.opt_strs("test-args"),
-                    no_fail_fast: matches.opt_present("no-fail-fast"),
+                    fail_fast: !matches.opt_present("no-fail-fast"),
                 }
             }
             "bench" => {
@@ -316,12 +305,15 @@ Arguments:
 
         let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
 
-        if matches.opt_present("incremental") {
-            if stage.is_none() {
-                stage = Some(1);
-            }
+        if matches.opt_present("incremental") && stage.is_none() {
+            stage = Some(1);
         }
 
+        let cwd = t!(env::current_dir());
+        let src = matches.opt_str("src").map(PathBuf::from)
+            .or_else(|| env::var_os("SRC").map(PathBuf::from))
+            .unwrap_or(cwd);
+
         Flags {
             verbose: matches.opt_count("verbose"),
             stage: stage,
@@ -333,7 +325,7 @@ Arguments:
             host: split(matches.opt_strs("host")),
             target: split(matches.opt_strs("target")),
             config: cfg_file,
-            src: matches.opt_str("src").map(PathBuf::from),
+            src: src,
             jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
             cmd: cmd,
             incremental: matches.opt_present("incremental"),
@@ -352,9 +344,9 @@ impl Subcommand {
         }
     }
 
-    pub fn no_fail_fast(&self) -> bool {
+    pub fn fail_fast(&self) -> bool {
         match *self {
-            Subcommand::Test { no_fail_fast, .. } => no_fail_fast,
+            Subcommand::Test { fail_fast, .. } => fail_fast,
             _ => false,
         }
     }
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 21e21628dc9..8e2ef527b16 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -146,5 +146,5 @@ fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
             _ => {}
         }
     }
-    return ret
+    ret
 }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 86180077b82..69b0c4a2756 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -161,25 +161,35 @@ pub struct Build {
     flags: Flags,
 
     // Derived properties from the above two configurations
-    cargo: PathBuf,
-    rustc: PathBuf,
     src: PathBuf,
     out: PathBuf,
     rust_info: channel::GitInfo,
     cargo_info: channel::GitInfo,
     rls_info: channel::GitInfo,
     local_rebuild: bool,
+    fail_fast: bool,
+    verbosity: usize,
+
+    // Targets for which to build.
+    build: String,
+    hosts: Vec<String>,
+    targets: Vec<String>,
+
+    // Stage 0 (downloaded) compiler and cargo or their local rust equivalents.
+    initial_rustc: PathBuf,
+    initial_cargo: PathBuf,
 
     // Probed tools at runtime
     lldb_version: Option<String>,
     lldb_python_dir: Option<String>,
 
     // Runtime state filled in later on
+    // target -> (cc, ar)
     cc: HashMap<String, (gcc::Tool, Option<PathBuf>)>,
+    // host -> (cc, ar)
     cxx: HashMap<String, gcc::Tool>,
     crates: HashMap<String, Crate>,
     is_sudo: bool,
-    src_is_git: bool,
     ci_env: CiEnv,
     delayed_failures: Cell<usize>,
 }
@@ -202,20 +212,16 @@ struct Crate {
 /// build system, with each mod generating output in a different directory.
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum Mode {
-    /// This cargo is going to build the standard library, placing output in the
-    /// "stageN-std" directory.
+    /// Build the standard library, placing output in the "stageN-std" directory.
     Libstd,
 
-    /// This cargo is going to build libtest, placing output in the
-    /// "stageN-test" directory.
+    /// Build libtest, placing output in the "stageN-test" directory.
     Libtest,
 
-    /// This cargo is going to build librustc and compiler libraries, placing
-    /// output in the "stageN-rustc" directory.
+    /// Build librustc and compiler libraries, placing output in the "stageN-rustc" directory.
     Librustc,
 
-    /// This cargo is going to build some tool, placing output in the
-    /// "stageN-tools" directory.
+    /// Build some tool, placing output in the "stageN-tools" directory.
     Tool,
 }
 
@@ -226,22 +232,9 @@ impl Build {
     /// By default all build output will be placed in the current directory.
     pub fn new(flags: Flags, config: Config) -> Build {
         let cwd = t!(env::current_dir());
-        let src = flags.src.clone().or_else(|| {
-            env::var_os("SRC").map(|x| x.into())
-        }).unwrap_or(cwd.clone());
+        let src = flags.src.clone();
         let out = cwd.join("build");
 
-        let stage0_root = out.join(&config.build).join("stage0/bin");
-        let rustc = match config.rustc {
-            Some(ref s) => PathBuf::from(s),
-            None => stage0_root.join(exe("rustc", &config.build)),
-        };
-        let cargo = match config.cargo {
-            Some(ref s) => PathBuf::from(s),
-            None => stage0_root.join(exe("cargo", &config.build)),
-        };
-        let local_rebuild = config.local_rebuild;
-
         let is_sudo = match env::var_os("SUDO_USER") {
             Some(sudo_user) => {
                 match env::var_os("USER") {
@@ -254,32 +247,64 @@ impl Build {
         let rust_info = channel::GitInfo::new(&src);
         let cargo_info = channel::GitInfo::new(&src.join("src/tools/cargo"));
         let rls_info = channel::GitInfo::new(&src.join("src/tools/rls"));
-        let src_is_git = src.join(".git").exists();
+
+        let hosts = if !flags.host.is_empty() {
+            for host in flags.host.iter() {
+                if !config.host.contains(host) {
+                    panic!("specified host `{}` is not in configuration", host);
+                }
+            }
+            flags.host.clone()
+        } else {
+            config.host.clone()
+        };
+        let targets = if !flags.target.is_empty() {
+            for target in flags.target.iter() {
+                if !config.target.contains(target) {
+                    panic!("specified target `{}` is not in configuration", target);
+                }
+            }
+            flags.target.clone()
+        } else {
+            config.target.clone()
+        };
 
         Build {
+            initial_rustc: config.initial_rustc.clone(),
+            initial_cargo: config.initial_cargo.clone(),
+            local_rebuild: config.local_rebuild,
+            fail_fast: flags.cmd.fail_fast(),
+            verbosity: cmp::max(flags.verbose, config.verbose),
+
+            build: config.host[0].clone(),
+            hosts: hosts,
+            targets: targets,
+
             flags: flags,
             config: config,
-            cargo: cargo,
-            rustc: rustc,
             src: src,
             out: out,
 
             rust_info: rust_info,
             cargo_info: cargo_info,
             rls_info: rls_info,
-            local_rebuild: local_rebuild,
             cc: HashMap::new(),
             cxx: HashMap::new(),
             crates: HashMap::new(),
             lldb_version: None,
             lldb_python_dir: None,
             is_sudo: is_sudo,
-            src_is_git: src_is_git,
             ci_env: CiEnv::current(),
             delayed_failures: Cell::new(0),
         }
     }
 
+    fn build_slice(&self) -> &[String] {
+        unsafe {
+            std::slice::from_raw_parts(&self.build, 1)
+        }
+    }
+
     /// Executes the entire build, as configured by the flags and configuration.
     pub fn build(&mut self) {
         unsafe {
@@ -296,7 +321,7 @@ impl Build {
         sanity::check(self);
         // If local-rust is the same major.minor as the current version, then force a local-rebuild
         let local_version_verbose = output(
-            Command::new(&self.rustc).arg("--version").arg("--verbose"));
+            Command::new(&self.initial_rustc).arg("--version").arg("--verbose"));
         let local_release = local_version_verbose
             .lines().filter(|x| x.starts_with("release:"))
             .next().unwrap().trim_left_matches("release:").trim();
@@ -338,7 +363,7 @@ impl Build {
              mode: Mode,
              target: &str,
              cmd: &str) -> Command {
-        let mut cargo = Command::new(&self.cargo);
+        let mut cargo = Command::new(&self.initial_cargo);
         let out_dir = self.stage_out(compiler, mode);
         cargo.env("CARGO_TARGET_DIR", out_dir)
              .arg(cmd)
@@ -422,7 +447,7 @@ impl Build {
         // library up and running, so we can use the normal compiler to compile
         // build scripts in that situation.
         if mode == Mode::Libstd {
-            cargo.env("RUSTC_SNAPSHOT", &self.rustc)
+            cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
                  .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
         } else {
             cargo.env("RUSTC_SNAPSHOT", self.compiler_path(compiler))
@@ -441,8 +466,7 @@ impl Build {
             cargo.env("RUSTC_ON_FAIL", on_fail);
         }
 
-        let verbose = cmp::max(self.config.verbose, self.flags.verbose);
-        cargo.env("RUSTC_VERBOSE", format!("{}", verbose));
+        cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
 
         // Specify some various options for build scripts used throughout
         // the build.
@@ -480,7 +504,7 @@ impl Build {
         // FIXME: should update code to not require this env var
         cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
 
-        if self.config.verbose() || self.flags.verbose() {
+        if self.is_verbose() {
             cargo.arg("-v");
         }
         // FIXME: cargo bench does not accept `--release`
@@ -496,13 +520,13 @@ impl Build {
 
         self.ci_env.force_coloring_in_ci(&mut cargo);
 
-        return cargo
+        cargo
     }
 
     /// Get a path to the compiler specified.
     fn compiler_path(&self, compiler: &Compiler) -> PathBuf {
         if compiler.is_snapshot(self) {
-            self.rustc.clone()
+            self.initial_rustc.clone()
         } else {
             self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
         }
@@ -519,7 +543,7 @@ impl Build {
         let mut rustdoc = self.compiler_path(compiler);
         rustdoc.pop();
         rustdoc.push(exe("rustdoc", compiler.host));
-        return rustdoc
+        rustdoc
     }
 
     /// Get a `Command` which is ready to run `tool` in `stage` built for
@@ -527,7 +551,7 @@ impl Build {
     fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command {
         let mut cmd = Command::new(self.tool(&compiler, tool));
         self.prepare_tool_cmd(compiler, &mut cmd);
-        return cmd
+        cmd
     }
 
     /// Prepares the `cmd` provided to be able to run the `compiler` provided.
@@ -578,7 +602,7 @@ impl Build {
         if self.config.profiler {
             features.push_str(" profiler");
         }
-        return features
+        features
     }
 
     /// Get the space-separated set of activated features for the compiler.
@@ -587,7 +611,7 @@ impl Build {
         if self.config.use_jemalloc {
             features.push_str(" jemalloc");
         }
-        return features
+        features
     }
 
     /// Component directory that Cargo will produce output into (e.g.
@@ -760,7 +784,7 @@ impl Build {
 
     /// Returns the libdir of the snapshot compiler.
     fn rustc_snapshot_libdir(&self) -> PathBuf {
-        self.rustc.parent().unwrap().parent().unwrap()
+        self.initial_rustc.parent().unwrap().parent().unwrap()
             .join(libdir(&self.config.build))
     }
 
@@ -792,9 +816,17 @@ impl Build {
         try_run_suppressed(cmd)
     }
 
+    pub fn is_verbose(&self) -> bool {
+        self.verbosity > 0
+    }
+
+    pub fn is_very_verbose(&self) -> bool {
+        self.verbosity > 1
+    }
+
     /// Prints a message if this build is configured in verbose mode.
     fn verbose(&self, msg: &str) {
-        if self.flags.verbose() || self.config.verbose() {
+        if self.is_verbose() {
             println!("{}", msg);
         }
     }
@@ -802,7 +834,7 @@ impl Build {
     /// Returns the number of parallel jobs that have been configured for this
     /// build.
     fn jobs(&self) -> u32 {
-        self.flags.jobs.unwrap_or(num_cpus::get() as u32)
+        self.flags.jobs.unwrap_or_else(|| num_cpus::get() as u32)
     }
 
     /// Returns the path to the C compiler for the target specified.
@@ -834,7 +866,7 @@ impl Build {
         if target == "i686-pc-windows-gnu" {
             base.push("-fno-omit-frame-pointer".into());
         }
-        return base
+        base
     }
 
     /// Returns the path to the `ar` archive utility for the target specified.
@@ -866,7 +898,7 @@ impl Build {
             !target.contains("emscripten") {
             base.push(format!("-Clinker={}", self.cc(target).display()));
         }
-        return base
+        base
     }
 
     /// Returns the "musl root" for this `target`, if defined
@@ -1047,7 +1079,7 @@ impl<'a> Compiler<'a> {
 
     /// Returns whether this is a snapshot compiler for `build`'s configuration
     fn is_snapshot(&self, build: &Build) -> bool {
-        self.stage == 0 && self.host == build.config.build
+        self.stage == 0 && self.host == build.build
     }
 
     /// Returns if this compiler should be treated as a final stage one in the
diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs
index 7b6b01655df..9326bb7129a 100644
--- a/src/bootstrap/metadata.rs
+++ b/src/bootstrap/metadata.rs
@@ -56,7 +56,7 @@ fn build_krate(build: &mut Build, krate: &str) {
     // of packages we're going to have to know what `-p` arguments to pass it
     // to know what crates to test. Here we run `cargo metadata` to learn about
     // the dependency graph and what `-p` arguments there are.
-    let mut cargo = Command::new(&build.cargo);
+    let mut cargo = Command::new(&build.initial_cargo);
     cargo.arg("metadata")
          .arg("--format-version").arg("1")
          .arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml"));
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index f150df6cdcd..20eec97d8e5 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -94,7 +94,7 @@ pub fn llvm(build: &Build, target: &str) {
     let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
 
     cfg.target(target)
-       .host(&build.config.build)
+       .host(&build.build)
        .out_dir(&out_dir)
        .profile(profile)
        .define("LLVM_ENABLE_ASSERTIONS", assertions)
@@ -129,11 +129,11 @@ pub fn llvm(build: &Build, target: &str) {
     }
 
     // http://llvm.org/docs/HowToCrossCompileLLVM.html
-    if target != build.config.build {
+    if target != build.build {
         // FIXME: if the llvm root for the build triple is overridden then we
         //        should use llvm-tblgen from there, also should verify that it
         //        actually exists most of the time in normal installs of LLVM.
-        let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
+        let host = build.llvm_out(&build.build).join("bin/llvm-tblgen");
         cfg.define("CMAKE_CROSSCOMPILING", "True")
            .define("LLVM_TABLEGEN", &host);
     }
@@ -243,7 +243,7 @@ pub fn test_helpers(build: &Build, target: &str) {
     cfg.cargo_metadata(false)
        .out_dir(&dst)
        .target(target)
-       .host(&build.config.build)
+       .host(&build.build)
        .opt_level(0)
        .debug(false)
        .file(build.src.join("src/rt/rust_test_helpers.c"))
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 46d047bb015..a9c1b023dd4 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -18,9 +18,9 @@
 //! In theory if we get past this phase it's a bug if a build fails, but in
 //! practice that's likely not true!
 
-use std::collections::HashSet;
+use std::collections::HashMap;
 use std::env;
-use std::ffi::{OsStr, OsString};
+use std::ffi::{OsString, OsStr};
 use std::fs;
 use std::process::Command;
 use std::path::PathBuf;
@@ -29,45 +29,59 @@ use build_helper::output;
 
 use Build;
 
+struct Finder {
+    cache: HashMap<OsString, Option<PathBuf>>,
+    path: OsString,
+}
+
+impl Finder {
+    fn new() -> Self {
+        Self {
+            cache: HashMap::new(),
+            path: env::var_os("PATH").unwrap_or_default()
+        }
+    }
+
+    fn maybe_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> Option<PathBuf> {
+        let cmd: OsString = cmd.as_ref().into();
+        let path = self.path.clone();
+        self.cache.entry(cmd.clone()).or_insert_with(|| {
+            for path in env::split_paths(&path) {
+                let target = path.join(&cmd);
+                let mut cmd_alt = cmd.clone();
+                cmd_alt.push(".exe");
+                if target.is_file() || // some/path/git
+                target.with_extension("exe").exists() || // some/path/git.exe
+                target.join(&cmd_alt).exists() { // some/path/git/git.exe
+                    return Some(target);
+                }
+            }
+            None
+        }).clone()
+    }
+
+    fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf {
+        self.maybe_have(&cmd).unwrap_or_else(|| {
+            panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref());
+        })
+    }
+}
+
 pub fn check(build: &mut Build) {
-    let mut checked = HashSet::new();
-    let path = env::var_os("PATH").unwrap_or(OsString::new());
+    let path = env::var_os("PATH").unwrap_or_default();
     // On Windows, quotes are invalid characters for filename paths, and if
     // one is present as part of the PATH then that can lead to the system
     // being unable to identify the files properly. See
     // https://github.com/rust-lang/rust/issues/34959 for more details.
-    if cfg!(windows) {
-        if path.to_string_lossy().contains("\"") {
-            panic!("PATH contains invalid character '\"'");
-        }
+    if cfg!(windows) && path.to_string_lossy().contains("\"") {
+        panic!("PATH contains invalid character '\"'");
     }
-    let have_cmd = |cmd: &OsStr| {
-        for path in env::split_paths(&path) {
-            let target = path.join(cmd);
-            let mut cmd_alt = cmd.to_os_string();
-            cmd_alt.push(".exe");
-            if target.is_file() ||
-               target.with_extension("exe").exists() ||
-               target.join(cmd_alt).exists() {
-                return Some(target);
-            }
-        }
-        return None;
-    };
-
-    let mut need_cmd = |cmd: &OsStr| {
-        if !checked.insert(cmd.to_owned()) {
-            return
-        }
-        if have_cmd(cmd).is_none() {
-            panic!("\n\ncouldn't find required command: {:?}\n\n", cmd);
-        }
-    };
 
+    let mut cmd_finder = Finder::new();
     // If we've got a git directory we're gona need git to update
     // submodules and learn about various other aspects.
-    if build.src_is_git {
-        need_cmd("git".as_ref());
+    if build.rust_info.is_git() {
+        cmd_finder.must_have("git");
     }
 
     // We need cmake, but only if we're actually building LLVM or sanitizers.
@@ -75,57 +89,32 @@ pub fn check(build: &mut Build) {
         .filter_map(|host| build.config.target_config.get(host))
         .any(|config| config.llvm_config.is_none());
     if building_llvm || build.config.sanitizers {
-        need_cmd("cmake".as_ref());
+        cmd_finder.must_have("cmake");
     }
 
     // Ninja is currently only used for LLVM itself.
-    if building_llvm && build.config.ninja {
-        // Some Linux distros rename `ninja` to `ninja-build`.
-        // CMake can work with either binary name.
-        if have_cmd("ninja-build".as_ref()).is_none() {
-            need_cmd("ninja".as_ref());
-        }
+    // Some Linux distros rename `ninja` to `ninja-build`.
+    // CMake can work with either binary name.
+    if building_llvm && build.config.ninja && cmd_finder.maybe_have("ninja-build").is_none() {
+        cmd_finder.must_have("ninja");
     }
 
-    if build.config.python.is_none() {
-        // set by bootstrap.py
-        if let Some(v) = env::var_os("BOOTSTRAP_PYTHON") {
-            build.config.python = Some(PathBuf::from(v));
-        }
-    }
-    if build.config.python.is_none() {
-        build.config.python = have_cmd("python2.7".as_ref());
-    }
-    if build.config.python.is_none() {
-        build.config.python = have_cmd("python2".as_ref());
-    }
-    if build.config.python.is_none() {
-        need_cmd("python".as_ref());
-        build.config.python = Some("python".into());
-    }
-    need_cmd(build.config.python.as_ref().unwrap().as_ref());
+    build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p))
+        .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py
+        .or_else(|| cmd_finder.maybe_have("python2.7"))
+        .or_else(|| cmd_finder.maybe_have("python2"))
+        .or_else(|| Some(cmd_finder.must_have("python")));
 
+    build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p))
+        .or_else(|| cmd_finder.maybe_have("node"))
+        .or_else(|| cmd_finder.maybe_have("nodejs"));
 
-    if let Some(ref s) = build.config.nodejs {
-        need_cmd(s.as_ref());
-    } else {
-        // Look for the nodejs command, needed for emscripten testing
-        if let Some(node) = have_cmd("node".as_ref()) {
-            build.config.nodejs = Some(node);
-        } else if let Some(node) = have_cmd("nodejs".as_ref()) {
-            build.config.nodejs = Some(node);
-        }
-    }
-
-    if let Some(ref gdb) = build.config.gdb {
-        need_cmd(gdb.as_ref());
-    } else {
-        build.config.gdb = have_cmd("gdb".as_ref());
-    }
+    build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p))
+        .or_else(|| cmd_finder.maybe_have("gdb"));
 
     // We're gonna build some custom C code here and there, host triples
     // also build some C++ shims for LLVM so we need a C++ compiler.
-    for target in build.config.target.iter() {
+    for target in &build.config.target {
         // On emscripten we don't actually need the C compiler to just
         // build the target artifacts, only for testing. For the sake
         // of easier bot configuration, just skip detection.
@@ -133,33 +122,32 @@ pub fn check(build: &mut Build) {
             continue;
         }
 
-        need_cmd(build.cc(target).as_ref());
+        cmd_finder.must_have(build.cc(target));
         if let Some(ar) = build.ar(target) {
-            need_cmd(ar.as_ref());
+            cmd_finder.must_have(ar);
         }
     }
-    for host in build.config.host.iter() {
-        need_cmd(build.cxx(host).unwrap().as_ref());
-    }
 
-    // The msvc hosts don't use jemalloc, turn it off globally to
-    // avoid packaging the dummy liballoc_jemalloc on that platform.
     for host in build.config.host.iter() {
+        cmd_finder.must_have(build.cxx(host).unwrap());
+
+        // The msvc hosts don't use jemalloc, turn it off globally to
+        // avoid packaging the dummy liballoc_jemalloc on that platform.
         if host.contains("msvc") {
             build.config.use_jemalloc = false;
         }
     }
 
     // Externally configured LLVM requires FileCheck to exist
-    let filecheck = build.llvm_filecheck(&build.config.build);
+    let filecheck = build.llvm_filecheck(&build.build);
     if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
         panic!("FileCheck executable {:?} does not exist", filecheck);
     }
 
-    for target in build.config.target.iter() {
+    for target in &build.config.target {
         // Can't compile for iOS unless we're on macOS
         if target.contains("apple-ios") &&
-           !build.config.build.contains("apple-darwin") {
+           !build.build.contains("apple-darwin") {
             panic!("the iOS target is only supported on macOS");
         }
 
@@ -206,18 +194,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
         }
     }
 
-    for host in build.flags.host.iter() {
-        if !build.config.host.contains(host) {
-            panic!("specified host `{}` is not in the ./configure list", host);
-        }
-    }
-    for target in build.flags.target.iter() {
-        if !build.config.target.contains(target) {
-            panic!("specified target `{}` is not in the ./configure list",
-                   target);
-        }
-    }
-
     let run = |cmd: &mut Command| {
         cmd.output().map(|output| {
             String::from_utf8_lossy(&output.stdout)
@@ -231,6 +207,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
     }
 
     if let Some(ref s) = build.config.ccache {
-        need_cmd(s.as_ref());
+        cmd_finder.must_have(s);
     }
 }
diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs
index 5f0724c6577..c221d707683 100644
--- a/src/bootstrap/step.rs
+++ b/src/bootstrap/step.rs
@@ -104,10 +104,10 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.build("llvm", "src/llvm")
          .host(true)
          .dep(move |s| {
-             if s.target == build.config.build {
+             if s.target == build.build {
                  Step::noop()
              } else {
-                 s.target(&build.config.build)
+                 s.target(&build.build)
              }
          })
          .run(move |s| native::llvm(build, s.target));
@@ -124,7 +124,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
                  Step::noop()
              } else {
                  s.name("librustc")
-                  .host(&build.config.build)
+                  .host(&build.build)
                   .stage(s.stage - 1)
              }
          })
@@ -148,7 +148,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
                 }
             }
         }
-        return ret
+        ret
     };
 
     // ========================================================================
@@ -215,29 +215,29 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
         let mut rule = rules.build(&krate, "path/to/nowhere");
         rule.dep(move |s| {
                 if build.force_use_stage1(&s.compiler(), s.target) {
-                    s.host(&build.config.build).stage(1)
-                } else if s.host == build.config.build {
+                    s.host(&build.build).stage(1)
+                } else if s.host == build.build {
                     s.name(dep)
                 } else {
-                    s.host(&build.config.build)
+                    s.host(&build.build)
                 }
             })
             .run(move |s| {
                 if build.force_use_stage1(&s.compiler(), s.target) {
                     link(build,
-                         &s.stage(1).host(&build.config.build).compiler(),
+                         &s.stage(1).host(&build.build).compiler(),
                          &s.compiler(),
                          s.target)
-                } else if s.host == build.config.build {
+                } else if s.host == build.build {
                     link(build, &s.compiler(), &s.compiler(), s.target)
                 } else {
                     link(build,
-                         &s.host(&build.config.build).compiler(),
+                         &s.host(&build.build).compiler(),
                          &s.compiler(),
                          s.target)
                 }
             });
-            return rule
+            rule
     }
 
     // Similar to the `libstd`, `libtest`, and `librustc` rules above, except
@@ -269,7 +269,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     for (krate, path, _default) in krates("std") {
         rules.build(&krate.build_step, path)
              .dep(|s| s.name("startup-objects"))
-             .dep(move |s| s.name("rustc").host(&build.config.build).target(s.host))
+             .dep(move |s| s.name("rustc").host(&build.build).target(s.host))
              .run(move |s| compile::std(build, s.target, &s.compiler()));
     }
     for (krate, path, _default) in krates("test") {
@@ -280,7 +280,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     for (krate, path, _default) in krates("rustc-main") {
         rules.build(&krate.build_step, path)
              .dep(|s| s.name("libtest-link"))
-             .dep(move |s| s.name("llvm").host(&build.config.build).stage(0))
+             .dep(move |s| s.name("llvm").host(&build.build).stage(0))
              .dep(|s| s.name("may-run-build-script"))
              .run(move |s| compile::rustc(build, s.target, &s.compiler()));
     }
@@ -291,8 +291,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.build("may-run-build-script", "path/to/nowhere")
          .dep(move |s| {
              s.name("libstd-link")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
          });
     rules.build("startup-objects", "src/rtstartup")
          .dep(|s| s.name("create-sysroot").target(s.host))
@@ -332,7 +332,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
               "incremental");
     }
 
-    if build.config.build.contains("msvc") {
+    if build.build.contains("msvc") {
         // nothing to do for debuginfo tests
     } else {
         rules.test("check-debuginfo-lldb", "src/test/debuginfo-lldb")
@@ -352,7 +352,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
                                          "debuginfo-gdb", "debuginfo"));
         let mut rule = rules.test("check-debuginfo", "src/test/debuginfo");
         rule.default(true);
-        if build.config.build.contains("apple") {
+        if build.build.contains("apple") {
             rule.dep(|s| s.name("check-debuginfo-lldb"));
         } else {
             rule.dep(|s| s.name("check-debuginfo-gdb"));
@@ -594,8 +594,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
              // Cargo depends on procedural macros, which requires a full host
              // compiler to be available, so we need to depend on that.
              s.name("librustc-link")
-              .target(&build.config.build)
-              .host(&build.config.build)
+              .target(&build.build)
+              .host(&build.build)
          })
          .run(move |s| compile::tool(build, s.stage, s.target, "cargo"));
     rules.build("tool-rls", "src/tools/rls")
@@ -606,8 +606,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
          .dep(move |s| {
              // rls, like cargo, uses procedural macros
              s.name("librustc-link")
-              .target(&build.config.build)
-              .host(&build.config.build)
+              .target(&build.build)
+              .host(&build.build)
          })
          .run(move |s| compile::tool(build, s.stage, s.target, "rls"));
 
@@ -635,8 +635,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-book", "src/doc/book")
          .dep(move |s| {
              s.name("tool-rustbook")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .default(build.config.docs)
@@ -644,8 +644,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-nomicon", "src/doc/nomicon")
          .dep(move |s| {
              s.name("tool-rustbook")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .default(build.config.docs)
@@ -653,8 +653,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-reference", "src/doc/reference")
          .dep(move |s| {
              s.name("tool-rustbook")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .default(build.config.docs)
@@ -662,8 +662,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-unstable-book", "src/doc/unstable-book")
          .dep(move |s| {
              s.name("tool-rustbook")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .dep(move |s| s.name("doc-unstable-book-gen"))
@@ -675,14 +675,14 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-standalone", "src/doc")
          .dep(move |s| {
              s.name("rustc")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .default(build.config.docs)
          .run(move |s| doc::standalone(build, s.target));
     rules.doc("doc-error-index", "src/tools/error_index_generator")
-         .dep(move |s| s.name("tool-error-index").target(&build.config.build).stage(0))
+         .dep(move |s| s.name("tool-error-index").target(&build.build).stage(0))
          .dep(move |s| s.name("librustc-link"))
          .default(build.config.docs)
          .host(true)
@@ -690,8 +690,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen")
          .dep(move |s| {
              s.name("tool-unstable-book-gen")
-              .host(&build.config.build)
-              .target(&build.config.build)
+              .host(&build.build)
+              .target(&build.build)
               .stage(0)
          })
          .dep(move |s| s.name("libstd-link"))
@@ -725,7 +725,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     // ========================================================================
     // Distribution targets
     rules.dist("dist-rustc", "src/librustc")
-         .dep(move |s| s.name("rustc").host(&build.config.build))
+         .dep(move |s| s.name("rustc").host(&build.build))
          .host(true)
          .only_host_build(true)
          .default(true)
@@ -811,7 +811,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
          .host(true)
          .only_build(true)
          .only_host_build(true)
-         .dep(move |s| s.name("tool-build-manifest").target(&build.config.build).stage(0))
+         .dep(move |s| s.name("tool-build-manifest").target(&build.build).stage(0))
          .run(move |_| dist::hash_and_sign(build));
 
     rules.install("install-docs", "src/doc")
@@ -861,8 +861,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     /// Helper to depend on a stage0 build-only rust-installer tool.
     fn tool_rust_installer<'a>(build: &'a Build, step: &Step<'a>) -> Step<'a> {
         step.name("tool-rust-installer")
-            .host(&build.config.build)
-            .target(&build.config.build)
+            .host(&build.build)
+            .target(&build.build)
             .stage(0)
     }
 }
@@ -1058,8 +1058,8 @@ impl<'a> Rules<'a> {
             build: build,
             sbuild: Step {
                 stage: build.flags.stage.unwrap_or(2),
-                target: &build.config.build,
-                host: &build.config.build,
+                target: &build.build,
+                host: &build.build,
                 name: "",
             },
             rules: BTreeMap::new(),
@@ -1218,16 +1218,9 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
 
         rules.into_iter().flat_map(|(rule, _)| {
             let hosts = if rule.only_host_build || rule.only_build {
-                &self.build.config.host[..1]
-            } else if self.build.flags.host.len() > 0 {
-                &self.build.flags.host
+                self.build.build_slice()
             } else {
-                &self.build.config.host
-            };
-            let targets = if self.build.flags.target.len() > 0 {
-                &self.build.flags.target
-            } else {
-                &self.build.config.target
+                &self.build.hosts
             };
             // Determine the actual targets participating in this rule.
             // NOTE: We should keep the full projection from build triple to
@@ -1236,19 +1229,18 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
             // the original non-shadowed hosts array is used below.
             let arr = if rule.host {
                 // If --target was specified but --host wasn't specified,
-                // don't run any host-only tests. Also, respect any `--host`
-                // overrides as done for `hosts`.
+                // don't run any host-only tests.
                 if self.build.flags.host.len() > 0 {
-                    &self.build.flags.host[..]
+                    &self.build.hosts
                 } else if self.build.flags.target.len() > 0 {
                     &[]
                 } else if rule.only_build {
-                    &self.build.config.host[..1]
+                    self.build.build_slice()
                 } else {
-                    &self.build.config.host[..]
+                    &self.build.hosts
                 }
             } else {
-                targets
+                &self.build.targets
             };
 
             hosts.iter().flat_map(move |host| {
@@ -1326,7 +1318,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
         for idx in 0..nodes.len() {
             self.topo_sort(idx, &idx_to_node, &edges, &mut visited, &mut order);
         }
-        return order
+        order
     }
 
     /// Builds the dependency graph rooted at `step`.
@@ -1365,7 +1357,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
         }
 
         edges.entry(idx).or_insert(HashSet::new()).extend(deps);
-        return idx
+        idx
     }
 
     /// Given a dependency graph with a finished list of `nodes`, fill out more
@@ -1494,8 +1486,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(plan.contains(&step.name("dist-docs")));
@@ -1517,8 +1509,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(plan.contains(&step.name("dist-docs")));
@@ -1545,8 +1537,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(!plan.iter().any(|s| s.host == "B"));
@@ -1575,8 +1567,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(!plan.iter().any(|s| s.host == "B"));
@@ -1612,8 +1604,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(!plan.iter().any(|s| s.target == "A"));
@@ -1639,8 +1631,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         assert!(!plan.iter().any(|s| s.target == "A"));
@@ -1683,8 +1675,8 @@ mod tests {
         let step = super::Step {
             name: "",
             stage: 2,
-            host: &build.config.build,
-            target: &build.config.build,
+            host: &build.build,
+            target: &build.build,
         };
 
         // rustc built for all for of (A, B) x (A, B)
diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs
index 61bd85e76c5..092fb04637b 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/util.rs
@@ -14,7 +14,6 @@
 //! not a lot of interesting happenings here unfortunately.
 
 use std::env;
-use std::ffi::OsString;
 use std::fs;
 use std::io::{self, Write};
 use std::path::{Path, PathBuf};
@@ -32,16 +31,9 @@ pub fn staticlib(name: &str, target: &str) -> String {
     }
 }
 
-/// Copies a file from `src` to `dst`, attempting to use hard links and then
-/// falling back to an actually filesystem copy if necessary.
+/// Copies a file from `src` to `dst`
 pub fn copy(src: &Path, dst: &Path) {
-    // A call to `hard_link` will fail if `dst` exists, so remove it if it
-    // already exists so we can try to help `hard_link` succeed.
     let _ = fs::remove_file(&dst);
-
-    // Attempt to "easy copy" by creating a hard link (symlinks don't work on
-    // windows), but if that fails just fall back to a slow `copy` operation.
-    // let res = fs::hard_link(src, dst);
     let res = fs::copy(src, dst);
     if let Err(e) = res {
         panic!("failed to copy `{}` to `{}`: {}", src.display(),
@@ -149,8 +141,7 @@ pub fn dylib_path_var() -> &'static str {
 /// Parses the `dylib_path_var()` environment variable, returning a list of
 /// paths that are members of this lookup path.
 pub fn dylib_path() -> Vec<PathBuf> {
-    env::split_paths(&env::var_os(dylib_path_var()).unwrap_or(OsString::new()))
-        .collect()
+    env::split_paths(&env::var_os(dylib_path_var()).unwrap_or_default()).collect()
 }
 
 /// `push` all components to `buf`. On windows, append `.exe` to the last component.
@@ -422,4 +413,4 @@ impl CiEnv {
             cmd.env("TERM", "xterm").args(&["--color", "always"]);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs
index 79d1ccf637d..622cc68964b 100644
--- a/src/liballoc/string.rs
+++ b/src/liballoc/string.rs
@@ -2008,10 +2008,10 @@ impl From<Box<str>> for String {
     }
 }
 
-#[stable(feature = "box_from_str", since = "1.18.0")]
-impl Into<Box<str>> for String {
-    fn into(self) -> Box<str> {
-        self.into_boxed_str()
+#[stable(feature = "box_from_str", since = "1.20.0")]
+impl From<String> for Box<str> {
+    fn from(s: String) -> Box<str> {
+        s.into_boxed_str()
     }
 }
 
diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs
index fdf453b39cf..17f1229c206 100644
--- a/src/liballoc/tests/vec.rs
+++ b/src/liballoc/tests/vec.rs
@@ -274,6 +274,11 @@ fn test_dedup_by() {
     vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b));
 
     assert_eq!(vec, ["foo", "bar", "baz", "bar"]);
+
+    let mut vec = vec![("foo", 1), ("foo", 2), ("bar", 3), ("bar", 4), ("bar", 5)];
+    vec.dedup_by(|a, b| a.0 == b.0 && { b.1 += a.1; true });
+
+    assert_eq!(vec, [("foo", 3), ("bar", 12)]);
 }
 
 #[test]
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index 5d1999a4262..1a5975686df 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -823,7 +823,8 @@ impl<T> Vec<T> {
         }
     }
 
-    /// Removes consecutive elements in the vector that resolve to the same key.
+    /// Removes all but the first of consecutive elements in the vector that resolve to the same
+    /// key.
     ///
     /// If the vector is sorted, this removes all duplicates.
     ///
@@ -842,11 +843,13 @@ impl<T> Vec<T> {
         self.dedup_by(|a, b| key(a) == key(b))
     }
 
-    /// Removes consecutive elements in the vector according to a predicate.
+    /// Removes all but the first of consecutive elements in the vector satisfying a given equality
+    /// relation.
     ///
     /// The `same_bucket` function is passed references to two elements from the vector, and
-    /// returns `true` if the elements compare equal, or `false` if they do not. Only the first
-    /// of adjacent equal items is kept.
+    /// returns `true` if the elements compare equal, or `false` if they do not. The elements are
+    /// passed in opposite order from their order in the vector, so if `same_bucket(a, b)` returns
+    /// `true`, `a` is removed.
     ///
     /// If the vector is sorted, this removes all duplicates.
     ///
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index 7882a8ce5c8..f133bd93c91 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -343,7 +343,7 @@ impl Ordering {
 /// ```
 #[derive(PartialEq, Eq, Debug)]
 #[stable(feature = "reverse_cmp_key", since = "1.19.0")]
-pub struct Reverse<T>(pub T);
+pub struct Reverse<T>(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T);
 
 #[stable(feature = "reverse_cmp_key", since = "1.19.0")]
 impl<T: PartialOrd> PartialOrd for Reverse<T> {
diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs
index 102e3c0bd7b..322df6e5b47 100644
--- a/src/libcore/fmt/builders.rs
+++ b/src/libcore/fmt/builders.rs
@@ -49,9 +49,37 @@ impl<'a, 'b: 'a> fmt::Write for PadAdapter<'a, 'b> {
     }
 }
 
-/// A struct to help with `fmt::Debug` implementations.
+/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations.
 ///
-/// Constructed by the `Formatter::debug_struct` method.
+/// This is useful when you wish to output a formatted struct as a part of your
+/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation.
+///
+/// This can be constructed by the
+/// [`Formatter::debug_struct`](struct.Formatter.html#method.debug_struct)
+/// method.
+///
+/// # Example
+///
+/// ```
+/// use std::fmt;
+///
+/// struct Foo {
+///     bar: i32,
+///     baz: String,
+/// }
+///
+/// impl fmt::Debug for Foo {
+///     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+///         fmt.debug_struct("Foo")
+///            .field("bar", &self.bar)
+///            .field("baz", &self.baz)
+///            .finish()
+///     }
+/// }
+///
+/// // prints "Foo { bar: 10, baz: "Hello World" }"
+/// println!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() });
+/// ```
 #[must_use]
 #[allow(missing_debug_implementations)]
 #[stable(feature = "debug_builders", since = "1.2.0")]
@@ -116,9 +144,34 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
     }
 }
 
-/// A struct to help with `fmt::Debug` implementations.
+/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations.
+///
+/// This is useful when you wish to output a formatted tuple as a part of your
+/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation.
+///
+/// This can be constructed by the
+/// [`Formatter::debug_tuple`](struct.Formatter.html#method.debug_tuple)
+/// method.
 ///
-/// Constructed by the `Formatter::debug_tuple` method.
+/// # Example
+///
+/// ```
+/// use std::fmt;
+///
+/// struct Foo(i32, String);
+///
+/// impl fmt::Debug for Foo {
+///     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+///         fmt.debug_tuple("Foo")
+///            .field(&self.0)
+///            .field(&self.1)
+///            .finish()
+///     }
+/// }
+///
+/// // prints "Foo(10, "Hello World")"
+/// println!("{:?}", Foo(10, "Hello World".to_string()));
+/// ```
 #[must_use]
 #[allow(missing_debug_implementations)]
 #[stable(feature = "debug_builders", since = "1.2.0")]
@@ -228,9 +281,31 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> {
     }
 }
 
-/// A struct to help with `fmt::Debug` implementations.
+/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations.
+///
+/// This is useful when you wish to output a formatted set of items as a part
+/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation.
+///
+/// This can be constructed by the
+/// [`Formatter::debug_set`](struct.Formatter.html#method.debug_set)
+/// method.
+///
+/// # Example
 ///
-/// Constructed by the `Formatter::debug_set` method.
+/// ```
+/// use std::fmt;
+///
+/// struct Foo(Vec<i32>);
+///
+/// impl fmt::Debug for Foo {
+///     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+///         fmt.debug_set().entries(self.0.iter()).finish()
+///     }
+/// }
+///
+/// // prints "{10, 11}"
+/// println!("{:?}", Foo(vec![10, 11]));
+/// ```
 #[must_use]
 #[allow(missing_debug_implementations)]
 #[stable(feature = "debug_builders", since = "1.2.0")]
@@ -277,9 +352,31 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
     }
 }
 
-/// A struct to help with `fmt::Debug` implementations.
+/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations.
+///
+/// This is useful when you wish to output a formatted list of items as a part
+/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation.
+///
+/// This can be constructed by the
+/// [`Formatter::debug_list`](struct.Formatter.html#method.debug_list)
+/// method.
+///
+/// # Example
+///
+/// ```
+/// use std::fmt;
+///
+/// struct Foo(Vec<i32>);
 ///
-/// Constructed by the `Formatter::debug_list` method.
+/// impl fmt::Debug for Foo {
+///     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+///         fmt.debug_list().entries(self.0.iter()).finish()
+///     }
+/// }
+///
+/// // prints "[10, 11]"
+/// println!("{:?}", Foo(vec![10, 11]));
+/// ```
 #[must_use]
 #[allow(missing_debug_implementations)]
 #[stable(feature = "debug_builders", since = "1.2.0")]
@@ -326,9 +423,31 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
     }
 }
 
-/// A struct to help with `fmt::Debug` implementations.
+/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations.
+///
+/// This is useful when you wish to output a formatted map as a part of your
+/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation.
+///
+/// This can be constructed by the
+/// [`Formatter::debug_map`](struct.Formatter.html#method.debug_map)
+/// method.
+///
+/// # Example
+///
+/// ```
+/// use std::fmt;
+///
+/// struct Foo(Vec<(String, i32)>);
+///
+/// impl fmt::Debug for Foo {
+///     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+///         fmt.debug_map().entries(self.0.iter().map(|&(ref k, ref v)| (k, v))).finish()
+///     }
+/// }
 ///
-/// Constructed by the `Formatter::debug_map` method.
+/// // prints "{"A": 10, "B": 11}"
+/// println!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)]));
+/// ```
 #[must_use]
 #[allow(missing_debug_implementations)]
 #[stable(feature = "debug_builders", since = "1.2.0")]
diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs
index 1a91417ca0e..5f0b11a616e 100644
--- a/src/libstd/ffi/c_str.rs
+++ b/src/libstd/ffi/c_str.rs
@@ -585,11 +585,11 @@ impl From<Box<CStr>> for CString {
     }
 }
 
-#[stable(feature = "box_from_c_string", since = "1.18.0")]
-impl Into<Box<CStr>> for CString {
+#[stable(feature = "box_from_c_string", since = "1.20.0")]
+impl From<CString> for Box<CStr> {
     #[inline]
-    fn into(self) -> Box<CStr> {
-        self.into_boxed_c_str()
+    fn from(s: CString) -> Box<CStr> {
+        s.into_boxed_c_str()
     }
 }
 
diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs
index 42d641706a8..02a13ed7a5a 100644
--- a/src/libstd/ffi/os_str.rs
+++ b/src/libstd/ffi/os_str.rs
@@ -540,10 +540,10 @@ impl From<Box<OsStr>> for OsString {
     }
 }
 
-#[stable(feature = "box_from_os_string", since = "1.18.0")]
-impl Into<Box<OsStr>> for OsString {
-    fn into(self) -> Box<OsStr> {
-        self.into_boxed_os_str()
+#[stable(feature = "box_from_os_string", since = "1.20.0")]
+impl From<OsString> for Box<OsStr> {
+    fn from(s: OsString) -> Box<OsStr> {
+        s.into_boxed_os_str()
     }
 }
 
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index 472ce6bc4fe..e7c7be981d2 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -1348,10 +1348,10 @@ impl From<Box<Path>> for PathBuf {
     }
 }
 
-#[stable(feature = "box_from_path_buf", since = "1.18.0")]
-impl Into<Box<Path>> for PathBuf {
-    fn into(self) -> Box<Path> {
-        self.into_boxed_path()
+#[stable(feature = "box_from_path_buf", since = "1.20.0")]
+impl From<PathBuf> for Box<Path> {
+    fn from(p: PathBuf) -> Box<Path> {
+        p.into_boxed_path()
     }
 }
 
diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs
index 48d9cdcb2c9..c5a19e8debe 100644
--- a/src/libstd/sys/redox/fs.rs
+++ b/src/libstd/sys/redox/fs.rs
@@ -420,12 +420,19 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
 }
 
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
-    canonicalize(p)
-}
-
-pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
-    ::sys_common::util::dumb_print(format_args!("Symlink\n"));
-    unimplemented!();
+    let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_SYMLINK | syscall::O_RDONLY))?;
+    let mut buf: [u8; 4096] = [0; 4096];
+    let count = cvt(syscall::read(fd, &mut buf))?;
+    cvt(syscall::close(fd))?;
+    Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) }))
+}
+
+pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
+    let fd = cvt(syscall::open(dst.to_str().unwrap(),
+                               syscall::O_SYMLINK | syscall::O_CREAT | syscall::O_WRONLY | 0o777))?;
+    cvt(syscall::write(fd, src.to_str().unwrap().as_bytes()))?;
+    cvt(syscall::close(fd))?;
+    Ok(())
 }
 
 pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs
index 9f0d3e6f779..bd603cfe6ef 100644
--- a/src/libstd/sys/redox/syscall/flag.rs
+++ b/src/libstd/sys/redox/syscall/flag.rs
@@ -33,6 +33,7 @@ pub const MAP_WRITE_COMBINE: usize = 2;
 pub const MODE_TYPE: u16 = 0xF000;
 pub const MODE_DIR: u16 = 0x4000;
 pub const MODE_FILE: u16 = 0x8000;
+pub const MODE_SYMLINK: u16 = 0xA000;
 
 pub const MODE_PERM: u16 = 0x0FFF;
 pub const MODE_SETUID: u16 = 0o4000;
@@ -53,6 +54,7 @@ pub const O_TRUNC: usize =      0x0400_0000;
 pub const O_EXCL: usize =       0x0800_0000;
 pub const O_DIRECTORY: usize =  0x1000_0000;
 pub const O_STAT: usize =       0x2000_0000;
+pub const O_SYMLINK: usize =    0x4000_0000;
 pub const O_ACCMODE: usize =    O_RDONLY | O_WRONLY | O_RDWR;
 
 pub const SEEK_SET: usize = 0;