Add a Cargo-based build system

This commit is the start of a series of commits which start to replace the
makefiles with a Cargo-based build system. The aim is not to remove the
makefiles entirely just yet but rather just replace the portions that invoke the
compiler to do the bootstrap. This commit specifically adds enough support to
perform the bootstrap (and all the cross compilation within) along with
generating documentation.

More commits will follow up in this series to actually wire up the makefiles to
call this build system, so stay tuned!
This commit is contained in:
Alex Crichton 2015-11-19 15:20:12 -08:00
parent a91ff1c9d1
commit 046e6874c4
33 changed files with 3324 additions and 1 deletions

1
.gitignore vendored
View File

@ -93,3 +93,4 @@ tmp.*.rs
version.md
version.ml
version.texi
/target

109
src/bootstrap/Cargo.lock generated Normal file
View File

@ -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"

29
src/bootstrap/Cargo.toml Normal file
View File

@ -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"

110
src/bootstrap/README.md Normal file
View File

@ -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/
```

300
src/bootstrap/bootstrap.py Normal file
View File

@ -0,0 +1,300 @@
# Copyright 2015-2016 The Rust Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution and at
# http://rust-lang.org/COPYRIGHT.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
import argparse
import contextlib
import os
import shutil
import subprocess
import sys
import tarfile
def get(url, path, verbose=False):
print("downloading " + url)
# see http://serverfault.com/questions/301128/how-to-download
if sys.platform == 'win32':
run(["PowerShell.exe", "/nologo", "-Command",
"(New-Object System.Net.WebClient).DownloadFile('" + url +
"', '" + path + "')"], verbose=verbose)
else:
run(["curl", "-o", path, url], verbose=verbose)
def unpack(tarball, dst, verbose=False, match=None):
print("extracting " + tarball)
fname = os.path.basename(tarball).replace(".tar.gz", "")
with contextlib.closing(tarfile.open(tarball)) as tar:
for p in tar.getnames():
if "/" not in p:
continue
name = p.replace(fname + "/", "", 1)
if match is not None and not name.startswith(match):
continue
name = name[len(match) + 1:]
fp = os.path.join(dst, name)
if verbose:
print(" extracting " + p)
tar.extract(p, dst)
tp = os.path.join(dst, p)
if os.path.isdir(tp) and os.path.exists(fp):
continue
shutil.move(tp, fp)
shutil.rmtree(os.path.join(dst, fname))
def run(args, verbose=False):
if verbose:
print("running: " + ' '.join(args))
sys.stdout.flush()
# Use Popen here instead of call() as it apparently allows powershell on
# Windows to not lock up waiting for input presumably.
ret = subprocess.Popen(args)
code = ret.wait()
if code != 0:
if not verbose:
print("failed to run: " + ' '.join(args))
raise RuntimeError("failed to run command")
class RustBuild:
def download_rust_nightly(self):
cache_dst = os.path.join(self.build_dir, "cache")
rustc_cache = os.path.join(cache_dst, self.snap_rustc_date())
cargo_cache = os.path.join(cache_dst, self.snap_cargo_date())
if not os.path.exists(rustc_cache):
os.makedirs(rustc_cache)
if not os.path.exists(cargo_cache):
os.makedirs(cargo_cache)
if self.rustc().startswith(self.bin_root()) and \
(not os.path.exists(self.rustc()) or self.rustc_out_of_date()):
filename = "rust-std-nightly-" + self.build + ".tar.gz"
url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
tarball = os.path.join(rustc_cache, filename)
if not os.path.exists(tarball):
get(url + "/" + filename, tarball, verbose=self.verbose)
unpack(tarball, self.bin_root(),
match="rust-std-" + self.build,
verbose=self.verbose)
filename = "rustc-nightly-" + self.build + ".tar.gz"
url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
tarball = os.path.join(rustc_cache, filename)
if not os.path.exists(tarball):
get(url + "/" + filename, tarball, verbose=self.verbose)
unpack(tarball, self.bin_root(), match="rustc", verbose=self.verbose)
with open(self.rustc_stamp(), 'w') as f:
f.write(self.snap_rustc_date())
if self.cargo().startswith(self.bin_root()) and \
(not os.path.exists(self.cargo()) or self.cargo_out_of_date()):
filename = "cargo-nightly-" + self.build + ".tar.gz"
url = "https://static.rust-lang.org/cargo-dist/" + self.snap_cargo_date()
tarball = os.path.join(cargo_cache, filename)
if not os.path.exists(tarball):
get(url + "/" + filename, tarball, verbose=self.verbose)
unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose)
with open(self.cargo_stamp(), 'w') as f:
f.write(self.snap_cargo_date())
def snap_cargo_date(self):
return self._cargo_date
def snap_rustc_date(self):
return self._rustc_date
def rustc_stamp(self):
return os.path.join(self.bin_root(), '.rustc-stamp')
def cargo_stamp(self):
return os.path.join(self.bin_root(), '.cargo-stamp')
def rustc_out_of_date(self):
if not os.path.exists(self.rustc_stamp()):
return True
with open(self.rustc_stamp(), 'r') as f:
return self.snap_rustc_date() != f.read()
def cargo_out_of_date(self):
if not os.path.exists(self.cargo_stamp()):
return True
with open(self.cargo_stamp(), 'r') as f:
return self.snap_cargo_date() != f.read()
def bin_root(self):
return os.path.join(self.build_dir, self.build, "stage0")
def get_toml(self, key):
for line in self.config_toml.splitlines():
if line.startswith(key + ' ='):
return self.get_string(line)
return None
def cargo(self):
config = self.get_toml('cargo')
if config:
return config
return os.path.join(self.bin_root(), "bin/cargo" + self.exe_suffix())
def rustc(self):
config = self.get_toml('rustc')
if config:
return config
return os.path.join(self.bin_root(), "bin/rustc" + self.exe_suffix())
def get_string(self, line):
start = line.find('"')
end = start + 1 + line[start+1:].find('"')
return line[start+1:end]
def exe_suffix(self):
if sys.platform == 'win32':
return '.exe'
else:
return ''
def parse_nightly_dates(self):
nightlies = os.path.join(self.rust_root, "src/nightlies.txt")
with open(nightlies, 'r') as nightlies:
rustc, cargo = nightlies.read().split("\n")[:2]
assert rustc.startswith("rustc: ")
assert cargo.startswith("cargo: ")
self._rustc_date = rustc[len("rustc: "):]
self._cargo_date = cargo[len("cargo: "):]
def build_bootstrap(self):
env = os.environ.copy()
env["CARGO_TARGET_DIR"] = os.path.join(self.build_dir, "bootstrap")
env["RUSTC"] = self.rustc()
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
os.pathsep + env["PATH"]
self.run([self.cargo(), "build", "--manifest-path",
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")],
env)
def run(self, args, env):
proc = subprocess.Popen(args, env = env)
ret = proc.wait()
if ret != 0:
sys.exit(ret)
def build_triple(self):
config = self.get_toml('build')
if config:
return config
try:
ostype = subprocess.check_output(['uname', '-s']).strip()
cputype = subprocess.check_output(['uname', '-m']).strip()
except FileNotFoundError:
if sys.platform == 'win32':
return 'x86_64-pc-windows-msvc'
else:
raise
# Darwin's `uname -s` lies and always returns i386. We have to use
# sysctl instead.
if ostype == 'Darwin' and cputype == 'i686':
sysctl = subprocess.check_output(['sysctl', 'hw.optional.x86_64'])
if sysctl.contains(': 1'):
cputype = 'x86_64'
# The goal here is to come up with the same triple as LLVM would,
# at least for the subset of platforms we're willing to target.
if ostype == 'Linux':
ostype = 'unknown-linux-gnu'
elif ostype == 'FreeBSD':
ostype = 'unknown-freebsd'
elif ostype == 'DragonFly':
ostype = 'unknown-dragonfly'
elif ostype == 'Bitrig':
ostype = 'unknown-bitrig'
elif ostype == 'OpenBSD':
ostype = 'unknown-openbsd'
elif ostype == 'NetBSD':
ostype = 'unknown-netbsd'
elif ostype == 'Darwin':
ostype = 'apple-darwin'
elif ostype.startswith('MINGW'):
# msys' `uname` does not print gcc configuration, but prints msys
# configuration. so we cannot believe `uname -m`:
# msys1 is always i686 and msys2 is always x86_64.
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
# MINGW64 on x86_64.
ostype = 'pc-windows-gnu'
cputype = 'i686'
if os.environ.get('MSYSTEM') == 'MINGW64':
cputype = 'x86_64'
elif ostype.startswith('MSYS'):
ostype = 'pc-windows-gnu'
elif ostype.startswith('CYGWIN_NT'):
cputype = 'i686'
if ostype.endswith('WOW64'):
cputype = 'x86_64'
ostype = 'pc-windows-gnu'
else:
raise ValueError("unknown OS type: " + ostype)
if cputype in {'i386', 'i486', 'i686', 'i786', 'x86'}:
cputype = 'i686'
elif cputype in {'xscale', 'arm'}:
cputype = 'arm'
elif cputype == 'armv7l':
cputype = 'arm'
ostype += 'eabihf'
elif cputype == 'aarch64':
cputype = 'aarch64'
elif cputype in {'powerpc', 'ppc', 'ppc64'}:
cputype = 'powerpc'
elif cputype in {'amd64', 'x86_64', 'x86-64', 'x64'}:
cputype = 'x86_64'
else:
raise ValueError("unknown cpu type: " + cputype)
return cputype + '-' + ostype
parser = argparse.ArgumentParser(description='Build rust')
parser.add_argument('--config')
parser.add_argument('-v', '--verbose', action='store_true')
args = [a for a in sys.argv if a != '-h']
args, _ = parser.parse_known_args(args)
# Configure initial bootstrap
rb = RustBuild()
rb.config_toml = ''
rb.config_mk = ''
rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
rb.build_dir = os.path.join(os.getcwd(), "build")
rb.verbose = args.verbose
try:
with open(args.config or 'config.toml') as config:
rb.config_toml = config.read()
except:
pass
# Fetch/build the bootstrap
rb.build = rb.build_triple()
rb.parse_nightly_dates()
rb.download_rust_nightly()
sys.stdout.flush()
rb.build_bootstrap()
sys.stdout.flush()
# Run the bootstrap
args = [os.path.join(rb.build_dir, "bootstrap/debug/bootstrap")]
args.extend(sys.argv[1:])
args.append('--src')
args.append(rb.rust_root)
args.append('--build')
args.append(rb.build)
env = os.environ.copy()
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
rb.run(args, env)

98
src/bootstrap/build/cc.rs Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::process::Command;
use build_helper::{cc2ar, output};
use gcc;
use build::Build;
use build::config::Target;
pub fn find(build: &mut Build) {
// For all targets we're going to need a C compiler for building some shims
// and such as well as for being a linker for Rust code.
for target in build.config.target.iter() {
let mut cfg = gcc::Config::new();
cfg.cargo_metadata(false).opt_level(0).debug(false)
.target(target).host(&build.config.build);
let config = build.config.target_config.get(target);
if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
cfg.compiler(cc);
} else {
set_compiler(&mut cfg, "gcc", target, config);
}
let compiler = cfg.get_compiler();
let ar = cc2ar(compiler.path(), target);
build.verbose(&format!("CC_{} = {:?}", target, compiler.path()));
build.verbose(&format!("AR_{} = {:?}", target, ar));
build.cc.insert(target.to_string(), (compiler, ar));
}
// For all host triples we need to find a C++ compiler as well
for host in build.config.host.iter() {
let mut cfg = gcc::Config::new();
cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true)
.target(host).host(&build.config.build);
let config = build.config.target_config.get(host);
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
cfg.compiler(cxx);
} else {
set_compiler(&mut cfg, "g++", host, config);
}
let compiler = cfg.get_compiler();
build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
build.cxx.insert(host.to_string(), compiler);
}
}
fn set_compiler(cfg: &mut gcc::Config,
gnu_compiler: &str,
target: &str,
config: Option<&Target>) {
match target {
// When compiling for android we may have the NDK configured in the
// config.toml in which case we look there. Otherwise the default
// compiler already takes into account the triple in question.
t if t.contains("android") => {
if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
let compiler = format!("{}-{}", target, gnu_compiler);
cfg.compiler(ndk.join("bin").join(compiler));
}
}
// The default gcc version from OpenBSD may be too old, try using egcc,
// which is a gcc version from ports, if this is the case.
t if t.contains("openbsd") => {
let c = cfg.get_compiler();
if !c.path().ends_with(gnu_compiler) {
return
}
let output = output(c.to_command().arg("--version"));
let i = match output.find(" 4.") {
Some(i) => i,
None => return,
};
match output[i + 3..].chars().next().unwrap() {
'0' ... '6' => {}
_ => return,
}
let alternative = format!("e{}", gnu_compiler);
if Command::new(&alternative).output().is_ok() {
cfg.compiler(alternative);
}
}
_ => {}
}
}

View File

@ -0,0 +1,82 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use build_helper::output;
use build::Build;
use build::util::mtime;
pub fn collect(build: &mut Build) {
let mut main_mk = String::new();
t!(t!(File::open(build.src.join("mk/main.mk"))).read_to_string(&mut main_mk));
let mut release_num = "";
let mut prerelease_version = "";
for line in main_mk.lines() {
if line.starts_with("CFG_RELEASE_NUM") {
release_num = line.split('=').skip(1).next().unwrap().trim();
}
if line.starts_with("CFG_PRERELEASE_VERSION") {
prerelease_version = line.split('=').skip(1).next().unwrap().trim();
}
}
// FIXME: this is duplicating makefile logic
match &build.config.channel[..] {
"stable" => {
build.release = release_num.to_string();
build.unstable_features = false;
}
"beta" => {
build.release = format!("{}-beta{}", release_num,
prerelease_version);
build.unstable_features = false;
}
"nightly" => {
build.release = format!("{}-nightly", release_num);
build.unstable_features = true;
}
_ => {
build.release = format!("{}-dev", release_num);
build.unstable_features = true;
}
}
build.version = build.release.clone();
if fs::metadata(build.src.join(".git")).is_ok() {
let ver_date = output(Command::new("git").current_dir(&build.src)
.arg("log").arg("-1")
.arg("--date=short")
.arg("--pretty=format:%cd"));
let ver_hash = output(Command::new("git").current_dir(&build.src)
.arg("rev-parse").arg("HEAD"));
let short_ver_hash = output(Command::new("git")
.current_dir(&build.src)
.arg("rev-parse")
.arg("--short=9")
.arg("HEAD"));
let ver_date = ver_date.trim().to_string();
let ver_hash = ver_hash.trim().to_string();
let short_ver_hash = short_ver_hash.trim().to_string();
build.version.push_str(&format!(" ({} {})", short_ver_hash,
ver_date));
build.ver_date = Some(ver_date.to_string());
build.ver_hash = Some(ver_hash);
build.short_ver_hash = Some(short_ver_hash);
}
build.bootstrap_key = mtime(Path::new("config.toml")).seconds()
.to_string();
}

View File

@ -0,0 +1,248 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use build_helper::output;
use build::util::{exe, staticlib, libdir, mtime, is_dylib};
use build::{Build, Compiler};
/// Build the standard library.
///
/// This will build the standard library for a particular stage of the build
/// using the `compiler` targeting the `target` architecture. The artifacts
/// created will also be linked into the sysroot directory.
pub fn std<'a>(build: &'a Build, stage: u32, target: &str,
compiler: &Compiler<'a>) {
let host = compiler.host;
println!("Building stage{} std artifacts ({} -> {})", stage,
host, target);
// Move compiler-rt into place as it'll be required by the compiler when
// building the standard library to link the dylib of libstd
let libdir = build.sysroot_libdir(stage, &host, target);
let _ = fs::remove_dir_all(&libdir);
t!(fs::create_dir_all(&libdir));
t!(fs::hard_link(&build.compiler_rt_built.borrow()[target],
libdir.join(staticlib("compiler-rt", target))));
build_startup_objects(build, target, &libdir);
let out_dir = build.cargo_out(stage, &host, true, target);
build.clear_if_dirty(&out_dir, &build.compiler_path(compiler));
let mut cargo = build.cargo(stage, compiler, true, target, "build");
cargo.arg("--features").arg(build.std_features())
.arg("--manifest-path")
.arg(build.src.join("src/rustc/std_shim/Cargo.toml"));
if let Some(target) = build.config.target_config.get(target) {
if let Some(ref jemalloc) = target.jemalloc {
cargo.env("JEMALLOC_OVERRIDE", jemalloc);
}
}
if let Some(ref p) = build.config.musl_root {
if target.contains("musl") {
cargo.env("MUSL_ROOT", p);
}
}
build.run(&mut cargo);
add_to_sysroot(&out_dir, &libdir);
}
/// Build and prepare startup objects like rsbegin.o and rsend.o
///
/// These are primarily used on Windows right now for linking executables/dlls.
/// They don't require any library support as they're just plain old object
/// files, so we just use the nightly snapshot compiler to always build them (as
/// no other compilers are guaranteed to be available).
fn build_startup_objects(build: &Build, target: &str, into: &Path) {
if !target.contains("pc-windows-gnu") {
return
}
let compiler = Compiler::new(0, &build.config.build);
let compiler = build.compiler_path(&compiler);
for file in t!(fs::read_dir(build.src.join("src/rtstartup"))) {
let file = t!(file);
build.run(Command::new(&compiler)
.arg("--emit=obj")
.arg("--out-dir").arg(into)
.arg(file.path()));
}
for obj in ["crt2.o", "dllcrt2.o"].iter() {
t!(fs::copy(compiler_file(build.cc(target), obj), into.join(obj)));
}
}
/// Build the compiler.
///
/// This will build the compiler for a particular stage of the build using
/// the `compiler` targeting the `target` architecture. The artifacts
/// created will also be linked into the sysroot directory.
pub fn rustc<'a>(build: &'a Build, stage: u32, target: &str,
compiler: &Compiler<'a>) {
let host = compiler.host;
println!("Building stage{} compiler artifacts ({} -> {})", stage,
host, target);
let out_dir = build.cargo_out(stage, &host, false, target);
let rustc = out_dir.join(exe("rustc", target));
build.clear_if_dirty(&out_dir, &libstd_shim(build, stage, &host, target));
let mut cargo = build.cargo(stage, compiler, false, target, "build");
cargo.arg("--features").arg(build.rustc_features(stage))
.arg("--manifest-path")
.arg(build.src.join("src/rustc/Cargo.toml"));
// In stage0 we may not need to build as many executables
if stage == 0 {
cargo.arg("--bin").arg("rustc");
}
// Set some configuration variables picked up by build scripts and
// the compiler alike
cargo.env("CFG_RELEASE", &build.release)
.env("CFG_RELEASE_CHANNEL", &build.config.channel)
.env("CFG_VERSION", &build.version)
.env("CFG_BOOTSTRAP_KEY", &build.bootstrap_key)
.env("RUSTC_BOOTSTRAP_KEY", &build.bootstrap_key)
.env("CFG_LIBDIR_RELATIVE", "lib");
if let Some(ref ver_date) = build.ver_date {
cargo.env("CFG_VER_DATE", ver_date);
}
if let Some(ref ver_hash) = build.ver_hash {
cargo.env("CFG_VER_HASH", ver_hash);
}
if !build.unstable_features {
cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
}
if let Some(config) = build.config.target_config.get(target) {
if let Some(ref s) = config.llvm_config {
cargo.env("LLVM_CONFIG", s);
}
}
if build.config.llvm_static_stdcpp {
cargo.env("LLVM_STATIC_STDCPP",
compiler_file(build.cxx(target), "libstdc++.a"));
}
if let Some(ref s) = build.config.rustc_default_linker {
cargo.env("CFG_DEFAULT_LINKER", s);
}
if let Some(ref s) = build.config.rustc_default_ar {
cargo.env("CFG_DEFAULT_AR", s);
}
build.run(&mut cargo);
let sysroot_libdir = build.sysroot_libdir(stage, host, target);
add_to_sysroot(&out_dir, &sysroot_libdir);
if host == target {
assemble_compiler(build, stage, target, &rustc);
}
}
/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
fn libstd_shim(build: &Build, stage: u32, host: &str, target: &str) -> PathBuf {
build.cargo_out(stage, host, true, target).join("libstd_shim.rlib")
}
fn compiler_file(compiler: &Path, file: &str) -> String {
output(Command::new(compiler)
.arg(format!("-print-file-name={}", file))).trim().to_string()
}
/// Prepare a new compiler from the artifacts in `stage`
///
/// This will link the compiler built by `host` during the stage
/// specified to the sysroot location for `host` to be the official
/// `stage + 1` compiler for that host. This means that the `rustc` binary
/// itself will be linked into place along with all supporting dynamic
/// libraries.
fn assemble_compiler(build: &Build, stage: u32, host: &str, rustc: &Path) {
// Clear out old files
let sysroot = build.sysroot(stage + 1, host);
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
// Link in all dylibs to the libdir
let sysroot_libdir = sysroot.join(libdir(host));
t!(fs::create_dir_all(&sysroot_libdir));
let src_libdir = build.sysroot_libdir(stage, host, host);
for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
let filename = f.file_name().into_string().unwrap();
if is_dylib(&filename) {
t!(fs::hard_link(&f.path(), sysroot_libdir.join(&filename)));
}
}
// Link the compiler binary itself into place
let bindir = sysroot.join("bin");
t!(fs::create_dir_all(&bindir));
let compiler = build.compiler_path(&Compiler::new(stage + 1, host));
let _ = fs::remove_file(&compiler);
t!(fs::hard_link(rustc, compiler));
// See if rustdoc exists to link it into place
let exe = exe("rustdoc", host);
let rustdoc_src = rustc.parent().unwrap().join(&exe);
let rustdoc_dst = bindir.join(exe);
if fs::metadata(&rustdoc_src).is_ok() {
let _ = fs::remove_file(&rustdoc_dst);
t!(fs::hard_link(&rustdoc_src, &rustdoc_dst));
}
}
/// Link some files into a rustc sysroot.
///
/// For a particular stage this will link all of the contents of `out_dir`
/// into the sysroot of the `host` compiler, assuming the artifacts are
/// compiled for the specified `target`.
fn add_to_sysroot(out_dir: &Path, sysroot_dst: &Path) {
// Collect the set of all files in the dependencies directory, keyed
// off the name of the library. We assume everything is of the form
// `foo-<hash>.{rlib,so,...}`, and there could be multiple different
// `<hash>` values for the same name (of old builds).
let mut map = HashMap::new();
for file in t!(fs::read_dir(out_dir.join("deps"))).map(|f| t!(f)) {
let filename = file.file_name().into_string().unwrap();
// We're only interested in linking rlibs + dylibs, other things like
// unit tests don't get linked in
if !filename.ends_with(".rlib") &&
!filename.ends_with(".lib") &&
!is_dylib(&filename) {
continue
}
let file = file.path();
let dash = filename.find("-").unwrap();
let key = (filename[..dash].to_string(),
file.extension().unwrap().to_owned());
map.entry(key).or_insert(Vec::new())
.push(file.clone());
}
// For all hash values found, pick the most recent one to move into the
// sysroot, that should be the one we just built.
for (_, paths) in map {
let (_, path) = paths.iter().map(|path| {
(mtime(&path).seconds(), path)
}).max().unwrap();
t!(fs::hard_link(&path,
sysroot_dst.join(path.file_name().unwrap())));
}
}

View File

@ -0,0 +1,255 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process;
use num_cpus;
use rustc_serialize::Decodable;
use toml::{Parser, Decoder, Value};
/// Global configuration for the entire build and/or bootstrap.
///
/// This structure is derived from a combination of both `config.toml` and
/// `config.mk`. As of the time of this writing it's unlikely that `config.toml`
/// is used all that much, so this is primarily filled out by `config.mk` which
/// is generated from `./configure`.
///
/// Note that this structure is not decoded directly into, but rather it is
/// filled out from the decoded forms of the structs below.
#[derive(Default)]
pub struct Config {
pub ccache: bool,
pub verbose: bool,
pub submodules: bool,
pub compiler_docs: bool,
pub docs: bool,
pub target_config: HashMap<String, Target>,
// llvm codegen options
pub llvm_assertions: bool,
pub llvm_optimize: bool,
pub llvm_version_check: bool,
pub llvm_static_stdcpp: bool,
// rust codegen options
pub rust_optimize: bool,
pub rust_codegen_units: u32,
pub rust_debug_assertions: bool,
pub rust_debuginfo: bool,
pub rust_rpath: bool,
pub rustc_default_linker: Option<String>,
pub rustc_default_ar: Option<String>,
pub build: String,
pub host: Vec<String>,
pub target: Vec<String>,
pub rustc: Option<String>,
pub cargo: Option<String>,
// libstd features
pub debug_jemalloc: bool,
pub use_jemalloc: bool,
// misc
pub channel: String,
pub musl_root: Option<PathBuf>,
}
/// Per-target configuration stored in the global configuration structure.
#[derive(Default)]
pub struct Target {
pub llvm_config: Option<PathBuf>,
pub jemalloc: Option<PathBuf>,
pub cc: Option<PathBuf>,
pub cxx: Option<PathBuf>,
pub ndk: Option<PathBuf>,
}
/// Structure of the `config.toml` file that configuration is read from.
///
/// This structure uses `Decodable` to automatically decode a TOML configuration
/// file into this format, and then this is traversed and written into the above
/// `Config` structure.
#[derive(RustcDecodable, Default)]
struct TomlConfig {
build: Option<Build>,
llvm: Option<Llvm>,
rust: Option<Rust>,
target: Option<HashMap<String, TomlTarget>>,
}
/// TOML representation of various global build decisions.
#[derive(RustcDecodable, Default, Clone)]
struct Build {
build: Option<String>,
host: Vec<String>,
target: Vec<String>,
cargo: Option<String>,
rustc: Option<String>,
compiler_docs: Option<bool>,
docs: Option<bool>,
}
/// TOML representation of how the LLVM build is configured.
#[derive(RustcDecodable, Default)]
struct Llvm {
ccache: Option<bool>,
assertions: Option<bool>,
optimize: Option<bool>,
version_check: Option<bool>,
static_libstdcpp: Option<bool>,
}
/// TOML representation of how the Rust build is configured.
#[derive(RustcDecodable, Default)]
struct Rust {
optimize: Option<bool>,
codegen_units: Option<u32>,
debug_assertions: Option<bool>,
debuginfo: Option<bool>,
debug_jemalloc: Option<bool>,
use_jemalloc: Option<bool>,
default_linker: Option<String>,
default_ar: Option<String>,
channel: Option<String>,
musl_root: Option<String>,
rpath: Option<bool>,
}
/// TOML representation of how each build target is configured.
#[derive(RustcDecodable, Default)]
struct TomlTarget {
llvm_config: Option<String>,
jemalloc: Option<String>,
cc: Option<String>,
cxx: Option<String>,
android_ndk: Option<String>,
}
impl Config {
pub fn parse(build: &str, file: Option<PathBuf>) -> Config {
let mut config = Config::default();
config.llvm_optimize = true;
config.use_jemalloc = true;
config.rust_optimize = true;
config.submodules = true;
config.docs = true;
config.rust_rpath = true;
config.rust_codegen_units = 1;
config.build = build.to_string();
config.channel = "dev".to_string();
let toml = file.map(|file| {
let mut f = t!(File::open(&file));
let mut toml = String::new();
t!(f.read_to_string(&mut toml));
let mut p = Parser::new(&toml);
let table = match p.parse() {
Some(table) => table,
None => {
println!("failed to parse TOML configuration:");
for err in p.errors.iter() {
let (loline, locol) = p.to_linecol(err.lo);
let (hiline, hicol) = p.to_linecol(err.hi);
println!("{}:{}-{}:{}: {}", loline, locol, hiline,
hicol, err.desc);
}
process::exit(2);
}
};
let mut d = Decoder::new(Value::Table(table));
match Decodable::decode(&mut d) {
Ok(cfg) => cfg,
Err(e) => {
println!("failed to decode TOML: {}", e);
process::exit(2);
}
}
}).unwrap_or_else(|| TomlConfig::default());
let build = toml.build.clone().unwrap_or(Build::default());
set(&mut config.build, build.build.clone());
config.host.push(config.build.clone());
for host in build.host.iter() {
if !config.host.contains(host) {
config.host.push(host.clone());
}
}
for target in config.host.iter().chain(&build.target) {
if !config.target.contains(target) {
config.target.push(target.clone());
}
}
config.rustc = build.rustc;
config.cargo = build.cargo;
set(&mut config.compiler_docs, build.compiler_docs);
set(&mut config.docs, build.docs);
if let Some(ref llvm) = toml.llvm {
set(&mut config.ccache, llvm.ccache);
set(&mut config.llvm_assertions, llvm.assertions);
set(&mut config.llvm_optimize, llvm.optimize);
set(&mut config.llvm_optimize, llvm.optimize);
set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
}
if let Some(ref rust) = toml.rust {
set(&mut config.rust_debug_assertions, rust.debug_assertions);
set(&mut config.rust_debuginfo, rust.debuginfo);
set(&mut config.rust_optimize, rust.optimize);
set(&mut config.rust_rpath, rust.rpath);
set(&mut config.debug_jemalloc, rust.debug_jemalloc);
set(&mut config.use_jemalloc, rust.use_jemalloc);
set(&mut config.channel, rust.channel.clone());
config.rustc_default_linker = rust.default_linker.clone();
config.rustc_default_ar = rust.default_ar.clone();
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
match rust.codegen_units {
Some(0) => config.rust_codegen_units = num_cpus::get() as u32,
Some(n) => config.rust_codegen_units = n,
None => {}
}
}
if let Some(ref t) = toml.target {
for (triple, cfg) in t {
let mut target = Target::default();
if let Some(ref s) = cfg.llvm_config {
target.llvm_config = Some(env::current_dir().unwrap().join(s));
}
if let Some(ref s) = cfg.jemalloc {
target.jemalloc = Some(env::current_dir().unwrap().join(s));
}
if let Some(ref s) = cfg.android_ndk {
target.ndk = Some(env::current_dir().unwrap().join(s));
}
target.cxx = cfg.cxx.clone().map(PathBuf::from);
target.cc = cfg.cc.clone().map(PathBuf::from);
config.target_config.insert(triple.clone(), target);
}
}
return config
}
}
fn set<T>(field: &mut T, val: Option<T>) {
if let Some(v) = val {
*field = v;
}
}

View File

@ -0,0 +1,99 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fs;
use std::path::PathBuf;
use std::process;
use std::slice;
use getopts::Options;
pub struct Flags {
pub verbose: bool,
pub stage: Option<u32>,
pub build: String,
pub host: Filter,
pub target: Filter,
pub step: Vec<String>,
pub config: Option<PathBuf>,
pub src: Option<PathBuf>,
pub jobs: Option<u32>,
pub args: Vec<String>,
}
pub struct Filter {
values: Vec<String>,
}
impl Flags {
pub fn parse(args: &[String]) -> Flags {
let mut opts = Options::new();
opts.optflag("v", "verbose", "use verbose output");
opts.optopt("", "config", "TOML configuration file for build", "FILE");
opts.optmulti("", "host", "host targets to build", "HOST");
opts.reqopt("", "build", "build target of the stage0 compiler", "BUILD");
opts.optmulti("", "target", "targets to build", "TARGET");
opts.optmulti("s", "step", "build step to execute", "STEP");
opts.optopt("", "stage", "stage to build", "N");
opts.optopt("", "src", "path to repo root", "DIR");
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
opts.optflag("h", "help", "print this help message");
let usage = |n| -> ! {
let brief = format!("Usage: rust.py [options]");
print!("{}", opts.usage(&brief));
process::exit(n);
};
let m = opts.parse(args).unwrap_or_else(|e| {
println!("failed to parse options: {}", e);
usage(1);
});
if m.opt_present("h") {
usage(0);
}
if m.free.len() > 0 {
println!("free arguments are not currently accepted");
usage(1);
}
let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| {
if fs::metadata("config.toml").is_ok() {
Some(PathBuf::from("config.toml"))
} else {
None
}
});
Flags {
verbose: m.opt_present("v"),
stage: m.opt_str("stage").map(|j| j.parse().unwrap()),
build: m.opt_str("build").unwrap(),
host: Filter { values: m.opt_strs("host") },
target: Filter { values: m.opt_strs("target") },
step: m.opt_strs("step"),
config: cfg_file,
src: m.opt_str("src").map(PathBuf::from),
jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
args: m.free.clone(),
}
}
}
impl Filter {
pub fn contains(&self, name: &str) -> bool {
self.values.len() == 0 || self.values.iter().any(|s| s == name)
}
pub fn iter(&self) -> slice::Iter<String> {
self.values.iter()
}
}

100
src/bootstrap/build/job.rs Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Job management on Windows for bootstrapping
//!
//! Most of the time when you're running a build system (e.g. make) you expect
//! Ctrl-C or abnormal termination to actually terminate the entire tree of
//! process in play, not just the one at the top. This currently works "by
//! default" on Unix platforms because Ctrl-C actually sends a signal to the
//! *process group* rather than the parent process, so everything will get torn
//! down. On Windows, however, this does not happen and Ctrl-C just kills the
//! parent process.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!
//!
//! Unfortunately most of the time the build system is actually called from a
//! python wrapper (which manages things like building the build system) so this
//! all doesn't quite cut it so far. To go the last mile we duplicate the job
//! object handle into our parent process (a python process probably) and then
//! close our own handle. This means that the only handle to the job object
//! resides in the parent python process, so when python dies the whole build
//! system dies (as one would probably expect!).
//!
//! Note that this module has a #[cfg(windows)] above it as none of this logic
//! is required on Unix.
extern crate kernel32;
extern crate winapi;
use std::env;
use std::io;
use std::mem;
use self::winapi::*;
use self::kernel32::*;
pub unsafe fn setup() {
// Create a new job object for us to use
let job = CreateJobObjectW(0 as *mut _, 0 as *const _);
assert!(job != 0 as *mut _, "{}", io::Error::last_os_error());
// Indicate that when all handles to the job object are gone that all
// process in the object should be killed. Note that this includes our
// entire process tree by default because we've added ourselves and and our
// children will reside in the job by default.
let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let r = SetInformationJobObject(job,
JobObjectExtendedLimitInformation,
&mut info as *mut _ as LPVOID,
mem::size_of_val(&info) as DWORD);
assert!(r != 0, "{}", io::Error::last_os_error());
// Assign our process to this job object
let r = AssignProcessToJobObject(job, GetCurrentProcess());
assert!(r != 0, "{}", io::Error::last_os_error());
// If we've got a parent process (e.g. the python script that called us)
// then move ownership of this job object up to them. That way if the python
// script is killed (e.g. via ctrl-c) then we'll all be torn down.
//
// If we don't have a parent (e.g. this was run directly) then we
// intentionally leak the job object handle. When our process exits
// (normally or abnormally) it will close the handle implicitly, causing all
// processes in the job to be cleaned up.
let pid = match env::var("BOOTSTRAP_PARENT_ID") {
Ok(s) => s,
Err(..) => return,
};
let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
assert!(parent != 0 as *mut _, "{}", io::Error::last_os_error());
let mut parent_handle = 0 as *mut _;
let r = DuplicateHandle(GetCurrentProcess(), job,
parent, &mut parent_handle,
0, FALSE, DUPLICATE_SAME_ACCESS);
// If this failed, well at least we tried! An example of DuplicateHandle
// failing in the past has been when the wrong python2 package spawed this
// build system (e.g. the `python2` package in MSYS instead of
// `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
// mode" here is that we only clean everything up when the build system
// dies, not when the python parent does, so not too bad.
if r != 0 {
CloseHandle(job);
}
}

452
src/bootstrap/build/mod.rs Normal file
View File

@ -0,0 +1,452 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::path::{PathBuf, Path};
use std::process::Command;
use build_helper::{run_silent, output};
use gcc;
use num_cpus;
use build::util::{exe, mtime, libdir, add_lib_path};
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
mod cc;
mod channel;
mod compile;
mod config;
mod flags;
mod native;
mod sanity;
mod step;
mod util;
pub use build::config::Config;
pub use build::flags::Flags;
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
pub struct Compiler<'a> {
stage: u32,
host: &'a str,
}
pub struct Build {
// User-specified configuration via config.toml
config: Config,
// User-specified configuration via CLI flags
flags: Flags,
// Derived properties from the above two configurations
cargo: PathBuf,
rustc: PathBuf,
src: PathBuf,
out: PathBuf,
release: String,
unstable_features: bool,
ver_hash: Option<String>,
short_ver_hash: Option<String>,
ver_date: Option<String>,
version: String,
bootstrap_key: String,
// Runtime state filled in later on
cc: HashMap<String, (gcc::Tool, PathBuf)>,
cxx: HashMap<String, gcc::Tool>,
compiler_rt_built: RefCell<HashMap<String, PathBuf>>,
}
impl Build {
pub fn new(flags: Flags, config: Config) -> Build {
let cwd = t!(env::current_dir());
let src = flags.src.clone().unwrap_or(cwd.clone());
let out = cwd.join("build");
let stage0_root = out.join(&config.build).join("stage0/bin");
let rustc = match config.rustc {
Some(ref s) => PathBuf::from(s),
None => stage0_root.join(exe("rustc", &config.build)),
};
let cargo = match config.cargo {
Some(ref s) => PathBuf::from(s),
None => stage0_root.join(exe("cargo", &config.build)),
};
Build {
flags: flags,
config: config,
cargo: cargo,
rustc: rustc,
src: src,
out: out,
release: String::new(),
unstable_features: false,
ver_hash: None,
short_ver_hash: None,
ver_date: None,
version: String::new(),
bootstrap_key: String::new(),
cc: HashMap::new(),
cxx: HashMap::new(),
compiler_rt_built: RefCell::new(HashMap::new()),
}
}
pub fn build(&mut self) {
use build::step::Source::*;
// see comments in job.rs for what's going on here
#[cfg(windows)]
fn setup_job() {
mod job;
unsafe { job::setup() }
}
#[cfg(not(windows))] fn setup_job() {}
setup_job();
cc::find(self);
sanity::check(self);
channel::collect(self);
self.update_submodules();
for target in step::all(self) {
match target.src {
Llvm { _dummy } => {
native::llvm(self, target.target);
}
CompilerRt { _dummy } => {
native::compiler_rt(self, target.target);
}
Libstd { stage, compiler } => {
compile::std(self, stage, target.target, &compiler);
}
Librustc { stage, compiler } => {
compile::rustc(self, stage, target.target, &compiler);
}
Rustc { stage } => {
println!("ok, rustc stage{} in {}", stage, target.target);
}
}
}
}
fn update_submodules(&self) {
if !self.config.submodules {
return
}
if fs::metadata(self.src.join(".git")).is_err() {
return
}
let out = output(Command::new("git").arg("submodule").arg("status"));
if !out.lines().any(|l| l.starts_with("+") || l.starts_with("-")) {
return
}
self.run(Command::new("git").arg("submodule").arg("sync"));
self.run(Command::new("git").arg("submodule").arg("init"));
self.run(Command::new("git").arg("submodule").arg("update"));
self.run(Command::new("git").arg("submodule").arg("update")
.arg("--recursive"));
self.run(Command::new("git").arg("submodule").arg("status")
.arg("--recursive"));
self.run(Command::new("git").arg("submodule").arg("foreach")
.arg("--recursive")
.arg("git").arg("clean").arg("-fdx"));
self.run(Command::new("git").arg("submodule").arg("foreach")
.arg("--recursive")
.arg("git").arg("checkout").arg("."));
}
/// Clear out `dir` if our build has been flagged as dirty, and also set
/// ourselves as dirty if `file` changes when `f` is executed.
fn clear_if_dirty(&self, dir: &Path, input: &Path) {
let stamp = dir.join(".stamp");
if mtime(&stamp) < mtime(input) {
self.verbose(&format!("Dirty - {}", dir.display()));
let _ = fs::remove_dir_all(dir);
}
t!(fs::create_dir_all(dir));
t!(File::create(stamp));
}
/// Prepares an invocation of `cargo` to be run.
///
/// This will create a `Command` that represents a pending execution of
/// Cargo for the specified stage, whether or not the standard library is
/// being built, and using the specified compiler targeting `target`.
// FIXME: aren't stage/compiler duplicated?
fn cargo(&self, stage: u32, compiler: &Compiler, is_std: bool,
target: &str, cmd: &str) -> Command {
let mut cargo = Command::new(&self.cargo);
let host = compiler.host;
let out_dir = self.stage_out(stage, host, is_std);
cargo.env("CARGO_TARGET_DIR", out_dir)
.arg(cmd)
.arg("--target").arg(target)
.arg("-j").arg(self.jobs().to_string());
// Customize the compiler we're running. Specify the compiler to cargo
// as our shim and then pass it some various options used to configure
// how the actual compiler itself is called.
cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
.env("RUSTC_REAL", self.compiler_path(compiler))
.env("RUSTC_STAGE", self.stage_arg(stage, compiler).to_string())
.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
.env("RUSTC_CODEGEN_UNITS",
self.config.rust_codegen_units.to_string())
.env("RUSTC_DEBUG_ASSERTIONS",
self.config.rust_debug_assertions.to_string())
.env("RUSTC_SNAPSHOT", &self.rustc)
.env("RUSTC_SYSROOT", self.sysroot(stage, host))
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir())
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "))
.env("RUSTC_RPATH", self.config.rust_rpath.to_string())
.env("RUSTDOC", self.tool(compiler, "rustdoc"));
// Specify some variuos options for build scripts used throughout the
// build.
//
// FIXME: the guard against msvc shouldn't need to be here
if !target.contains("msvc") {
cargo.env(format!("CC_{}", target), self.cc(target))
.env(format!("AR_{}", target), self.ar(target))
.env(format!("CFLAGS_{}", target), self.cflags(target));
}
// Environment variables *required* needed throughout the build
//
// FIXME: should update code to not require this env vars
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
if self.config.verbose || self.flags.verbose {
cargo.arg("-v");
}
if self.config.rust_optimize {
cargo.arg("--release");
}
self.add_rustc_lib_path(compiler, &mut cargo);
return cargo
}
/// Get a path to the compiler specified.
fn compiler_path(&self, compiler: &Compiler) -> PathBuf {
if compiler.is_snapshot(self) {
self.rustc.clone()
} else {
self.sysroot(compiler.stage, compiler.host).join("bin")
.join(exe("rustc", compiler.host))
}
}
/// Get the specified tool next to the specified compiler
fn tool(&self, compiler: &Compiler, tool: &str) -> PathBuf {
if compiler.is_snapshot(self) {
assert!(tool == "rustdoc", "no tools other than rustdoc in stage0");
let mut rustdoc = self.rustc.clone();
rustdoc.pop();
rustdoc.push(exe("rustdoc", &self.config.build));
return rustdoc
}
let (stage, host) = (compiler.stage, compiler.host);
self.cargo_out(stage - 1, host, false, host).join(exe(tool, host))
}
/// Get a `Command` which is ready to run `tool` in `stage` built for
/// `host`.
#[allow(dead_code)] // this will be used soon
fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command {
let mut cmd = Command::new(self.tool(&compiler, tool));
let host = compiler.host;
let stage = compiler.stage;
let paths = vec![
self.cargo_out(stage - 1, host, true, host).join("deps"),
self.cargo_out(stage - 1, host, false, host).join("deps"),
];
add_lib_path(paths, &mut cmd);
return cmd
}
fn stage_arg(&self, stage: u32, compiler: &Compiler) -> u32 {
if stage == 0 && compiler.host != self.config.build {1} else {stage}
}
/// Get the space-separated set of activated features for the standard
/// library.
fn std_features(&self) -> String {
let mut features = String::new();
if self.config.debug_jemalloc {
features.push_str(" debug-jemalloc");
}
if self.config.use_jemalloc {
features.push_str(" jemalloc");
}
return features
}
/// Get the space-separated set of activated features for the compiler.
fn rustc_features(&self, stage: u32) -> String {
let mut features = String::new();
if self.config.use_jemalloc {
features.push_str(" jemalloc");
}
if stage > 0 {
features.push_str(" rustdoc");
features.push_str(" rustbook");
}
return features
}
/// Component directory that Cargo will produce output into (e.g.
/// release/debug)
fn cargo_dir(&self) -> &'static str {
if self.config.rust_optimize {"release"} else {"debug"}
}
fn sysroot(&self, stage: u32, host: &str) -> PathBuf {
if stage == 0 {
self.stage_out(stage, host, false)
} else {
self.out.join(host).join(format!("stage{}", stage))
}
}
fn sysroot_libdir(&self, stage: u32, host: &str, target: &str) -> PathBuf {
self.sysroot(stage, host).join("lib").join("rustlib")
.join(target).join("lib")
}
/// Returns the root directory for all output generated in a particular
/// stage when running with a particular host compiler.
///
/// The `is_std` flag indicates whether the root directory is for the
/// bootstrap of the standard library or for the compiler.
fn stage_out(&self, stage: u32, host: &str, is_std: bool) -> PathBuf {
self.out.join(host)
.join(format!("stage{}{}", stage, if is_std {"-std"} else {"-rustc"}))
}
/// Returns the root output directory for all Cargo output in a given stage,
/// running a particular comipler, wehther or not we're building the
/// standard library, and targeting the specified architecture.
fn cargo_out(&self, stage: u32, host: &str, is_std: bool,
target: &str) -> PathBuf {
self.stage_out(stage, host, is_std).join(target).join(self.cargo_dir())
}
/// Root output directory for LLVM compiled for `target`
fn llvm_out(&self, target: &str) -> PathBuf {
self.out.join(target).join("llvm")
}
/// Root output directory for compiler-rt compiled for `target`
fn compiler_rt_out(&self, target: &str) -> PathBuf {
self.out.join(target).join("compiler-rt")
}
fn add_rustc_lib_path(&self, compiler: &Compiler, cmd: &mut Command) {
// Windows doesn't need dylib path munging because the dlls for the
// compiler live next to the compiler and the system will find them
// automatically.
if cfg!(windows) { return }
add_lib_path(vec![self.rustc_libdir(compiler)], cmd);
}
fn rustc_libdir(&self, compiler: &Compiler) -> PathBuf {
if compiler.is_snapshot(self) {
self.rustc_snapshot_libdir()
} else {
self.sysroot(compiler.stage, compiler.host)
.join(libdir(compiler.host))
}
}
fn rustc_snapshot_libdir(&self) -> PathBuf {
self.rustc.parent().unwrap().parent().unwrap()
.join(libdir(&self.config.build))
}
fn run(&self, cmd: &mut Command) {
self.verbose(&format!("running: {:?}", cmd));
run_silent(cmd)
}
fn verbose(&self, msg: &str) {
if self.flags.verbose || self.config.verbose {
println!("{}", msg);
}
}
fn jobs(&self) -> u32 {
self.flags.jobs.unwrap_or(num_cpus::get() as u32)
}
fn cc(&self, target: &str) -> &Path {
self.cc[target].0.path()
}
fn cflags(&self, target: &str) -> String {
self.cc[target].0.args().iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(" ")
}
fn ar(&self, target: &str) -> &Path {
&self.cc[target].1
}
fn cxx(&self, target: &str) -> &Path {
self.cxx[target].path()
}
fn rustc_flags(&self, target: &str) -> Vec<String> {
let mut base = match target {
"arm-unknown-linux-gnueabihf" => {
vec!["-Ctarget-feature=+v6,+vfp2".to_string()]
}
"mips-unknown-linux-gnu" => {
vec!["-Ctarget-cpu=mips32r2".to_string(),
"-Ctarget-feature=+mips32r2".to_string(),
"-Csoft-float".to_string()]
}
"mipsel-unknown-linux-gnu" => {
vec!["-Ctarget-cpu=mips32".to_string(),
"-Ctarget-feature=+mips32".to_string()]
}
_ => Vec::new(),
};
if target != self.config.build && !target.contains("msvc") {
base.push(format!("-Clinker={}", self.cc(target).display()));
}
return base
}
}
impl<'a> Compiler<'a> {
fn new(stage: u32, host: &'a str) -> Compiler<'a> {
Compiler { stage: stage, host: host }
}
fn is_snapshot(&self, build: &Build) -> bool {
self.stage == 0 && self.host == build.config.build
}
}

View File

@ -0,0 +1,157 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::path::Path;
use std::process::Command;
use std::fs;
use build_helper::output;
use cmake;
use build::Build;
use build::util::{exe, staticlib};
pub fn llvm(build: &Build, target: &str) {
// If we're using a custom LLVM bail out here, but we can only use a
// custom LLVM for the build triple.
if let Some(config) = build.config.target_config.get(target) {
if let Some(ref s) = config.llvm_config {
return check_llvm_version(build, s);
}
}
// If the cleaning trigger is newer than our built artifacts (or if the
// artifacts are missing) then we keep going, otherwise we bail out.
let dst = build.llvm_out(target);
let stamp = build.src.join("src/rustllvm/llvm-auto-clean-trigger");
let llvm_config = dst.join("bin").join(exe("llvm-config", target));
build.clear_if_dirty(&dst, &stamp);
if fs::metadata(llvm_config).is_ok() {
return
}
let _ = fs::remove_dir_all(&dst.join("build"));
t!(fs::create_dir_all(&dst.join("build")));
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
// http://llvm.org/docs/CMake.html
let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
cfg.target(target)
.host(&build.config.build)
.out_dir(&dst)
.profile(if build.config.llvm_optimize {"Release"} else {"Debug"})
.define("LLVM_ENABLE_ASSERTIONS", assertions)
.define("LLVM_TARGETS_TO_BUILD", "X86;ARM;AArch64;Mips;PowerPC")
.define("LLVM_INCLUDE_EXAMPLES", "OFF")
.define("LLVM_INCLUDE_TESTS", "OFF")
.define("LLVM_INCLUDE_DOCS", "OFF")
.define("LLVM_ENABLE_ZLIB", "OFF")
.define("WITH_POLLY", "OFF")
.define("LLVM_ENABLE_TERMINFO", "OFF")
.define("LLVM_ENABLE_LIBEDIT", "OFF")
.define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string());
if target.starts_with("i686") {
cfg.define("LLVM_BUILD_32_BITS", "ON");
}
// http://llvm.org/docs/HowToCrossCompileLLVM.html
if target != build.config.build {
// FIXME: if the llvm root for the build triple is overridden then we
// should use llvm-tblgen from there, also should verify that it
// actually exists most of the time in normal installs of LLVM.
let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
cfg.define("CMAKE_CROSSCOMPILING", "True")
.define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
.define("LLVM_TABLEGEN", &host)
.define("LLVM_DEFAULT_TARGET_TRIPLE", target);
}
// MSVC handles compiler business itself
if !target.contains("msvc") {
if build.config.ccache {
cfg.define("CMAKE_C_COMPILER", "ccache")
.define("CMAKE_C_COMPILER_ARG1", build.cc(target))
.define("CMAKE_CXX_COMPILER", "ccache")
.define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
} else {
cfg.define("CMAKE_C_COMPILER", build.cc(target))
.define("CMAKE_CXX_COMPILER", build.cxx(target));
}
cfg.build_arg("-j").build_arg(build.jobs().to_string());
}
// FIXME: we don't actually need to build all LLVM tools and all LLVM
// libraries here, e.g. we just want a few components and a few
// tools. Figure out how to filter them down and only build the right
// tools and libs on all platforms.
cfg.build();
}
fn check_llvm_version(build: &Build, llvm_config: &Path) {
if !build.config.llvm_version_check {
return
}
let mut cmd = Command::new(llvm_config);
let version = output(cmd.arg("--version"));
if version.starts_with("3.5") || version.starts_with("3.6") ||
version.starts_with("3.7") {
return
}
panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
}
pub fn compiler_rt(build: &Build, target: &str) {
let dst = build.compiler_rt_out(target);
let arch = target.split('-').next().unwrap();
let mode = if build.config.rust_optimize {"Release"} else {"Debug"};
let (dir, build_target, libname) = if target.contains("linux") {
let os = if target.contains("android") {"-android"} else {""};
let target = format!("clang_rt.builtins-{}{}", arch, os);
("linux".to_string(), target.clone(), target)
} else if target.contains("darwin") {
let target = format!("clang_rt.builtins_{}_osx", arch);
("builtins".to_string(), target.clone(), target)
} else if target.contains("windows-gnu") {
let target = format!("clang_rt.builtins-{}", arch);
("windows".to_string(), target.clone(), target)
} else if target.contains("windows-msvc") {
(format!("windows/{}", mode),
"lib/builtins/builtins".to_string(),
format!("clang_rt.builtins-{}", arch.replace("i686", "i386")))
} else {
panic!("can't get os from target: {}", target)
};
let output = dst.join("build/lib").join(dir)
.join(staticlib(&libname, target));
build.compiler_rt_built.borrow_mut().insert(target.to_string(),
output.clone());
if fs::metadata(&output).is_ok() {
return
}
let _ = fs::remove_dir_all(&dst);
t!(fs::create_dir_all(&dst));
let build_llvm_config = build.llvm_out(&build.config.build)
.join("bin")
.join(exe("llvm-config", &build.config.build));
let mut cfg = cmake::Config::new(build.src.join("src/compiler-rt"));
cfg.target(target)
.host(&build.config.build)
.out_dir(&dst)
.profile(mode)
.define("LLVM_CONFIG_PATH", build_llvm_config)
.define("COMPILER_RT_DEFAULT_TARGET_TRIPLE", target)
.define("COMPILER_RT_BUILD_SANITIZERS", "OFF")
.define("COMPILER_RT_BUILD_EMUTLS", "OFF")
.define("CMAKE_C_COMPILER", build.cc(target))
.build_target(&build_target);
cfg.build();
}

View File

@ -0,0 +1,122 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashSet;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::process::Command;
use build_helper::output;
use build::Build;
pub fn check(build: &mut Build) {
let mut checked = HashSet::new();
let path = env::var_os("PATH").unwrap_or(OsString::new());
let mut need_cmd = |cmd: &OsStr| {
if !checked.insert(cmd.to_owned()) {
return
}
for path in env::split_paths(&path).map(|p| p.join(cmd)) {
if fs::metadata(&path).is_ok() ||
fs::metadata(path.with_extension("exe")).is_ok() {
return
}
}
panic!("\n\ncouldn't find required command: {:?}\n\n", cmd);
};
// If we've got a git directory we're gona need git to update
// submodules and learn about various other aspects.
if fs::metadata(build.src.join(".git")).is_ok() {
need_cmd("git".as_ref());
}
// We need cmake, but only if we're actually building LLVM
for host in build.config.host.iter() {
if let Some(config) = build.config.target_config.get(host) {
if config.llvm_config.is_some() {
continue
}
}
need_cmd("cmake".as_ref());
break
}
need_cmd("python".as_ref());
// We're gonna build some custom C code here and there, host triples
// also build some C++ shims for LLVM so we need a C++ compiler.
for target in build.config.target.iter() {
need_cmd(build.cc(target).as_ref());
need_cmd(build.ar(target).as_ref());
}
for host in build.config.host.iter() {
need_cmd(build.cxx(host).as_ref());
}
for target in build.config.target.iter() {
// Either can't build or don't want to run jemalloc on these targets
if target.contains("rumprun") ||
target.contains("bitrig") ||
target.contains("openbsd") ||
target.contains("msvc") {
build.config.use_jemalloc = false;
}
// Can't compile for iOS unless we're on OSX
if target.contains("apple-ios") &&
!build.config.build.contains("apple-darwin") {
panic!("the iOS target is only supported on OSX");
}
// Make sure musl-root is valid if specified
if target.contains("musl") {
match build.config.musl_root {
Some(ref root) => {
if fs::metadata(root.join("lib/libc.a")).is_err() {
panic!("couldn't find libc.a in musl dir: {}",
root.join("lib").display());
}
if fs::metadata(root.join("lib/libunwind.a")).is_err() {
panic!("couldn't find libunwind.a in musl dir: {}",
root.join("lib").display());
}
}
None => {
panic!("when targeting MUSL the build.musl-root option \
must be specified in config.toml")
}
}
}
if target.contains("msvc") {
// There are three builds of cmake on windows: MSVC, MinGW, and
// Cygwin. The Cygwin build does not have generators for Visual
// Studio, so detect that here and error.
let out = output(Command::new("cmake").arg("--help"));
if !out.contains("Visual Studio") {
panic!("
cmake does not support Visual Studio generators.
This is likely due to it being an msys/cygwin build of cmake,
rather than the required windows version, built using MinGW
or Visual Studio.
If you are building under msys2 try installing the mingw-w64-x86_64-cmake
package instead of cmake:
$ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
");
}
}
}
}

177
src/bootstrap/build/step.rs Normal file
View File

@ -0,0 +1,177 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashSet;
use build::{Build, Compiler};
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct Step<'a> {
pub src: Source<'a>,
pub target: &'a str,
}
macro_rules! targets {
($m:ident) => {
$m! {
(rustc, Rustc { stage: u32 }),
(libstd, Libstd { stage: u32, compiler: Compiler<'a> }),
(librustc, Librustc { stage: u32, compiler: Compiler<'a> }),
(llvm, Llvm { _dummy: () }),
(compiler_rt, CompilerRt { _dummy: () }),
}
}
}
macro_rules! item { ($a:item) => ($a) }
macro_rules! define_source {
($(($short:ident, $name:ident { $($args:tt)* }),)*) => {
item! {
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub enum Source<'a> {
$($name { $($args)* }),*
}
}
}
}
targets!(define_source);
pub fn all(build: &Build) -> Vec<Step> {
let mut ret = Vec::new();
let mut all = HashSet::new();
for target in top_level(build) {
fill(build, &target, &mut ret, &mut all);
}
return ret;
fn fill<'a>(build: &'a Build,
target: &Step<'a>,
ret: &mut Vec<Step<'a>>,
set: &mut HashSet<Step<'a>>) {
if set.insert(target.clone()) {
for dep in target.deps(build) {
fill(build, &dep, ret, set);
}
ret.push(target.clone());
}
}
}
fn top_level(build: &Build) -> Vec<Step> {
let mut targets = Vec::new();
let stage = build.flags.stage.unwrap_or(2);
let host = Step {
src: Source::Llvm { _dummy: () },
target: build.flags.host.iter().next()
.unwrap_or(&build.config.build),
};
let target = Step {
src: Source::Llvm { _dummy: () },
target: build.flags.target.iter().next().map(|x| &x[..])
.unwrap_or(host.target)
};
add_steps(build, stage, &host, &target, &mut targets);
if targets.len() == 0 {
let t = Step {
src: Source::Llvm { _dummy: () },
target: &build.config.build,
};
for host in build.config.host.iter() {
if !build.flags.host.contains(host) {
continue
}
let host = t.target(host);
targets.push(host.librustc(stage, host.compiler(stage)));
for target in build.config.target.iter() {
if !build.flags.target.contains(target) {
continue
}
targets.push(host.target(target)
.libstd(stage, host.compiler(stage)));
}
}
}
return targets
}
fn add_steps<'a>(build: &'a Build,
stage: u32,
host: &Step<'a>,
target: &Step<'a>,
targets: &mut Vec<Step<'a>>) {
for step in build.flags.step.iter() {
let compiler = host.compiler(stage);
match &step[..] {
"libstd" => targets.push(target.libstd(stage, compiler)),
"librustc" => targets.push(target.libstd(stage, compiler)),
"rustc" => targets.push(host.rustc(stage)),
"llvm" => targets.push(target.llvm(())),
"compiler-rt" => targets.push(target.compiler_rt(())),
_ => panic!("unknown build target: `{}`", step),
}
}
}
macro_rules! constructors {
($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => {$(
fn $short(&self, $($arg: $t),*) -> Step<'a> {
Step {
src: Source::$name { $($arg: $arg),* },
target: self.target,
}
}
)*}
}
impl<'a> Step<'a> {
fn compiler(&self, stage: u32) -> Compiler<'a> {
Compiler::new(stage, self.target)
}
fn target(&self, target: &'a str) -> Step<'a> {
Step { target: target, src: self.src.clone() }
}
targets!(constructors);
pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
match self.src {
Source::Rustc { stage: 0 } => {
if self.target == build.config.build {
Vec::new()
} else {
let compiler = Compiler::new(0, &build.config.build);
vec![self.librustc(0, compiler)]
}
}
Source::Rustc { stage } => {
vec![self.librustc(stage - 1, self.compiler(stage - 1))]
}
Source::Librustc { stage, compiler } => {
vec![self.libstd(stage, compiler), self.llvm(())]
}
Source::Libstd { stage: _, compiler } => {
vec![self.compiler_rt(()),
self.rustc(compiler.stage).target(compiler.host)]
}
Source::CompilerRt { _dummy } => {
vec![self.llvm(()).target(&build.config.build)]
}
Source::Llvm { _dummy } => Vec::new(),
}
}
}

View File

@ -0,0 +1,97 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::path::{Path, PathBuf};
use std::fs;
use std::process::Command;
use bootstrap::{dylib_path, dylib_path_var};
use filetime::FileTime;
pub fn staticlib(name: &str, target: &str) -> String {
if target.contains("windows-msvc") {
format!("{}.lib", name)
} else {
format!("lib{}.a", name)
}
}
pub fn mtime(path: &Path) -> FileTime {
fs::metadata(path).map(|f| {
FileTime::from_last_modification_time(&f)
}).unwrap_or(FileTime::zero())
}
#[allow(dead_code)] // this will be used soon
pub fn cp_r(src: &Path, dst: &Path) {
for f in t!(fs::read_dir(src)) {
let f = t!(f);
let path = f.path();
let name = path.file_name().unwrap();
let dst = dst.join(name);
if t!(f.file_type()).is_dir() {
let _ = fs::remove_dir_all(&dst);
t!(fs::create_dir(&dst));
cp_r(&path, &dst);
} else {
let _ = fs::remove_file(&dst);
t!(fs::hard_link(&path, dst));
}
}
}
/// Given an executable called `name`, return the filename for the
/// executable for a particular target.
pub fn exe(name: &str, target: &str) -> String {
if target.contains("windows") {
format!("{}.exe", name)
} else {
name.to_string()
}
}
pub fn is_dylib(name: &str) -> bool {
name.ends_with(".dylib") || name.ends_with(".so") || name.ends_with(".dll")
}
pub fn libdir(target: &str) -> &'static str {
if target.contains("windows") {"bin"} else {"lib"}
}
pub fn add_lib_path(path: Vec<PathBuf>, cmd: &mut Command) {
let mut list = dylib_path();
for path in path {
list.insert(0, path);
}
cmd.env(dylib_path_var(), t!(env::join_paths(list)));
}
#[allow(dead_code)] // this will be used soon
pub fn up_to_date(src: &Path, dst: &Path) -> bool {
let threshold = mtime(dst);
let meta = t!(fs::metadata(src));
if meta.is_dir() {
dir_up_to_date(src, &threshold)
} else {
FileTime::from_last_modification_time(&meta) <= threshold
}
}
fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
let meta = t!(e.metadata());
if meta.is_dir() {
dir_up_to_date(&e.path(), threshold)
} else {
FileTime::from_last_modification_time(&meta) < *threshold
}
})
}

28
src/bootstrap/lib.rs Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
pub fn dylib_path_var() -> &'static str {
if cfg!(target_os = "windows") {
"PATH"
} else if cfg!(target_os = "macos") {
"DYLD_LIBRARY_PATH"
} else {
"LD_LIBRARY_PATH"
}
}
pub fn dylib_path() -> Vec<PathBuf> {
env::split_paths(&env::var_os(dylib_path_var()).unwrap_or(OsString::new()))
.collect()
}

38
src/bootstrap/main.rs Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(warnings)]
extern crate bootstrap;
extern crate build_helper;
extern crate cmake;
extern crate filetime;
extern crate gcc;
extern crate getopts;
extern crate libc;
extern crate num_cpus;
extern crate rustc_serialize;
extern crate toml;
use std::env;
use build::{Flags, Config, Build};
mod build;
fn main() {
let args = env::args().skip(1).collect::<Vec<_>>();
let flags = Flags::parse(&args);
let mut config = Config::parse(&flags.build, flags.config.clone());
if std::fs::metadata("config.mk").is_ok() {
config.update_with_config_mk();
}
Build::new(flags, config).build();
}

91
src/bootstrap/rustc.rs Normal file
View File

@ -0,0 +1,91 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate bootstrap;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let args = env::args_os().skip(1).collect::<Vec<_>>();
// Detect whether or not we're a build script depending on whether --target
// is passed (a bit janky...)
let is_build_script = args.iter()
.position(|i| i.to_str() == Some("--target"))
.is_none();
// Build scripts always use the snapshot compiler which is guaranteed to be
// able to produce an executable, whereas intermediate compilers may not
// have the standard library built yet and may not be able to produce an
// executable. Otherwise we just use the standard compiler we're
// bootstrapping with.
let rustc = if is_build_script {
env::var_os("RUSTC_SNAPSHOT").unwrap()
} else {
env::var_os("RUSTC_REAL").unwrap()
};
let mut cmd = Command::new(rustc);
cmd.args(&args)
.arg("--cfg").arg(format!("stage{}", env::var("RUSTC_STAGE").unwrap()));
if is_build_script {
// Build scripts are always built with the snapshot compiler, so we need
// to be sure to set up the right path information for the OS dynamic
// linker to find the libraries in question.
if let Some(p) = env::var_os("RUSTC_SNAPSHOT_LIBDIR") {
let mut path = bootstrap::dylib_path();
path.insert(0, PathBuf::from(p));
cmd.env(bootstrap::dylib_path_var(), env::join_paths(path).unwrap());
}
} else {
cmd.arg("--sysroot").arg(env::var_os("RUSTC_SYSROOT").unwrap());
// When we build Rust dylibs they're all intended for intermediate
// usage, so make sure we pass the -Cprefer-dynamic flag instead of
// linking all deps statically into the dylib.
cmd.arg("-Cprefer-dynamic");
if let Some(s) = env::var_os("MUSL_ROOT") {
let mut root = OsString::from("native=");
root.push(&s);
root.push("/lib");
cmd.arg("-L").arg(&root);
}
}
// Set various options from config.toml to configure how we're building
// code.
if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) {
cmd.arg("-g");
}
if env::var("RUSTC_RPATH") == Ok("true".to_string()) {
cmd.arg("-Crpath");
}
let debug_assertions = match env::var("RUSTC_DEBUG_ASSERTIONS") {
Ok(s) => if s == "true" {"y"} else {"n"},
Err(..) => "n",
};
cmd.arg("-C").arg(format!("debug-assertions={}", debug_assertions));
if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
cmd.arg("-C").arg(format!("codegen-units={}", s));
}
if let Ok(s) = env::var("RUSTC_FLAGS") {
cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
}
// Actually run the compiler!
std::process::exit(match cmd.status() {
Ok(s) => s.code().unwrap_or(1),
Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
})
}

View File

@ -0,0 +1,8 @@
[package]
name = "build_helper"
version = "0.1.0"
authors = ["The Rust Project Developers"]
[lib]
name = "build_helper"
path = "lib.rs"

68
src/build_helper/lib.rs Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(warnings)]
use std::process::{Command, Stdio};
use std::path::{Path, PathBuf};
pub fn run(cmd: &mut Command) {
println!("running: {:?}", cmd);
run_silent(cmd);
}
pub fn run_silent(cmd: &mut Command) {
let status = match cmd.status() {
Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {}", e)),
};
if !status.success() {
fail(&format!("command did not execute successfully: {:?}\n\
expected success, got: {}", cmd, status));
}
}
pub fn gnu_target(target: &str) -> String {
match target {
"i686-pc-windows-msvc" => "i686-pc-win32".to_string(),
"x86_64-pc-windows-msvc" => "x86_64-pc-win32".to_string(),
"i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(),
"x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(),
s => s.to_string(),
}
}
pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
if target.contains("musl") || target.contains("msvc") {
PathBuf::from("ar")
} else {
let file = cc.file_name().unwrap().to_str().unwrap();
cc.parent().unwrap().join(file.replace("gcc", "ar")
.replace("cc", "ar")
.replace("clang", "ar"))
}
}
pub fn output(cmd: &mut Command) -> String {
let output = match cmd.stderr(Stdio::inherit()).output() {
Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {}", e)),
};
if !output.status.success() {
panic!("command did not execute successfully: {:?}\n\
expected success, got: {}", cmd, output.status);
}
String::from_utf8(output.stdout).unwrap()
}
fn fail(s: &str) -> ! {
println!("\n\n{}\n\n", s);
std::process::exit(1);
}

View File

@ -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:

2
src/nightlies.txt Normal file
View File

@ -0,0 +1,2 @@
rustc: 2015-12-19
cargo: 2016-01-21

356
src/rustc/Cargo.lock generated Normal file
View File

@ -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"

36
src/rustc/Cargo.toml Normal file
View File

@ -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"]

View File

@ -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" }

View File

@ -0,0 +1,15 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// See comments in Cargo.toml for why this exists
fn main() {
println!("cargo:rustc-cfg=stdbuild");
}

14
src/rustc/rustbook.rs Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate rustbook;
fn main() { rustbook::main() }

15
src/rustc/rustc.rs Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private)]
extern crate rustc_driver;
fn main() { rustc_driver::main() }

15
src/rustc/rustdoc.rs Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustdoc)]
extern crate rustdoc;
fn main() { rustdoc::main() }

124
src/rustc/std_shim/Cargo.lock generated Normal file
View File

@ -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"

View File

@ -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"]

11
src/rustc/std_shim/lib.rs Normal file
View File

@ -0,0 +1,11 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// See comments in Cargo.toml for why this exists