about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/bootstrap/Cargo.lock109
-rw-r--r--src/bootstrap/Cargo.toml29
-rw-r--r--src/bootstrap/README.md110
-rw-r--r--src/bootstrap/bootstrap.py300
-rw-r--r--src/bootstrap/build/cc.rs98
-rw-r--r--src/bootstrap/build/channel.rs82
-rw-r--r--src/bootstrap/build/compile.rs248
-rw-r--r--src/bootstrap/build/config.rs255
-rw-r--r--src/bootstrap/build/flags.rs99
-rw-r--r--src/bootstrap/build/job.rs100
-rw-r--r--src/bootstrap/build/mod.rs452
-rw-r--r--src/bootstrap/build/native.rs157
-rw-r--r--src/bootstrap/build/sanity.rs122
-rw-r--r--src/bootstrap/build/step.rs177
-rw-r--r--src/bootstrap/build/util.rs97
-rw-r--r--src/bootstrap/lib.rs28
-rw-r--r--src/bootstrap/main.rs38
-rw-r--r--src/bootstrap/rustc.rs91
-rw-r--r--src/build_helper/Cargo.toml8
-rw-r--r--src/build_helper/lib.rs68
-rw-r--r--src/etc/tidy.py2
-rw-r--r--src/nightlies.txt2
-rw-r--r--src/rustc/Cargo.lock356
-rw-r--r--src/rustc/Cargo.toml36
-rw-r--r--src/rustc/libc_shim/Cargo.toml20
-rw-r--r--src/rustc/libc_shim/build.rs15
-rw-r--r--src/rustc/rustbook.rs14
-rw-r--r--src/rustc/rustc.rs15
-rw-r--r--src/rustc/rustdoc.rs15
-rw-r--r--src/rustc/std_shim/Cargo.lock124
-rw-r--r--src/rustc/std_shim/Cargo.toml46
-rw-r--r--src/rustc/std_shim/lib.rs11
33 files changed, 3324 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 572111bf961..b6f5f04bff6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,3 +93,4 @@ tmp.*.rs
 version.md
 version.ml
 version.texi
+/target
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
new file mode 100644
index 00000000000..f9593eb1609
--- /dev/null
+++ b/src/bootstrap/Cargo.lock
@@ -0,0 +1,109 @@
+[root]
+name = "bootstrap"
+version = "0.0.0"
+dependencies = [
+ "build_helper 0.1.0",
+ "cmake 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "filetime 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "advapi32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "build_helper"
+version = "0.1.0"
+
+[[package]]
+name = "cmake"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "filetime"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gcc"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-serialize"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "toml"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
new file mode 100644
index 00000000000..8321f93c90f
--- /dev/null
+++ b/src/bootstrap/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "bootstrap"
+version = "0.0.0"
+
+[lib]
+name = "bootstrap"
+path = "lib.rs"
+
+[[bin]]
+name = "bootstrap"
+path = "main.rs"
+
+[[bin]]
+name = "rustc"
+path = "rustc.rs"
+
+[dependencies]
+build_helper = { path = "../build_helper" }
+cmake = "0.1.10"
+filetime = "0.1"
+num_cpus = "0.2"
+toml = "0.1"
+getopts = "0.2"
+rustc-serialize = "0.3"
+winapi = "0.2"
+kernel32-sys = "0.2"
+gcc = "0.3.17"
+libc = "0.2"
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
new file mode 100644
index 00000000000..9e97ec4da07
--- /dev/null
+++ b/src/bootstrap/README.md
@@ -0,0 +1,110 @@
+# Bootstrapping Rust
+
+This is an in-progress README which is targeted at helping to explain how Rust
+is bootstrapped and in general some of the technical details of the build
+system.
+
+> **Note**: This build system is currently under active development and is not
+> intended to be the primarily used one just yet. The makefiles are currently
+> the ones that are still "guaranteed to work" as much as possible at least.
+
+## Using the new build system
+
+When configuring Rust via `./configure`, pass the following to enable building
+via this build system:
+
+```
+./configure --enable-rustbuild
+```
+
+## ...
+
+## Directory Layout
+
+This build system houses all output under the `target` directory, which looks
+like this:
+
+```
+# Root folder of all output. Everything is scoped underneath here
+build/
+
+  # Location where the stage0 compiler downloads are all cached. This directory
+  # only contains the tarballs themselves as they're extracted elsewhere.
+  cache/
+    2015-12-19/
+    2016-01-15/
+    2016-01-21/
+    ...
+
+  # Output directory for building this build system itself. The stage0
+  # cargo/rustc are used to build the build system into this location.
+  bootstrap/
+    debug/
+    release/
+
+  # Each remaining directory is scoped by the "host" triple of compilation at
+  # hand.
+  x86_64-unknown-linux-gnu/
+
+    # The build artifacts for the `compiler-rt` library for the target this
+    # folder is under. The exact layout here will likely depend on the platform,
+    # and this is also built with CMake so the build system is also likely
+    # different.
+    compiler-rt/build/
+
+    # Output folder for LLVM if it is compiled for this target
+    llvm/
+
+      # build folder (e.g. the platform-specific build system). Like with
+      # compiler-rt this is compiled with CMake
+      build/
+
+      # Installation of LLVM. Note that we run the equivalent of 'make install'
+      # for LLVM to setup these folders.
+      bin/
+      lib/
+      include/
+      share/
+      ...
+
+    # Location where the stage0 Cargo and Rust compiler are unpacked. This
+    # directory is purely an extracted and overlaid tarball of these two (done
+    # by the bootstrapy python script). In theory the build system does not
+    # modify anything under this directory afterwards.
+    stage0/
+
+    # These to build directories are the cargo output directories for builds of
+    # the standard library and compiler, respectively. Internally these may also
+    # have other target directories, which represent artifacts being compiled
+    # from the host to the specified target.
+    #
+    # Essentially, each of these directories is filled in by one `cargo`
+    # invocation. The build system instruments calling Cargo in the right order
+    # with the right variables to ensure these are filled in correctly.
+    stageN-std/
+    stageN-rustc/
+
+    # This is a special case of the above directories, **not** filled in via
+    # Cargo but rather the build system itself. The stage0 compiler already has
+    # a set of target libraries for its own host triple (in its own sysroot)
+    # inside of stage0/. When we run the stage0 compiler to bootstrap more
+    # things, however, we don't want to use any of these libraries (as those are
+    # the ones that we're building). So essentially, when the stage1 compiler is
+    # being compiled (e.g. after libstd has been built), *this* is used as the
+    # sysroot for the stage0 compiler being run.
+    #
+    # Basically this directory is just a temporary artifact use to configure the
+    # stage0 compiler to ensure that the libstd we just built is used to
+    # compile the stage1 compiler.
+    stage0-rustc/lib/
+
+    # These output directories are intended to be standalone working
+    # implementations of the compiler (corresponding to each stage). The build
+    # system will link (using hard links) output from stageN-{std,rustc} into
+    # each of these directories.
+    #
+    # In theory there is no extra build output in these directories.
+    stage1/
+    stage2/
+    stage3/
+```
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
new file mode 100644
index 00000000000..43530464545
--- /dev/null
+++ b/src/bootstrap/bootstrap.py
@@ -0,0 +1,300 @@
+# Copyright 2015-2016 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+import argparse
+import contextlib
+import os
+import shutil
+import subprocess
+import sys
+import tarfile
+
+def get(url, path, verbose=False):
+    print("downloading " + url)
+    # see http://serverfault.com/questions/301128/how-to-download
+    if sys.platform == 'win32':
+        run(["PowerShell.exe", "/nologo", "-Command",
+             "(New-Object System.Net.WebClient).DownloadFile('" + url +
+                "', '" + path + "')"], verbose=verbose)
+    else:
+        run(["curl", "-o", path, url], verbose=verbose)
+
+def unpack(tarball, dst, verbose=False, match=None):
+    print("extracting " + tarball)
+    fname = os.path.basename(tarball).replace(".tar.gz", "")
+    with contextlib.closing(tarfile.open(tarball)) as tar:
+        for p in tar.getnames():
+            if "/" not in p:
+                continue
+            name = p.replace(fname + "/", "", 1)
+            if match is not None and not name.startswith(match):
+                continue
+            name = name[len(match) + 1:]
+
+            fp = os.path.join(dst, name)
+            if verbose:
+                print("  extracting " + p)
+            tar.extract(p, dst)
+            tp = os.path.join(dst, p)
+            if os.path.isdir(tp) and os.path.exists(fp):
+                continue
+            shutil.move(tp, fp)
+    shutil.rmtree(os.path.join(dst, fname))
+
+def run(args, verbose=False):
+    if verbose:
+        print("running: " + ' '.join(args))
+    sys.stdout.flush()
+    # Use Popen here instead of call() as it apparently allows powershell on
+    # Windows to not lock up waiting for input presumably.
+    ret = subprocess.Popen(args)
+    code = ret.wait()
+    if code != 0:
+        if not verbose:
+            print("failed to run: " + ' '.join(args))
+        raise RuntimeError("failed to run command")
+
+class RustBuild:
+    def download_rust_nightly(self):
+        cache_dst = os.path.join(self.build_dir, "cache")
+        rustc_cache = os.path.join(cache_dst, self.snap_rustc_date())
+        cargo_cache = os.path.join(cache_dst, self.snap_cargo_date())
+        if not os.path.exists(rustc_cache):
+            os.makedirs(rustc_cache)
+        if not os.path.exists(cargo_cache):
+            os.makedirs(cargo_cache)
+
+        if self.rustc().startswith(self.bin_root()) and \
+           (not os.path.exists(self.rustc()) or self.rustc_out_of_date()):
+            filename = "rust-std-nightly-" + self.build + ".tar.gz"
+            url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
+            tarball = os.path.join(rustc_cache, filename)
+            if not os.path.exists(tarball):
+                get(url + "/" + filename, tarball, verbose=self.verbose)
+            unpack(tarball, self.bin_root(),
+                   match="rust-std-" + self.build,
+                   verbose=self.verbose)
+
+            filename = "rustc-nightly-" + self.build + ".tar.gz"
+            url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
+            tarball = os.path.join(rustc_cache, filename)
+            if not os.path.exists(tarball):
+                get(url + "/" + filename, tarball, verbose=self.verbose)
+            unpack(tarball, self.bin_root(), match="rustc", verbose=self.verbose)
+            with open(self.rustc_stamp(), 'w') as f:
+                f.write(self.snap_rustc_date())
+
+        if self.cargo().startswith(self.bin_root()) and \
+           (not os.path.exists(self.cargo()) or self.cargo_out_of_date()):
+            filename = "cargo-nightly-" + self.build + ".tar.gz"
+            url = "https://static.rust-lang.org/cargo-dist/" + self.snap_cargo_date()
+            tarball = os.path.join(cargo_cache, filename)
+            if not os.path.exists(tarball):
+                get(url + "/" + filename, tarball, verbose=self.verbose)
+            unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose)
+            with open(self.cargo_stamp(), 'w') as f:
+                f.write(self.snap_cargo_date())
+
+    def snap_cargo_date(self):
+        return self._cargo_date
+
+    def snap_rustc_date(self):
+        return self._rustc_date
+
+    def rustc_stamp(self):
+        return os.path.join(self.bin_root(), '.rustc-stamp')
+
+    def cargo_stamp(self):
+        return os.path.join(self.bin_root(), '.cargo-stamp')
+
+    def rustc_out_of_date(self):
+        if not os.path.exists(self.rustc_stamp()):
+            return True
+        with open(self.rustc_stamp(), 'r') as f:
+            return self.snap_rustc_date() != f.read()
+
+    def cargo_out_of_date(self):
+        if not os.path.exists(self.cargo_stamp()):
+            return True
+        with open(self.cargo_stamp(), 'r') as f:
+            return self.snap_cargo_date() != f.read()
+
+    def bin_root(self):
+        return os.path.join(self.build_dir, self.build, "stage0")
+
+    def get_toml(self, key):
+        for line in self.config_toml.splitlines():
+            if line.startswith(key + ' ='):
+                return self.get_string(line)
+        return None
+
+    def cargo(self):
+        config = self.get_toml('cargo')
+        if config:
+            return config
+        return os.path.join(self.bin_root(), "bin/cargo" + self.exe_suffix())
+
+    def rustc(self):
+        config = self.get_toml('rustc')
+        if config:
+            return config
+        return os.path.join(self.bin_root(), "bin/rustc" + self.exe_suffix())
+
+    def get_string(self, line):
+        start = line.find('"')
+        end = start + 1 + line[start+1:].find('"')
+        return line[start+1:end]
+
+    def exe_suffix(self):
+        if sys.platform == 'win32':
+            return '.exe'
+        else:
+            return ''
+
+    def parse_nightly_dates(self):
+        nightlies = os.path.join(self.rust_root, "src/nightlies.txt")
+        with open(nightlies, 'r') as nightlies:
+            rustc, cargo = nightlies.read().split("\n")[:2]
+            assert rustc.startswith("rustc: ")
+            assert cargo.startswith("cargo: ")
+            self._rustc_date = rustc[len("rustc: "):]
+            self._cargo_date = cargo[len("cargo: "):]
+
+    def build_bootstrap(self):
+        env = os.environ.copy()
+        env["CARGO_TARGET_DIR"] = os.path.join(self.build_dir, "bootstrap")
+        env["RUSTC"] = self.rustc()
+        env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
+        env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
+        env["PATH"] = os.path.join(self.bin_root(), "bin") + \
+                      os.pathsep + env["PATH"]
+        self.run([self.cargo(), "build", "--manifest-path",
+                  os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")],
+                 env)
+
+    def run(self, args, env):
+        proc = subprocess.Popen(args, env = env)
+        ret = proc.wait()
+        if ret != 0:
+            sys.exit(ret)
+
+    def build_triple(self):
+        config = self.get_toml('build')
+        if config:
+            return config
+        try:
+            ostype = subprocess.check_output(['uname', '-s']).strip()
+            cputype = subprocess.check_output(['uname', '-m']).strip()
+        except FileNotFoundError:
+            if sys.platform == 'win32':
+                return 'x86_64-pc-windows-msvc'
+            else:
+                raise
+
+        # Darwin's `uname -s` lies and always returns i386. We have to use
+        # sysctl instead.
+        if ostype == 'Darwin' and cputype == 'i686':
+            sysctl = subprocess.check_output(['sysctl', 'hw.optional.x86_64'])
+            if sysctl.contains(': 1'):
+                cputype = 'x86_64'
+
+        # The goal here is to come up with the same triple as LLVM would,
+        # at least for the subset of platforms we're willing to target.
+        if ostype == 'Linux':
+            ostype = 'unknown-linux-gnu'
+        elif ostype == 'FreeBSD':
+            ostype = 'unknown-freebsd'
+        elif ostype == 'DragonFly':
+            ostype = 'unknown-dragonfly'
+        elif ostype == 'Bitrig':
+            ostype = 'unknown-bitrig'
+        elif ostype == 'OpenBSD':
+            ostype = 'unknown-openbsd'
+        elif ostype == 'NetBSD':
+            ostype = 'unknown-netbsd'
+        elif ostype == 'Darwin':
+            ostype = 'apple-darwin'
+        elif ostype.startswith('MINGW'):
+            # msys' `uname` does not print gcc configuration, but prints msys
+            # configuration. so we cannot believe `uname -m`:
+            # msys1 is always i686 and msys2 is always x86_64.
+            # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
+            # MINGW64 on x86_64.
+            ostype = 'pc-windows-gnu'
+            cputype = 'i686'
+            if os.environ.get('MSYSTEM') == 'MINGW64':
+                cputype = 'x86_64'
+        elif ostype.startswith('MSYS'):
+            ostype = 'pc-windows-gnu'
+        elif ostype.startswith('CYGWIN_NT'):
+            cputype = 'i686'
+            if ostype.endswith('WOW64'):
+                cputype = 'x86_64'
+            ostype = 'pc-windows-gnu'
+        else:
+            raise ValueError("unknown OS type: " + ostype)
+
+        if cputype in {'i386', 'i486', 'i686', 'i786', 'x86'}:
+            cputype = 'i686'
+        elif cputype in {'xscale', 'arm'}:
+            cputype = 'arm'
+        elif cputype == 'armv7l':
+            cputype = 'arm'
+            ostype += 'eabihf'
+        elif cputype == 'aarch64':
+            cputype = 'aarch64'
+        elif cputype in {'powerpc', 'ppc', 'ppc64'}:
+            cputype = 'powerpc'
+        elif cputype in {'amd64', 'x86_64', 'x86-64', 'x64'}:
+            cputype = 'x86_64'
+        else:
+            raise ValueError("unknown cpu type: " + cputype)
+
+        return cputype + '-' + ostype
+
+parser = argparse.ArgumentParser(description='Build rust')
+parser.add_argument('--config')
+parser.add_argument('-v', '--verbose', action='store_true')
+
+args = [a for a in sys.argv if a != '-h']
+args, _ = parser.parse_known_args(args)
+
+# Configure initial bootstrap
+rb = RustBuild()
+rb.config_toml = ''
+rb.config_mk = ''
+rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
+rb.build_dir = os.path.join(os.getcwd(), "build")
+rb.verbose = args.verbose
+
+try:
+    with open(args.config or 'config.toml') as config:
+        rb.config_toml = config.read()
+except:
+    pass
+
+# Fetch/build the bootstrap
+rb.build = rb.build_triple()
+rb.parse_nightly_dates()
+rb.download_rust_nightly()
+sys.stdout.flush()
+rb.build_bootstrap()
+sys.stdout.flush()
+
+# Run the bootstrap
+args = [os.path.join(rb.build_dir, "bootstrap/debug/bootstrap")]
+args.extend(sys.argv[1:])
+args.append('--src')
+args.append(rb.rust_root)
+args.append('--build')
+args.append(rb.build)
+env = os.environ.copy()
+env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
+rb.run(args, env)
diff --git a/src/bootstrap/build/cc.rs b/src/bootstrap/build/cc.rs
new file mode 100644
index 00000000000..9f962e9d9e6
--- /dev/null
+++ b/src/bootstrap/build/cc.rs
@@ -0,0 +1,98 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::process::Command;
+
+use build_helper::{cc2ar, output};
+use gcc;
+
+use build::Build;
+use build::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() {
+        let mut cfg = gcc::Config::new();
+        cfg.cargo_metadata(false).opt_level(0).debug(false)
+           .target(target).host(&build.config.build);
+
+        let config = build.config.target_config.get(target);
+        if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
+            cfg.compiler(cc);
+        } else {
+            set_compiler(&mut cfg, "gcc", target, config);
+        }
+
+        let compiler = cfg.get_compiler();
+        let ar = cc2ar(compiler.path(), target);
+        build.verbose(&format!("CC_{} = {:?}", target, compiler.path()));
+        build.verbose(&format!("AR_{} = {:?}", target, ar));
+        build.cc.insert(target.to_string(), (compiler, ar));
+    }
+
+    // For all host triples we need to find a C++ compiler as well
+    for host in build.config.host.iter() {
+        let mut cfg = gcc::Config::new();
+        cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true)
+           .target(host).host(&build.config.build);
+        let config = build.config.target_config.get(host);
+        if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
+            cfg.compiler(cxx);
+        } else {
+            set_compiler(&mut cfg, "g++", host, config);
+        }
+        let compiler = cfg.get_compiler();
+        build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
+        build.cxx.insert(host.to_string(), compiler);
+    }
+}
+
+fn set_compiler(cfg: &mut gcc::Config,
+                gnu_compiler: &str,
+                target: &str,
+                config: Option<&Target>) {
+    match target {
+        // When compiling for android we may have the NDK configured in the
+        // config.toml in which case we look there. Otherwise the default
+        // compiler already takes into account the triple in question.
+        t if t.contains("android") => {
+            if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
+                let compiler = format!("{}-{}", target, gnu_compiler);
+                cfg.compiler(ndk.join("bin").join(compiler));
+            }
+        }
+
+        // The default gcc version from OpenBSD may be too old, try using egcc,
+        // which is a gcc version from ports, if this is the case.
+        t if t.contains("openbsd") => {
+            let c = cfg.get_compiler();
+            if !c.path().ends_with(gnu_compiler) {
+                return
+            }
+
+            let output = output(c.to_command().arg("--version"));
+            let i = match output.find(" 4.") {
+                Some(i) => i,
+                None => return,
+            };
+            match output[i + 3..].chars().next().unwrap() {
+                '0' ... '6' => {}
+                _ => return,
+            }
+            let alternative = format!("e{}", gnu_compiler);
+            if Command::new(&alternative).output().is_ok() {
+                cfg.compiler(alternative);
+            }
+        }
+
+        _ => {}
+    }
+}
diff --git a/src/bootstrap/build/channel.rs b/src/bootstrap/build/channel.rs
new file mode 100644
index 00000000000..628b1d76432
--- /dev/null
+++ b/src/bootstrap/build/channel.rs
@@ -0,0 +1,82 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fs::{self, File};
+use std::io::prelude::*;
+use std::path::Path;
+use std::process::Command;
+
+use build_helper::output;
+
+use build::Build;
+use build::util::mtime;
+
+pub fn collect(build: &mut Build) {
+    let mut main_mk = String::new();
+    t!(t!(File::open(build.src.join("mk/main.mk"))).read_to_string(&mut main_mk));
+    let mut release_num = "";
+    let mut prerelease_version = "";
+    for line in main_mk.lines() {
+        if line.starts_with("CFG_RELEASE_NUM") {
+            release_num = line.split('=').skip(1).next().unwrap().trim();
+        }
+        if line.starts_with("CFG_PRERELEASE_VERSION") {
+            prerelease_version = line.split('=').skip(1).next().unwrap().trim();
+        }
+    }
+
+    // FIXME: this is duplicating makefile logic
+    match &build.config.channel[..] {
+        "stable" => {
+            build.release = release_num.to_string();
+            build.unstable_features = false;
+        }
+        "beta" => {
+            build.release = format!("{}-beta{}", release_num,
+                                   prerelease_version);
+            build.unstable_features = false;
+        }
+        "nightly" => {
+            build.release = format!("{}-nightly", release_num);
+            build.unstable_features = true;
+        }
+        _ => {
+            build.release = format!("{}-dev", release_num);
+            build.unstable_features = true;
+        }
+    }
+    build.version = build.release.clone();
+
+    if fs::metadata(build.src.join(".git")).is_ok() {
+        let ver_date = output(Command::new("git").current_dir(&build.src)
+                                      .arg("log").arg("-1")
+                                      .arg("--date=short")
+                                      .arg("--pretty=format:%cd"));
+        let ver_hash = output(Command::new("git").current_dir(&build.src)
+                                      .arg("rev-parse").arg("HEAD"));
+        let short_ver_hash = output(Command::new("git")
+                                            .current_dir(&build.src)
+                                            .arg("rev-parse")
+                                            .arg("--short=9")
+                                            .arg("HEAD"));
+        let ver_date = ver_date.trim().to_string();
+        let ver_hash = ver_hash.trim().to_string();
+        let short_ver_hash = short_ver_hash.trim().to_string();
+        build.version.push_str(&format!(" ({} {})", short_ver_hash,
+                                       ver_date));
+        build.ver_date = Some(ver_date.to_string());
+        build.ver_hash = Some(ver_hash);
+        build.short_ver_hash = Some(short_ver_hash);
+    }
+
+    build.bootstrap_key = mtime(Path::new("config.toml")).seconds()
+                                                        .to_string();
+}
+
diff --git a/src/bootstrap/build/compile.rs b/src/bootstrap/build/compile.rs
new file mode 100644
index 00000000000..05b444633b4
--- /dev/null
+++ b/src/bootstrap/build/compile.rs
@@ -0,0 +1,248 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use build_helper::output;
+
+use build::util::{exe, staticlib, libdir, mtime, is_dylib};
+use build::{Build, Compiler};
+
+/// Build the standard library.
+///
+/// This will build the standard library for a particular stage of the build
+/// using the `compiler` targeting the `target` architecture. The artifacts
+/// created will also be linked into the sysroot directory.
+pub fn std<'a>(build: &'a Build, stage: u32, target: &str,
+               compiler: &Compiler<'a>) {
+    let host = compiler.host;
+    println!("Building stage{} std artifacts ({} -> {})", stage,
+             host, target);
+
+    // Move compiler-rt into place as it'll be required by the compiler when
+    // building the standard library to link the dylib of libstd
+    let libdir = build.sysroot_libdir(stage, &host, target);
+    let _ = fs::remove_dir_all(&libdir);
+    t!(fs::create_dir_all(&libdir));
+    t!(fs::hard_link(&build.compiler_rt_built.borrow()[target],
+                     libdir.join(staticlib("compiler-rt", target))));
+
+    build_startup_objects(build, target, &libdir);
+
+    let out_dir = build.cargo_out(stage, &host, true, target);
+    build.clear_if_dirty(&out_dir, &build.compiler_path(compiler));
+    let mut cargo = build.cargo(stage, compiler, true, target, "build");
+    cargo.arg("--features").arg(build.std_features())
+         .arg("--manifest-path")
+         .arg(build.src.join("src/rustc/std_shim/Cargo.toml"));
+
+    if let Some(target) = build.config.target_config.get(target) {
+        if let Some(ref jemalloc) = target.jemalloc {
+            cargo.env("JEMALLOC_OVERRIDE", jemalloc);
+        }
+    }
+    if let Some(ref p) = build.config.musl_root {
+        if target.contains("musl") {
+            cargo.env("MUSL_ROOT", p);
+        }
+    }
+
+    build.run(&mut cargo);
+    add_to_sysroot(&out_dir, &libdir);
+}
+
+/// Build and prepare startup objects like rsbegin.o and rsend.o
+///
+/// These are primarily used on Windows right now for linking executables/dlls.
+/// They don't require any library support as they're just plain old object
+/// files, so we just use the nightly snapshot compiler to always build them (as
+/// no other compilers are guaranteed to be available).
+fn build_startup_objects(build: &Build, target: &str, into: &Path) {
+    if !target.contains("pc-windows-gnu") {
+        return
+    }
+    let compiler = Compiler::new(0, &build.config.build);
+    let compiler = build.compiler_path(&compiler);
+
+    for file in t!(fs::read_dir(build.src.join("src/rtstartup"))) {
+        let file = t!(file);
+        build.run(Command::new(&compiler)
+                          .arg("--emit=obj")
+                          .arg("--out-dir").arg(into)
+                          .arg(file.path()));
+    }
+
+    for obj in ["crt2.o", "dllcrt2.o"].iter() {
+        t!(fs::copy(compiler_file(build.cc(target), obj), into.join(obj)));
+    }
+}
+
+/// Build the compiler.
+///
+/// This will build the compiler for a particular stage of the build using
+/// the `compiler` targeting the `target` architecture. The artifacts
+/// created will also be linked into the sysroot directory.
+pub fn rustc<'a>(build: &'a Build, stage: u32, target: &str,
+                 compiler: &Compiler<'a>) {
+    let host = compiler.host;
+    println!("Building stage{} compiler artifacts ({} -> {})", stage,
+             host, target);
+
+    let out_dir = build.cargo_out(stage, &host, false, target);
+    let rustc = out_dir.join(exe("rustc", target));
+    build.clear_if_dirty(&out_dir, &libstd_shim(build, stage, &host, target));
+
+    let mut cargo = build.cargo(stage, compiler, false, target, "build");
+    cargo.arg("--features").arg(build.rustc_features(stage))
+         .arg("--manifest-path")
+         .arg(build.src.join("src/rustc/Cargo.toml"));
+
+    // In stage0 we may not need to build as many executables
+    if stage == 0 {
+        cargo.arg("--bin").arg("rustc");
+    }
+
+    // Set some configuration variables picked up by build scripts and
+    // the compiler alike
+    cargo.env("CFG_RELEASE", &build.release)
+         .env("CFG_RELEASE_CHANNEL", &build.config.channel)
+         .env("CFG_VERSION", &build.version)
+         .env("CFG_BOOTSTRAP_KEY", &build.bootstrap_key)
+         .env("RUSTC_BOOTSTRAP_KEY", &build.bootstrap_key)
+         .env("CFG_LIBDIR_RELATIVE", "lib");
+
+    if let Some(ref ver_date) = build.ver_date {
+        cargo.env("CFG_VER_DATE", ver_date);
+    }
+    if let Some(ref ver_hash) = build.ver_hash {
+        cargo.env("CFG_VER_HASH", ver_hash);
+    }
+    if !build.unstable_features {
+        cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
+    }
+    if let Some(config) = build.config.target_config.get(target) {
+        if let Some(ref s) = config.llvm_config {
+            cargo.env("LLVM_CONFIG", s);
+        }
+    }
+    if build.config.llvm_static_stdcpp {
+        cargo.env("LLVM_STATIC_STDCPP",
+                  compiler_file(build.cxx(target), "libstdc++.a"));
+    }
+    if let Some(ref s) = build.config.rustc_default_linker {
+        cargo.env("CFG_DEFAULT_LINKER", s);
+    }
+    if let Some(ref s) = build.config.rustc_default_ar {
+        cargo.env("CFG_DEFAULT_AR", s);
+    }
+    build.run(&mut cargo);
+
+    let sysroot_libdir = build.sysroot_libdir(stage, host, target);
+    add_to_sysroot(&out_dir, &sysroot_libdir);
+
+    if host == target {
+        assemble_compiler(build, stage, target, &rustc);
+    }
+}
+
+/// Cargo's output path for the standard library in a given stage, compiled
+/// by a particular compiler for the specified target.
+fn libstd_shim(build: &Build, stage: u32, host: &str, target: &str) -> PathBuf {
+    build.cargo_out(stage, host, true, target).join("libstd_shim.rlib")
+}
+
+fn compiler_file(compiler: &Path, file: &str) -> String {
+    output(Command::new(compiler)
+                   .arg(format!("-print-file-name={}", file))).trim().to_string()
+}
+
+/// Prepare a new compiler from the artifacts in `stage`
+///
+/// This will link the compiler built by `host` during the stage
+/// specified to the sysroot location for `host` to be the official
+/// `stage + 1` compiler for that host. This means that the `rustc` binary
+/// itself will be linked into place along with all supporting dynamic
+/// libraries.
+fn assemble_compiler(build: &Build, stage: u32, host: &str, rustc: &Path) {
+    // Clear out old files
+    let sysroot = build.sysroot(stage + 1, host);
+    let _ = fs::remove_dir_all(&sysroot);
+    t!(fs::create_dir_all(&sysroot));
+
+    // Link in all dylibs to the libdir
+    let sysroot_libdir = sysroot.join(libdir(host));
+    t!(fs::create_dir_all(&sysroot_libdir));
+    let src_libdir = build.sysroot_libdir(stage, host, host);
+    for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
+        let filename = f.file_name().into_string().unwrap();
+        if is_dylib(&filename) {
+            t!(fs::hard_link(&f.path(), sysroot_libdir.join(&filename)));
+        }
+    }
+
+    // Link the compiler binary itself into place
+    let bindir = sysroot.join("bin");
+    t!(fs::create_dir_all(&bindir));
+    let compiler = build.compiler_path(&Compiler::new(stage + 1, host));
+    let _ = fs::remove_file(&compiler);
+    t!(fs::hard_link(rustc, compiler));
+
+    // See if rustdoc exists to link it into place
+    let exe = exe("rustdoc", host);
+    let rustdoc_src = rustc.parent().unwrap().join(&exe);
+    let rustdoc_dst = bindir.join(exe);
+    if fs::metadata(&rustdoc_src).is_ok() {
+        let _ = fs::remove_file(&rustdoc_dst);
+        t!(fs::hard_link(&rustdoc_src, &rustdoc_dst));
+    }
+}
+
+/// Link some files into a rustc sysroot.
+///
+/// For a particular stage this will link all of the contents of `out_dir`
+/// into the sysroot of the `host` compiler, assuming the artifacts are
+/// compiled for the specified `target`.
+fn add_to_sysroot(out_dir: &Path, sysroot_dst: &Path) {
+    // Collect the set of all files in the dependencies directory, keyed
+    // off the name of the library. We assume everything is of the form
+    // `foo-<hash>.{rlib,so,...}`, and there could be multiple different
+    // `<hash>` values for the same name (of old builds).
+    let mut map = HashMap::new();
+    for file in t!(fs::read_dir(out_dir.join("deps"))).map(|f| t!(f)) {
+        let filename = file.file_name().into_string().unwrap();
+
+        // We're only interested in linking rlibs + dylibs, other things like
+        // unit tests don't get linked in
+        if !filename.ends_with(".rlib") &&
+           !filename.ends_with(".lib") &&
+           !is_dylib(&filename) {
+            continue
+        }
+        let file = file.path();
+        let dash = filename.find("-").unwrap();
+        let key = (filename[..dash].to_string(),
+                   file.extension().unwrap().to_owned());
+        map.entry(key).or_insert(Vec::new())
+           .push(file.clone());
+    }
+
+    // For all hash values found, pick the most recent one to move into the
+    // sysroot, that should be the one we just built.
+    for (_, paths) in map {
+        let (_, path) = paths.iter().map(|path| {
+            (mtime(&path).seconds(), path)
+        }).max().unwrap();
+        t!(fs::hard_link(&path,
+                         sysroot_dst.join(path.file_name().unwrap())));
+    }
+}
diff --git a/src/bootstrap/build/config.rs b/src/bootstrap/build/config.rs
new file mode 100644
index 00000000000..862ee15cb08
--- /dev/null
+++ b/src/bootstrap/build/config.rs
@@ -0,0 +1,255 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+use std::env;
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::PathBuf;
+use std::process;
+
+use num_cpus;
+use rustc_serialize::Decodable;
+use toml::{Parser, Decoder, Value};
+
+/// Global configuration for the entire build and/or bootstrap.
+///
+/// This structure is derived from a combination of both `config.toml` and
+/// `config.mk`. As of the time of this writing it's unlikely that `config.toml`
+/// is used all that much, so this is primarily filled out by `config.mk` which
+/// is generated from `./configure`.
+///
+/// Note that this structure is not decoded directly into, but rather it is
+/// filled out from the decoded forms of the structs below.
+#[derive(Default)]
+pub struct Config {
+    pub ccache: bool,
+    pub verbose: bool,
+    pub submodules: bool,
+    pub compiler_docs: bool,
+    pub docs: bool,
+    pub target_config: HashMap<String, Target>,
+
+    // llvm codegen options
+    pub llvm_assertions: bool,
+    pub llvm_optimize: bool,
+    pub llvm_version_check: bool,
+    pub llvm_static_stdcpp: bool,
+
+    // rust codegen options
+    pub rust_optimize: bool,
+    pub rust_codegen_units: u32,
+    pub rust_debug_assertions: bool,
+    pub rust_debuginfo: bool,
+    pub rust_rpath: bool,
+    pub rustc_default_linker: Option<String>,
+    pub rustc_default_ar: Option<String>,
+
+    pub build: String,
+    pub host: Vec<String>,
+    pub target: Vec<String>,
+    pub rustc: Option<String>,
+    pub cargo: Option<String>,
+
+    // libstd features
+    pub debug_jemalloc: bool,
+    pub use_jemalloc: bool,
+
+    // misc
+    pub channel: String,
+    pub musl_root: Option<PathBuf>,
+}
+
+/// Per-target configuration stored in the global configuration structure.
+#[derive(Default)]
+pub struct Target {
+    pub llvm_config: Option<PathBuf>,
+    pub jemalloc: Option<PathBuf>,
+    pub cc: Option<PathBuf>,
+    pub cxx: Option<PathBuf>,
+    pub ndk: Option<PathBuf>,
+}
+
+/// Structure of the `config.toml` file that configuration is read from.
+///
+/// This structure uses `Decodable` to automatically decode a TOML configuration
+/// file into this format, and then this is traversed and written into the above
+/// `Config` structure.
+#[derive(RustcDecodable, Default)]
+struct TomlConfig {
+    build: Option<Build>,
+    llvm: Option<Llvm>,
+    rust: Option<Rust>,
+    target: Option<HashMap<String, TomlTarget>>,
+}
+
+/// TOML representation of various global build decisions.
+#[derive(RustcDecodable, Default, Clone)]
+struct Build {
+    build: Option<String>,
+    host: Vec<String>,
+    target: Vec<String>,
+    cargo: Option<String>,
+    rustc: Option<String>,
+    compiler_docs: Option<bool>,
+    docs: Option<bool>,
+}
+
+/// TOML representation of how the LLVM build is configured.
+#[derive(RustcDecodable, Default)]
+struct Llvm {
+    ccache: Option<bool>,
+    assertions: Option<bool>,
+    optimize: Option<bool>,
+    version_check: Option<bool>,
+    static_libstdcpp: Option<bool>,
+}
+
+/// TOML representation of how the Rust build is configured.
+#[derive(RustcDecodable, Default)]
+struct Rust {
+    optimize: Option<bool>,
+    codegen_units: Option<u32>,
+    debug_assertions: Option<bool>,
+    debuginfo: Option<bool>,
+    debug_jemalloc: Option<bool>,
+    use_jemalloc: Option<bool>,
+    default_linker: Option<String>,
+    default_ar: Option<String>,
+    channel: Option<String>,
+    musl_root: Option<String>,
+    rpath: Option<bool>,
+}
+
+/// TOML representation of how each build target is configured.
+#[derive(RustcDecodable, Default)]
+struct TomlTarget {
+    llvm_config: Option<String>,
+    jemalloc: Option<String>,
+    cc: Option<String>,
+    cxx: Option<String>,
+    android_ndk: Option<String>,
+}
+
+impl Config {
+    pub fn parse(build: &str, file: Option<PathBuf>) -> Config {
+        let mut config = Config::default();
+        config.llvm_optimize = true;
+        config.use_jemalloc = true;
+        config.rust_optimize = true;
+        config.submodules = true;
+        config.docs = true;
+        config.rust_rpath = true;
+        config.rust_codegen_units = 1;
+        config.build = build.to_string();
+        config.channel = "dev".to_string();
+
+        let toml = file.map(|file| {
+            let mut f = t!(File::open(&file));
+            let mut toml = String::new();
+            t!(f.read_to_string(&mut toml));
+            let mut p = Parser::new(&toml);
+            let table = match p.parse() {
+                Some(table) => table,
+                None => {
+                    println!("failed to parse TOML configuration:");
+                    for err in p.errors.iter() {
+                        let (loline, locol) = p.to_linecol(err.lo);
+                        let (hiline, hicol) = p.to_linecol(err.hi);
+                        println!("{}:{}-{}:{}: {}", loline, locol, hiline,
+                                 hicol, err.desc);
+                    }
+                    process::exit(2);
+                }
+            };
+            let mut d = Decoder::new(Value::Table(table));
+            match Decodable::decode(&mut d) {
+                Ok(cfg) => cfg,
+                Err(e) => {
+                    println!("failed to decode TOML: {}", e);
+                    process::exit(2);
+                }
+            }
+        }).unwrap_or_else(|| TomlConfig::default());
+
+        let build = toml.build.clone().unwrap_or(Build::default());
+        set(&mut config.build, build.build.clone());
+        config.host.push(config.build.clone());
+        for host in build.host.iter() {
+            if !config.host.contains(host) {
+                config.host.push(host.clone());
+            }
+        }
+        for target in config.host.iter().chain(&build.target) {
+            if !config.target.contains(target) {
+                config.target.push(target.clone());
+            }
+        }
+        config.rustc = build.rustc;
+        config.cargo = build.cargo;
+        set(&mut config.compiler_docs, build.compiler_docs);
+        set(&mut config.docs, build.docs);
+
+        if let Some(ref llvm) = toml.llvm {
+            set(&mut config.ccache, llvm.ccache);
+            set(&mut config.llvm_assertions, llvm.assertions);
+            set(&mut config.llvm_optimize, llvm.optimize);
+            set(&mut config.llvm_optimize, llvm.optimize);
+            set(&mut config.llvm_version_check, llvm.version_check);
+            set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
+        }
+        if let Some(ref rust) = toml.rust {
+            set(&mut config.rust_debug_assertions, rust.debug_assertions);
+            set(&mut config.rust_debuginfo, rust.debuginfo);
+            set(&mut config.rust_optimize, rust.optimize);
+            set(&mut config.rust_rpath, rust.rpath);
+            set(&mut config.debug_jemalloc, rust.debug_jemalloc);
+            set(&mut config.use_jemalloc, rust.use_jemalloc);
+            set(&mut config.channel, rust.channel.clone());
+            config.rustc_default_linker = rust.default_linker.clone();
+            config.rustc_default_ar = rust.default_ar.clone();
+            config.musl_root = rust.musl_root.clone().map(PathBuf::from);
+
+            match rust.codegen_units {
+                Some(0) => config.rust_codegen_units = num_cpus::get() as u32,
+                Some(n) => config.rust_codegen_units = n,
+                None => {}
+            }
+        }
+
+        if let Some(ref t) = toml.target {
+            for (triple, cfg) in t {
+                let mut target = Target::default();
+
+                if let Some(ref s) = cfg.llvm_config {
+                    target.llvm_config = Some(env::current_dir().unwrap().join(s));
+                }
+                if let Some(ref s) = cfg.jemalloc {
+                    target.jemalloc = Some(env::current_dir().unwrap().join(s));
+                }
+                if let Some(ref s) = cfg.android_ndk {
+                    target.ndk = Some(env::current_dir().unwrap().join(s));
+                }
+                target.cxx = cfg.cxx.clone().map(PathBuf::from);
+                target.cc = cfg.cc.clone().map(PathBuf::from);
+
+                config.target_config.insert(triple.clone(), target);
+            }
+        }
+
+        return config
+    }
+}
+
+fn set<T>(field: &mut T, val: Option<T>) {
+    if let Some(v) = val {
+        *field = v;
+    }
+}
diff --git a/src/bootstrap/build/flags.rs b/src/bootstrap/build/flags.rs
new file mode 100644
index 00000000000..cd538bb0a90
--- /dev/null
+++ b/src/bootstrap/build/flags.rs
@@ -0,0 +1,99 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fs;
+use std::path::PathBuf;
+use std::process;
+use std::slice;
+
+use getopts::Options;
+
+pub struct Flags {
+    pub verbose: bool,
+    pub stage: Option<u32>,
+    pub build: String,
+    pub host: Filter,
+    pub target: Filter,
+    pub step: Vec<String>,
+    pub config: Option<PathBuf>,
+    pub src: Option<PathBuf>,
+    pub jobs: Option<u32>,
+    pub args: Vec<String>,
+}
+
+pub struct Filter {
+    values: Vec<String>,
+}
+
+impl Flags {
+    pub fn parse(args: &[String]) -> Flags {
+        let mut opts = Options::new();
+        opts.optflag("v", "verbose", "use verbose output");
+        opts.optopt("", "config", "TOML configuration file for build", "FILE");
+        opts.optmulti("", "host", "host targets to build", "HOST");
+        opts.reqopt("", "build", "build target of the stage0 compiler", "BUILD");
+        opts.optmulti("", "target", "targets to build", "TARGET");
+        opts.optmulti("s", "step", "build step to execute", "STEP");
+        opts.optopt("", "stage", "stage to build", "N");
+        opts.optopt("", "src", "path to repo root", "DIR");
+        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
+        opts.optflag("h", "help", "print this help message");
+
+        let usage = |n| -> ! {
+            let brief = format!("Usage: rust.py [options]");
+            print!("{}", opts.usage(&brief));
+            process::exit(n);
+        };
+
+        let m = opts.parse(args).unwrap_or_else(|e| {
+            println!("failed to parse options: {}", e);
+            usage(1);
+        });
+        if m.opt_present("h") {
+            usage(0);
+        }
+
+        if m.free.len() > 0 {
+            println!("free arguments are not currently accepted");
+            usage(1);
+        }
+
+        let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| {
+            if fs::metadata("config.toml").is_ok() {
+                Some(PathBuf::from("config.toml"))
+            } else {
+                None
+            }
+        });
+
+        Flags {
+            verbose: m.opt_present("v"),
+            stage: m.opt_str("stage").map(|j| j.parse().unwrap()),
+            build: m.opt_str("build").unwrap(),
+            host: Filter { values: m.opt_strs("host") },
+            target: Filter { values: m.opt_strs("target") },
+            step: m.opt_strs("step"),
+            config: cfg_file,
+            src: m.opt_str("src").map(PathBuf::from),
+            jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
+            args: m.free.clone(),
+        }
+    }
+}
+
+impl Filter {
+    pub fn contains(&self, name: &str) -> bool {
+        self.values.len() == 0 || self.values.iter().any(|s| s == name)
+    }
+
+    pub fn iter(&self) -> slice::Iter<String> {
+        self.values.iter()
+    }
+}
diff --git a/src/bootstrap/build/job.rs b/src/bootstrap/build/job.rs
new file mode 100644
index 00000000000..49e027ffda5
--- /dev/null
+++ b/src/bootstrap/build/job.rs
@@ -0,0 +1,100 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Job management on Windows for bootstrapping
+//!
+//! Most of the time when you're running a build system (e.g. make) you expect
+//! Ctrl-C or abnormal termination to actually terminate the entire tree of
+//! process in play, not just the one at the top. This currently works "by
+//! default" on Unix platforms because Ctrl-C actually sends a signal to the
+//! *process group* rather than the parent process, so everything will get torn
+//! down. On Windows, however, this does not happen and Ctrl-C just kills the
+//! parent process.
+//!
+//! To achieve the same semantics on Windows we use Job Objects to ensure that
+//! all processes die at the same time. Job objects have a mode of operation
+//! where when all handles to the object are closed it causes all child
+//! processes associated with the object to be terminated immediately.
+//! Conveniently whenever a process in the job object spawns a new process the
+//! child will be associated with the job object as well. This means if we add
+//! ourselves to the job object we create then everything will get torn down!
+//!
+//! Unfortunately most of the time the build system is actually called from a
+//! python wrapper (which manages things like building the build system) so this
+//! all doesn't quite cut it so far. To go the last mile we duplicate the job
+//! object handle into our parent process (a python process probably) and then
+//! close our own handle. This means that the only handle to the job object
+//! resides in the parent python process, so when python dies the whole build
+//! system dies (as one would probably expect!).
+//!
+//! Note that this module has a #[cfg(windows)] above it as none of this logic
+//! is required on Unix.
+
+extern crate kernel32;
+extern crate winapi;
+
+use std::env;
+use std::io;
+use std::mem;
+
+use self::winapi::*;
+use self::kernel32::*;
+
+pub unsafe fn setup() {
+    // Create a new job object for us to use
+    let job = CreateJobObjectW(0 as *mut _, 0 as *const _);
+    assert!(job != 0 as *mut _, "{}", io::Error::last_os_error());
+
+    // Indicate that when all handles to the job object are gone that all
+    // process in the object should be killed. Note that this includes our
+    // entire process tree by default because we've added ourselves and and our
+    // children will reside in the job by default.
+    let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
+    info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+    let r = SetInformationJobObject(job,
+                                    JobObjectExtendedLimitInformation,
+                                    &mut info as *mut _ as LPVOID,
+                                    mem::size_of_val(&info) as DWORD);
+    assert!(r != 0, "{}", io::Error::last_os_error());
+
+    // Assign our process to this job object
+    let r = AssignProcessToJobObject(job, GetCurrentProcess());
+    assert!(r != 0, "{}", io::Error::last_os_error());
+
+    // If we've got a parent process (e.g. the python script that called us)
+    // then move ownership of this job object up to them. That way if the python
+    // script is killed (e.g. via ctrl-c) then we'll all be torn down.
+    //
+    // If we don't have a parent (e.g. this was run directly) then we
+    // intentionally leak the job object handle. When our process exits
+    // (normally or abnormally) it will close the handle implicitly, causing all
+    // processes in the job to be cleaned up.
+    let pid = match env::var("BOOTSTRAP_PARENT_ID") {
+        Ok(s) => s,
+        Err(..) => return,
+    };
+
+    let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
+    assert!(parent != 0 as *mut _, "{}", io::Error::last_os_error());
+    let mut parent_handle = 0 as *mut _;
+    let r = DuplicateHandle(GetCurrentProcess(), job,
+                            parent, &mut parent_handle,
+                            0, FALSE, DUPLICATE_SAME_ACCESS);
+
+    // If this failed, well at least we tried! An example of DuplicateHandle
+    // failing in the past has been when the wrong python2 package spawed this
+    // build system (e.g. the `python2` package in MSYS instead of
+    // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
+    // mode" here is that we only clean everything up when the build system
+    // dies, not when the python parent does, so not too bad.
+    if r != 0 {
+        CloseHandle(job);
+    }
+}
diff --git a/src/bootstrap/build/mod.rs b/src/bootstrap/build/mod.rs
new file mode 100644
index 00000000000..6f962aae923
--- /dev/null
+++ b/src/bootstrap/build/mod.rs
@@ -0,0 +1,452 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::env;
+use std::fs::{self, File};
+use std::path::{PathBuf, Path};
+use std::process::Command;
+
+use build_helper::{run_silent, output};
+use gcc;
+use num_cpus;
+
+use build::util::{exe, mtime, libdir, add_lib_path};
+
+macro_rules! t {
+    ($e:expr) => (match $e {
+        Ok(e) => e,
+        Err(e) => panic!("{} failed with {}", stringify!($e), e),
+    })
+}
+
+mod cc;
+mod channel;
+mod compile;
+mod config;
+mod flags;
+mod native;
+mod sanity;
+mod step;
+mod util;
+
+pub use build::config::Config;
+pub use build::flags::Flags;
+
+#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
+pub struct Compiler<'a> {
+    stage: u32,
+    host: &'a str,
+}
+
+pub struct Build {
+    // User-specified configuration via config.toml
+    config: Config,
+
+    // User-specified configuration via CLI flags
+    flags: Flags,
+
+    // Derived properties from the above two configurations
+    cargo: PathBuf,
+    rustc: PathBuf,
+    src: PathBuf,
+    out: PathBuf,
+    release: String,
+    unstable_features: bool,
+    ver_hash: Option<String>,
+    short_ver_hash: Option<String>,
+    ver_date: Option<String>,
+    version: String,
+    bootstrap_key: String,
+
+    // Runtime state filled in later on
+    cc: HashMap<String, (gcc::Tool, PathBuf)>,
+    cxx: HashMap<String, gcc::Tool>,
+    compiler_rt_built: RefCell<HashMap<String, PathBuf>>,
+}
+
+impl Build {
+    pub fn new(flags: Flags, config: Config) -> Build {
+        let cwd = t!(env::current_dir());
+        let src = flags.src.clone().unwrap_or(cwd.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)),
+        };
+
+        Build {
+            flags: flags,
+            config: config,
+            cargo: cargo,
+            rustc: rustc,
+            src: src,
+            out: out,
+
+            release: String::new(),
+            unstable_features: false,
+            ver_hash: None,
+            short_ver_hash: None,
+            ver_date: None,
+            version: String::new(),
+            bootstrap_key: String::new(),
+            cc: HashMap::new(),
+            cxx: HashMap::new(),
+            compiler_rt_built: RefCell::new(HashMap::new()),
+        }
+    }
+
+    pub fn build(&mut self) {
+        use build::step::Source::*;
+
+        // see comments in job.rs for what's going on here
+        #[cfg(windows)]
+        fn setup_job() {
+            mod job;
+            unsafe { job::setup() }
+        }
+        #[cfg(not(windows))] fn setup_job() {}
+        setup_job();
+
+        cc::find(self);
+        sanity::check(self);
+        channel::collect(self);
+        self.update_submodules();
+
+        for target in step::all(self) {
+            match target.src {
+                Llvm { _dummy } => {
+                    native::llvm(self, target.target);
+                }
+                CompilerRt { _dummy } => {
+                    native::compiler_rt(self, target.target);
+                }
+                Libstd { stage, compiler } => {
+                    compile::std(self, stage, target.target, &compiler);
+                }
+                Librustc { stage, compiler } => {
+                    compile::rustc(self, stage, target.target, &compiler);
+                }
+                Rustc { stage } => {
+                    println!("ok, rustc stage{} in {}", stage, target.target);
+                }
+            }
+        }
+    }
+
+    fn update_submodules(&self) {
+        if !self.config.submodules {
+            return
+        }
+        if fs::metadata(self.src.join(".git")).is_err() {
+            return
+        }
+        let out = output(Command::new("git").arg("submodule").arg("status"));
+        if !out.lines().any(|l| l.starts_with("+") || l.starts_with("-")) {
+            return
+        }
+
+        self.run(Command::new("git").arg("submodule").arg("sync"));
+        self.run(Command::new("git").arg("submodule").arg("init"));
+        self.run(Command::new("git").arg("submodule").arg("update"));
+        self.run(Command::new("git").arg("submodule").arg("update")
+                                    .arg("--recursive"));
+        self.run(Command::new("git").arg("submodule").arg("status")
+                                    .arg("--recursive"));
+        self.run(Command::new("git").arg("submodule").arg("foreach")
+                                    .arg("--recursive")
+                                    .arg("git").arg("clean").arg("-fdx"));
+        self.run(Command::new("git").arg("submodule").arg("foreach")
+                                    .arg("--recursive")
+                                    .arg("git").arg("checkout").arg("."));
+    }
+
+    /// Clear out `dir` if our build has been flagged as dirty, and also set
+    /// ourselves as dirty if `file` changes when `f` is executed.
+    fn clear_if_dirty(&self, dir: &Path, input: &Path) {
+        let stamp = dir.join(".stamp");
+        if mtime(&stamp) < mtime(input) {
+            self.verbose(&format!("Dirty - {}", dir.display()));
+            let _ = fs::remove_dir_all(dir);
+        }
+        t!(fs::create_dir_all(dir));
+        t!(File::create(stamp));
+    }
+
+    /// Prepares an invocation of `cargo` to be run.
+    ///
+    /// This will create a `Command` that represents a pending execution of
+    /// Cargo for the specified stage, whether or not the standard library is
+    /// being built, and using the specified compiler targeting `target`.
+    // FIXME: aren't stage/compiler duplicated?
+    fn cargo(&self, stage: u32, compiler: &Compiler, is_std: bool,
+             target: &str, cmd: &str) -> Command {
+        let mut cargo = Command::new(&self.cargo);
+        let host = compiler.host;
+        let out_dir = self.stage_out(stage, host, is_std);
+        cargo.env("CARGO_TARGET_DIR", out_dir)
+             .arg(cmd)
+             .arg("--target").arg(target)
+             .arg("-j").arg(self.jobs().to_string());
+
+        // Customize the compiler we're running. Specify the compiler to cargo
+        // as our shim and then pass it some various options used to configure
+        // how the actual compiler itself is called.
+        cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
+             .env("RUSTC_REAL", self.compiler_path(compiler))
+             .env("RUSTC_STAGE", self.stage_arg(stage, compiler).to_string())
+             .env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
+             .env("RUSTC_CODEGEN_UNITS",
+                  self.config.rust_codegen_units.to_string())
+             .env("RUSTC_DEBUG_ASSERTIONS",
+                  self.config.rust_debug_assertions.to_string())
+             .env("RUSTC_SNAPSHOT", &self.rustc)
+             .env("RUSTC_SYSROOT", self.sysroot(stage, host))
+             .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir())
+             .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "))
+             .env("RUSTC_RPATH", self.config.rust_rpath.to_string())
+             .env("RUSTDOC", self.tool(compiler, "rustdoc"));
+
+        // Specify some variuos options for build scripts used throughout the
+        // build.
+        //
+        // FIXME: the guard against msvc shouldn't need to be here
+        if !target.contains("msvc") {
+            cargo.env(format!("CC_{}", target), self.cc(target))
+                 .env(format!("AR_{}", target), self.ar(target))
+                 .env(format!("CFLAGS_{}", target), self.cflags(target));
+        }
+
+        // Environment variables *required* needed throughout the build
+        //
+        // FIXME: should update code to not require this env vars
+        cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
+
+        if self.config.verbose || self.flags.verbose {
+            cargo.arg("-v");
+        }
+        if self.config.rust_optimize {
+            cargo.arg("--release");
+        }
+        self.add_rustc_lib_path(compiler, &mut cargo);
+        return cargo
+    }
+
+    /// Get a path to the compiler specified.
+    fn compiler_path(&self, compiler: &Compiler) -> PathBuf {
+        if compiler.is_snapshot(self) {
+            self.rustc.clone()
+        } else {
+            self.sysroot(compiler.stage, compiler.host).join("bin")
+                .join(exe("rustc", compiler.host))
+        }
+    }
+
+    /// Get the specified tool next to the specified compiler
+    fn tool(&self, compiler: &Compiler, tool: &str) -> PathBuf {
+        if compiler.is_snapshot(self) {
+            assert!(tool == "rustdoc", "no tools other than rustdoc in stage0");
+            let mut rustdoc = self.rustc.clone();
+            rustdoc.pop();
+            rustdoc.push(exe("rustdoc", &self.config.build));
+            return rustdoc
+        }
+        let (stage, host) = (compiler.stage, compiler.host);
+        self.cargo_out(stage - 1, host, false, host).join(exe(tool, host))
+    }
+
+    /// Get a `Command` which is ready to run `tool` in `stage` built for
+    /// `host`.
+    #[allow(dead_code)] // this will be used soon
+    fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command {
+        let mut cmd = Command::new(self.tool(&compiler, tool));
+        let host = compiler.host;
+        let stage = compiler.stage;
+        let paths = vec![
+            self.cargo_out(stage - 1, host, true, host).join("deps"),
+            self.cargo_out(stage - 1, host, false, host).join("deps"),
+        ];
+        add_lib_path(paths, &mut cmd);
+        return cmd
+    }
+
+    fn stage_arg(&self, stage: u32, compiler: &Compiler) -> u32 {
+        if stage == 0 && compiler.host != self.config.build {1} else {stage}
+    }
+
+    /// Get the space-separated set of activated features for the standard
+    /// library.
+    fn std_features(&self) -> String {
+        let mut features = String::new();
+        if self.config.debug_jemalloc {
+            features.push_str(" debug-jemalloc");
+        }
+        if self.config.use_jemalloc {
+            features.push_str(" jemalloc");
+        }
+        return features
+    }
+
+    /// Get the space-separated set of activated features for the compiler.
+    fn rustc_features(&self, stage: u32) -> String {
+        let mut features = String::new();
+        if self.config.use_jemalloc {
+            features.push_str(" jemalloc");
+        }
+        if stage > 0 {
+            features.push_str(" rustdoc");
+            features.push_str(" rustbook");
+        }
+        return features
+    }
+
+    /// Component directory that Cargo will produce output into (e.g.
+    /// release/debug)
+    fn cargo_dir(&self) -> &'static str {
+        if self.config.rust_optimize {"release"} else {"debug"}
+    }
+
+    fn sysroot(&self, stage: u32, host: &str) -> PathBuf {
+        if stage == 0 {
+            self.stage_out(stage, host, false)
+        } else {
+            self.out.join(host).join(format!("stage{}", stage))
+        }
+    }
+
+    fn sysroot_libdir(&self, stage: u32, host: &str, target: &str) -> PathBuf {
+        self.sysroot(stage, host).join("lib").join("rustlib")
+            .join(target).join("lib")
+    }
+
+    /// Returns the root directory for all output generated in a particular
+    /// stage when running with a particular host compiler.
+    ///
+    /// The `is_std` flag indicates whether the root directory is for the
+    /// bootstrap of the standard library or for the compiler.
+    fn stage_out(&self, stage: u32, host: &str, is_std: bool) -> PathBuf {
+        self.out.join(host)
+            .join(format!("stage{}{}", stage, if is_std {"-std"} else {"-rustc"}))
+    }
+
+    /// Returns the root output directory for all Cargo output in a given stage,
+    /// running a particular comipler, wehther or not we're building the
+    /// standard library, and targeting the specified architecture.
+    fn cargo_out(&self, stage: u32, host: &str, is_std: bool,
+                 target: &str) -> PathBuf {
+        self.stage_out(stage, host, is_std).join(target).join(self.cargo_dir())
+    }
+
+    /// Root output directory for LLVM compiled for `target`
+    fn llvm_out(&self, target: &str) -> PathBuf {
+        self.out.join(target).join("llvm")
+    }
+
+    /// Root output directory for compiler-rt compiled for `target`
+    fn compiler_rt_out(&self, target: &str) -> PathBuf {
+        self.out.join(target).join("compiler-rt")
+    }
+
+    fn add_rustc_lib_path(&self, compiler: &Compiler, cmd: &mut Command) {
+        // Windows doesn't need dylib path munging because the dlls for the
+        // compiler live next to the compiler and the system will find them
+        // automatically.
+        if cfg!(windows) { return }
+
+        add_lib_path(vec![self.rustc_libdir(compiler)], cmd);
+    }
+
+    fn rustc_libdir(&self, compiler: &Compiler) -> PathBuf {
+        if compiler.is_snapshot(self) {
+            self.rustc_snapshot_libdir()
+        } else {
+            self.sysroot(compiler.stage, compiler.host)
+                .join(libdir(compiler.host))
+        }
+    }
+
+    fn rustc_snapshot_libdir(&self) -> PathBuf {
+        self.rustc.parent().unwrap().parent().unwrap()
+            .join(libdir(&self.config.build))
+    }
+
+    fn run(&self, cmd: &mut Command) {
+        self.verbose(&format!("running: {:?}", cmd));
+        run_silent(cmd)
+    }
+
+    fn verbose(&self, msg: &str) {
+        if self.flags.verbose || self.config.verbose {
+            println!("{}", msg);
+        }
+    }
+
+    fn jobs(&self) -> u32 {
+        self.flags.jobs.unwrap_or(num_cpus::get() as u32)
+    }
+
+    fn cc(&self, target: &str) -> &Path {
+        self.cc[target].0.path()
+    }
+
+    fn cflags(&self, target: &str) -> String {
+        self.cc[target].0.args().iter()
+            .map(|s| s.to_string_lossy())
+            .collect::<Vec<_>>()
+            .join(" ")
+    }
+
+    fn ar(&self, target: &str) -> &Path {
+        &self.cc[target].1
+    }
+
+    fn cxx(&self, target: &str) -> &Path {
+        self.cxx[target].path()
+    }
+
+    fn rustc_flags(&self, target: &str) -> Vec<String> {
+        let mut base = match target {
+            "arm-unknown-linux-gnueabihf" => {
+                vec!["-Ctarget-feature=+v6,+vfp2".to_string()]
+            }
+            "mips-unknown-linux-gnu" => {
+                vec!["-Ctarget-cpu=mips32r2".to_string(),
+                     "-Ctarget-feature=+mips32r2".to_string(),
+                     "-Csoft-float".to_string()]
+            }
+            "mipsel-unknown-linux-gnu" => {
+                vec!["-Ctarget-cpu=mips32".to_string(),
+                     "-Ctarget-feature=+mips32".to_string()]
+            }
+            _ => Vec::new(),
+        };
+        if target != self.config.build && !target.contains("msvc") {
+            base.push(format!("-Clinker={}", self.cc(target).display()));
+        }
+        return base
+    }
+}
+
+impl<'a> Compiler<'a> {
+    fn new(stage: u32, host: &'a str) -> Compiler<'a> {
+        Compiler { stage: stage, host: host }
+    }
+
+    fn is_snapshot(&self, build: &Build) -> bool {
+        self.stage == 0 && self.host == build.config.build
+    }
+}
diff --git a/src/bootstrap/build/native.rs b/src/bootstrap/build/native.rs
new file mode 100644
index 00000000000..6ad5f404123
--- /dev/null
+++ b/src/bootstrap/build/native.rs
@@ -0,0 +1,157 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::path::Path;
+use std::process::Command;
+use std::fs;
+
+use build_helper::output;
+use cmake;
+
+use build::Build;
+use build::util::{exe, staticlib};
+
+pub fn llvm(build: &Build, target: &str) {
+    // If we're using a custom LLVM bail out here, but we can only use a
+    // custom LLVM for the build triple.
+    if let Some(config) = build.config.target_config.get(target) {
+        if let Some(ref s) = config.llvm_config {
+            return check_llvm_version(build, s);
+        }
+    }
+
+    // If the cleaning trigger is newer than our built artifacts (or if the
+    // artifacts are missing) then we keep going, otherwise we bail out.
+    let dst = build.llvm_out(target);
+    let stamp = build.src.join("src/rustllvm/llvm-auto-clean-trigger");
+    let llvm_config = dst.join("bin").join(exe("llvm-config", target));
+    build.clear_if_dirty(&dst, &stamp);
+    if fs::metadata(llvm_config).is_ok() {
+        return
+    }
+
+    let _ = fs::remove_dir_all(&dst.join("build"));
+    t!(fs::create_dir_all(&dst.join("build")));
+    let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
+
+    // http://llvm.org/docs/CMake.html
+    let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
+    cfg.target(target)
+       .host(&build.config.build)
+       .out_dir(&dst)
+       .profile(if build.config.llvm_optimize {"Release"} else {"Debug"})
+       .define("LLVM_ENABLE_ASSERTIONS", assertions)
+       .define("LLVM_TARGETS_TO_BUILD", "X86;ARM;AArch64;Mips;PowerPC")
+       .define("LLVM_INCLUDE_EXAMPLES", "OFF")
+       .define("LLVM_INCLUDE_TESTS", "OFF")
+       .define("LLVM_INCLUDE_DOCS", "OFF")
+       .define("LLVM_ENABLE_ZLIB", "OFF")
+       .define("WITH_POLLY", "OFF")
+       .define("LLVM_ENABLE_TERMINFO", "OFF")
+       .define("LLVM_ENABLE_LIBEDIT", "OFF")
+       .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string());
+
+    if target.starts_with("i686") {
+        cfg.define("LLVM_BUILD_32_BITS", "ON");
+    }
+
+    // http://llvm.org/docs/HowToCrossCompileLLVM.html
+    if target != build.config.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");
+        cfg.define("CMAKE_CROSSCOMPILING", "True")
+           .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
+           .define("LLVM_TABLEGEN", &host)
+           .define("LLVM_DEFAULT_TARGET_TRIPLE", target);
+    }
+
+    // MSVC handles compiler business itself
+    if !target.contains("msvc") {
+        if build.config.ccache {
+           cfg.define("CMAKE_C_COMPILER", "ccache")
+              .define("CMAKE_C_COMPILER_ARG1", build.cc(target))
+              .define("CMAKE_CXX_COMPILER", "ccache")
+              .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
+        } else {
+           cfg.define("CMAKE_C_COMPILER", build.cc(target))
+              .define("CMAKE_CXX_COMPILER", build.cxx(target));
+        }
+        cfg.build_arg("-j").build_arg(build.jobs().to_string());
+    }
+
+    // FIXME: we don't actually need to build all LLVM tools and all LLVM
+    //        libraries here, e.g. we just want a few components and a few
+    //        tools. Figure out how to filter them down and only build the right
+    //        tools and libs on all platforms.
+    cfg.build();
+}
+
+fn check_llvm_version(build: &Build, llvm_config: &Path) {
+    if !build.config.llvm_version_check {
+        return
+    }
+
+    let mut cmd = Command::new(llvm_config);
+    let version = output(cmd.arg("--version"));
+    if version.starts_with("3.5") || version.starts_with("3.6") ||
+       version.starts_with("3.7") {
+        return
+    }
+    panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
+}
+
+pub fn compiler_rt(build: &Build, target: &str) {
+    let dst = build.compiler_rt_out(target);
+    let arch = target.split('-').next().unwrap();
+    let mode = if build.config.rust_optimize {"Release"} else {"Debug"};
+    let (dir, build_target, libname) = if target.contains("linux") {
+        let os = if target.contains("android") {"-android"} else {""};
+        let target = format!("clang_rt.builtins-{}{}", arch, os);
+        ("linux".to_string(), target.clone(), target)
+    } else if target.contains("darwin") {
+        let target = format!("clang_rt.builtins_{}_osx", arch);
+        ("builtins".to_string(), target.clone(), target)
+    } else if target.contains("windows-gnu") {
+        let target = format!("clang_rt.builtins-{}", arch);
+        ("windows".to_string(), target.clone(), target)
+    } else if target.contains("windows-msvc") {
+        (format!("windows/{}", mode),
+         "lib/builtins/builtins".to_string(),
+         format!("clang_rt.builtins-{}", arch.replace("i686", "i386")))
+    } else {
+        panic!("can't get os from target: {}", target)
+    };
+    let output = dst.join("build/lib").join(dir)
+                    .join(staticlib(&libname, target));
+    build.compiler_rt_built.borrow_mut().insert(target.to_string(),
+                                                output.clone());
+    if fs::metadata(&output).is_ok() {
+        return
+    }
+    let _ = fs::remove_dir_all(&dst);
+    t!(fs::create_dir_all(&dst));
+    let build_llvm_config = build.llvm_out(&build.config.build)
+                                 .join("bin")
+                                 .join(exe("llvm-config", &build.config.build));
+    let mut cfg = cmake::Config::new(build.src.join("src/compiler-rt"));
+    cfg.target(target)
+       .host(&build.config.build)
+       .out_dir(&dst)
+       .profile(mode)
+       .define("LLVM_CONFIG_PATH", build_llvm_config)
+       .define("COMPILER_RT_DEFAULT_TARGET_TRIPLE", target)
+       .define("COMPILER_RT_BUILD_SANITIZERS", "OFF")
+       .define("COMPILER_RT_BUILD_EMUTLS", "OFF")
+       .define("CMAKE_C_COMPILER", build.cc(target))
+       .build_target(&build_target);
+    cfg.build();
+}
diff --git a/src/bootstrap/build/sanity.rs b/src/bootstrap/build/sanity.rs
new file mode 100644
index 00000000000..40f4c707609
--- /dev/null
+++ b/src/bootstrap/build/sanity.rs
@@ -0,0 +1,122 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashSet;
+use std::env;
+use std::ffi::{OsStr, OsString};
+use std::fs;
+use std::process::Command;
+
+use build_helper::output;
+
+use build::Build;
+
+pub fn check(build: &mut Build) {
+    let mut checked = HashSet::new();
+    let path = env::var_os("PATH").unwrap_or(OsString::new());
+    let mut need_cmd = |cmd: &OsStr| {
+        if !checked.insert(cmd.to_owned()) {
+            return
+        }
+        for path in env::split_paths(&path).map(|p| p.join(cmd)) {
+            if fs::metadata(&path).is_ok() ||
+               fs::metadata(path.with_extension("exe")).is_ok() {
+                return
+            }
+        }
+        panic!("\n\ncouldn't find required command: {:?}\n\n", cmd);
+    };
+
+    // If we've got a git directory we're gona need git to update
+    // submodules and learn about various other aspects.
+    if fs::metadata(build.src.join(".git")).is_ok() {
+        need_cmd("git".as_ref());
+    }
+
+    // We need cmake, but only if we're actually building LLVM
+    for host in build.config.host.iter() {
+        if let Some(config) = build.config.target_config.get(host) {
+            if config.llvm_config.is_some() {
+                continue
+            }
+        }
+        need_cmd("cmake".as_ref());
+        break
+    }
+
+    need_cmd("python".as_ref());
+
+    // 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() {
+        need_cmd(build.cc(target).as_ref());
+        need_cmd(build.ar(target).as_ref());
+    }
+    for host in build.config.host.iter() {
+        need_cmd(build.cxx(host).as_ref());
+    }
+
+    for target in build.config.target.iter() {
+        // Either can't build or don't want to run jemalloc on these targets
+        if target.contains("rumprun") ||
+           target.contains("bitrig") ||
+           target.contains("openbsd") ||
+           target.contains("msvc") {
+            build.config.use_jemalloc = false;
+        }
+
+        // Can't compile for iOS unless we're on OSX
+        if target.contains("apple-ios") &&
+           !build.config.build.contains("apple-darwin") {
+            panic!("the iOS target is only supported on OSX");
+        }
+
+        // Make sure musl-root is valid if specified
+        if target.contains("musl") {
+            match build.config.musl_root {
+                Some(ref root) => {
+                    if fs::metadata(root.join("lib/libc.a")).is_err() {
+                        panic!("couldn't find libc.a in musl dir: {}",
+                               root.join("lib").display());
+                    }
+                    if fs::metadata(root.join("lib/libunwind.a")).is_err() {
+                        panic!("couldn't find libunwind.a in musl dir: {}",
+                               root.join("lib").display());
+                    }
+                }
+                None => {
+                    panic!("when targeting MUSL the build.musl-root option \
+                            must be specified in config.toml")
+                }
+            }
+        }
+
+        if target.contains("msvc") {
+            // There are three builds of cmake on windows: MSVC, MinGW, and
+            // Cygwin. The Cygwin build does not have generators for Visual
+            // Studio, so detect that here and error.
+            let out = output(Command::new("cmake").arg("--help"));
+            if !out.contains("Visual Studio") {
+                panic!("
+cmake does not support Visual Studio generators.
+
+This is likely due to it being an msys/cygwin build of cmake,
+rather than the required windows version, built using MinGW
+or Visual Studio.
+
+If you are building under msys2 try installing the mingw-w64-x86_64-cmake
+package instead of cmake:
+
+$ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
+");
+            }
+        }
+    }
+}
diff --git a/src/bootstrap/build/step.rs b/src/bootstrap/build/step.rs
new file mode 100644
index 00000000000..2fbf1a6ad1d
--- /dev/null
+++ b/src/bootstrap/build/step.rs
@@ -0,0 +1,177 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashSet;
+
+use build::{Build, Compiler};
+
+#[derive(Hash, Eq, PartialEq, Clone, Debug)]
+pub struct Step<'a> {
+    pub src: Source<'a>,
+    pub target: &'a str,
+}
+
+macro_rules! targets {
+    ($m:ident) => {
+        $m! {
+            (rustc, Rustc { stage: u32 }),
+            (libstd, Libstd { stage: u32, compiler: Compiler<'a> }),
+            (librustc, Librustc { stage: u32, compiler: Compiler<'a> }),
+            (llvm, Llvm { _dummy: () }),
+            (compiler_rt, CompilerRt { _dummy: () }),
+        }
+    }
+}
+
+macro_rules! item { ($a:item) => ($a) }
+
+macro_rules! define_source {
+    ($(($short:ident, $name:ident { $($args:tt)* }),)*) => {
+        item! {
+            #[derive(Hash, Eq, PartialEq, Clone, Debug)]
+            pub enum Source<'a> {
+                $($name { $($args)* }),*
+            }
+        }
+    }
+}
+
+targets!(define_source);
+
+pub fn all(build: &Build) -> Vec<Step> {
+    let mut ret = Vec::new();
+    let mut all = HashSet::new();
+    for target in top_level(build) {
+        fill(build, &target, &mut ret, &mut all);
+    }
+    return ret;
+
+    fn fill<'a>(build: &'a Build,
+                target: &Step<'a>,
+                ret: &mut Vec<Step<'a>>,
+                set: &mut HashSet<Step<'a>>) {
+        if set.insert(target.clone()) {
+            for dep in target.deps(build) {
+                fill(build, &dep, ret, set);
+            }
+            ret.push(target.clone());
+        }
+    }
+}
+
+fn top_level(build: &Build) -> Vec<Step> {
+    let mut targets = Vec::new();
+    let stage = build.flags.stage.unwrap_or(2);
+
+    let host = Step {
+        src: Source::Llvm { _dummy: () },
+        target: build.flags.host.iter().next()
+                     .unwrap_or(&build.config.build),
+    };
+    let target = Step {
+        src: Source::Llvm { _dummy: () },
+        target: build.flags.target.iter().next().map(|x| &x[..])
+                     .unwrap_or(host.target)
+    };
+
+    add_steps(build, stage, &host, &target, &mut targets);
+
+    if targets.len() == 0 {
+        let t = Step {
+            src: Source::Llvm { _dummy: () },
+            target: &build.config.build,
+        };
+        for host in build.config.host.iter() {
+            if !build.flags.host.contains(host) {
+                continue
+            }
+            let host = t.target(host);
+            targets.push(host.librustc(stage, host.compiler(stage)));
+            for target in build.config.target.iter() {
+                if !build.flags.target.contains(target) {
+                    continue
+                }
+                targets.push(host.target(target)
+                                 .libstd(stage, host.compiler(stage)));
+            }
+        }
+    }
+
+    return targets
+
+}
+
+fn add_steps<'a>(build: &'a Build,
+                 stage: u32,
+                 host: &Step<'a>,
+                 target: &Step<'a>,
+                 targets: &mut Vec<Step<'a>>) {
+    for step in build.flags.step.iter() {
+        let compiler = host.compiler(stage);
+        match &step[..] {
+            "libstd" => targets.push(target.libstd(stage, compiler)),
+            "librustc" => targets.push(target.libstd(stage, compiler)),
+            "rustc" => targets.push(host.rustc(stage)),
+            "llvm" => targets.push(target.llvm(())),
+            "compiler-rt" => targets.push(target.compiler_rt(())),
+            _ => panic!("unknown build target: `{}`", step),
+        }
+    }
+}
+
+macro_rules! constructors {
+    ($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => {$(
+        fn $short(&self, $($arg: $t),*) -> Step<'a> {
+            Step {
+                src: Source::$name { $($arg: $arg),* },
+                target: self.target,
+            }
+        }
+    )*}
+}
+
+impl<'a> Step<'a> {
+    fn compiler(&self, stage: u32) -> Compiler<'a> {
+        Compiler::new(stage, self.target)
+    }
+
+    fn target(&self, target: &'a str) -> Step<'a> {
+        Step { target: target, src: self.src.clone() }
+    }
+
+    targets!(constructors);
+
+    pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
+        match self.src {
+            Source::Rustc { stage: 0 } => {
+                if self.target == build.config.build {
+                    Vec::new()
+                } else {
+                    let compiler = Compiler::new(0, &build.config.build);
+                    vec![self.librustc(0, compiler)]
+                }
+            }
+            Source::Rustc { stage } => {
+                vec![self.librustc(stage - 1, self.compiler(stage - 1))]
+            }
+            Source::Librustc { stage, compiler } => {
+                vec![self.libstd(stage, compiler), self.llvm(())]
+            }
+            Source::Libstd { stage: _, compiler } => {
+                vec![self.compiler_rt(()),
+                     self.rustc(compiler.stage).target(compiler.host)]
+            }
+            Source::CompilerRt { _dummy } => {
+                vec![self.llvm(()).target(&build.config.build)]
+            }
+            Source::Llvm { _dummy } => Vec::new(),
+        }
+    }
+}
diff --git a/src/bootstrap/build/util.rs b/src/bootstrap/build/util.rs
new file mode 100644
index 00000000000..6c700671f11
--- /dev/null
+++ b/src/bootstrap/build/util.rs
@@ -0,0 +1,97 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::env;
+use std::path::{Path, PathBuf};
+use std::fs;
+use std::process::Command;
+
+use bootstrap::{dylib_path, dylib_path_var};
+use filetime::FileTime;
+
+pub fn staticlib(name: &str, target: &str) -> String {
+    if target.contains("windows-msvc") {
+        format!("{}.lib", name)
+    } else {
+        format!("lib{}.a", name)
+    }
+}
+
+pub fn mtime(path: &Path) -> FileTime {
+    fs::metadata(path).map(|f| {
+        FileTime::from_last_modification_time(&f)
+    }).unwrap_or(FileTime::zero())
+}
+
+#[allow(dead_code)] // this will be used soon
+pub fn cp_r(src: &Path, dst: &Path) {
+    for f in t!(fs::read_dir(src)) {
+        let f = t!(f);
+        let path = f.path();
+        let name = path.file_name().unwrap();
+        let dst = dst.join(name);
+        if t!(f.file_type()).is_dir() {
+            let _ = fs::remove_dir_all(&dst);
+            t!(fs::create_dir(&dst));
+            cp_r(&path, &dst);
+        } else {
+            let _ = fs::remove_file(&dst);
+            t!(fs::hard_link(&path, dst));
+        }
+    }
+}
+
+/// Given an executable called `name`, return the filename for the
+/// executable for a particular target.
+pub fn exe(name: &str, target: &str) -> String {
+    if target.contains("windows") {
+        format!("{}.exe", name)
+    } else {
+        name.to_string()
+    }
+}
+
+pub fn is_dylib(name: &str) -> bool {
+    name.ends_with(".dylib") || name.ends_with(".so") || name.ends_with(".dll")
+}
+
+pub fn libdir(target: &str) -> &'static str {
+    if target.contains("windows") {"bin"} else {"lib"}
+}
+
+pub fn add_lib_path(path: Vec<PathBuf>, cmd: &mut Command) {
+    let mut list = dylib_path();
+    for path in path {
+        list.insert(0, path);
+    }
+    cmd.env(dylib_path_var(), t!(env::join_paths(list)));
+}
+
+#[allow(dead_code)] // this will be used soon
+pub fn up_to_date(src: &Path, dst: &Path) -> bool {
+    let threshold = mtime(dst);
+    let meta = t!(fs::metadata(src));
+    if meta.is_dir() {
+        dir_up_to_date(src, &threshold)
+    } else {
+        FileTime::from_last_modification_time(&meta) <= threshold
+    }
+}
+
+fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
+    t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
+        let meta = t!(e.metadata());
+        if meta.is_dir() {
+            dir_up_to_date(&e.path(), threshold)
+        } else {
+            FileTime::from_last_modification_time(&meta) < *threshold
+        }
+    })
+}
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
new file mode 100644
index 00000000000..3158a3ab058
--- /dev/null
+++ b/src/bootstrap/lib.rs
@@ -0,0 +1,28 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::env;
+use std::ffi::OsString;
+use std::path::PathBuf;
+
+pub fn dylib_path_var() -> &'static str {
+    if cfg!(target_os = "windows") {
+        "PATH"
+    } else if cfg!(target_os = "macos") {
+        "DYLD_LIBRARY_PATH"
+    } else {
+        "LD_LIBRARY_PATH"
+    }
+}
+
+pub fn dylib_path() -> Vec<PathBuf> {
+    env::split_paths(&env::var_os(dylib_path_var()).unwrap_or(OsString::new()))
+        .collect()
+}
diff --git a/src/bootstrap/main.rs b/src/bootstrap/main.rs
new file mode 100644
index 00000000000..32432132c17
--- /dev/null
+++ b/src/bootstrap/main.rs
@@ -0,0 +1,38 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(warnings)]
+
+extern crate bootstrap;
+extern crate build_helper;
+extern crate cmake;
+extern crate filetime;
+extern crate gcc;
+extern crate getopts;
+extern crate libc;
+extern crate num_cpus;
+extern crate rustc_serialize;
+extern crate toml;
+
+use std::env;
+
+use build::{Flags, Config, Build};
+
+mod build;
+
+fn main() {
+    let args = env::args().skip(1).collect::<Vec<_>>();
+    let flags = Flags::parse(&args);
+    let mut config = Config::parse(&flags.build, flags.config.clone());
+    if std::fs::metadata("config.mk").is_ok() {
+        config.update_with_config_mk();
+    }
+    Build::new(flags, config).build();
+}
diff --git a/src/bootstrap/rustc.rs b/src/bootstrap/rustc.rs
new file mode 100644
index 00000000000..0c30360ba79
--- /dev/null
+++ b/src/bootstrap/rustc.rs
@@ -0,0 +1,91 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate bootstrap;
+
+use std::env;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::process::Command;
+
+fn main() {
+    let args = env::args_os().skip(1).collect::<Vec<_>>();
+    // Detect whether or not we're a build script depending on whether --target
+    // is passed (a bit janky...)
+    let is_build_script = args.iter()
+                              .position(|i| i.to_str() == Some("--target"))
+                              .is_none();
+
+    // 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.
+    let rustc = if is_build_script {
+        env::var_os("RUSTC_SNAPSHOT").unwrap()
+    } else {
+        env::var_os("RUSTC_REAL").unwrap()
+    };
+
+    let mut cmd = Command::new(rustc);
+    cmd.args(&args)
+       .arg("--cfg").arg(format!("stage{}", env::var("RUSTC_STAGE").unwrap()));
+
+    if is_build_script {
+        // Build scripts are always built with the snapshot compiler, so we need
+        // to be sure to set up the right path information for the OS dynamic
+        // linker to find the libraries in question.
+        if let Some(p) = env::var_os("RUSTC_SNAPSHOT_LIBDIR") {
+            let mut path = bootstrap::dylib_path();
+            path.insert(0, PathBuf::from(p));
+            cmd.env(bootstrap::dylib_path_var(), env::join_paths(path).unwrap());
+        }
+    } else {
+        cmd.arg("--sysroot").arg(env::var_os("RUSTC_SYSROOT").unwrap());
+
+        // When we build Rust dylibs they're all intended for intermediate
+        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
+        // linking all deps statically into the dylib.
+        cmd.arg("-Cprefer-dynamic");
+
+        if let Some(s) = env::var_os("MUSL_ROOT") {
+            let mut root = OsString::from("native=");
+            root.push(&s);
+            root.push("/lib");
+            cmd.arg("-L").arg(&root);
+        }
+    }
+
+    // Set various options from config.toml to configure how we're building
+    // code.
+    if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) {
+        cmd.arg("-g");
+    }
+    if env::var("RUSTC_RPATH") == Ok("true".to_string()) {
+        cmd.arg("-Crpath");
+    }
+    let debug_assertions = match env::var("RUSTC_DEBUG_ASSERTIONS") {
+        Ok(s) => if s == "true" {"y"} else {"n"},
+        Err(..) => "n",
+    };
+    cmd.arg("-C").arg(format!("debug-assertions={}", debug_assertions));
+    if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
+        cmd.arg("-C").arg(format!("codegen-units={}", s));
+    }
+    if let Ok(s) = env::var("RUSTC_FLAGS") {
+        cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
+    }
+
+    // Actually run the compiler!
+    std::process::exit(match cmd.status() {
+        Ok(s) => s.code().unwrap_or(1),
+        Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
+    })
+}
diff --git a/src/build_helper/Cargo.toml b/src/build_helper/Cargo.toml
new file mode 100644
index 00000000000..01d704f816b
--- /dev/null
+++ b/src/build_helper/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "build_helper"
+version = "0.1.0"
+authors = ["The Rust Project Developers"]
+
+[lib]
+name = "build_helper"
+path = "lib.rs"
diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs
new file mode 100644
index 00000000000..092a1cabc74
--- /dev/null
+++ b/src/build_helper/lib.rs
@@ -0,0 +1,68 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(warnings)]
+
+use std::process::{Command, Stdio};
+use std::path::{Path, PathBuf};
+
+pub fn run(cmd: &mut Command) {
+    println!("running: {:?}", cmd);
+    run_silent(cmd);
+}
+
+pub fn run_silent(cmd: &mut Command) {
+    let status = match cmd.status() {
+        Ok(status) => status,
+        Err(e) => fail(&format!("failed to execute command: {}", e)),
+    };
+    if !status.success() {
+        fail(&format!("command did not execute successfully: {:?}\n\
+                       expected success, got: {}", cmd, status));
+    }
+}
+
+pub fn gnu_target(target: &str) -> String {
+    match target {
+        "i686-pc-windows-msvc" => "i686-pc-win32".to_string(),
+        "x86_64-pc-windows-msvc" => "x86_64-pc-win32".to_string(),
+        "i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(),
+        "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(),
+        s => s.to_string(),
+    }
+}
+
+pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
+    if target.contains("musl") || target.contains("msvc") {
+        PathBuf::from("ar")
+    } else {
+        let file = cc.file_name().unwrap().to_str().unwrap();
+        cc.parent().unwrap().join(file.replace("gcc", "ar")
+                                      .replace("cc", "ar")
+                                      .replace("clang", "ar"))
+    }
+}
+
+pub fn output(cmd: &mut Command) -> String {
+    let output = match cmd.stderr(Stdio::inherit()).output() {
+        Ok(status) => status,
+        Err(e) => fail(&format!("failed to execute command: {}", e)),
+    };
+    if !output.status.success() {
+        panic!("command did not execute successfully: {:?}\n\
+                expected success, got: {}", cmd, output.status);
+    }
+    String::from_utf8(output.stdout).unwrap()
+}
+
+fn fail(s: &str) -> ! {
+    println!("\n\n{}\n\n", s);
+    std::process::exit(1);
+}
diff --git a/src/etc/tidy.py b/src/etc/tidy.py
index 942793adc31..fd3f4bf0b13 100644
--- a/src/etc/tidy.py
+++ b/src/etc/tidy.py
@@ -147,7 +147,7 @@ try:
                         report_err("snapshot out of date (" + date
                             + "): " + line)
                 else:
-                    if "SNAP" in line:
+                    if "SNAP " in line:
                         report_warn("unmatched SNAP line: " + line)
 
             if cr_flag in line:
diff --git a/src/nightlies.txt b/src/nightlies.txt
new file mode 100644
index 00000000000..86186222d90
--- /dev/null
+++ b/src/nightlies.txt
@@ -0,0 +1,2 @@
+rustc: 2015-12-19
+cargo: 2016-01-21
diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock
new file mode 100644
index 00000000000..db1a87679c2
--- /dev/null
+++ b/src/rustc/Cargo.lock
@@ -0,0 +1,356 @@
+[root]
+name = "rustc-main"
+version = "0.0.0"
+dependencies = [
+ "rustbook 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_driver 0.0.0",
+ "rustdoc 0.0.0",
+]
+
+[[package]]
+name = "advapi32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "arena"
+version = "0.0.0"
+
+[[package]]
+name = "build_helper"
+version = "0.1.0"
+
+[[package]]
+name = "flate"
+version = "0.0.0"
+dependencies = [
+ "build_helper 0.1.0",
+ "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fmt_macros"
+version = "0.0.0"
+
+[[package]]
+name = "gcc"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getopts"
+version = "0.0.0"
+
+[[package]]
+name = "graphviz"
+version = "0.0.0"
+
+[[package]]
+name = "log"
+version = "0.0.0"
+
+[[package]]
+name = "rbml"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "serialize 0.0.0",
+]
+
+[[package]]
+name = "rustbook"
+version = "0.0.0"
+dependencies = [
+ "rustc_back 0.0.0",
+ "rustdoc 0.0.0",
+]
+
+[[package]]
+name = "rustc"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "flate 0.0.0",
+ "fmt_macros 0.0.0",
+ "getopts 0.0.0",
+ "graphviz 0.0.0",
+ "log 0.0.0",
+ "rbml 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_llvm 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_back"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_llvm 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_borrowck"
+version = "0.0.0"
+dependencies = [
+ "graphviz 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_front 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_data_structures"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "serialize 0.0.0",
+]
+
+[[package]]
+name = "rustc_driver"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "flate 0.0.0",
+ "getopts 0.0.0",
+ "graphviz 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_borrowck 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_lint 0.0.0",
+ "rustc_llvm 0.0.0",
+ "rustc_metadata 0.0.0",
+ "rustc_mir 0.0.0",
+ "rustc_plugin 0.0.0",
+ "rustc_privacy 0.0.0",
+ "rustc_resolve 0.0.0",
+ "rustc_trans 0.0.0",
+ "rustc_typeck 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+ "syntax_ext 0.0.0",
+]
+
+[[package]]
+name = "rustc_front"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_lint"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_front 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_llvm"
+version = "0.0.0"
+dependencies = [
+ "build_helper 0.1.0",
+ "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc_metadata"
+version = "0.0.0"
+dependencies = [
+ "flate 0.0.0",
+ "log 0.0.0",
+ "rbml 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_llvm 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_mir"
+version = "0.0.0"
+dependencies = [
+ "graphviz 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_front 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_platform_intrinsics"
+version = "0.0.0"
+dependencies = [
+ "rustc 0.0.0",
+ "rustc_llvm 0.0.0",
+]
+
+[[package]]
+name = "rustc_plugin"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_metadata 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_privacy"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_front 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_resolve"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_front 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_trans"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "flate 0.0.0",
+ "getopts 0.0.0",
+ "graphviz 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_llvm 0.0.0",
+ "rustc_mir 0.0.0",
+ "rustc_platform_intrinsics 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustc_typeck"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "fmt_macros 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_platform_intrinsics 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "rustdoc"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "build_helper 0.1.0",
+ "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getopts 0.0.0",
+ "log 0.0.0",
+ "rustc 0.0.0",
+ "rustc_back 0.0.0",
+ "rustc_driver 0.0.0",
+ "rustc_front 0.0.0",
+ "rustc_lint 0.0.0",
+ "rustc_metadata 0.0.0",
+ "rustc_resolve 0.0.0",
+ "rustc_trans 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+ "test 0.0.0",
+]
+
+[[package]]
+name = "serialize"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+]
+
+[[package]]
+name = "syntax"
+version = "0.0.0"
+dependencies = [
+ "arena 0.0.0",
+ "fmt_macros 0.0.0",
+ "log 0.0.0",
+ "serialize 0.0.0",
+ "term 0.0.0",
+]
+
+[[package]]
+name = "syntax_ext"
+version = "0.0.0"
+dependencies = [
+ "fmt_macros 0.0.0",
+ "syntax 0.0.0",
+]
+
+[[package]]
+name = "term"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+]
+
+[[package]]
+name = "test"
+version = "0.0.0"
+dependencies = [
+ "getopts 0.0.0",
+ "serialize 0.0.0",
+ "term 0.0.0",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml
new file mode 100644
index 00000000000..9fcefd9d3a4
--- /dev/null
+++ b/src/rustc/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc-main"
+version = "0.0.0"
+
+[[bin]]
+name = "rustc"
+path = "rustc.rs"
+
+[[bin]]
+name = "rustdoc"
+path = "rustdoc.rs"
+
+[[bin]]
+name = "rustbook"
+path = "rustbook.rs"
+
+[profile.release]
+opt-level = 2
+
+# These options are controlled from our rustc wrapper script, so turn them off
+# here and have them controlled elsewhere.
+[profile.dev]
+debug = false
+debug-assertions = false
+
+# All optional dependencies so the features passed to this Cargo.toml select
+# what should actually be built.
+[dependencies]
+rustbook = { path = "../rustbook", optional = true }
+rustc_back = { path = "../librustc_back" }
+rustc_driver = { path = "../librustc_driver" }
+rustdoc = { path = "../librustdoc", optional = true }
+
+[features]
+jemalloc = ["rustc_back/jemalloc"]
diff --git a/src/rustc/libc_shim/Cargo.toml b/src/rustc/libc_shim/Cargo.toml
new file mode 100644
index 00000000000..a7860b50e08
--- /dev/null
+++ b/src/rustc/libc_shim/Cargo.toml
@@ -0,0 +1,20 @@
+# This is a shim Cargo.toml over the "real Cargo.toml" found in the libc
+# repository itself. The purpose for this is to add a build script which prints
+# out `--cfg stdbuild` to mirror the makefiles' build system.
+#
+# Note that other than that this isn't actually needed, and we should probably
+# remove this shim in favor of just working with cargo features directly with
+# libc. That should make everything nicer!
+
+[package]
+name = "libc"
+version = "0.0.0"
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+
+[lib]
+name = "libc"
+path = "../../liblibc/src/lib.rs"
+
+[dependencies]
+core = { path = "../../libcore" }
diff --git a/src/rustc/libc_shim/build.rs b/src/rustc/libc_shim/build.rs
new file mode 100644
index 00000000000..bc428d69082
--- /dev/null
+++ b/src/rustc/libc_shim/build.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// See comments in Cargo.toml for why this exists
+
+fn main() {
+    println!("cargo:rustc-cfg=stdbuild");
+}
diff --git a/src/rustc/rustbook.rs b/src/rustc/rustbook.rs
new file mode 100644
index 00000000000..6f78f78bc55
--- /dev/null
+++ b/src/rustc/rustbook.rs
@@ -0,0 +1,14 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate rustbook;
+
+fn main() { rustbook::main() }
+
diff --git a/src/rustc/rustc.rs b/src/rustc/rustc.rs
new file mode 100644
index 00000000000..bfd01146d2c
--- /dev/null
+++ b/src/rustc/rustc.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_private)]
+
+extern crate rustc_driver;
+
+fn main() { rustc_driver::main() }
diff --git a/src/rustc/rustdoc.rs b/src/rustc/rustdoc.rs
new file mode 100644
index 00000000000..6fecd3a27a8
--- /dev/null
+++ b/src/rustc/rustdoc.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustdoc)]
+
+extern crate rustdoc;
+
+fn main() { rustdoc::main() }
diff --git a/src/rustc/std_shim/Cargo.lock b/src/rustc/std_shim/Cargo.lock
new file mode 100644
index 00000000000..d88e9c7e5aa
--- /dev/null
+++ b/src/rustc/std_shim/Cargo.lock
@@ -0,0 +1,124 @@
+[root]
+name = "std_shim"
+version = "0.1.0"
+dependencies = [
+ "std 0.0.0",
+]
+
+[[package]]
+name = "advapi32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "alloc"
+version = "0.0.0"
+dependencies = [
+ "alloc_system 0.0.0",
+ "core 0.0.0",
+ "libc 0.0.0",
+]
+
+[[package]]
+name = "alloc_jemalloc"
+version = "0.0.0"
+dependencies = [
+ "build_helper 0.1.0",
+ "core 0.0.0",
+ "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.0.0",
+]
+
+[[package]]
+name = "alloc_system"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+ "libc 0.0.0",
+]
+
+[[package]]
+name = "build_helper"
+version = "0.1.0"
+
+[[package]]
+name = "collections"
+version = "0.0.0"
+dependencies = [
+ "alloc 0.0.0",
+ "core 0.0.0",
+ "rustc_unicode 0.0.0",
+]
+
+[[package]]
+name = "core"
+version = "0.0.0"
+
+[[package]]
+name = "gcc"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
+[[package]]
+name = "rand"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
+[[package]]
+name = "rustc_bitflags"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
+[[package]]
+name = "rustc_unicode"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
+[[package]]
+name = "std"
+version = "0.0.0"
+dependencies = [
+ "alloc 0.0.0",
+ "alloc_jemalloc 0.0.0",
+ "alloc_system 0.0.0",
+ "build_helper 0.1.0",
+ "collections 0.0.0",
+ "core 0.0.0",
+ "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.0.0",
+ "rand 0.0.0",
+ "rustc_bitflags 0.0.0",
+ "rustc_unicode 0.0.0",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
diff --git a/src/rustc/std_shim/Cargo.toml b/src/rustc/std_shim/Cargo.toml
new file mode 100644
index 00000000000..1ce3937157d
--- /dev/null
+++ b/src/rustc/std_shim/Cargo.toml
@@ -0,0 +1,46 @@
+# This is a shim Cargo.toml which serves as a proxy for building the standard
+# library. The reason for this is a little subtle, as one might reasonably
+# expect that we just `cargo build` the standard library itself.
+#
+# One of the output artifacts for the standard library is a dynamic library, and
+# on platforms like OSX the name of the output artifact is actually encoded into
+# the library itself (similar to a soname on Linux). When the library is linked
+# against, this encoded name is what's literally looked for at runtime when the
+# dynamic loader is probing for libraries.
+#
+# Cargo, however, by default will not mangle the output filename of the
+# top-level target. If we were to run `cargo build` on libstd itself, we would
+# generate a file `libstd.so`. When installing, however, this file is called
+# something like `libstd-abcdef0123.so`. On OSX at least this causes a failure
+# at runtime because the encoded "soname" is `libstd.so`, not what the file is
+# actually called.
+#
+# By using this shim library to build the standard library by proxy we sidestep
+# this problem. The standard library is built with mangled hex already in its
+# name so there's nothing extra we need to do.
+
+[package]
+name = "std_shim"
+version = "0.1.0"
+authors = ["The Rust Project Developers"]
+
+[lib]
+name = "std_shim"
+path = "lib.rs"
+
+[profile.release]
+opt-level = 2
+
+# These options are controlled from our rustc wrapper script, so turn them off
+# here and have them controlled elsewhere.
+[profile.dev]
+debug = false
+debug-assertions = false
+
+[dependencies]
+std = { path = "../../libstd" }
+
+# Reexport features from std
+[features]
+jemalloc = ["std/jemalloc"]
+debug-jemalloc = ["std/debug-jemalloc"]
diff --git a/src/rustc/std_shim/lib.rs b/src/rustc/std_shim/lib.rs
new file mode 100644
index 00000000000..3cf4cfab135
--- /dev/null
+++ b/src/rustc/std_shim/lib.rs
@@ -0,0 +1,11 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// See comments in Cargo.toml for why this exists