2015-11-19 15:20:12 -08:00
|
|
|
# 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.
|
|
|
|
|
2017-03-03 05:27:07 +03:00
|
|
|
from __future__ import print_function
|
2015-11-19 15:20:12 -08:00
|
|
|
import argparse
|
|
|
|
import contextlib
|
2016-07-02 19:49:27 +05:30
|
|
|
import datetime
|
2016-04-13 22:10:42 -04:00
|
|
|
import hashlib
|
2015-11-19 15:20:12 -08:00
|
|
|
import os
|
2017-05-13 14:21:35 +09:00
|
|
|
import re
|
2015-11-19 15:20:12 -08:00
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tarfile
|
2016-04-30 08:09:53 +02:00
|
|
|
import tempfile
|
|
|
|
|
2016-07-02 19:49:27 +05:30
|
|
|
from time import time
|
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
def get(url, path, verbose=False):
|
2016-04-13 22:10:42 -04:00
|
|
|
sha_url = url + ".sha256"
|
2016-05-08 09:54:50 +02:00
|
|
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
|
|
temp_path = temp_file.name
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".sha256", delete=False) as sha_file:
|
|
|
|
sha_path = sha_file.name
|
|
|
|
|
|
|
|
try:
|
2016-11-16 12:31:19 -08:00
|
|
|
download(sha_path, sha_url, False, verbose)
|
2016-07-04 16:37:46 +02:00
|
|
|
if os.path.exists(path):
|
2016-07-06 00:44:31 +02:00
|
|
|
if verify(path, sha_path, False):
|
2016-11-16 12:31:19 -08:00
|
|
|
if verbose:
|
|
|
|
print("using already-download file " + path)
|
2016-07-04 16:37:46 +02:00
|
|
|
return
|
2016-07-06 00:07:26 +02:00
|
|
|
else:
|
2016-11-16 12:31:19 -08:00
|
|
|
if verbose:
|
2017-05-19 20:16:29 +09:00
|
|
|
print("ignoring already-download file " +
|
|
|
|
path + " due to failed verification")
|
2016-07-04 16:37:46 +02:00
|
|
|
os.unlink(path)
|
2016-11-16 12:31:19 -08:00
|
|
|
download(temp_path, url, True, verbose)
|
|
|
|
if not verify(temp_path, sha_path, verbose):
|
2016-07-06 00:07:26 +02:00
|
|
|
raise RuntimeError("failed verification")
|
2016-11-16 12:31:19 -08:00
|
|
|
if verbose:
|
|
|
|
print("moving {} to {}".format(temp_path, path))
|
2016-05-08 09:54:50 +02:00
|
|
|
shutil.move(temp_path, path)
|
|
|
|
finally:
|
2016-11-16 12:31:19 -08:00
|
|
|
delete_if_present(sha_path, verbose)
|
|
|
|
delete_if_present(temp_path, verbose)
|
2016-05-08 10:00:36 +02:00
|
|
|
|
|
|
|
|
2016-11-16 12:31:19 -08:00
|
|
|
def delete_if_present(path, verbose):
|
2016-05-08 10:00:36 +02:00
|
|
|
if os.path.isfile(path):
|
2016-11-16 12:31:19 -08:00
|
|
|
if verbose:
|
|
|
|
print("removing " + path)
|
2016-05-08 10:00:36 +02:00
|
|
|
os.unlink(path)
|
2016-04-30 08:09:53 +02:00
|
|
|
|
|
|
|
|
2016-11-16 12:31:19 -08:00
|
|
|
def download(path, url, probably_big, verbose):
|
2017-02-23 07:04:29 -08:00
|
|
|
for x in range(0, 4):
|
|
|
|
try:
|
|
|
|
_download(path, url, probably_big, verbose, True)
|
|
|
|
return
|
|
|
|
except RuntimeError:
|
|
|
|
print("\nspurious failure, trying again")
|
|
|
|
_download(path, url, probably_big, verbose, False)
|
|
|
|
|
|
|
|
|
|
|
|
def _download(path, url, probably_big, verbose, exception):
|
2016-11-16 12:31:19 -08:00
|
|
|
if probably_big or verbose:
|
|
|
|
print("downloading {}".format(url))
|
2016-04-30 08:43:01 +02:00
|
|
|
# see http://serverfault.com/questions/301128/how-to-download
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
run(["PowerShell.exe", "/nologo", "-Command",
|
|
|
|
"(New-Object System.Net.WebClient)"
|
|
|
|
".DownloadFile('{}', '{}')".format(url, path)],
|
2017-02-23 07:04:29 -08:00
|
|
|
verbose=verbose,
|
|
|
|
exception=exception)
|
2016-04-30 08:43:01 +02:00
|
|
|
else:
|
2016-11-16 12:31:19 -08:00
|
|
|
if probably_big or verbose:
|
|
|
|
option = "-#"
|
|
|
|
else:
|
|
|
|
option = "-s"
|
2017-02-23 07:04:29 -08:00
|
|
|
run(["curl", option, "--retry", "3", "-Sf", "-o", path, url],
|
|
|
|
verbose=verbose,
|
|
|
|
exception=exception)
|
2016-04-30 08:09:53 +02:00
|
|
|
|
|
|
|
|
2016-05-08 09:54:50 +02:00
|
|
|
def verify(path, sha_path, verbose):
|
2016-11-16 12:31:19 -08:00
|
|
|
if verbose:
|
|
|
|
print("verifying " + path)
|
2016-05-08 09:54:50 +02:00
|
|
|
with open(path, "rb") as f:
|
2016-04-13 22:10:42 -04:00
|
|
|
found = hashlib.sha256(f.read()).hexdigest()
|
|
|
|
with open(sha_path, "r") as f:
|
2016-12-19 09:45:42 -08:00
|
|
|
expected = f.readline().split()[0]
|
2016-07-06 00:07:26 +02:00
|
|
|
verified = found == expected
|
2016-11-16 12:31:19 -08:00
|
|
|
if not verified:
|
2016-07-06 00:07:26 +02:00
|
|
|
print("invalid checksum:\n"
|
2017-05-19 20:16:29 +09:00
|
|
|
" found: {}\n"
|
|
|
|
" expected: {}".format(found, expected))
|
2016-07-06 00:07:26 +02:00
|
|
|
return verified
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2016-04-30 08:09:53 +02:00
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
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))
|
|
|
|
|
2017-05-18 17:33:24 +09:00
|
|
|
def run(args, verbose=False, exception=False, **kwargs):
|
2015-11-19 15:20:12 -08:00
|
|
|
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.
|
2017-05-18 17:33:24 +09:00
|
|
|
ret = subprocess.Popen(args, **kwargs)
|
2015-11-19 15:20:12 -08:00
|
|
|
code = ret.wait()
|
|
|
|
if code != 0:
|
2016-04-13 22:10:25 -04:00
|
|
|
err = "failed to run: " + ' '.join(args)
|
2017-02-23 07:04:29 -08:00
|
|
|
if verbose or exception:
|
2016-04-13 22:10:25 -04:00
|
|
|
raise RuntimeError(err)
|
|
|
|
sys.exit(err)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2017-05-19 20:16:29 +09:00
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
def stage0_data(rust_root):
|
|
|
|
nightlies = os.path.join(rust_root, "src/stage0.txt")
|
2016-04-21 23:07:51 +02:00
|
|
|
data = {}
|
2016-04-13 11:18:35 -07:00
|
|
|
with open(nightlies, 'r') as nightlies:
|
2016-04-21 23:07:51 +02:00
|
|
|
for line in nightlies:
|
|
|
|
line = line.rstrip() # Strip newline character, '\n'
|
2016-04-13 11:18:35 -07:00
|
|
|
if line.startswith("#") or line == '':
|
|
|
|
continue
|
|
|
|
a, b = line.split(": ", 1)
|
|
|
|
data[a] = b
|
2016-04-21 23:07:51 +02:00
|
|
|
return data
|
2016-04-13 11:18:35 -07:00
|
|
|
|
2017-05-19 20:16:29 +09:00
|
|
|
|
2016-07-02 19:49:27 +05:30
|
|
|
def format_build_time(duration):
|
|
|
|
return str(datetime.timedelta(seconds=int(duration)))
|
|
|
|
|
2016-09-17 23:31:06 -07:00
|
|
|
|
|
|
|
class RustBuild(object):
|
2017-05-19 20:16:29 +09:00
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
def download_stage0(self):
|
2015-11-19 15:20:12 -08:00
|
|
|
cache_dst = os.path.join(self.build_dir, "cache")
|
2017-04-20 14:32:54 -07:00
|
|
|
rustc_cache = os.path.join(cache_dst, self.stage0_date())
|
2015-11-19 15:20:12 -08:00
|
|
|
if not os.path.exists(rustc_cache):
|
|
|
|
os.makedirs(rustc_cache)
|
|
|
|
|
2017-04-20 14:32:54 -07:00
|
|
|
rustc_channel = self.stage0_rustc_channel()
|
|
|
|
cargo_channel = self.stage0_cargo_channel()
|
2017-03-24 10:58:18 -07:00
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
if self.rustc().startswith(self.bin_root()) and \
|
2016-09-17 23:31:06 -07:00
|
|
|
(not os.path.exists(self.rustc()) or self.rustc_out_of_date()):
|
2016-11-16 12:31:19 -08:00
|
|
|
self.print_what_it_means_to_bootstrap()
|
2016-03-01 12:02:11 -08:00
|
|
|
if os.path.exists(self.bin_root()):
|
|
|
|
shutil.rmtree(self.bin_root())
|
2017-05-19 20:16:29 +09:00
|
|
|
filename = "rust-std-{}-{}.tar.gz".format(
|
|
|
|
rustc_channel, self.build)
|
2017-04-20 14:32:54 -07:00
|
|
|
url = self._download_url + "/dist/" + self.stage0_date()
|
2015-11-19 15:20:12 -08:00
|
|
|
tarball = os.path.join(rustc_cache, filename)
|
|
|
|
if not os.path.exists(tarball):
|
2017-05-19 20:16:29 +09:00
|
|
|
get("{}/{}".format(url, filename),
|
|
|
|
tarball, verbose=self.verbose)
|
2015-11-19 15:20:12 -08:00
|
|
|
unpack(tarball, self.bin_root(),
|
|
|
|
match="rust-std-" + self.build,
|
|
|
|
verbose=self.verbose)
|
|
|
|
|
2017-04-20 14:32:54 -07:00
|
|
|
filename = "rustc-{}-{}.tar.gz".format(rustc_channel, self.build)
|
|
|
|
url = self._download_url + "/dist/" + self.stage0_date()
|
2015-11-19 15:20:12 -08:00
|
|
|
tarball = os.path.join(rustc_cache, filename)
|
|
|
|
if not os.path.exists(tarball):
|
2017-05-19 20:16:29 +09:00
|
|
|
get("{}/{}".format(url, filename),
|
|
|
|
tarball, verbose=self.verbose)
|
|
|
|
unpack(tarball, self.bin_root(),
|
|
|
|
match="rustc", verbose=self.verbose)
|
2017-02-06 16:30:01 +08:00
|
|
|
self.fix_executable(self.bin_root() + "/bin/rustc")
|
|
|
|
self.fix_executable(self.bin_root() + "/bin/rustdoc")
|
2015-11-19 15:20:12 -08:00
|
|
|
with open(self.rustc_stamp(), 'w') as f:
|
2017-04-20 14:32:54 -07:00
|
|
|
f.write(self.stage0_date())
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2017-03-24 10:58:18 -07:00
|
|
|
if "pc-windows-gnu" in self.build:
|
2017-05-19 20:16:29 +09:00
|
|
|
filename = "rust-mingw-{}-{}.tar.gz".format(
|
|
|
|
rustc_channel, self.build)
|
2017-04-20 14:32:54 -07:00
|
|
|
url = self._download_url + "/dist/" + self.stage0_date()
|
2017-03-24 10:58:18 -07:00
|
|
|
tarball = os.path.join(rustc_cache, filename)
|
|
|
|
if not os.path.exists(tarball):
|
2017-05-19 20:16:29 +09:00
|
|
|
get("{}/{}".format(url, filename),
|
|
|
|
tarball, verbose=self.verbose)
|
|
|
|
unpack(tarball, self.bin_root(),
|
|
|
|
match="rust-mingw", verbose=self.verbose)
|
2017-03-24 10:58:18 -07:00
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
if self.cargo().startswith(self.bin_root()) and \
|
2016-09-17 23:31:06 -07:00
|
|
|
(not os.path.exists(self.cargo()) or self.cargo_out_of_date()):
|
2016-11-16 12:31:19 -08:00
|
|
|
self.print_what_it_means_to_bootstrap()
|
2017-04-20 14:32:54 -07:00
|
|
|
filename = "cargo-{}-{}.tar.gz".format(cargo_channel, self.build)
|
|
|
|
url = self._download_url + "/dist/" + self.stage0_date()
|
2017-03-14 12:22:38 -07:00
|
|
|
tarball = os.path.join(rustc_cache, filename)
|
2015-11-19 15:20:12 -08:00
|
|
|
if not os.path.exists(tarball):
|
2017-05-19 20:16:29 +09:00
|
|
|
get("{}/{}".format(url, filename),
|
|
|
|
tarball, verbose=self.verbose)
|
|
|
|
unpack(tarball, self.bin_root(),
|
|
|
|
match="cargo", verbose=self.verbose)
|
2017-02-06 16:30:01 +08:00
|
|
|
self.fix_executable(self.bin_root() + "/bin/cargo")
|
2015-11-19 15:20:12 -08:00
|
|
|
with open(self.cargo_stamp(), 'w') as f:
|
2017-04-20 14:32:54 -07:00
|
|
|
f.write(self.stage0_date())
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2017-02-06 16:30:01 +08:00
|
|
|
def fix_executable(self, fname):
|
|
|
|
# If we're on NixOS we need to change the path to the dynamic loader
|
|
|
|
|
|
|
|
default_encoding = sys.getdefaultencoding()
|
|
|
|
try:
|
2017-05-19 20:16:29 +09:00
|
|
|
ostype = subprocess.check_output(
|
|
|
|
['uname', '-s']).strip().decode(default_encoding)
|
2017-02-06 16:30:01 +08:00
|
|
|
except (subprocess.CalledProcessError, WindowsError):
|
|
|
|
return
|
|
|
|
|
|
|
|
if ostype != "Linux":
|
|
|
|
return
|
|
|
|
|
2017-02-11 16:17:54 +08:00
|
|
|
if not os.path.exists("/etc/NIXOS"):
|
2017-02-06 16:30:01 +08:00
|
|
|
return
|
|
|
|
if os.path.exists("/lib"):
|
|
|
|
return
|
|
|
|
|
|
|
|
# At this point we're pretty sure the user is running NixOS
|
2017-02-17 14:00:58 +08:00
|
|
|
print("info: you seem to be running NixOS. Attempting to patch " + fname)
|
2017-02-06 16:30:01 +08:00
|
|
|
|
|
|
|
try:
|
2017-05-19 20:16:29 +09:00
|
|
|
interpreter = subprocess.check_output(
|
|
|
|
["patchelf", "--print-interpreter", fname])
|
2017-02-06 16:30:01 +08:00
|
|
|
interpreter = interpreter.strip().decode(default_encoding)
|
|
|
|
except subprocess.CalledProcessError as e:
|
2017-02-17 14:00:58 +08:00
|
|
|
print("warning: failed to call patchelf: %s" % e)
|
2017-02-06 16:30:01 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
loader = interpreter.split("/")[-1]
|
|
|
|
|
|
|
|
try:
|
2017-05-19 20:16:29 +09:00
|
|
|
ldd_output = subprocess.check_output(
|
|
|
|
['ldd', '/run/current-system/sw/bin/sh'])
|
2017-02-06 16:30:01 +08:00
|
|
|
ldd_output = ldd_output.strip().decode(default_encoding)
|
|
|
|
except subprocess.CalledProcessError as e:
|
2017-02-17 14:00:58 +08:00
|
|
|
print("warning: unable to call ldd: %s" % e)
|
2017-02-06 16:30:01 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
for line in ldd_output.splitlines():
|
|
|
|
libname = line.split()[0]
|
|
|
|
if libname.endswith(loader):
|
|
|
|
loader_path = libname[:len(libname) - len(loader)]
|
|
|
|
break
|
|
|
|
else:
|
2017-02-17 14:00:58 +08:00
|
|
|
print("warning: unable to find the path to the dynamic linker")
|
2017-02-06 16:30:01 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
correct_interpreter = loader_path + loader
|
|
|
|
|
|
|
|
try:
|
2017-05-19 20:16:29 +09:00
|
|
|
subprocess.check_output(
|
|
|
|
["patchelf", "--set-interpreter", correct_interpreter, fname])
|
2017-02-06 16:30:01 +08:00
|
|
|
except subprocess.CalledProcessError as e:
|
2017-02-17 14:00:58 +08:00
|
|
|
print("warning: failed to call patchelf: %s" % e)
|
2017-02-06 16:30:01 +08:00
|
|
|
return
|
|
|
|
|
2017-04-20 14:32:54 -07:00
|
|
|
def stage0_date(self):
|
|
|
|
return self._date
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
def stage0_rustc_channel(self):
|
|
|
|
return self._rustc_channel
|
|
|
|
|
2017-04-20 14:32:54 -07:00
|
|
|
def stage0_cargo_channel(self):
|
|
|
|
return self._cargo_channel
|
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
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):
|
2016-05-31 13:24:28 -07:00
|
|
|
if not os.path.exists(self.rustc_stamp()) or self.clean:
|
2015-11-19 15:20:12 -08:00
|
|
|
return True
|
|
|
|
with open(self.rustc_stamp(), 'r') as f:
|
2017-04-20 14:32:54 -07:00
|
|
|
return self.stage0_date() != f.read()
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
def cargo_out_of_date(self):
|
2016-05-31 13:24:28 -07:00
|
|
|
if not os.path.exists(self.cargo_stamp()) or self.clean:
|
2015-11-19 15:20:12 -08:00
|
|
|
return True
|
|
|
|
with open(self.cargo_stamp(), 'r') as f:
|
2017-04-20 14:32:54 -07:00
|
|
|
return self.stage0_date() != f.read()
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
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():
|
2017-05-13 14:21:35 +09:00
|
|
|
match = re.match(r'^{}\s*=(.*)$'.format(key), line)
|
|
|
|
if match is not None:
|
|
|
|
value = match.group(1)
|
|
|
|
return self.get_string(value) or value.strip()
|
2015-11-19 15:20:12 -08:00
|
|
|
return None
|
|
|
|
|
2015-11-19 16:55:21 -08:00
|
|
|
def get_mk(self, key):
|
|
|
|
for line in iter(self.config_mk.splitlines()):
|
2017-02-12 11:27:39 -08:00
|
|
|
if line.startswith(key + ' '):
|
|
|
|
var = line[line.find(':=') + 2:].strip()
|
|
|
|
if var != '':
|
|
|
|
return var
|
2015-11-19 16:55:21 -08:00
|
|
|
return None
|
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
def cargo(self):
|
|
|
|
config = self.get_toml('cargo')
|
|
|
|
if config:
|
|
|
|
return config
|
2016-11-09 22:18:02 -08:00
|
|
|
config = self.get_mk('CFG_LOCAL_RUST_ROOT')
|
|
|
|
if config:
|
|
|
|
return config + '/bin/cargo' + self.exe_suffix()
|
2015-11-19 15:20:12 -08:00
|
|
|
return os.path.join(self.bin_root(), "bin/cargo" + self.exe_suffix())
|
|
|
|
|
|
|
|
def rustc(self):
|
|
|
|
config = self.get_toml('rustc')
|
|
|
|
if config:
|
|
|
|
return config
|
2016-11-09 22:18:02 -08:00
|
|
|
config = self.get_mk('CFG_LOCAL_RUST_ROOT')
|
2015-11-19 16:55:21 -08:00
|
|
|
if config:
|
|
|
|
return config + '/bin/rustc' + self.exe_suffix()
|
2015-11-19 15:20:12 -08:00
|
|
|
return os.path.join(self.bin_root(), "bin/rustc" + self.exe_suffix())
|
|
|
|
|
|
|
|
def get_string(self, line):
|
|
|
|
start = line.find('"')
|
2017-05-13 14:21:35 +09:00
|
|
|
if start == -1:
|
|
|
|
return None
|
2016-09-17 23:31:06 -07:00
|
|
|
end = start + 1 + line[start + 1:].find('"')
|
|
|
|
return line[start + 1:end]
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
def exe_suffix(self):
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
return '.exe'
|
|
|
|
else:
|
|
|
|
return ''
|
|
|
|
|
2016-11-16 12:31:19 -08:00
|
|
|
def print_what_it_means_to_bootstrap(self):
|
|
|
|
if hasattr(self, 'printed'):
|
|
|
|
return
|
|
|
|
self.printed = True
|
|
|
|
if os.path.exists(self.bootstrap_binary()):
|
|
|
|
return
|
|
|
|
if not '--help' in sys.argv or len(sys.argv) == 1:
|
|
|
|
return
|
|
|
|
|
|
|
|
print('info: the build system for Rust is written in Rust, so this')
|
|
|
|
print(' script is now going to download a stage0 rust compiler')
|
|
|
|
print(' and then compile the build system itself')
|
|
|
|
print('')
|
|
|
|
print('info: in the meantime you can read more about rustbuild at')
|
|
|
|
print(' src/bootstrap/README.md before the download finishes')
|
|
|
|
|
|
|
|
def bootstrap_binary(self):
|
|
|
|
return os.path.join(self.build_dir, "bootstrap/debug/bootstrap")
|
|
|
|
|
2015-11-19 15:20:12 -08:00
|
|
|
def build_bootstrap(self):
|
2016-11-16 12:31:19 -08:00
|
|
|
self.print_what_it_means_to_bootstrap()
|
2016-05-31 13:24:28 -07:00
|
|
|
build_dir = os.path.join(self.build_dir, "bootstrap")
|
|
|
|
if self.clean and os.path.exists(build_dir):
|
|
|
|
shutil.rmtree(build_dir)
|
2015-11-19 15:20:12 -08:00
|
|
|
env = os.environ.copy()
|
2017-06-06 19:32:43 -07:00
|
|
|
env["RUSTC_BOOTSTRAP"] = '1'
|
2016-05-31 13:24:28 -07:00
|
|
|
env["CARGO_TARGET_DIR"] = build_dir
|
2015-11-19 15:20:12 -08:00
|
|
|
env["RUSTC"] = self.rustc()
|
2017-02-19 10:41:56 +09:00
|
|
|
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 20:16:29 +09:00
|
|
|
(os.pathsep + env["LD_LIBRARY_PATH"]) \
|
|
|
|
if "LD_LIBRARY_PATH" in env else ""
|
2017-02-19 10:41:56 +09:00
|
|
|
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 20:16:29 +09:00
|
|
|
(os.pathsep + env["DYLD_LIBRARY_PATH"]) \
|
|
|
|
if "DYLD_LIBRARY_PATH" in env else ""
|
2017-04-24 14:21:36 +00:00
|
|
|
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
2017-05-19 20:16:29 +09:00
|
|
|
(os.pathsep + env["LIBRARY_PATH"]) \
|
|
|
|
if "LIBRARY_PATH" in env else ""
|
2015-11-19 15:20:12 -08:00
|
|
|
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
|
2017-05-19 20:16:29 +09:00
|
|
|
os.pathsep + env["PATH"]
|
2016-11-16 18:02:56 -05:00
|
|
|
if not os.path.isfile(self.cargo()):
|
|
|
|
raise Exception("no cargo executable found at `%s`" % self.cargo())
|
2016-11-01 13:46:38 -07:00
|
|
|
args = [self.cargo(), "build", "--manifest-path",
|
|
|
|
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
|
2017-05-24 09:10:15 +02:00
|
|
|
if self.verbose:
|
|
|
|
args.append("--verbose")
|
|
|
|
if self.verbose > 1:
|
|
|
|
args.append("--verbose")
|
2017-02-10 22:59:40 +02:00
|
|
|
if self.use_locked_deps:
|
|
|
|
args.append("--locked")
|
2016-11-01 13:46:38 -07:00
|
|
|
if self.use_vendored_sources:
|
|
|
|
args.append("--frozen")
|
2017-05-24 09:11:10 +02:00
|
|
|
run(args, env=env, verbose=self.verbose)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
def build_triple(self):
|
2016-09-16 01:48:27 +03:00
|
|
|
default_encoding = sys.getdefaultencoding()
|
2015-11-19 15:20:12 -08:00
|
|
|
config = self.get_toml('build')
|
2015-11-19 16:55:21 -08:00
|
|
|
if config:
|
|
|
|
return config
|
|
|
|
config = self.get_mk('CFG_BUILD')
|
2015-11-19 15:20:12 -08:00
|
|
|
if config:
|
|
|
|
return config
|
|
|
|
try:
|
2017-05-19 20:16:29 +09:00
|
|
|
ostype = subprocess.check_output(
|
|
|
|
['uname', '-s']).strip().decode(default_encoding)
|
|
|
|
cputype = subprocess.check_output(
|
|
|
|
['uname', '-m']).strip().decode(default_encoding)
|
2017-02-12 11:24:06 -08:00
|
|
|
except (subprocess.CalledProcessError, OSError):
|
2015-11-19 15:20:12 -08:00
|
|
|
if sys.platform == 'win32':
|
|
|
|
return 'x86_64-pc-windows-msvc'
|
2016-04-21 23:07:51 +02:00
|
|
|
err = "uname not found"
|
|
|
|
if self.verbose:
|
|
|
|
raise Exception(err)
|
|
|
|
sys.exit(err)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
# 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':
|
2017-05-19 20:16:29 +09:00
|
|
|
os_from_sp = subprocess.check_output(
|
|
|
|
['uname', '-o']).strip().decode(default_encoding)
|
2017-04-30 16:10:31 -04:00
|
|
|
if os_from_sp == 'Android':
|
2017-04-18 13:40:00 -03:00
|
|
|
ostype = 'linux-android'
|
|
|
|
else:
|
|
|
|
ostype = 'unknown-linux-gnu'
|
2015-11-19 15:20:12 -08:00
|
|
|
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'
|
2017-02-11 09:24:33 -08:00
|
|
|
elif ostype == 'SunOS':
|
|
|
|
ostype = 'sun-solaris'
|
|
|
|
# On Solaris, uname -m will return a machine classification instead
|
|
|
|
# of a cpu type, so uname -p is recommended instead. However, the
|
|
|
|
# output from that option is too generic for our purposes (it will
|
|
|
|
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
|
|
|
|
# must be used instead.
|
|
|
|
try:
|
|
|
|
cputype = subprocess.check_output(['isainfo',
|
2017-05-19 20:16:29 +09:00
|
|
|
'-k']).strip().decode(default_encoding)
|
2017-02-11 22:55:25 -08:00
|
|
|
except (subprocess.CalledProcessError, OSError):
|
2017-02-11 09:24:33 -08:00
|
|
|
err = "isainfo not found"
|
|
|
|
if self.verbose:
|
|
|
|
raise Exception(err)
|
|
|
|
sys.exit(err)
|
2015-11-19 15:20:12 -08:00
|
|
|
elif ostype == 'Darwin':
|
|
|
|
ostype = 'apple-darwin'
|
2017-02-12 11:27:39 -08:00
|
|
|
elif ostype == 'Haiku':
|
|
|
|
ostype = 'unknown-haiku'
|
2015-11-19 15:20:12 -08:00
|
|
|
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:
|
2016-04-13 22:10:25 -04:00
|
|
|
err = "unknown OS type: " + ostype
|
|
|
|
if self.verbose:
|
|
|
|
raise ValueError(err)
|
|
|
|
sys.exit(err)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
|
|
|
if cputype in {'i386', 'i486', 'i686', 'i786', 'x86'}:
|
|
|
|
cputype = 'i686'
|
|
|
|
elif cputype in {'xscale', 'arm'}:
|
|
|
|
cputype = 'arm'
|
2017-04-18 13:40:00 -03:00
|
|
|
if ostype == 'linux-android':
|
|
|
|
ostype = 'linux-androideabi'
|
2017-04-07 17:16:52 -07:00
|
|
|
elif cputype == 'armv6l':
|
2015-11-19 15:20:12 -08:00
|
|
|
cputype = 'arm'
|
2017-04-18 13:40:00 -03:00
|
|
|
if ostype == 'linux-android':
|
|
|
|
ostype = 'linux-androideabi'
|
|
|
|
else:
|
|
|
|
ostype += 'eabihf'
|
2017-04-07 17:16:52 -07:00
|
|
|
elif cputype in {'armv7l', 'armv8l'}:
|
2017-02-12 11:27:39 -08:00
|
|
|
cputype = 'armv7'
|
2017-04-18 13:40:00 -03:00
|
|
|
if ostype == 'linux-android':
|
|
|
|
ostype = 'linux-androideabi'
|
|
|
|
else:
|
|
|
|
ostype += 'eabihf'
|
|
|
|
elif cputype in {'aarch64', 'arm64'}:
|
2017-02-03 08:56:46 +01:00
|
|
|
cputype = 'aarch64'
|
2016-11-07 14:29:15 +08:00
|
|
|
elif cputype == 'mips':
|
|
|
|
if sys.byteorder == 'big':
|
|
|
|
cputype = 'mips'
|
|
|
|
elif sys.byteorder == 'little':
|
|
|
|
cputype = 'mipsel'
|
|
|
|
else:
|
|
|
|
raise ValueError('unknown byteorder: ' + sys.byteorder)
|
|
|
|
elif cputype == 'mips64':
|
|
|
|
if sys.byteorder == 'big':
|
|
|
|
cputype = 'mips64'
|
|
|
|
elif sys.byteorder == 'little':
|
|
|
|
cputype = 'mips64el'
|
|
|
|
else:
|
|
|
|
raise ValueError('unknown byteorder: ' + sys.byteorder)
|
|
|
|
# only the n64 ABI is supported, indicate it
|
|
|
|
ostype += 'abi64'
|
2017-02-12 11:27:39 -08:00
|
|
|
elif cputype in {'powerpc', 'ppc'}:
|
2015-11-19 15:20:12 -08:00
|
|
|
cputype = 'powerpc'
|
2017-02-12 11:27:39 -08:00
|
|
|
elif cputype in {'powerpc64', 'ppc64'}:
|
|
|
|
cputype = 'powerpc64'
|
|
|
|
elif cputype in {'powerpc64le', 'ppc64le'}:
|
|
|
|
cputype = 'powerpc64le'
|
2017-02-16 21:19:43 -08:00
|
|
|
elif cputype == 'sparcv9':
|
|
|
|
pass
|
2015-11-19 15:20:12 -08:00
|
|
|
elif cputype in {'amd64', 'x86_64', 'x86-64', 'x64'}:
|
|
|
|
cputype = 'x86_64'
|
2017-02-12 11:27:39 -08:00
|
|
|
elif cputype == 's390x':
|
|
|
|
cputype = 's390x'
|
|
|
|
elif cputype == 'BePC':
|
|
|
|
cputype = 'i686'
|
2015-11-19 15:20:12 -08:00
|
|
|
else:
|
2016-04-13 22:10:25 -04:00
|
|
|
err = "unknown cpu type: " + cputype
|
|
|
|
if self.verbose:
|
|
|
|
raise ValueError(err)
|
|
|
|
sys.exit(err)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2016-06-01 12:24:41 -04:00
|
|
|
return "{}-{}".format(cputype, ostype)
|
2015-11-19 15:20:12 -08:00
|
|
|
|
2017-05-13 14:21:35 +09:00
|
|
|
def update_submodules(self):
|
|
|
|
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
|
2017-05-19 20:16:29 +09:00
|
|
|
self.get_toml('submodules') == "false" or \
|
|
|
|
self.get_mk('CFG_DISABLE_MANAGE_SUBMODULES') == "1":
|
2017-05-13 14:21:35 +09:00
|
|
|
return
|
|
|
|
print('Updating submodules')
|
2017-05-22 16:04:34 +09:00
|
|
|
default_encoding = sys.getdefaultencoding()
|
2017-05-18 17:33:24 +09:00
|
|
|
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root)
|
2017-05-22 16:04:34 +09:00
|
|
|
submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
|
|
|
|
["git", "config", "--file", os.path.join(self.rust_root, ".gitmodules"),
|
|
|
|
"--get-regexp", "path"]
|
|
|
|
).decode(default_encoding).splitlines()]
|
2017-05-19 20:36:49 +09:00
|
|
|
submodules = [module for module in submodules
|
2017-05-22 16:04:34 +09:00
|
|
|
if not ((module.endswith("llvm") and
|
|
|
|
(self.get_toml('llvm-config') or self.get_mk('CFG_LLVM_ROOT'))) or
|
|
|
|
(module.endswith("jemalloc") and
|
|
|
|
(self.get_toml('jemalloc') or self.get_mk('CFG_JEMALLOC_ROOT'))))
|
|
|
|
]
|
2017-05-19 20:36:49 +09:00
|
|
|
run(["git", "submodule", "update",
|
2017-05-25 14:00:05 +09:00
|
|
|
"--init"] + submodules, cwd=self.rust_root, verbose=self.verbose)
|
2017-05-18 17:33:24 +09:00
|
|
|
run(["git", "submodule", "-q", "foreach", "git",
|
2017-05-25 14:00:05 +09:00
|
|
|
"reset", "-q", "--hard"], cwd=self.rust_root, verbose=self.verbose)
|
2017-05-18 17:33:24 +09:00
|
|
|
run(["git", "submodule", "-q", "foreach", "git",
|
2017-05-25 14:00:05 +09:00
|
|
|
"clean", "-qdfx"], cwd=self.rust_root, verbose=self.verbose)
|
2017-05-18 17:33:24 +09:00
|
|
|
|
2017-05-17 09:15:44 -07:00
|
|
|
|
2017-03-03 05:27:07 +03:00
|
|
|
def bootstrap():
|
2016-04-13 11:18:35 -07:00
|
|
|
parser = argparse.ArgumentParser(description='Build rust')
|
|
|
|
parser.add_argument('--config')
|
2016-05-31 13:24:28 -07:00
|
|
|
parser.add_argument('--clean', action='store_true')
|
2016-04-13 11:18:35 -07:00
|
|
|
parser.add_argument('-v', '--verbose', action='store_true')
|
|
|
|
|
2016-06-29 12:43:57 -05:00
|
|
|
args = [a for a in sys.argv if a != '-h' and a != '--help']
|
2016-04-13 11:18:35 -07:00
|
|
|
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
|
2016-05-31 13:24:28 -07:00
|
|
|
rb.clean = args.clean
|
2016-04-13 11:18:35 -07:00
|
|
|
|
|
|
|
try:
|
|
|
|
with open(args.config or 'config.toml') as config:
|
|
|
|
rb.config_toml = config.read()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
try:
|
|
|
|
rb.config_mk = open('config.mk').read()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2017-05-24 09:09:17 +02:00
|
|
|
if '\nverbose = 2' in rb.config_toml:
|
|
|
|
rb.verbose = 2
|
|
|
|
elif '\nverbose = 1' in rb.config_toml:
|
|
|
|
rb.verbose = 1
|
|
|
|
|
2016-11-01 13:46:38 -07:00
|
|
|
rb.use_vendored_sources = '\nvendor = true' in rb.config_toml or \
|
|
|
|
'CFG_ENABLE_VENDOR' in rb.config_mk
|
|
|
|
|
2017-02-10 22:59:40 +02:00
|
|
|
rb.use_locked_deps = '\nlocked-deps = true' in rb.config_toml or \
|
|
|
|
'CFG_ENABLE_LOCKED_DEPS' in rb.config_mk
|
|
|
|
|
2017-02-02 22:27:15 +01:00
|
|
|
if 'SUDO_USER' in os.environ and not rb.use_vendored_sources:
|
2017-02-02 22:28:00 +01:00
|
|
|
if os.environ.get('USER') != os.environ['SUDO_USER']:
|
2016-11-16 12:31:19 -08:00
|
|
|
rb.use_vendored_sources = True
|
|
|
|
print('info: looks like you are running this command under `sudo`')
|
|
|
|
print(' and so in order to preserve your $HOME this will now')
|
|
|
|
print(' use vendored sources by default. Note that if this')
|
|
|
|
print(' does not work you should run a normal build first')
|
2017-02-02 22:28:26 +01:00
|
|
|
print(' before running a command like `sudo make install`')
|
2016-11-16 12:31:19 -08:00
|
|
|
|
2016-11-01 13:46:38 -07:00
|
|
|
if rb.use_vendored_sources:
|
|
|
|
if not os.path.exists('.cargo'):
|
|
|
|
os.makedirs('.cargo')
|
2017-05-19 20:16:29 +09:00
|
|
|
with open('.cargo/config', 'w') as f:
|
2016-11-16 12:31:19 -08:00
|
|
|
f.write("""
|
|
|
|
[source.crates-io]
|
|
|
|
replace-with = 'vendored-sources'
|
|
|
|
registry = 'https://example.com'
|
|
|
|
|
|
|
|
[source.vendored-sources]
|
|
|
|
directory = '{}/src/vendor'
|
|
|
|
""".format(rb.rust_root))
|
2016-11-01 13:46:38 -07:00
|
|
|
else:
|
|
|
|
if os.path.exists('.cargo'):
|
|
|
|
shutil.rmtree('.cargo')
|
2016-11-16 12:31:19 -08:00
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
data = stage0_data(rb.rust_root)
|
2017-04-20 14:32:54 -07:00
|
|
|
rb._date = data['date']
|
|
|
|
rb._rustc_channel = data['rustc']
|
|
|
|
rb._cargo_channel = data['cargo']
|
|
|
|
if 'dev' in data:
|
|
|
|
rb._download_url = 'https://dev-static.rust-lang.org'
|
|
|
|
else:
|
|
|
|
rb._download_url = 'https://static.rust-lang.org'
|
2016-04-13 11:18:35 -07:00
|
|
|
|
2017-05-13 14:21:35 +09:00
|
|
|
rb.update_submodules()
|
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
# Fetch/build the bootstrap
|
|
|
|
rb.build = rb.build_triple()
|
|
|
|
rb.download_stage0()
|
|
|
|
sys.stdout.flush()
|
|
|
|
rb.build_bootstrap()
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
# Run the bootstrap
|
2016-11-16 12:31:19 -08:00
|
|
|
args = [rb.bootstrap_binary()]
|
2016-04-13 11:18:35 -07:00
|
|
|
args.extend(sys.argv[1:])
|
|
|
|
env = os.environ.copy()
|
2016-10-21 13:18:09 -07:00
|
|
|
env["BUILD"] = rb.build
|
|
|
|
env["SRC"] = rb.rust_root
|
2016-04-13 11:18:35 -07:00
|
|
|
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
|
2017-06-21 19:01:24 +03:00
|
|
|
env["BOOTSTRAP_PYTHON"] = sys.executable
|
2017-05-24 09:11:10 +02:00
|
|
|
run(args, env=env, verbose=rb.verbose)
|
2016-04-13 11:18:35 -07:00
|
|
|
|
2017-05-19 20:16:29 +09:00
|
|
|
|
2017-03-03 05:27:07 +03:00
|
|
|
def main():
|
|
|
|
start_time = time()
|
2017-05-19 20:16:29 +09:00
|
|
|
help_triggered = (
|
|
|
|
'-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1)
|
2017-03-03 05:27:07 +03:00
|
|
|
try:
|
|
|
|
bootstrap()
|
2017-04-01 15:48:03 -06:00
|
|
|
if not help_triggered:
|
2017-05-19 20:16:29 +09:00
|
|
|
print("Build completed successfully in %s" %
|
|
|
|
format_build_time(time() - start_time))
|
2017-03-03 05:27:07 +03:00
|
|
|
except (SystemExit, KeyboardInterrupt) as e:
|
|
|
|
if hasattr(e, 'code') and isinstance(e.code, int):
|
|
|
|
exit_code = e.code
|
|
|
|
else:
|
|
|
|
exit_code = 1
|
|
|
|
print(e)
|
2017-04-01 15:48:03 -06:00
|
|
|
if not help_triggered:
|
2017-05-19 20:16:29 +09:00
|
|
|
print("Build completed unsuccessfully in %s" %
|
|
|
|
format_build_time(time() - start_time))
|
2017-03-03 05:27:07 +03:00
|
|
|
sys.exit(exit_code)
|
2016-07-02 19:49:27 +05:30
|
|
|
|
2016-04-13 11:18:35 -07:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|