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 or the MIT license +# , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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-.{rlib,so,...}`, and there could be multiple different + // `` 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 or the MIT license +// , 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, + + // 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, + pub rustc_default_ar: Option, + + pub build: String, + pub host: Vec, + pub target: Vec, + pub rustc: Option, + pub cargo: Option, + + // libstd features + pub debug_jemalloc: bool, + pub use_jemalloc: bool, + + // misc + pub channel: String, + pub musl_root: Option, +} + +/// Per-target configuration stored in the global configuration structure. +#[derive(Default)] +pub struct Target { + pub llvm_config: Option, + pub jemalloc: Option, + pub cc: Option, + pub cxx: Option, + pub ndk: Option, +} + +/// 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, + llvm: Option, + rust: Option, + target: Option>, +} + +/// TOML representation of various global build decisions. +#[derive(RustcDecodable, Default, Clone)] +struct Build { + build: Option, + host: Vec, + target: Vec, + cargo: Option, + rustc: Option, + compiler_docs: Option, + docs: Option, +} + +/// TOML representation of how the LLVM build is configured. +#[derive(RustcDecodable, Default)] +struct Llvm { + ccache: Option, + assertions: Option, + optimize: Option, + version_check: Option, + static_libstdcpp: Option, +} + +/// TOML representation of how the Rust build is configured. +#[derive(RustcDecodable, Default)] +struct Rust { + optimize: Option, + codegen_units: Option, + debug_assertions: Option, + debuginfo: Option, + debug_jemalloc: Option, + use_jemalloc: Option, + default_linker: Option, + default_ar: Option, + channel: Option, + musl_root: Option, + rpath: Option, +} + +/// TOML representation of how each build target is configured. +#[derive(RustcDecodable, Default)] +struct TomlTarget { + llvm_config: Option, + jemalloc: Option, + cc: Option, + cxx: Option, + android_ndk: Option, +} + +impl Config { + pub fn parse(build: &str, file: Option) -> 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(field: &mut T, val: Option) { + 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 or the MIT license +// , 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, + pub build: String, + pub host: Filter, + pub target: Filter, + pub step: Vec, + pub config: Option, + pub src: Option, + pub jobs: Option, + pub args: Vec, +} + +pub struct Filter { + values: Vec, +} + +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 { + 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 or the MIT license +// , 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::(); + 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 or the MIT license +// , 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, + short_ver_hash: Option, + ver_date: Option, + version: String, + bootstrap_key: String, + + // Runtime state filled in later on + cc: HashMap, + cxx: HashMap, + compiler_rt_built: RefCell>, +} + +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::>() + .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 { + 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 { + 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>, + set: &mut HashSet>) { + 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 { + 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>) { + 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> { + 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 or the MIT license +// , 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, 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 or the MIT license +// , 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 { + 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 or the MIT license +// , 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::>(); + 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 or the MIT license +// , 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::>(); + // 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::>()); + } + + // 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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