about summary refs log tree commit diff
path: root/src/bootstrap/bootstrap.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/bootstrap/bootstrap.py')
-rw-r--r--src/bootstrap/bootstrap.py300
1 files changed, 300 insertions, 0 deletions
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)