Auto merge of #43059 - Mark-Simulacrum:rustbuild-2.0, r=alexcrichton
Rework Rustbuild to an eagerly compiling approach This introduces a new dependency on `serde`; I don't believe that's a problem since bootstrap is compiled with nightly/beta always so proc macros are available. Compile times are slightly longer -- about 2-3x (30 seconds vs. 10 seconds). I don't think this is too big a problem, especially since recompiling bootstrap is somewhat rare. I think we can remove the dependency on Serde if necessary, though, so let me know. r? @alexcrichton
This commit is contained in:
commit
8d22af87d8
21
src/Cargo.lock
generated
21
src/Cargo.lock
generated
@ -134,10 +134,13 @@ dependencies = [
|
||||
"filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -153,8 +156,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "build-manifest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1901,14 +1905,6 @@ dependencies = [
|
||||
name = "tidy"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.2.1"
|
||||
@ -2206,7 +2202,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
|
||||
"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796"
|
||||
"checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4"
|
||||
"checksum toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0601da6c97135c8d330c7a13a013ca6cd4143221b01de2f8d4edc50a9e551c7"
|
||||
"checksum typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5934776c3ac1bea4a9d56620d6bf2d483b20d394e49581db40f187e1118ff667"
|
||||
|
@ -33,8 +33,11 @@ build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.23"
|
||||
filetime = "0.1"
|
||||
num_cpus = "1.0"
|
||||
toml = "0.1"
|
||||
getopts = "0.2"
|
||||
rustc-serialize = "0.3"
|
||||
gcc = "0.3.50"
|
||||
libc = "0.2"
|
||||
serde = "1.0.8"
|
||||
serde_derive = "1.0.8"
|
||||
serde_json = "1.0.2"
|
||||
toml = "0.4"
|
||||
lazy_static = "0.2"
|
||||
|
618
src/bootstrap/builder.rs
Normal file
618
src/bootstrap/builder.rs
Normal file
@ -0,0 +1,618 @@
|
||||
// Copyright 2017 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::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::fs;
|
||||
use std::ops::Deref;
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use compile;
|
||||
use install;
|
||||
use dist;
|
||||
use util::{exe, libdir, add_lib_path};
|
||||
use {Build, Mode};
|
||||
use cache::{INTERNER, Interned, Cache};
|
||||
use check;
|
||||
use flags::Subcommand;
|
||||
use doc;
|
||||
use tool;
|
||||
|
||||
pub use Compiler;
|
||||
|
||||
pub struct Builder<'a> {
|
||||
pub build: &'a Build,
|
||||
pub top_stage: u32,
|
||||
pub kind: Kind,
|
||||
cache: Cache,
|
||||
stack: RefCell<Vec<Box<Any>>>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Builder<'a> {
|
||||
type Target = Build;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.build
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
|
||||
/// `PathBuf` when directories are created or to return a `Compiler` once
|
||||
/// it's been assembled.
|
||||
type Output: Clone;
|
||||
|
||||
const DEFAULT: bool = false;
|
||||
|
||||
/// Run this rule for all hosts without cross compiling.
|
||||
const ONLY_HOSTS: bool = false;
|
||||
|
||||
/// Run this rule for all targets, but only with the native host.
|
||||
const ONLY_BUILD_TARGETS: bool = false;
|
||||
|
||||
/// Only run this step with the build triple as host and target.
|
||||
const ONLY_BUILD: bool = false;
|
||||
|
||||
/// Primary function to execute this rule. Can call `builder.ensure(...)`
|
||||
/// with other steps to run those.
|
||||
fn run(self, builder: &Builder) -> Self::Output;
|
||||
|
||||
/// When bootstrap is passed a set of paths, this controls whether this rule
|
||||
/// will execute. However, it does not get called in a "default" context
|
||||
/// when we are not passed any paths; in that case, make_run is called
|
||||
/// directly.
|
||||
fn should_run(run: ShouldRun) -> ShouldRun;
|
||||
|
||||
/// Build up a "root" rule, either as a default rule or from a path passed
|
||||
/// to us.
|
||||
///
|
||||
/// When path is `None`, we are executing in a context where no paths were
|
||||
/// passed. When `./x.py build` is run, for example, this rule could get
|
||||
/// called if it is in the correct list below with a path of `None`.
|
||||
fn make_run(_run: RunConfig) {
|
||||
// It is reasonable to not have an implementation of make_run for rules
|
||||
// who do not want to get called from the root context. This means that
|
||||
// they are likely dependencies (e.g., sysroot creation) or similar, and
|
||||
// as such calling them from ./x.py isn't logical.
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunConfig<'a> {
|
||||
pub builder: &'a Builder<'a>,
|
||||
pub host: Interned<String>,
|
||||
pub target: Interned<String>,
|
||||
pub path: Option<&'a Path>,
|
||||
}
|
||||
|
||||
struct StepDescription {
|
||||
default: bool,
|
||||
only_hosts: bool,
|
||||
only_build_targets: bool,
|
||||
only_build: bool,
|
||||
should_run: fn(ShouldRun) -> ShouldRun,
|
||||
make_run: fn(RunConfig),
|
||||
}
|
||||
|
||||
impl StepDescription {
|
||||
fn from<S: Step>() -> StepDescription {
|
||||
StepDescription {
|
||||
default: S::DEFAULT,
|
||||
only_hosts: S::ONLY_HOSTS,
|
||||
only_build_targets: S::ONLY_BUILD_TARGETS,
|
||||
only_build: S::ONLY_BUILD,
|
||||
should_run: S::should_run,
|
||||
make_run: S::make_run,
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_run(&self, builder: &Builder, path: Option<&Path>) {
|
||||
let build = builder.build;
|
||||
let hosts = if self.only_build_targets || self.only_build {
|
||||
&build.config.host[..1]
|
||||
} else {
|
||||
&build.hosts
|
||||
};
|
||||
|
||||
// Determine the actual targets participating in this rule.
|
||||
// NOTE: We should keep the full projection from build triple to
|
||||
// the hosts for the dist steps, now that the hosts array above is
|
||||
// truncated to avoid duplication of work in that case. Therefore
|
||||
// the original non-shadowed hosts array is used below.
|
||||
let targets = if self.only_hosts {
|
||||
// If --target was specified but --host wasn't specified,
|
||||
// don't run any host-only tests. Also, respect any `--host`
|
||||
// overrides as done for `hosts`.
|
||||
if build.flags.host.len() > 0 {
|
||||
&build.flags.host[..]
|
||||
} else if build.flags.target.len() > 0 {
|
||||
&[]
|
||||
} else if self.only_build {
|
||||
&build.config.host[..1]
|
||||
} else {
|
||||
&build.config.host[..]
|
||||
}
|
||||
} else {
|
||||
&build.targets
|
||||
};
|
||||
|
||||
for host in hosts {
|
||||
for target in targets {
|
||||
let run = RunConfig {
|
||||
builder,
|
||||
path,
|
||||
host: *host,
|
||||
target: *target,
|
||||
};
|
||||
(self.make_run)(run);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(v: &[StepDescription], builder: &Builder, paths: &[PathBuf]) {
|
||||
let should_runs = v.iter().map(|desc| {
|
||||
(desc.should_run)(ShouldRun::new(builder))
|
||||
}).collect::<Vec<_>>();
|
||||
if paths.is_empty() {
|
||||
for (desc, should_run) in v.iter().zip(should_runs) {
|
||||
if desc.default && should_run.is_really_default {
|
||||
desc.maybe_run(builder, None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for path in paths {
|
||||
let mut attempted_run = false;
|
||||
for (desc, should_run) in v.iter().zip(&should_runs) {
|
||||
if should_run.run(path) {
|
||||
attempted_run = true;
|
||||
desc.maybe_run(builder, Some(path));
|
||||
}
|
||||
}
|
||||
|
||||
if !attempted_run {
|
||||
eprintln!("Warning: no rules matched {}.", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ShouldRun<'a> {
|
||||
pub builder: &'a Builder<'a>,
|
||||
// use a BTreeSet to maintain sort order
|
||||
paths: BTreeSet<PathBuf>,
|
||||
|
||||
// If this is a default rule, this is an additional constraint placed on
|
||||
// it's run. Generally something like compiler docs being enabled.
|
||||
is_really_default: bool,
|
||||
}
|
||||
|
||||
impl<'a> ShouldRun<'a> {
|
||||
fn new(builder: &'a Builder) -> ShouldRun<'a> {
|
||||
ShouldRun {
|
||||
builder: builder,
|
||||
paths: BTreeSet::new(),
|
||||
is_really_default: true, // by default no additional conditions
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_condition(mut self, cond: bool) -> Self {
|
||||
self.is_really_default = cond;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn krate(mut self, name: &str) -> Self {
|
||||
for (_, krate_path) in self.builder.crates(name) {
|
||||
self.paths.insert(PathBuf::from(krate_path));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn path(mut self, path: &str) -> Self {
|
||||
self.paths.insert(PathBuf::from(path));
|
||||
self
|
||||
}
|
||||
|
||||
// allows being more explicit about why should_run in Step returns the value passed to it
|
||||
pub fn never(self) -> ShouldRun<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
fn run(&self, path: &Path) -> bool {
|
||||
self.paths.iter().any(|p| path.ends_with(p))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Kind {
|
||||
Build,
|
||||
Test,
|
||||
Bench,
|
||||
Dist,
|
||||
Doc,
|
||||
Install,
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
|
||||
macro_rules! describe {
|
||||
($($rule:ty),+ $(,)*) => {{
|
||||
vec![$(StepDescription::from::<$rule>()),+]
|
||||
}};
|
||||
}
|
||||
match kind {
|
||||
Kind::Build => describe!(compile::Std, compile::Test, compile::Rustc,
|
||||
compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex,
|
||||
tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest,
|
||||
tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
|
||||
tool::RustInstaller, tool::Cargo, tool::Rls),
|
||||
Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest,
|
||||
check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Linkcheck,
|
||||
check::Cargotest, check::Cargo, check::Rls, check::Docs, check::ErrorIndex,
|
||||
check::Distcheck),
|
||||
Kind::Bench => describe!(check::Crate, check::CrateLibrustc),
|
||||
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
|
||||
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
|
||||
doc::Reference),
|
||||
Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
|
||||
dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo,
|
||||
dist::Rls, dist::Extended, dist::HashSign),
|
||||
Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
|
||||
install::Analysis, install::Src, install::Rustc),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_help(build: &Build, subcommand: &str) -> Option<String> {
|
||||
let kind = match subcommand {
|
||||
"build" => Kind::Build,
|
||||
"doc" => Kind::Doc,
|
||||
"test" => Kind::Test,
|
||||
"bench" => Kind::Bench,
|
||||
"dist" => Kind::Dist,
|
||||
"install" => Kind::Install,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let builder = Builder {
|
||||
build: build,
|
||||
top_stage: build.flags.stage.unwrap_or(2),
|
||||
kind: kind,
|
||||
cache: Cache::new(),
|
||||
stack: RefCell::new(Vec::new()),
|
||||
};
|
||||
|
||||
let builder = &builder;
|
||||
let mut should_run = ShouldRun::new(builder);
|
||||
for desc in Builder::get_step_descriptions(builder.kind) {
|
||||
should_run = (desc.should_run)(should_run);
|
||||
}
|
||||
let mut help = String::from("Available paths:\n");
|
||||
for path in should_run.paths {
|
||||
help.push_str(format!(" ./x.py {} {}\n", subcommand, path.display()).as_str());
|
||||
}
|
||||
Some(help)
|
||||
}
|
||||
|
||||
pub fn run(build: &Build) {
|
||||
let (kind, paths) = match build.flags.cmd {
|
||||
Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
|
||||
Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]),
|
||||
Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]),
|
||||
Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
|
||||
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
|
||||
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
|
||||
Subcommand::Clean => panic!(),
|
||||
};
|
||||
|
||||
let builder = Builder {
|
||||
build: build,
|
||||
top_stage: build.flags.stage.unwrap_or(2),
|
||||
kind: kind,
|
||||
cache: Cache::new(),
|
||||
stack: RefCell::new(Vec::new()),
|
||||
};
|
||||
|
||||
StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths);
|
||||
}
|
||||
|
||||
pub fn default_doc(&self, paths: Option<&[PathBuf]>) {
|
||||
let paths = paths.unwrap_or(&[]);
|
||||
StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths);
|
||||
}
|
||||
|
||||
/// Obtain a compiler at a given stage and for a given host. Explictly does
|
||||
/// not take `Compiler` since all `Compiler` instances are meant to be
|
||||
/// obtained through this function, since it ensures that they are valid
|
||||
/// (i.e., built and assembled).
|
||||
pub fn compiler(&self, stage: u32, host: Interned<String>) -> Compiler {
|
||||
self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
|
||||
}
|
||||
|
||||
pub fn sysroot(&self, compiler: Compiler) -> Interned<PathBuf> {
|
||||
self.ensure(compile::Sysroot { compiler })
|
||||
}
|
||||
|
||||
/// Returns the libdir where the standard library and other artifacts are
|
||||
/// found for a compiler's sysroot.
|
||||
pub fn sysroot_libdir(
|
||||
&self, compiler: Compiler, target: Interned<String>
|
||||
) -> Interned<PathBuf> {
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
struct Libdir {
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
}
|
||||
impl Step for Libdir {
|
||||
type Output = Interned<PathBuf>;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Interned<PathBuf> {
|
||||
let compiler = self.compiler;
|
||||
let lib = if compiler.stage >= 2 && builder.build.config.libdir_relative.is_some() {
|
||||
builder.build.config.libdir_relative.clone().unwrap()
|
||||
} else {
|
||||
PathBuf::from("lib")
|
||||
};
|
||||
let sysroot = builder.sysroot(self.compiler).join(lib)
|
||||
.join("rustlib").join(self.target).join("lib");
|
||||
let _ = fs::remove_dir_all(&sysroot);
|
||||
t!(fs::create_dir_all(&sysroot));
|
||||
INTERNER.intern_path(sysroot)
|
||||
}
|
||||
}
|
||||
self.ensure(Libdir { compiler, target })
|
||||
}
|
||||
|
||||
/// Returns the compiler's libdir where it stores the dynamic libraries that
|
||||
/// it itself links against.
|
||||
///
|
||||
/// For example this returns `<sysroot>/lib` on Unix and `<sysroot>/bin` on
|
||||
/// Windows.
|
||||
pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
|
||||
if compiler.is_snapshot(self) {
|
||||
self.build.rustc_snapshot_libdir()
|
||||
} else {
|
||||
self.sysroot(compiler).join(libdir(&compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the compiler's directory of dynamic libraries to `cmd`'s dynamic
|
||||
/// library lookup path.
|
||||
pub 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);
|
||||
}
|
||||
|
||||
/// Get a path to the compiler specified.
|
||||
pub fn rustc(&self, compiler: Compiler) -> PathBuf {
|
||||
if compiler.is_snapshot(self) {
|
||||
self.initial_rustc.clone()
|
||||
} else {
|
||||
self.sysroot(compiler).join("bin").join(exe("rustc", &compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `rustdoc` executable next to the specified compiler
|
||||
pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
|
||||
let mut rustdoc = self.rustc(compiler);
|
||||
rustdoc.pop();
|
||||
rustdoc.push(exe("rustdoc", &compiler.host));
|
||||
rustdoc
|
||||
}
|
||||
|
||||
/// Prepares an invocation of `cargo` to be run.
|
||||
///
|
||||
/// This will create a `Command` that represents a pending execution of
|
||||
/// Cargo. This cargo will be configured to use `compiler` as the actual
|
||||
/// rustc compiler, its output will be scoped by `mode`'s output directory,
|
||||
/// it will pass the `--target` flag for the specified `target`, and will be
|
||||
/// executing the Cargo command `cmd`.
|
||||
pub fn cargo(&self,
|
||||
compiler: Compiler,
|
||||
mode: Mode,
|
||||
target: Interned<String>,
|
||||
cmd: &str) -> Command {
|
||||
let mut cargo = Command::new(&self.initial_cargo);
|
||||
let out_dir = self.stage_out(compiler, mode);
|
||||
cargo.env("CARGO_TARGET_DIR", out_dir)
|
||||
.arg(cmd)
|
||||
.arg("-j").arg(self.jobs().to_string())
|
||||
.arg("--target").arg(target);
|
||||
|
||||
// FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
|
||||
// Force cargo to output binaries with disambiguating hashes in the name
|
||||
cargo.env("__CARGO_DEFAULT_LIB_METADATA", &self.config.channel);
|
||||
|
||||
let stage;
|
||||
if compiler.stage == 0 && self.local_rebuild {
|
||||
// Assume the local-rebuild rustc already has stage1 features.
|
||||
stage = 1;
|
||||
} else {
|
||||
stage = compiler.stage;
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// These variables are primarily all read by
|
||||
// src/bootstrap/bin/{rustc.rs,rustdoc.rs}
|
||||
cargo.env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
|
||||
.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
|
||||
.env("RUSTC_REAL", self.rustc(compiler))
|
||||
.env("RUSTC_STAGE", stage.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_SYSROOT", self.sysroot(compiler))
|
||||
.env("RUSTC_LIBDIR", self.rustc_libdir(compiler))
|
||||
.env("RUSTC_RPATH", self.config.rust_rpath.to_string())
|
||||
.env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc"))
|
||||
.env("RUSTDOC_REAL", self.rustdoc(compiler))
|
||||
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
|
||||
|
||||
if mode != Mode::Tool {
|
||||
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
||||
// get compiled with debuginfo.
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
|
||||
.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string())
|
||||
.env("RUSTC_FORCE_UNSTABLE", "1");
|
||||
|
||||
// Currently the compiler depends on crates from crates.io, and
|
||||
// then other crates can depend on the compiler (e.g. proc-macro
|
||||
// crates). Let's say, for example that rustc itself depends on the
|
||||
// bitflags crate. If an external crate then depends on the
|
||||
// bitflags crate as well, we need to make sure they don't
|
||||
// conflict, even if they pick the same verison of bitflags. We'll
|
||||
// want to make sure that e.g. a plugin and rustc each get their
|
||||
// own copy of bitflags.
|
||||
|
||||
// Cargo ensures that this works in general through the -C metadata
|
||||
// flag. This flag will frob the symbols in the binary to make sure
|
||||
// they're different, even though the source code is the exact
|
||||
// same. To solve this problem for the compiler we extend Cargo's
|
||||
// already-passed -C metadata flag with our own. Our rustc.rs
|
||||
// wrapper around the actual rustc will detect -C metadata being
|
||||
// passed and frob it with this extra string we're passing in.
|
||||
cargo.env("RUSTC_METADATA_SUFFIX", "rustc");
|
||||
}
|
||||
|
||||
// Enable usage of unstable features
|
||||
cargo.env("RUSTC_BOOTSTRAP", "1");
|
||||
self.add_rust_test_threads(&mut cargo);
|
||||
|
||||
// Almost all of the crates that we compile as part of the bootstrap may
|
||||
// have a build script, including the standard library. To compile a
|
||||
// build script, however, it itself needs a standard library! This
|
||||
// introduces a bit of a pickle when we're compiling the standard
|
||||
// library itself.
|
||||
//
|
||||
// To work around this we actually end up using the snapshot compiler
|
||||
// (stage0) for compiling build scripts of the standard library itself.
|
||||
// The stage0 compiler is guaranteed to have a libstd available for use.
|
||||
//
|
||||
// For other crates, however, we know that we've already got a standard
|
||||
// library up and running, so we can use the normal compiler to compile
|
||||
// build scripts in that situation.
|
||||
if mode == Mode::Libstd {
|
||||
cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
|
||||
} else {
|
||||
cargo.env("RUSTC_SNAPSHOT", self.rustc(compiler))
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
|
||||
}
|
||||
|
||||
// Ignore incremental modes except for stage0, since we're
|
||||
// not guaranteeing correctness across builds if the compiler
|
||||
// is changing under your feet.`
|
||||
if self.flags.incremental && compiler.stage == 0 {
|
||||
let incr_dir = self.incremental_dir(compiler);
|
||||
cargo.env("RUSTC_INCREMENTAL", incr_dir);
|
||||
}
|
||||
|
||||
if let Some(ref on_fail) = self.flags.on_fail {
|
||||
cargo.env("RUSTC_ON_FAIL", on_fail);
|
||||
}
|
||||
|
||||
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
|
||||
|
||||
// Specify some various 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).unwrap()) // only msvc is None
|
||||
.env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
|
||||
|
||||
if let Ok(cxx) = self.cxx(target) {
|
||||
cargo.env(format!("CXX_{}", target), cxx);
|
||||
}
|
||||
}
|
||||
|
||||
if mode == Mode::Libstd && self.config.extended && compiler.is_final_stage(self) {
|
||||
cargo.env("RUSTC_SAVE_ANALYSIS", "api".to_string());
|
||||
}
|
||||
|
||||
// Environment variables *required* throughout the build
|
||||
//
|
||||
// FIXME: should update code to not require this env var
|
||||
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
|
||||
|
||||
if self.is_verbose() {
|
||||
cargo.arg("-v");
|
||||
}
|
||||
// FIXME: cargo bench does not accept `--release`
|
||||
if self.config.rust_optimize && cmd != "bench" {
|
||||
cargo.arg("--release");
|
||||
}
|
||||
if self.config.locked_deps {
|
||||
cargo.arg("--locked");
|
||||
}
|
||||
if self.config.vendor || self.is_sudo {
|
||||
cargo.arg("--frozen");
|
||||
}
|
||||
|
||||
self.ci_env.force_coloring_in_ci(&mut cargo);
|
||||
|
||||
cargo
|
||||
}
|
||||
|
||||
/// Ensure that a given step is built, returning it's output. This will
|
||||
/// cache the step, so it is safe (and good!) to call this as often as
|
||||
/// needed to ensure that all dependencies are built.
|
||||
pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
|
||||
{
|
||||
let mut stack = self.stack.borrow_mut();
|
||||
for stack_step in stack.iter() {
|
||||
// should skip
|
||||
if stack_step.downcast_ref::<S>().map_or(true, |stack_step| *stack_step != step) {
|
||||
continue;
|
||||
}
|
||||
let mut out = String::new();
|
||||
out += &format!("\n\nCycle in build detected when adding {:?}\n", step);
|
||||
for el in stack.iter().rev() {
|
||||
out += &format!("\t{:?}\n", el);
|
||||
}
|
||||
panic!(out);
|
||||
}
|
||||
if let Some(out) = self.cache.get(&step) {
|
||||
self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step));
|
||||
|
||||
return out;
|
||||
}
|
||||
self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step));
|
||||
stack.push(Box::new(step.clone()));
|
||||
}
|
||||
let out = step.clone().run(self);
|
||||
{
|
||||
let mut stack = self.stack.borrow_mut();
|
||||
let cur_step = stack.pop().expect("step stack empty");
|
||||
assert_eq!(cur_step.downcast_ref(), Some(&step));
|
||||
}
|
||||
self.build.verbose(&format!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step));
|
||||
self.cache.put(step, out.clone());
|
||||
out
|
||||
}
|
||||
}
|
267
src/bootstrap/cache.rs
Normal file
267
src/bootstrap/cache.rs
Normal file
@ -0,0 +1,267 @@
|
||||
// Copyright 2017 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::any::{Any, TypeId};
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::AsRef;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use builder::Step;
|
||||
|
||||
pub struct Interned<T>(usize, PhantomData<*const T>);
|
||||
|
||||
impl Default for Interned<String> {
|
||||
fn default() -> Self {
|
||||
INTERNER.intern_string(String::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Interned<PathBuf> {
|
||||
fn default() -> Self {
|
||||
INTERNER.intern_path(PathBuf::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Interned<T> {}
|
||||
impl<T> Clone for Interned<T> {
|
||||
fn clone(&self) -> Interned<T> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Interned<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl<T> Eq for Interned<T> {}
|
||||
|
||||
impl PartialEq<str> for Interned<String> {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
*self == other
|
||||
}
|
||||
}
|
||||
impl<'a> PartialEq<&'a str> for Interned<String> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
**self == **other
|
||||
}
|
||||
}
|
||||
impl<'a, T> PartialEq<&'a Interned<T>> for Interned<T> {
|
||||
fn eq(&self, other: &&Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl<'a, T> PartialEq<Interned<T>> for &'a Interned<T> {
|
||||
fn eq(&self, other: &Interned<T>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for Interned<T> {}
|
||||
unsafe impl<T> Sync for Interned<T> {}
|
||||
|
||||
impl fmt::Display for Interned<String> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &str = &*self;
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Interned<String> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &str = &*self;
|
||||
f.write_fmt(format_args!("{:?}", s))
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for Interned<PathBuf> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &Path = &*self;
|
||||
f.write_fmt(format_args!("{:?}", s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Interned<String> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
l.get(*self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Interned<PathBuf> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let l = INTERNER.paths.lock().unwrap();
|
||||
l.get(*self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Interned<String> {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &'static str {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
unsafe { mem::transmute::<&str, &'static str>(l.get(*self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Interned<PathBuf> {
|
||||
type Target = Path;
|
||||
fn deref(&self) -> &'static Path {
|
||||
let l = INTERNER.paths.lock().unwrap();
|
||||
unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Interned<PathBuf> {
|
||||
fn as_ref(&self) -> &'static Path {
|
||||
let l = INTERNER.paths.lock().unwrap();
|
||||
unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Interned<String> {
|
||||
fn as_ref(&self) -> &'static Path {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self).as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for Interned<PathBuf> {
|
||||
fn as_ref(&self) -> &'static OsStr {
|
||||
let l = INTERNER.paths.lock().unwrap();
|
||||
unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for Interned<String> {
|
||||
fn as_ref(&self) -> &'static OsStr {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct TyIntern<T> {
|
||||
items: Vec<T>,
|
||||
set: HashMap<T, Interned<T>>,
|
||||
}
|
||||
|
||||
impl<T: Hash + Clone + Eq> TyIntern<T> {
|
||||
fn new() -> TyIntern<T> {
|
||||
TyIntern {
|
||||
items: Vec::new(),
|
||||
set: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn intern_borrow<B>(&mut self, item: &B) -> Interned<T>
|
||||
where
|
||||
B: Eq + Hash + ToOwned<Owned=T> + ?Sized,
|
||||
T: Borrow<B>,
|
||||
{
|
||||
if let Some(i) = self.set.get(&item) {
|
||||
return *i;
|
||||
}
|
||||
let item = item.to_owned();
|
||||
let interned = Interned(self.items.len(), PhantomData::<*const T>);
|
||||
self.set.insert(item.clone(), interned);
|
||||
self.items.push(item);
|
||||
interned
|
||||
}
|
||||
|
||||
fn intern(&mut self, item: T) -> Interned<T> {
|
||||
if let Some(i) = self.set.get(&item) {
|
||||
return *i;
|
||||
}
|
||||
let interned = Interned(self.items.len(), PhantomData::<*const T>);
|
||||
self.set.insert(item.clone(), interned);
|
||||
self.items.push(item);
|
||||
interned
|
||||
}
|
||||
|
||||
fn get(&self, i: Interned<T>) -> &T {
|
||||
&self.items[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interner {
|
||||
strs: Mutex<TyIntern<String>>,
|
||||
paths: Mutex<TyIntern<PathBuf>>,
|
||||
}
|
||||
|
||||
impl Interner {
|
||||
fn new() -> Interner {
|
||||
Interner {
|
||||
strs: Mutex::new(TyIntern::new()),
|
||||
paths: Mutex::new(TyIntern::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intern_str(&self, s: &str) -> Interned<String> {
|
||||
self.strs.lock().unwrap().intern_borrow(s)
|
||||
}
|
||||
pub fn intern_string(&self, s: String) -> Interned<String> {
|
||||
self.strs.lock().unwrap().intern(s)
|
||||
}
|
||||
|
||||
pub fn intern_path(&self, s: PathBuf) -> Interned<PathBuf> {
|
||||
self.paths.lock().unwrap().intern(s)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref INTERNER: Interner = Interner::new();
|
||||
}
|
||||
|
||||
/// This is essentially a HashMap which allows storing any type in its input and
|
||||
/// any type in its output. It is a write-once cache; values are never evicted,
|
||||
/// which means that references to the value can safely be returned from the
|
||||
/// get() method.
|
||||
#[derive(Debug)]
|
||||
pub struct Cache(
|
||||
RefCell<HashMap<
|
||||
TypeId,
|
||||
Box<Any>, // actually a HashMap<Step, Interned<Step::Output>>
|
||||
>>
|
||||
);
|
||||
|
||||
impl Cache {
|
||||
pub fn new() -> Cache {
|
||||
Cache(RefCell::new(HashMap::new()))
|
||||
}
|
||||
|
||||
pub fn put<S: Step>(&self, step: S, value: S::Output) {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let stepcache = cache.entry(type_id)
|
||||
.or_insert_with(|| Box::new(HashMap::<S, S::Output>::new()))
|
||||
.downcast_mut::<HashMap<S, S::Output>>()
|
||||
.expect("invalid type mapped");
|
||||
assert!(!stepcache.contains_key(&step), "processing {:?} a second time", step);
|
||||
stepcache.insert(step, value);
|
||||
}
|
||||
|
||||
pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let stepcache = cache.entry(type_id)
|
||||
.or_insert_with(|| Box::new(HashMap::<S, S::Output>::new()))
|
||||
.downcast_mut::<HashMap<S, S::Output>>()
|
||||
.expect("invalid type mapped");
|
||||
stepcache.get(step).cloned()
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ use gcc;
|
||||
|
||||
use Build;
|
||||
use config::Target;
|
||||
use cache::Interned;
|
||||
|
||||
pub fn find(build: &mut Build) {
|
||||
// For all targets we're going to need a C compiler for building some shims
|
||||
@ -50,11 +51,11 @@ pub fn find(build: &mut Build) {
|
||||
cfg.cargo_metadata(false).opt_level(0).debug(false)
|
||||
.target(target).host(&build.build);
|
||||
|
||||
let config = build.config.target_config.get(target);
|
||||
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, build);
|
||||
set_compiler(&mut cfg, "gcc", *target, config, build);
|
||||
}
|
||||
|
||||
let compiler = cfg.get_compiler();
|
||||
@ -63,7 +64,7 @@ pub fn find(build: &mut Build) {
|
||||
if let Some(ref ar) = ar {
|
||||
build.verbose(&format!("AR_{} = {:?}", target, ar));
|
||||
}
|
||||
build.cc.insert(target.to_string(), (compiler, ar));
|
||||
build.cc.insert(*target, (compiler, ar));
|
||||
}
|
||||
|
||||
// For all host triples we need to find a C++ compiler as well
|
||||
@ -78,20 +79,20 @@ pub fn find(build: &mut Build) {
|
||||
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
||||
cfg.compiler(cxx);
|
||||
} else {
|
||||
set_compiler(&mut cfg, "g++", host, config, build);
|
||||
set_compiler(&mut cfg, "g++", *host, config, build);
|
||||
}
|
||||
let compiler = cfg.get_compiler();
|
||||
build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
|
||||
build.cxx.insert(host.to_string(), compiler);
|
||||
build.cxx.insert(*host, compiler);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_compiler(cfg: &mut gcc::Config,
|
||||
gnu_compiler: &str,
|
||||
target: &str,
|
||||
target: Interned<String>,
|
||||
config: Option<&Target>,
|
||||
build: &Build) {
|
||||
match 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.
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -21,9 +21,9 @@ use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use num_cpus;
|
||||
use rustc_serialize::Decodable;
|
||||
use toml::{Parser, Decoder, Value};
|
||||
use toml;
|
||||
use util::{exe, push_exe_path};
|
||||
use cache::{INTERNER, Interned};
|
||||
|
||||
/// Global configuration for the entire build and/or bootstrap.
|
||||
///
|
||||
@ -46,7 +46,7 @@ pub struct Config {
|
||||
pub docs: bool,
|
||||
pub locked_deps: bool,
|
||||
pub vendor: bool,
|
||||
pub target_config: HashMap<String, Target>,
|
||||
pub target_config: HashMap<Interned<String>, Target>,
|
||||
pub full_bootstrap: bool,
|
||||
pub extended: bool,
|
||||
pub sanitizers: bool,
|
||||
@ -78,9 +78,9 @@ pub struct Config {
|
||||
pub rust_debuginfo_tests: bool,
|
||||
pub rust_dist_src: bool,
|
||||
|
||||
pub build: String,
|
||||
pub host: Vec<String>,
|
||||
pub target: Vec<String>,
|
||||
pub build: Interned<String>,
|
||||
pub host: Vec<Interned<String>>,
|
||||
pub target: Vec<Interned<String>>,
|
||||
pub local_rebuild: bool,
|
||||
|
||||
// dist misc
|
||||
@ -138,7 +138,8 @@ pub struct Target {
|
||||
/// 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)]
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct TomlConfig {
|
||||
build: Option<Build>,
|
||||
install: Option<Install>,
|
||||
@ -149,10 +150,13 @@ struct TomlConfig {
|
||||
}
|
||||
|
||||
/// TOML representation of various global build decisions.
|
||||
#[derive(RustcDecodable, Default, Clone)]
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct Build {
|
||||
build: Option<String>,
|
||||
#[serde(default)]
|
||||
host: Vec<String>,
|
||||
#[serde(default)]
|
||||
target: Vec<String>,
|
||||
cargo: Option<String>,
|
||||
rustc: Option<String>,
|
||||
@ -174,7 +178,8 @@ struct Build {
|
||||
}
|
||||
|
||||
/// TOML representation of various global install decisions.
|
||||
#[derive(RustcDecodable, Default, Clone)]
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct Install {
|
||||
prefix: Option<String>,
|
||||
sysconfdir: Option<String>,
|
||||
@ -185,7 +190,8 @@ struct Install {
|
||||
}
|
||||
|
||||
/// TOML representation of how the LLVM build is configured.
|
||||
#[derive(RustcDecodable, Default)]
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct Llvm {
|
||||
ccache: Option<StringOrBool>,
|
||||
ninja: Option<bool>,
|
||||
@ -200,7 +206,8 @@ struct Llvm {
|
||||
clean_rebuild: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, Default, Clone)]
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct Dist {
|
||||
sign_folder: Option<String>,
|
||||
gpg_password_file: Option<String>,
|
||||
@ -208,7 +215,8 @@ struct Dist {
|
||||
src_tarball: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum StringOrBool {
|
||||
String(String),
|
||||
Bool(bool),
|
||||
@ -221,7 +229,8 @@ impl Default for StringOrBool {
|
||||
}
|
||||
|
||||
/// TOML representation of how the Rust build is configured.
|
||||
#[derive(RustcDecodable, Default)]
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct Rust {
|
||||
optimize: Option<bool>,
|
||||
codegen_units: Option<u32>,
|
||||
@ -243,7 +252,8 @@ struct Rust {
|
||||
}
|
||||
|
||||
/// TOML representation of how each build target is configured.
|
||||
#[derive(RustcDecodable, Default)]
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
struct TomlTarget {
|
||||
llvm_config: Option<String>,
|
||||
jemalloc: Option<String>,
|
||||
@ -266,50 +276,39 @@ impl Config {
|
||||
config.docs = true;
|
||||
config.rust_rpath = true;
|
||||
config.rust_codegen_units = 1;
|
||||
config.build = build.to_string();
|
||||
config.build = INTERNER.intern_str(build);
|
||||
config.channel = "dev".to_string();
|
||||
config.codegen_tests = true;
|
||||
config.rust_dist_src = true;
|
||||
|
||||
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 '{}':", file.to_str().unwrap());
|
||||
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);
|
||||
let mut contents = String::new();
|
||||
t!(f.read_to_string(&mut contents));
|
||||
match toml::from_str(&contents) {
|
||||
Ok(table) => table,
|
||||
Err(err) => {
|
||||
println!("failed to parse TOML configuration '{}': {}",
|
||||
file.display(), err);
|
||||
process::exit(2);
|
||||
}
|
||||
}
|
||||
}).unwrap_or_else(|| TomlConfig::default());
|
||||
|
||||
let build = toml.build.clone().unwrap_or(Build::default());
|
||||
set(&mut config.build, build.build.clone());
|
||||
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
|
||||
config.host.push(config.build.clone());
|
||||
for host in build.host.iter() {
|
||||
if !config.host.contains(host) {
|
||||
config.host.push(host.clone());
|
||||
let host = INTERNER.intern_str(host);
|
||||
if !config.host.contains(&host) {
|
||||
config.host.push(host);
|
||||
}
|
||||
}
|
||||
for target in config.host.iter().chain(&build.target) {
|
||||
if !config.target.contains(target) {
|
||||
config.target.push(target.clone());
|
||||
for target in config.host.iter().cloned()
|
||||
.chain(build.target.iter().map(|s| INTERNER.intern_str(s)))
|
||||
{
|
||||
if !config.target.contains(&target) {
|
||||
config.target.push(target);
|
||||
}
|
||||
}
|
||||
config.nodejs = build.nodejs.map(PathBuf::from);
|
||||
@ -402,7 +401,7 @@ impl Config {
|
||||
target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
|
||||
target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);
|
||||
|
||||
config.target_config.insert(triple.clone(), target);
|
||||
config.target_config.insert(INTERNER.intern_string(triple.clone()), target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,13 +503,13 @@ impl Config {
|
||||
}
|
||||
|
||||
match key {
|
||||
"CFG_BUILD" if value.len() > 0 => self.build = value.to_string(),
|
||||
"CFG_BUILD" if value.len() > 0 => self.build = INTERNER.intern_str(value),
|
||||
"CFG_HOST" if value.len() > 0 => {
|
||||
self.host.extend(value.split(" ").map(|s| s.to_string()));
|
||||
self.host.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
|
||||
|
||||
}
|
||||
"CFG_TARGET" if value.len() > 0 => {
|
||||
self.target.extend(value.split(" ").map(|s| s.to_string()));
|
||||
self.target.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
|
||||
}
|
||||
"CFG_EXPERIMENTAL_TARGETS" if value.len() > 0 => {
|
||||
self.llvm_experimental_targets = Some(value.to_string());
|
||||
@ -519,33 +518,28 @@ impl Config {
|
||||
self.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_X86_64" if value.len() > 0 => {
|
||||
let target = "x86_64-unknown-linux-musl".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("x86_64-unknown-linux-musl");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_I686" if value.len() > 0 => {
|
||||
let target = "i686-unknown-linux-musl".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("i686-unknown-linux-musl");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARM" if value.len() > 0 => {
|
||||
let target = "arm-unknown-linux-musleabi".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-musleabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARMHF" if value.len() > 0 => {
|
||||
let target = "arm-unknown-linux-musleabihf".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-musleabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARMV7" if value.len() > 0 => {
|
||||
let target = "armv7-unknown-linux-musleabihf".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("armv7-unknown-linux-musleabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_DEFAULT_AR" if value.len() > 0 => {
|
||||
@ -593,33 +587,28 @@ impl Config {
|
||||
target.jemalloc = Some(parse_configure_path(value).join("libjemalloc_pic.a"));
|
||||
}
|
||||
"CFG_ARM_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
|
||||
let target = "arm-linux-androideabi".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("arm-linux-androideabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_ARMV7_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
|
||||
let target = "armv7-linux-androideabi".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("armv7-linux-androideabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_I686_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = "i686-linux-android".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("i686-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_AARCH64_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = "aarch64-linux-android".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("aarch64-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_X86_64_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = "x86_64-linux-android".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("x86_64-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_LOCAL_RUST_ROOT" if value.len() > 0 => {
|
||||
@ -643,9 +632,8 @@ impl Config {
|
||||
.collect();
|
||||
}
|
||||
"CFG_QEMU_ARMHF_ROOTFS" if value.len() > 0 => {
|
||||
let target = "arm-unknown-linux-gnueabihf".to_string();
|
||||
let target = self.target_config.entry(target)
|
||||
.or_insert(Target::default());
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-gnueabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.qemu_rootfs = Some(parse_configure_path(value));
|
||||
}
|
||||
_ => {}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,86 +20,233 @@
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::Command;
|
||||
|
||||
use {Build, Compiler, Mode};
|
||||
use util::{cp_r, symlink_dir};
|
||||
use Mode;
|
||||
use build_helper::up_to_date;
|
||||
|
||||
/// Invoke `rustbook` for `target` for the doc book `name`.
|
||||
///
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
pub fn rustbook(build: &Build, target: &str, name: &str) {
|
||||
let src = build.src.join("src/doc");
|
||||
rustbook_src(build, target, name, &src);
|
||||
}
|
||||
use util::{cp_r, symlink_dir};
|
||||
use builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
use tool::Tool;
|
||||
use compile;
|
||||
use cache::{INTERNER, Interned};
|
||||
|
||||
/// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
|
||||
///
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
pub fn rustbook_src(build: &Build, target: &str, name: &str, src: &Path) {
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
macro_rules! book {
|
||||
($($name:ident, $path:expr, $book_name:expr;)+) => {
|
||||
$(
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct $name {
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
let out = out.join(name);
|
||||
let compiler = Compiler::new(0, &build.build);
|
||||
let src = src.join(name);
|
||||
let index = out.join("index.html");
|
||||
let rustbook = build.tool(&compiler, "rustbook");
|
||||
if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
|
||||
return
|
||||
}
|
||||
println!("Rustbook ({}) - {}", target, name);
|
||||
let _ = fs::remove_dir_all(&out);
|
||||
build.run(build.tool_cmd(&compiler, "rustbook")
|
||||
.arg("build")
|
||||
.arg(&src)
|
||||
.arg("-d")
|
||||
.arg(out));
|
||||
}
|
||||
impl Step for $name {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
/// Build the book and associated stuff.
|
||||
///
|
||||
/// We need to build:
|
||||
///
|
||||
/// * Book (first edition)
|
||||
/// * Book (second edition)
|
||||
/// * Index page
|
||||
/// * Redirect pages
|
||||
pub fn book(build: &Build, target: &str, name: &str) {
|
||||
// build book first edition
|
||||
rustbook(build, target, &format!("{}/first-edition", name));
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path($path).default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
// build book second edition
|
||||
rustbook(build, target, &format!("{}/second-edition", name));
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
// build the index page
|
||||
let index = format!("{}/index.md", name);
|
||||
println!("Documenting book index ({})", target);
|
||||
invoke_rustdoc(build, target, &index);
|
||||
|
||||
// build the redirect pages
|
||||
println!("Documenting book redirect pages ({})", target);
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let path = path.to_str().unwrap();
|
||||
|
||||
invoke_rustdoc(build, target, path);
|
||||
fn run(self, builder: &Builder) {
|
||||
builder.ensure(Rustbook {
|
||||
target: self.target,
|
||||
name: INTERNER.intern_str($book_name),
|
||||
})
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
|
||||
book!(
|
||||
Nomicon, "src/doc/book", "nomicon";
|
||||
Reference, "src/doc/reference", "reference";
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
struct Rustbook {
|
||||
target: Interned<String>,
|
||||
name: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustbook {
|
||||
type Output = ();
|
||||
|
||||
// rustbook is never directly called, and only serves as a shim for the nomicon and the
|
||||
// reference.
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
/// Invoke `rustbook` for `target` for the doc book `name`.
|
||||
///
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
fn run(self, builder: &Builder) {
|
||||
let src = builder.build.src.join("src/doc");
|
||||
builder.ensure(RustbookSrc {
|
||||
target: self.target,
|
||||
name: self.name,
|
||||
src: INTERNER.intern_path(src),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct UnstableBook {
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for UnstableBook {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc/unstable-book").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(UnstableBook {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
builder.ensure(UnstableBookGen {
|
||||
target: self.target,
|
||||
});
|
||||
builder.ensure(RustbookSrc {
|
||||
target: self.target,
|
||||
name: INTERNER.intern_str("unstable-book"),
|
||||
src: builder.build.md_doc_out(self.target),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
struct RustbookSrc {
|
||||
target: Interned<String>,
|
||||
name: Interned<String>,
|
||||
src: Interned<PathBuf>,
|
||||
}
|
||||
|
||||
impl Step for RustbookSrc {
|
||||
type Output = ();
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
/// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
|
||||
///
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let name = self.name;
|
||||
let src = self.src;
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let out = out.join(name);
|
||||
let src = src.join(name);
|
||||
let index = out.join("index.html");
|
||||
let rustbook = builder.tool_exe(Tool::Rustbook);
|
||||
if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
|
||||
return
|
||||
}
|
||||
println!("Rustbook ({}) - {}", target, name);
|
||||
let _ = fs::remove_dir_all(&out);
|
||||
build.run(builder.tool_cmd(Tool::Rustbook)
|
||||
.arg("build")
|
||||
.arg(&src)
|
||||
.arg("-d")
|
||||
.arg(out));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct TheBook {
|
||||
target: Interned<String>,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl Step for TheBook {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc/book").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(TheBook {
|
||||
target: run.target,
|
||||
name: "book",
|
||||
});
|
||||
}
|
||||
|
||||
/// Build the book and associated stuff.
|
||||
///
|
||||
/// We need to build:
|
||||
///
|
||||
/// * Book (first edition)
|
||||
/// * Book (second edition)
|
||||
/// * Index page
|
||||
/// * Redirect pages
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let name = self.name;
|
||||
// build book first edition
|
||||
builder.ensure(Rustbook {
|
||||
target: target,
|
||||
name: INTERNER.intern_string(format!("{}/first-edition", name)),
|
||||
});
|
||||
|
||||
// build book second edition
|
||||
builder.ensure(Rustbook {
|
||||
target: target,
|
||||
name: INTERNER.intern_string(format!("{}/second-edition", name)),
|
||||
});
|
||||
|
||||
// build the index page
|
||||
let index = format!("{}/index.md", name);
|
||||
println!("Documenting book index ({})", target);
|
||||
invoke_rustdoc(builder, target, &index);
|
||||
|
||||
// build the redirect pages
|
||||
println!("Documenting book redirect pages ({})", target);
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let path = path.to_str().unwrap();
|
||||
|
||||
invoke_rustdoc(builder, target, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_rustdoc(builder: &Builder, target: Interned<String>, markdown: &str) {
|
||||
let build = builder.build;
|
||||
let out = build.doc_out(target);
|
||||
|
||||
let compiler = Compiler::new(0, &build.build);
|
||||
let compiler = builder.compiler(0, build.build);
|
||||
|
||||
let path = build.src.join("src/doc").join(markdown);
|
||||
|
||||
let rustdoc = build.rustdoc(&compiler);
|
||||
let rustdoc = builder.rustdoc(compiler);
|
||||
|
||||
let favicon = build.src.join("src/doc/favicon.inc");
|
||||
let footer = build.src.join("src/doc/footer.inc");
|
||||
@ -118,7 +265,7 @@ fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
|
||||
|
||||
let mut cmd = Command::new(&rustdoc);
|
||||
|
||||
build.add_rustc_lib_path(&compiler, &mut cmd);
|
||||
builder.add_rustc_lib_path(compiler, &mut cmd);
|
||||
|
||||
let out = out.join("book");
|
||||
|
||||
@ -137,244 +284,416 @@ fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
|
||||
build.run(&mut cmd);
|
||||
}
|
||||
|
||||
/// Generates all standalone documentation as compiled by the rustdoc in `stage`
|
||||
/// for the `target` into `out`.
|
||||
///
|
||||
/// This will list all of `src/doc` looking for markdown files and appropriately
|
||||
/// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
|
||||
/// `STAMP` alongw ith providing the various header/footer HTML we've cutomized.
|
||||
///
|
||||
/// In the end, this is just a glorified wrapper around rustdoc!
|
||||
pub fn standalone(build: &Build, target: &str) {
|
||||
println!("Documenting standalone ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Standalone {
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
let compiler = Compiler::new(0, &build.build);
|
||||
impl Step for Standalone {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
let favicon = build.src.join("src/doc/favicon.inc");
|
||||
let footer = build.src.join("src/doc/footer.inc");
|
||||
let full_toc = build.src.join("src/doc/full-toc.inc");
|
||||
t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
|
||||
|
||||
let version_input = build.src.join("src/doc/version_info.html.template");
|
||||
let version_info = out.join("version_info.html");
|
||||
|
||||
if !up_to_date(&version_input, &version_info) {
|
||||
let mut info = String::new();
|
||||
t!(t!(File::open(&version_input)).read_to_string(&mut info));
|
||||
let info = info.replace("VERSION", &build.rust_release())
|
||||
.replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
|
||||
.replace("STAMP", build.rust_info.sha().unwrap_or(""));
|
||||
t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
if !filename.ends_with(".md") || filename == "README.md" {
|
||||
continue
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Standalone {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Generates all standalone documentation as compiled by the rustdoc in `stage`
|
||||
/// for the `target` into `out`.
|
||||
///
|
||||
/// This will list all of `src/doc` looking for markdown files and appropriately
|
||||
/// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
|
||||
/// `STAMP` alongw ith providing the various header/footer HTML we've cutomized.
|
||||
///
|
||||
/// In the end, this is just a glorified wrapper around rustdoc!
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
println!("Documenting standalone ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let compiler = builder.compiler(0, build.build);
|
||||
|
||||
let favicon = build.src.join("src/doc/favicon.inc");
|
||||
let footer = build.src.join("src/doc/footer.inc");
|
||||
let full_toc = build.src.join("src/doc/full-toc.inc");
|
||||
t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
|
||||
|
||||
let version_input = build.src.join("src/doc/version_info.html.template");
|
||||
let version_info = out.join("version_info.html");
|
||||
|
||||
if !up_to_date(&version_input, &version_info) {
|
||||
let mut info = String::new();
|
||||
t!(t!(File::open(&version_input)).read_to_string(&mut info));
|
||||
let info = info.replace("VERSION", &build.rust_release())
|
||||
.replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
|
||||
.replace("STAMP", build.rust_info.sha().unwrap_or(""));
|
||||
t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
|
||||
}
|
||||
|
||||
let html = out.join(filename).with_extension("html");
|
||||
let rustdoc = build.rustdoc(&compiler);
|
||||
if up_to_date(&path, &html) &&
|
||||
up_to_date(&footer, &html) &&
|
||||
up_to_date(&favicon, &html) &&
|
||||
up_to_date(&full_toc, &html) &&
|
||||
up_to_date(&version_info, &html) &&
|
||||
up_to_date(&rustdoc, &html) {
|
||||
continue
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
if !filename.ends_with(".md") || filename == "README.md" {
|
||||
continue
|
||||
}
|
||||
|
||||
let html = out.join(filename).with_extension("html");
|
||||
let rustdoc = builder.rustdoc(compiler);
|
||||
if up_to_date(&path, &html) &&
|
||||
up_to_date(&footer, &html) &&
|
||||
up_to_date(&favicon, &html) &&
|
||||
up_to_date(&full_toc, &html) &&
|
||||
up_to_date(&version_info, &html) &&
|
||||
up_to_date(&rustdoc, &html) {
|
||||
continue
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(&rustdoc);
|
||||
builder.add_rustc_lib_path(compiler, &mut cmd);
|
||||
cmd.arg("--html-after-content").arg(&footer)
|
||||
.arg("--html-before-content").arg(&version_info)
|
||||
.arg("--html-in-header").arg(&favicon)
|
||||
.arg("--markdown-playground-url")
|
||||
.arg("https://play.rust-lang.org/")
|
||||
.arg("-o").arg(&out)
|
||||
.arg(&path);
|
||||
|
||||
if filename == "not_found.md" {
|
||||
cmd.arg("--markdown-no-toc")
|
||||
.arg("--markdown-css")
|
||||
.arg("https://doc.rust-lang.org/rust.css");
|
||||
} else {
|
||||
cmd.arg("--markdown-css").arg("rust.css");
|
||||
}
|
||||
build.run(&mut cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(&rustdoc);
|
||||
build.add_rustc_lib_path(&compiler, &mut cmd);
|
||||
cmd.arg("--html-after-content").arg(&footer)
|
||||
.arg("--html-before-content").arg(&version_info)
|
||||
.arg("--html-in-header").arg(&favicon)
|
||||
.arg("--markdown-playground-url")
|
||||
.arg("https://play.rust-lang.org/")
|
||||
.arg("-o").arg(&out)
|
||||
.arg(&path);
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Std {
|
||||
stage: u32,
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
if filename == "not_found.md" {
|
||||
cmd.arg("--markdown-no-toc")
|
||||
.arg("--markdown-css")
|
||||
.arg("https://doc.rust-lang.org/rust.css");
|
||||
impl Step for Std {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("std").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Std {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target
|
||||
});
|
||||
}
|
||||
|
||||
/// Compile all standard library documentation.
|
||||
///
|
||||
/// This will generate all documentation for the standard library and its
|
||||
/// dependencies. This is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} std ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
cmd.arg("--markdown-css").arg("rust.css");
|
||||
compiler
|
||||
};
|
||||
|
||||
builder.ensure(compile::Std { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Libstd)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = builder.rustdoc(compiler);
|
||||
|
||||
// Here what we're doing is creating a *symlink* (directory junction on
|
||||
// Windows) to the final output location. This is not done as an
|
||||
// optimization but rather for correctness. We've got three trees of
|
||||
// documentation, one for std, one for test, and one for rustc. It's then
|
||||
// our job to merge them all together.
|
||||
//
|
||||
// Unfortunately rustbuild doesn't know nearly as well how to merge doc
|
||||
// trees as rustdoc does itself, so instead of actually having three
|
||||
// separate trees we just have rustdoc output to the same location across
|
||||
// all of them.
|
||||
//
|
||||
// This way rustdoc generates output directly into the output, and rustdoc
|
||||
// will also directly handle merging.
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libstd/Cargo.toml"))
|
||||
.arg("--features").arg(build.std_features());
|
||||
|
||||
// We don't want to build docs for internal std dependencies unless
|
||||
// in compiler-docs mode. When not in that mode, we whitelist the crates
|
||||
// for which docs must be built.
|
||||
if !build.config.compiler_docs {
|
||||
cargo.arg("--no-deps");
|
||||
for krate in &["alloc", "collections", "core", "std", "std_unicode"] {
|
||||
cargo.arg("-p").arg(krate);
|
||||
// Create all crate output directories first to make sure rustdoc uses
|
||||
// relative links.
|
||||
// FIXME: Cargo should probably do this itself.
|
||||
t!(fs::create_dir_all(out_dir.join(krate)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Test {
|
||||
stage: u32,
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Test {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("test").default_condition(builder.config.compiler_docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Test {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Compile all libtest documentation.
|
||||
///
|
||||
/// This will generate all documentation for libtest and its dependencies. This
|
||||
/// is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} test ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
// Build libstd docs so that we generate relative links
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Test { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Libtest)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = builder.rustdoc(compiler);
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libtest/Cargo.toml"));
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rustc {
|
||||
stage: u32,
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustc {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("rustc-main").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rustc {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Generate all compiler documentation.
|
||||
///
|
||||
/// This will generate all documentation for the compiler libraries and their
|
||||
/// dependencies. This is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} compiler ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
// Build libstd docs so that we generate relative links
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Librustc)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = builder.rustdoc(compiler);
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/rustc/Cargo.toml"))
|
||||
.arg("--features").arg(build.rustc_features());
|
||||
|
||||
if build.config.compiler_docs {
|
||||
// src/rustc/Cargo.toml contains bin crates called rustc and rustdoc
|
||||
// which would otherwise overwrite the docs for the real rustc and
|
||||
// rustdoc lib crates.
|
||||
cargo.arg("-p").arg("rustc_driver")
|
||||
.arg("-p").arg("rustdoc");
|
||||
} else {
|
||||
// Like with libstd above if compiler docs aren't enabled then we're not
|
||||
// documenting internal dependencies, so we have a whitelist.
|
||||
cargo.arg("--no-deps");
|
||||
for krate in &["proc_macro"] {
|
||||
cargo.arg("-p").arg(krate);
|
||||
}
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct ErrorIndex {
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for ErrorIndex {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/error_index_generator").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(ErrorIndex {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Generates the HTML rendered error-index by running the
|
||||
/// `error_index_generator` tool.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: builder.compiler(0, build.build),
|
||||
target,
|
||||
});
|
||||
|
||||
println!("Documenting error index ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let mut index = builder.tool_cmd(Tool::ErrorIndex);
|
||||
index.arg("html");
|
||||
index.arg(out.join("error-index.html"));
|
||||
|
||||
// FIXME: shouldn't have to pass this env var
|
||||
index.env("CFG_BUILD", &build.build);
|
||||
|
||||
build.run(&mut index);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct UnstableBookGen {
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for UnstableBookGen {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/unstable-book-gen").default_condition(builder.build.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(UnstableBookGen {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
|
||||
builder.ensure(compile::Std {
|
||||
compiler: builder.compiler(builder.top_stage, build.build),
|
||||
target,
|
||||
});
|
||||
|
||||
println!("Generating unstable book md files ({})", target);
|
||||
let out = build.md_doc_out(target).join("unstable-book");
|
||||
t!(fs::create_dir_all(&out));
|
||||
t!(fs::remove_dir_all(&out));
|
||||
let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
|
||||
cmd.arg(build.src.join("src"));
|
||||
cmd.arg(out);
|
||||
|
||||
build.run(&mut cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile all standard library documentation.
|
||||
///
|
||||
/// This will generate all documentation for the standard library and its
|
||||
/// dependencies. This is largely just a wrapper around `cargo doc`.
|
||||
pub fn std(build: &Build, stage: u32, target: &str) {
|
||||
println!("Documenting stage{} std ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = Compiler::new(stage, &build.build);
|
||||
let compiler = if build.force_use_stage1(&compiler, target) {
|
||||
Compiler::new(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
let out_dir = build.stage_out(&compiler, Mode::Libstd)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = build.rustdoc(&compiler);
|
||||
|
||||
// Here what we're doing is creating a *symlink* (directory junction on
|
||||
// Windows) to the final output location. This is not done as an
|
||||
// optimization but rather for correctness. We've got three trees of
|
||||
// documentation, one for std, one for test, and one for rustc. It's then
|
||||
// our job to merge them all together.
|
||||
//
|
||||
// Unfortunately rustbuild doesn't know nearly as well how to merge doc
|
||||
// trees as rustdoc does itself, so instead of actually having three
|
||||
// separate trees we just have rustdoc output to the same location across
|
||||
// all of them.
|
||||
//
|
||||
// This way rustdoc generates output directly into the output, and rustdoc
|
||||
// will also directly handle merging.
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = build.cargo(&compiler, Mode::Libstd, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libstd/Cargo.toml"))
|
||||
.arg("--features").arg(build.std_features());
|
||||
|
||||
// We don't want to build docs for internal std dependencies unless
|
||||
// in compiler-docs mode. When not in that mode, we whitelist the crates
|
||||
// for which docs must be built.
|
||||
if !build.config.compiler_docs {
|
||||
cargo.arg("--no-deps");
|
||||
for krate in &["alloc", "collections", "core", "std", "std_unicode"] {
|
||||
cargo.arg("-p").arg(krate);
|
||||
// Create all crate output directories first to make sure rustdoc uses
|
||||
// relative links.
|
||||
// FIXME: Cargo should probably do this itself.
|
||||
t!(fs::create_dir_all(out_dir.join(krate)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
|
||||
/// Compile all libtest documentation.
|
||||
///
|
||||
/// This will generate all documentation for libtest and its dependencies. This
|
||||
/// is largely just a wrapper around `cargo doc`.
|
||||
pub fn test(build: &Build, stage: u32, target: &str) {
|
||||
println!("Documenting stage{} test ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = Compiler::new(stage, &build.build);
|
||||
let compiler = if build.force_use_stage1(&compiler, target) {
|
||||
Compiler::new(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
let out_dir = build.stage_out(&compiler, Mode::Libtest)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = build.rustdoc(&compiler);
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = build.cargo(&compiler, Mode::Libtest, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libtest/Cargo.toml"));
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
|
||||
/// Generate all compiler documentation.
|
||||
///
|
||||
/// This will generate all documentation for the compiler libraries and their
|
||||
/// dependencies. This is largely just a wrapper around `cargo doc`.
|
||||
pub fn rustc(build: &Build, stage: u32, target: &str) {
|
||||
println!("Documenting stage{} compiler ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = Compiler::new(stage, &build.build);
|
||||
let compiler = if build.force_use_stage1(&compiler, target) {
|
||||
Compiler::new(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
let out_dir = build.stage_out(&compiler, Mode::Librustc)
|
||||
.join(target).join("doc");
|
||||
let rustdoc = build.rustdoc(&compiler);
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
|
||||
let mut cargo = build.cargo(&compiler, Mode::Librustc, target, "doc");
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/rustc/Cargo.toml"))
|
||||
.arg("--features").arg(build.rustc_features());
|
||||
|
||||
if build.config.compiler_docs {
|
||||
// src/rustc/Cargo.toml contains bin crates called rustc and rustdoc
|
||||
// which would otherwise overwrite the docs for the real rustc and
|
||||
// rustdoc lib crates.
|
||||
cargo.arg("-p").arg("rustc_driver")
|
||||
.arg("-p").arg("rustdoc");
|
||||
} else {
|
||||
// Like with libstd above if compiler docs aren't enabled then we're not
|
||||
// documenting internal dependencies, so we have a whitelist.
|
||||
cargo.arg("--no-deps");
|
||||
for krate in &["proc_macro"] {
|
||||
cargo.arg("-p").arg(krate);
|
||||
}
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
}
|
||||
|
||||
/// Generates the HTML rendered error-index by running the
|
||||
/// `error_index_generator` tool.
|
||||
pub fn error_index(build: &Build, target: &str) {
|
||||
println!("Documenting error index ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = Compiler::new(0, &build.build);
|
||||
let mut index = build.tool_cmd(&compiler, "error_index_generator");
|
||||
index.arg("html");
|
||||
index.arg(out.join("error-index.html"));
|
||||
|
||||
// FIXME: shouldn't have to pass this env var
|
||||
index.env("CFG_BUILD", &build.build);
|
||||
|
||||
build.run(&mut index);
|
||||
}
|
||||
|
||||
pub fn unstable_book_gen(build: &Build, target: &str) {
|
||||
println!("Generating unstable book md files ({})", target);
|
||||
let out = build.md_doc_out(target).join("unstable-book");
|
||||
t!(fs::create_dir_all(&out));
|
||||
t!(fs::remove_dir_all(&out));
|
||||
let compiler = Compiler::new(0, &build.build);
|
||||
let mut cmd = build.tool_cmd(&compiler, "unstable-book-gen");
|
||||
cmd.arg(build.src.join("src"));
|
||||
cmd.arg(out);
|
||||
|
||||
build.run(&mut cmd);
|
||||
}
|
||||
|
||||
fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
if let Ok(m) = fs::symlink_metadata(dst) {
|
||||
if m.file_type().is_dir() {
|
||||
|
@ -23,7 +23,9 @@ use getopts::Options;
|
||||
use Build;
|
||||
use config::Config;
|
||||
use metadata;
|
||||
use step;
|
||||
use builder::Builder;
|
||||
|
||||
use cache::{Interned, INTERNER};
|
||||
|
||||
/// Deserialized version of all flags for this compile.
|
||||
pub struct Flags {
|
||||
@ -31,9 +33,9 @@ pub struct Flags {
|
||||
pub on_fail: Option<String>,
|
||||
pub stage: Option<u32>,
|
||||
pub keep_stage: Option<u32>,
|
||||
pub build: String,
|
||||
pub host: Vec<String>,
|
||||
pub target: Vec<String>,
|
||||
pub build: Interned<String>,
|
||||
pub host: Vec<Interned<String>>,
|
||||
pub target: Vec<Interned<String>>,
|
||||
pub config: Option<PathBuf>,
|
||||
pub src: PathBuf,
|
||||
pub jobs: Option<u32>,
|
||||
@ -246,10 +248,9 @@ Arguments:
|
||||
config.build = flags.build.clone();
|
||||
let mut build = Build::new(flags, config);
|
||||
metadata::build(&mut build);
|
||||
let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
|
||||
if maybe_rules_help.is_some() {
|
||||
extra_help.push_str(maybe_rules_help.unwrap().as_str());
|
||||
}
|
||||
|
||||
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
|
||||
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
|
||||
} else {
|
||||
extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
|
||||
subcommand).as_str());
|
||||
@ -319,11 +320,13 @@ Arguments:
|
||||
stage: stage,
|
||||
on_fail: matches.opt_str("on-fail"),
|
||||
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
|
||||
build: matches.opt_str("build").unwrap_or_else(|| {
|
||||
build: INTERNER.intern_string(matches.opt_str("build").unwrap_or_else(|| {
|
||||
env::var("BUILD").unwrap()
|
||||
}),
|
||||
host: split(matches.opt_strs("host")),
|
||||
target: split(matches.opt_strs("target")),
|
||||
})),
|
||||
host: split(matches.opt_strs("host"))
|
||||
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
|
||||
target: split(matches.opt_strs("target"))
|
||||
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
|
||||
config: cfg_file,
|
||||
src: src,
|
||||
jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
|
||||
|
@ -18,121 +18,99 @@ use std::fs;
|
||||
use std::path::{Path, PathBuf, Component};
|
||||
use std::process::Command;
|
||||
|
||||
use Build;
|
||||
use dist::{pkgname, sanitize_sh, tmpdir};
|
||||
use dist::{self, pkgname, sanitize_sh, tmpdir};
|
||||
|
||||
pub struct Installer<'a> {
|
||||
build: &'a Build,
|
||||
prefix: PathBuf,
|
||||
sysconfdir: PathBuf,
|
||||
docdir: PathBuf,
|
||||
bindir: PathBuf,
|
||||
libdir: PathBuf,
|
||||
mandir: PathBuf,
|
||||
empty_dir: PathBuf,
|
||||
use builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
use cache::Interned;
|
||||
|
||||
pub fn install_docs(builder: &Builder, stage: u32, host: Interned<String>) {
|
||||
install_sh(builder, "docs", "rust-docs", stage, Some(host));
|
||||
}
|
||||
|
||||
impl<'a> Drop for Installer<'a> {
|
||||
fn drop(&mut self) {
|
||||
t!(fs::remove_dir_all(&self.empty_dir));
|
||||
pub fn install_std(builder: &Builder, stage: u32) {
|
||||
for target in builder.build.config.target.iter() {
|
||||
install_sh(builder, "std", "rust-std", stage, Some(*target));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Installer<'a> {
|
||||
pub fn new(build: &'a Build) -> Installer<'a> {
|
||||
let prefix_default = PathBuf::from("/usr/local");
|
||||
let sysconfdir_default = PathBuf::from("/etc");
|
||||
let docdir_default = PathBuf::from("share/doc/rust");
|
||||
let bindir_default = PathBuf::from("bin");
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = PathBuf::from("share/man");
|
||||
let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
|
||||
let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
|
||||
let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
|
||||
let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);
|
||||
pub fn install_cargo(builder: &Builder, stage: u32, host: Interned<String>) {
|
||||
install_sh(builder, "cargo", "cargo", stage, Some(host));
|
||||
}
|
||||
|
||||
let sysconfdir = prefix.join(sysconfdir);
|
||||
let docdir = prefix.join(docdir);
|
||||
let bindir = prefix.join(bindir);
|
||||
let libdir = prefix.join(libdir);
|
||||
let mandir = prefix.join(mandir);
|
||||
pub fn install_rls(builder: &Builder, stage: u32, host: Interned<String>) {
|
||||
install_sh(builder, "rls", "rls", stage, Some(host));
|
||||
}
|
||||
|
||||
let destdir = env::var_os("DESTDIR").map(PathBuf::from);
|
||||
pub fn install_analysis(builder: &Builder, stage: u32, host: Interned<String>) {
|
||||
install_sh(builder, "analysis", "rust-analysis", stage, Some(host));
|
||||
}
|
||||
|
||||
let prefix = add_destdir(&prefix, &destdir);
|
||||
let sysconfdir = add_destdir(&sysconfdir, &destdir);
|
||||
let docdir = add_destdir(&docdir, &destdir);
|
||||
let bindir = add_destdir(&bindir, &destdir);
|
||||
let libdir = add_destdir(&libdir, &destdir);
|
||||
let mandir = add_destdir(&mandir, &destdir);
|
||||
pub fn install_src(builder: &Builder, stage: u32) {
|
||||
install_sh(builder, "src", "rust-src", stage, None);
|
||||
}
|
||||
pub fn install_rustc(builder: &Builder, stage: u32, host: Interned<String>) {
|
||||
install_sh(builder, "rustc", "rustc", stage, Some(host));
|
||||
}
|
||||
|
||||
let empty_dir = build.out.join("tmp/empty_dir");
|
||||
fn install_sh(
|
||||
builder: &Builder,
|
||||
package: &str,
|
||||
name: &str,
|
||||
stage: u32,
|
||||
host: Option<Interned<String>>
|
||||
) {
|
||||
let build = builder.build;
|
||||
println!("Install {} stage{} ({:?})", package, stage, host);
|
||||
|
||||
t!(fs::create_dir_all(&empty_dir));
|
||||
let prefix_default = PathBuf::from("/usr/local");
|
||||
let sysconfdir_default = PathBuf::from("/etc");
|
||||
let docdir_default = PathBuf::from("share/doc/rust");
|
||||
let bindir_default = PathBuf::from("bin");
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = PathBuf::from("share/man");
|
||||
let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
|
||||
let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
|
||||
let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
|
||||
let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);
|
||||
|
||||
Installer {
|
||||
build,
|
||||
prefix,
|
||||
sysconfdir,
|
||||
docdir,
|
||||
bindir,
|
||||
libdir,
|
||||
mandir,
|
||||
empty_dir,
|
||||
}
|
||||
}
|
||||
let sysconfdir = prefix.join(sysconfdir);
|
||||
let docdir = prefix.join(docdir);
|
||||
let bindir = prefix.join(bindir);
|
||||
let libdir = prefix.join(libdir);
|
||||
let mandir = prefix.join(mandir);
|
||||
|
||||
pub fn install_docs(&self, stage: u32, host: &str) {
|
||||
self.install_sh("docs", "rust-docs", stage, Some(host));
|
||||
}
|
||||
let destdir = env::var_os("DESTDIR").map(PathBuf::from);
|
||||
|
||||
pub fn install_std(&self, stage: u32) {
|
||||
for target in self.build.config.target.iter() {
|
||||
self.install_sh("std", "rust-std", stage, Some(target));
|
||||
}
|
||||
}
|
||||
let prefix = add_destdir(&prefix, &destdir);
|
||||
let sysconfdir = add_destdir(&sysconfdir, &destdir);
|
||||
let docdir = add_destdir(&docdir, &destdir);
|
||||
let bindir = add_destdir(&bindir, &destdir);
|
||||
let libdir = add_destdir(&libdir, &destdir);
|
||||
let mandir = add_destdir(&mandir, &destdir);
|
||||
|
||||
pub fn install_cargo(&self, stage: u32, host: &str) {
|
||||
self.install_sh("cargo", "cargo", stage, Some(host));
|
||||
}
|
||||
let empty_dir = build.out.join("tmp/empty_dir");
|
||||
|
||||
pub fn install_rls(&self, stage: u32, host: &str) {
|
||||
self.install_sh("rls", "rls", stage, Some(host));
|
||||
}
|
||||
t!(fs::create_dir_all(&empty_dir));
|
||||
let package_name = if let Some(host) = host {
|
||||
format!("{}-{}", pkgname(build, name), host)
|
||||
} else {
|
||||
pkgname(build, name)
|
||||
};
|
||||
|
||||
pub fn install_analysis(&self, stage: u32, host: &str) {
|
||||
self.install_sh("analysis", "rust-analysis", stage, Some(host));
|
||||
}
|
||||
|
||||
pub fn install_src(&self, stage: u32) {
|
||||
self.install_sh("src", "rust-src", stage, None);
|
||||
}
|
||||
pub fn install_rustc(&self, stage: u32, host: &str) {
|
||||
self.install_sh("rustc", "rustc", stage, Some(host));
|
||||
}
|
||||
|
||||
fn install_sh(&self, package: &str, name: &str, stage: u32, host: Option<&str>) {
|
||||
println!("Install {} stage{} ({:?})", package, stage, host);
|
||||
let package_name = if let Some(host) = host {
|
||||
format!("{}-{}", pkgname(self.build, name), host)
|
||||
} else {
|
||||
pkgname(self.build, name)
|
||||
};
|
||||
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.current_dir(&self.empty_dir)
|
||||
.arg(sanitize_sh(&tmpdir(self.build).join(&package_name).join("install.sh")))
|
||||
.arg(format!("--prefix={}", sanitize_sh(&self.prefix)))
|
||||
.arg(format!("--sysconfdir={}", sanitize_sh(&self.sysconfdir)))
|
||||
.arg(format!("--docdir={}", sanitize_sh(&self.docdir)))
|
||||
.arg(format!("--bindir={}", sanitize_sh(&self.bindir)))
|
||||
.arg(format!("--libdir={}", sanitize_sh(&self.libdir)))
|
||||
.arg(format!("--mandir={}", sanitize_sh(&self.mandir)))
|
||||
.arg("--disable-ldconfig");
|
||||
self.build.run(&mut cmd);
|
||||
}
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.current_dir(&empty_dir)
|
||||
.arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
|
||||
.arg(format!("--prefix={}", sanitize_sh(&prefix)))
|
||||
.arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
|
||||
.arg(format!("--docdir={}", sanitize_sh(&docdir)))
|
||||
.arg(format!("--bindir={}", sanitize_sh(&bindir)))
|
||||
.arg(format!("--libdir={}", sanitize_sh(&libdir)))
|
||||
.arg(format!("--mandir={}", sanitize_sh(&mandir)))
|
||||
.arg("--disable-ldconfig");
|
||||
build.run(&mut cmd);
|
||||
t!(fs::remove_dir_all(&empty_dir));
|
||||
}
|
||||
|
||||
fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
|
||||
@ -148,3 +126,82 @@ fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
macro_rules! install {
|
||||
(($sel:ident, $builder:ident, $_config:ident),
|
||||
$($name:ident,
|
||||
$path:expr,
|
||||
$default_cond:expr,
|
||||
only_hosts: $only_hosts:expr,
|
||||
$run_item:block $(, $c:ident)*;)+) => {
|
||||
$(
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct $name {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
pub host: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for $name {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_BUILD_TARGETS: bool = true;
|
||||
const ONLY_HOSTS: bool = $only_hosts;
|
||||
$(const $c: bool = true;)*
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let $_config = &run.builder.config;
|
||||
run.path($path).default_condition($default_cond)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
host: run.host,
|
||||
});
|
||||
}
|
||||
|
||||
fn run($sel, $builder: &Builder) {
|
||||
$run_item
|
||||
}
|
||||
})+
|
||||
}
|
||||
}
|
||||
|
||||
install!((self, builder, _config),
|
||||
Docs, "src/doc", _config.docs, only_hosts: false, {
|
||||
builder.ensure(dist::Docs { stage: self.stage, target: self.target });
|
||||
install_docs(builder, self.stage, self.target);
|
||||
};
|
||||
Std, "src/libstd", true, only_hosts: true, {
|
||||
builder.ensure(dist::Std {
|
||||
compiler: builder.compiler(self.stage, self.host),
|
||||
target: self.target
|
||||
});
|
||||
install_std(builder, self.stage);
|
||||
};
|
||||
Cargo, "cargo", _config.extended, only_hosts: true, {
|
||||
builder.ensure(dist::Cargo { stage: self.stage, target: self.target });
|
||||
install_cargo(builder, self.stage, self.target);
|
||||
};
|
||||
Rls, "rls", _config.extended, only_hosts: true, {
|
||||
builder.ensure(dist::Rls { stage: self.stage, target: self.target });
|
||||
install_rls(builder, self.stage, self.target);
|
||||
};
|
||||
Analysis, "analysis", _config.extended, only_hosts: false, {
|
||||
builder.ensure(dist::Analysis {
|
||||
compiler: builder.compiler(self.stage, self.host),
|
||||
target: self.target
|
||||
});
|
||||
install_analysis(builder, self.stage, self.target);
|
||||
};
|
||||
Src, "src", _config.extended, only_hosts: true, {
|
||||
builder.ensure(dist::Src);
|
||||
install_src(builder, self.stage);
|
||||
}, ONLY_BUILD;
|
||||
Rustc, "src/librustc", _config.extended, only_hosts: true, {
|
||||
builder.ensure(dist::Rustc { stage: self.stage, target: self.target });
|
||||
install_rustc(builder, self.stage, self.target);
|
||||
};
|
||||
);
|
||||
|
@ -23,38 +23,87 @@
|
||||
//!
|
||||
//! ## Architecture
|
||||
//!
|
||||
//! Although this build system defers most of the complicated logic to Cargo
|
||||
//! itself, it still needs to maintain a list of targets and dependencies which
|
||||
//! it can itself perform. Rustbuild is made up of a list of rules with
|
||||
//! dependencies amongst them (created in the `step` module) and then knows how
|
||||
//! to execute each in sequence. Each time rustbuild is invoked, it will simply
|
||||
//! iterate through this list of steps and execute each serially in turn. For
|
||||
//! each step rustbuild relies on the step internally being incremental and
|
||||
//! The build system defers most of the complicated logic managing invocations
|
||||
//! of rustc and rustdoc to Cargo itself. However, moving through various stages
|
||||
//! and copying artifacts is still necessary for it to do. Each time rustbuild
|
||||
//! is invoked, it will iterate through the list of predefined steps and execute
|
||||
//! each serially in turn if it matches the paths passed or is a default rule.
|
||||
//! For each step rustbuild relies on the step internally being incremental and
|
||||
//! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded
|
||||
//! to appropriate test harnesses and such.
|
||||
//!
|
||||
//! Most of the "meaty" steps that matter are backed by Cargo, which does indeed
|
||||
//! have its own parallelism and incremental management. Later steps, like
|
||||
//! tests, aren't incremental and simply run the entire suite currently.
|
||||
//! However, compiletest itself tries to avoid running tests when the artifacts
|
||||
//! that are involved (mainly the compiler) haven't changed.
|
||||
//!
|
||||
//! When you execute `x.py build`, the steps which are executed are:
|
||||
//!
|
||||
//! * First, the python script is run. This will automatically download the
|
||||
//! stage0 rustc and cargo according to `src/stage0.txt`, or using the cached
|
||||
//! stage0 rustc and cargo according to `src/stage0.txt`, or use the cached
|
||||
//! versions if they're available. These are then used to compile rustbuild
|
||||
//! itself (using Cargo). Finally, control is then transferred to rustbuild.
|
||||
//!
|
||||
//! * Rustbuild takes over, performs sanity checks, probes the environment,
|
||||
//! reads configuration, builds up a list of steps, and then starts executing
|
||||
//! them.
|
||||
//! reads configuration, and starts executing steps as it reads the command
|
||||
//! line arguments (paths) or going through the default rules.
|
||||
//!
|
||||
//! * The stage0 libstd is compiled
|
||||
//! * The stage0 libtest is compiled
|
||||
//! * The stage0 librustc is compiled
|
||||
//! * The stage1 compiler is assembled
|
||||
//! * The stage1 libstd, libtest, librustc are compiled
|
||||
//! * The stage2 compiler is assembled
|
||||
//! * The stage2 libstd, libtest, librustc are compiled
|
||||
//! The build output will be something like the following:
|
||||
//!
|
||||
//! Building stage0 std artifacts
|
||||
//! Copying stage0 std
|
||||
//! Building stage0 test artifacts
|
||||
//! Copying stage0 test
|
||||
//! Building stage0 compiler artifacts
|
||||
//! Copying stage0 rustc
|
||||
//! Assembling stage1 compiler
|
||||
//! Building stage1 std artifacts
|
||||
//! Copying stage1 std
|
||||
//! Building stage1 test artifacts
|
||||
//! Copying stage1 test
|
||||
//! Building stage1 compiler artifacts
|
||||
//! Copying stage1 rustc
|
||||
//! Assembling stage2 compiler
|
||||
//! Uplifting stage1 std
|
||||
//! Uplifting stage1 test
|
||||
//! Uplifting stage1 rustc
|
||||
//!
|
||||
//! Let's disect that a little:
|
||||
//!
|
||||
//! ## Building stage0 {std,test,compiler} artifacts
|
||||
//!
|
||||
//! These steps use the provided (downloaded, usually) compiler to compile the
|
||||
//! local Rust source into libraries we can use.
|
||||
//!
|
||||
//! ## Copying stage0 {std,test,rustc}
|
||||
//!
|
||||
//! This copies the build output from Cargo into
|
||||
//! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: This step's
|
||||
//! documentation should be expanded -- the information already here may be
|
||||
//! incorrect.
|
||||
//!
|
||||
//! ## Assembling stage1 compiler
|
||||
//!
|
||||
//! This copies the libraries we built in "building stage0 ... artifacts" into
|
||||
//! the stage1 compiler's lib directory. These are the host libraries that the
|
||||
//! compiler itself uses to run. These aren't actually used by artifacts the new
|
||||
//! compiler generates. This step also copies the rustc and rustdoc binaries we
|
||||
//! generated into build/$HOST/stage/bin.
|
||||
//!
|
||||
//! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have
|
||||
//! any libraries to link built binaries or libraries to. The next 3 steps will
|
||||
//! provide those libraries for it; they are mostly equivalent to constructing
|
||||
//! the stage1/bin compiler so we don't go through them individually.
|
||||
//!
|
||||
//! ## Uplifting stage1 {std,test,rustc}
|
||||
//!
|
||||
//! This step copies the libraries from the stage1 compiler sysroot into the
|
||||
//! stage2 compiler. This is done to avoid rebuilding the compiler; libraries
|
||||
//! we'd build in this step should be identical (in function, if not necessarily
|
||||
//! identical on disk) so there's no need to recompile the compiler again. Note
|
||||
//! that if you want to, you can enable the full-bootstrap option to change this
|
||||
//! behavior.
|
||||
//!
|
||||
//! Each step is driven by a separate Cargo project and rustbuild orchestrates
|
||||
//! copying files between steps and otherwise preparing for Cargo to run.
|
||||
@ -65,15 +114,22 @@
|
||||
//! also check out the `src/bootstrap/README.md` file for more information.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![allow(stable_features)]
|
||||
#![feature(associated_consts)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate build_helper;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate cmake;
|
||||
extern crate filetime;
|
||||
extern crate gcc;
|
||||
extern crate getopts;
|
||||
extern crate num_cpus;
|
||||
extern crate rustc_serialize;
|
||||
extern crate toml;
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -81,9 +137,8 @@ extern crate libc;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::path::{PathBuf, Path};
|
||||
@ -91,7 +146,7 @@ use std::process::Command;
|
||||
|
||||
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
|
||||
|
||||
use util::{exe, libdir, add_lib_path, OutputFolder, CiEnv};
|
||||
use util::{exe, libdir, OutputFolder, CiEnv};
|
||||
|
||||
mod cc;
|
||||
mod channel;
|
||||
@ -106,8 +161,10 @@ mod flags;
|
||||
mod install;
|
||||
mod native;
|
||||
mod sanity;
|
||||
mod step;
|
||||
pub mod util;
|
||||
mod builder;
|
||||
mod cache;
|
||||
mod tool;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod job;
|
||||
@ -131,6 +188,7 @@ mod job {
|
||||
|
||||
pub use config::Config;
|
||||
pub use flags::{Flags, Subcommand};
|
||||
use cache::{Interned, INTERNER};
|
||||
|
||||
/// A structure representing a Rust compiler.
|
||||
///
|
||||
@ -138,9 +196,9 @@ pub use flags::{Flags, Subcommand};
|
||||
/// corresponds to the platform the compiler runs on. This structure is used as
|
||||
/// a parameter to many methods below.
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
|
||||
pub struct Compiler<'a> {
|
||||
pub struct Compiler {
|
||||
stage: u32,
|
||||
host: &'a str,
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
/// Global configuration for the build system.
|
||||
@ -171,9 +229,9 @@ pub struct Build {
|
||||
verbosity: usize,
|
||||
|
||||
// Targets for which to build.
|
||||
build: String,
|
||||
hosts: Vec<String>,
|
||||
targets: Vec<String>,
|
||||
build: Interned<String>,
|
||||
hosts: Vec<Interned<String>>,
|
||||
targets: Vec<Interned<String>>,
|
||||
|
||||
// Stage 0 (downloaded) compiler and cargo or their local rust equivalents.
|
||||
initial_rustc: PathBuf,
|
||||
@ -185,10 +243,10 @@ pub struct Build {
|
||||
|
||||
// Runtime state filled in later on
|
||||
// target -> (cc, ar)
|
||||
cc: HashMap<String, (gcc::Tool, Option<PathBuf>)>,
|
||||
cc: HashMap<Interned<String>, (gcc::Tool, Option<PathBuf>)>,
|
||||
// host -> (cc, ar)
|
||||
cxx: HashMap<String, gcc::Tool>,
|
||||
crates: HashMap<String, Crate>,
|
||||
cxx: HashMap<Interned<String>, gcc::Tool>,
|
||||
crates: HashMap<Interned<String>, Crate>,
|
||||
is_sudo: bool,
|
||||
ci_env: CiEnv,
|
||||
delayed_failures: Cell<usize>,
|
||||
@ -196,9 +254,9 @@ pub struct Build {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Crate {
|
||||
name: String,
|
||||
name: Interned<String>,
|
||||
version: String,
|
||||
deps: Vec<String>,
|
||||
deps: Vec<Interned<String>>,
|
||||
path: PathBuf,
|
||||
doc_step: String,
|
||||
build_step: String,
|
||||
@ -210,7 +268,7 @@ struct Crate {
|
||||
///
|
||||
/// These entries currently correspond to the various output directories of the
|
||||
/// build system, with each mod generating output in a different directory.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Mode {
|
||||
/// Build the standard library, placing output in the "stageN-std" directory.
|
||||
Libstd,
|
||||
@ -299,12 +357,6 @@ impl Build {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_slice(&self) -> &[String] {
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(&self.build, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the entire build, as configured by the flags and configuration.
|
||||
pub fn build(&mut self) {
|
||||
unsafe {
|
||||
@ -333,7 +385,7 @@ impl Build {
|
||||
self.verbose("learning about cargo");
|
||||
metadata::build(self);
|
||||
|
||||
step::run(self);
|
||||
builder::Builder::run(&self);
|
||||
}
|
||||
|
||||
/// Clear out `dir` if `input` is newer.
|
||||
@ -351,242 +403,6 @@ impl Build {
|
||||
t!(File::create(stamp));
|
||||
}
|
||||
|
||||
/// Prepares an invocation of `cargo` to be run.
|
||||
///
|
||||
/// This will create a `Command` that represents a pending execution of
|
||||
/// Cargo. This cargo will be configured to use `compiler` as the actual
|
||||
/// rustc compiler, its output will be scoped by `mode`'s output directory,
|
||||
/// it will pass the `--target` flag for the specified `target`, and will be
|
||||
/// executing the Cargo command `cmd`.
|
||||
fn cargo(&self,
|
||||
compiler: &Compiler,
|
||||
mode: Mode,
|
||||
target: &str,
|
||||
cmd: &str) -> Command {
|
||||
let mut cargo = Command::new(&self.initial_cargo);
|
||||
let out_dir = self.stage_out(compiler, mode);
|
||||
cargo.env("CARGO_TARGET_DIR", out_dir)
|
||||
.arg(cmd)
|
||||
.arg("-j").arg(self.jobs().to_string())
|
||||
.arg("--target").arg(target);
|
||||
|
||||
// FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
|
||||
// Force cargo to output binaries with disambiguating hashes in the name
|
||||
cargo.env("__CARGO_DEFAULT_LIB_METADATA", &self.config.channel);
|
||||
|
||||
let stage;
|
||||
if compiler.stage == 0 && self.local_rebuild {
|
||||
// Assume the local-rebuild rustc already has stage1 features.
|
||||
stage = 1;
|
||||
} else {
|
||||
stage = compiler.stage;
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// These variables are primarily all read by
|
||||
// src/bootstrap/bin/{rustc.rs,rustdoc.rs}
|
||||
cargo.env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
|
||||
.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
|
||||
.env("RUSTC_REAL", self.compiler_path(compiler))
|
||||
.env("RUSTC_STAGE", stage.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_SYSROOT", self.sysroot(compiler))
|
||||
.env("RUSTC_LIBDIR", self.rustc_libdir(compiler))
|
||||
.env("RUSTC_RPATH", self.config.rust_rpath.to_string())
|
||||
.env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc"))
|
||||
.env("RUSTDOC_REAL", self.rustdoc(compiler))
|
||||
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
|
||||
|
||||
if mode != Mode::Tool {
|
||||
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
||||
// get compiled with debuginfo.
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
|
||||
.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string())
|
||||
.env("RUSTC_FORCE_UNSTABLE", "1");
|
||||
|
||||
// Currently the compiler depends on crates from crates.io, and
|
||||
// then other crates can depend on the compiler (e.g. proc-macro
|
||||
// crates). Let's say, for example that rustc itself depends on the
|
||||
// bitflags crate. If an external crate then depends on the
|
||||
// bitflags crate as well, we need to make sure they don't
|
||||
// conflict, even if they pick the same verison of bitflags. We'll
|
||||
// want to make sure that e.g. a plugin and rustc each get their
|
||||
// own copy of bitflags.
|
||||
|
||||
// Cargo ensures that this works in general through the -C metadata
|
||||
// flag. This flag will frob the symbols in the binary to make sure
|
||||
// they're different, even though the source code is the exact
|
||||
// same. To solve this problem for the compiler we extend Cargo's
|
||||
// already-passed -C metadata flag with our own. Our rustc.rs
|
||||
// wrapper around the actual rustc will detect -C metadata being
|
||||
// passed and frob it with this extra string we're passing in.
|
||||
cargo.env("RUSTC_METADATA_SUFFIX", "rustc");
|
||||
}
|
||||
|
||||
// Enable usage of unstable features
|
||||
cargo.env("RUSTC_BOOTSTRAP", "1");
|
||||
self.add_rust_test_threads(&mut cargo);
|
||||
|
||||
// Almost all of the crates that we compile as part of the bootstrap may
|
||||
// have a build script, including the standard library. To compile a
|
||||
// build script, however, it itself needs a standard library! This
|
||||
// introduces a bit of a pickle when we're compiling the standard
|
||||
// library itself.
|
||||
//
|
||||
// To work around this we actually end up using the snapshot compiler
|
||||
// (stage0) for compiling build scripts of the standard library itself.
|
||||
// The stage0 compiler is guaranteed to have a libstd available for use.
|
||||
//
|
||||
// For other crates, however, we know that we've already got a standard
|
||||
// library up and running, so we can use the normal compiler to compile
|
||||
// build scripts in that situation.
|
||||
if mode == Mode::Libstd {
|
||||
cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
|
||||
} else {
|
||||
cargo.env("RUSTC_SNAPSHOT", self.compiler_path(compiler))
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
|
||||
}
|
||||
|
||||
// Ignore incremental modes except for stage0, since we're
|
||||
// not guaranteeing correctness across builds if the compiler
|
||||
// is changing under your feet.`
|
||||
if self.flags.incremental && compiler.stage == 0 {
|
||||
let incr_dir = self.incremental_dir(compiler);
|
||||
cargo.env("RUSTC_INCREMENTAL", incr_dir);
|
||||
}
|
||||
|
||||
if let Some(ref on_fail) = self.flags.on_fail {
|
||||
cargo.env("RUSTC_ON_FAIL", on_fail);
|
||||
}
|
||||
|
||||
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
|
||||
|
||||
// Specify some various 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).unwrap()) // only msvc is None
|
||||
.env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
|
||||
|
||||
if let Ok(cxx) = self.cxx(target) {
|
||||
cargo.env(format!("CXX_{}", target), cxx);
|
||||
}
|
||||
}
|
||||
|
||||
if mode == Mode::Libstd &&
|
||||
self.config.extended &&
|
||||
compiler.is_final_stage(self) {
|
||||
cargo.env("RUSTC_SAVE_ANALYSIS", "api".to_string());
|
||||
}
|
||||
|
||||
// When being built Cargo will at some point call `nmake.exe` on Windows
|
||||
// MSVC. Unfortunately `nmake` will read these two environment variables
|
||||
// below and try to intepret them. We're likely being run, however, from
|
||||
// MSYS `make` which uses the same variables.
|
||||
//
|
||||
// As a result, to prevent confusion and errors, we remove these
|
||||
// variables from our environment to prevent passing MSYS make flags to
|
||||
// nmake, causing it to blow up.
|
||||
if cfg!(target_env = "msvc") {
|
||||
cargo.env_remove("MAKE");
|
||||
cargo.env_remove("MAKEFLAGS");
|
||||
}
|
||||
|
||||
// Environment variables *required* throughout the build
|
||||
//
|
||||
// FIXME: should update code to not require this env var
|
||||
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
|
||||
|
||||
if self.is_verbose() {
|
||||
cargo.arg("-v");
|
||||
}
|
||||
// FIXME: cargo bench does not accept `--release`
|
||||
if self.config.rust_optimize && cmd != "bench" {
|
||||
cargo.arg("--release");
|
||||
}
|
||||
if self.config.locked_deps {
|
||||
cargo.arg("--locked");
|
||||
}
|
||||
if self.config.vendor || self.is_sudo {
|
||||
cargo.arg("--frozen");
|
||||
}
|
||||
|
||||
self.ci_env.force_coloring_in_ci(&mut cargo);
|
||||
|
||||
cargo
|
||||
}
|
||||
|
||||
/// Get a path to the compiler specified.
|
||||
fn compiler_path(&self, compiler: &Compiler) -> PathBuf {
|
||||
if compiler.is_snapshot(self) {
|
||||
self.initial_rustc.clone()
|
||||
} else {
|
||||
self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the specified tool built by the specified compiler
|
||||
fn tool(&self, compiler: &Compiler, tool: &str) -> PathBuf {
|
||||
self.cargo_out(compiler, Mode::Tool, compiler.host)
|
||||
.join(exe(tool, compiler.host))
|
||||
}
|
||||
|
||||
/// Get the `rustdoc` executable next to the specified compiler
|
||||
fn rustdoc(&self, compiler: &Compiler) -> PathBuf {
|
||||
let mut rustdoc = self.compiler_path(compiler);
|
||||
rustdoc.pop();
|
||||
rustdoc.push(exe("rustdoc", compiler.host));
|
||||
rustdoc
|
||||
}
|
||||
|
||||
/// Get a `Command` which is ready to run `tool` in `stage` built for
|
||||
/// `host`.
|
||||
fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command {
|
||||
let mut cmd = Command::new(self.tool(&compiler, tool));
|
||||
self.prepare_tool_cmd(compiler, &mut cmd);
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Prepares the `cmd` provided to be able to run the `compiler` provided.
|
||||
///
|
||||
/// Notably this munges the dynamic library lookup path to point to the
|
||||
/// right location to run `compiler`.
|
||||
fn prepare_tool_cmd(&self, compiler: &Compiler, cmd: &mut Command) {
|
||||
let host = compiler.host;
|
||||
let mut paths = vec![
|
||||
self.sysroot_libdir(compiler, compiler.host),
|
||||
self.cargo_out(compiler, Mode::Tool, host).join("deps"),
|
||||
];
|
||||
|
||||
// On MSVC a tool may invoke a C compiler (e.g. compiletest in run-make
|
||||
// mode) and that C compiler may need some extra PATH modification. Do
|
||||
// so here.
|
||||
if compiler.host.contains("msvc") {
|
||||
let curpaths = env::var_os("PATH").unwrap_or(OsString::new());
|
||||
let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
|
||||
for &(ref k, ref v) in self.cc[compiler.host].0.env() {
|
||||
if k != "PATH" {
|
||||
continue
|
||||
}
|
||||
for path in env::split_paths(v) {
|
||||
if !curpaths.contains(&path) {
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_lib_path(paths, cmd);
|
||||
}
|
||||
|
||||
/// Get the space-separated set of activated features for the standard
|
||||
/// library.
|
||||
fn std_features(&self) -> String {
|
||||
@ -622,51 +438,24 @@ impl Build {
|
||||
if self.config.rust_optimize {"release"} else {"debug"}
|
||||
}
|
||||
|
||||
/// Returns the sysroot for the `compiler` specified that *this build system
|
||||
/// generates*.
|
||||
///
|
||||
/// That is, the sysroot for the stage0 compiler is not what the compiler
|
||||
/// thinks it is by default, but it's the same as the default for stages
|
||||
/// 1-3.
|
||||
fn sysroot(&self, compiler: &Compiler) -> PathBuf {
|
||||
if compiler.stage == 0 {
|
||||
self.out.join(compiler.host).join("stage0-sysroot")
|
||||
} else {
|
||||
self.out.join(compiler.host).join(format!("stage{}", compiler.stage))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the directory for incremental by-products when using the
|
||||
/// given compiler.
|
||||
fn incremental_dir(&self, compiler: &Compiler) -> PathBuf {
|
||||
self.out.join(compiler.host).join(format!("stage{}-incremental", compiler.stage))
|
||||
}
|
||||
|
||||
/// Returns the libdir where the standard library and other artifacts are
|
||||
/// found for a compiler's sysroot.
|
||||
fn sysroot_libdir(&self, compiler: &Compiler, target: &str) -> PathBuf {
|
||||
if compiler.stage >= 2 {
|
||||
if let Some(ref libdir_relative) = self.config.libdir_relative {
|
||||
return self.sysroot(compiler).join(libdir_relative)
|
||||
.join("rustlib").join(target).join("lib")
|
||||
}
|
||||
}
|
||||
self.sysroot(compiler).join("lib").join("rustlib")
|
||||
.join(target).join("lib")
|
||||
fn incremental_dir(&self, compiler: Compiler) -> PathBuf {
|
||||
self.out.join(&*compiler.host).join(format!("stage{}-incremental", compiler.stage))
|
||||
}
|
||||
|
||||
/// Returns the root directory for all output generated in a particular
|
||||
/// stage when running with a particular host compiler.
|
||||
///
|
||||
/// The mode indicates what the root directory is for.
|
||||
fn stage_out(&self, compiler: &Compiler, mode: Mode) -> PathBuf {
|
||||
fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
|
||||
let suffix = match mode {
|
||||
Mode::Libstd => "-std",
|
||||
Mode::Libtest => "-test",
|
||||
Mode::Tool => "-tools",
|
||||
Mode::Librustc => "-rustc",
|
||||
};
|
||||
self.out.join(compiler.host)
|
||||
self.out.join(&*compiler.host)
|
||||
.join(format!("stage{}{}", compiler.stage, suffix))
|
||||
}
|
||||
|
||||
@ -674,42 +463,42 @@ impl Build {
|
||||
/// running a particular compiler, wehther or not we're building the
|
||||
/// standard library, and targeting the specified architecture.
|
||||
fn cargo_out(&self,
|
||||
compiler: &Compiler,
|
||||
compiler: Compiler,
|
||||
mode: Mode,
|
||||
target: &str) -> PathBuf {
|
||||
self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
|
||||
target: Interned<String>) -> PathBuf {
|
||||
self.stage_out(compiler, mode).join(&*target).join(self.cargo_dir())
|
||||
}
|
||||
|
||||
/// Root output directory for LLVM compiled for `target`
|
||||
///
|
||||
/// Note that if LLVM is configured externally then the directory returned
|
||||
/// will likely be empty.
|
||||
fn llvm_out(&self, target: &str) -> PathBuf {
|
||||
self.out.join(target).join("llvm")
|
||||
fn llvm_out(&self, target: Interned<String>) -> PathBuf {
|
||||
self.out.join(&*target).join("llvm")
|
||||
}
|
||||
|
||||
/// Output directory for all documentation for a target
|
||||
fn doc_out(&self, target: &str) -> PathBuf {
|
||||
self.out.join(target).join("doc")
|
||||
fn doc_out(&self, target: Interned<String>) -> PathBuf {
|
||||
self.out.join(&*target).join("doc")
|
||||
}
|
||||
|
||||
/// Output directory for some generated md crate documentation for a target (temporary)
|
||||
fn md_doc_out(&self, target: &str) -> PathBuf {
|
||||
self.out.join(target).join("md-doc")
|
||||
fn md_doc_out(&self, target: Interned<String>) -> Interned<PathBuf> {
|
||||
INTERNER.intern_path(self.out.join(&*target).join("md-doc"))
|
||||
}
|
||||
|
||||
/// Output directory for all crate documentation for a target (temporary)
|
||||
///
|
||||
/// The artifacts here are then copied into `doc_out` above.
|
||||
fn crate_doc_out(&self, target: &str) -> PathBuf {
|
||||
self.out.join(target).join("crate-docs")
|
||||
fn crate_doc_out(&self, target: Interned<String>) -> PathBuf {
|
||||
self.out.join(&*target).join("crate-docs")
|
||||
}
|
||||
|
||||
/// Returns true if no custom `llvm-config` is set for the specified target.
|
||||
///
|
||||
/// If no custom `llvm-config` was specified then Rust's llvm will be used.
|
||||
fn is_rust_llvm(&self, target: &str) -> bool {
|
||||
match self.config.target_config.get(target) {
|
||||
fn is_rust_llvm(&self, target: Interned<String>) -> bool {
|
||||
match self.config.target_config.get(&target) {
|
||||
Some(ref c) => c.llvm_config.is_none(),
|
||||
None => true
|
||||
}
|
||||
@ -719,25 +508,25 @@ impl Build {
|
||||
///
|
||||
/// If a custom `llvm-config` was specified for target then that's returned
|
||||
/// instead.
|
||||
fn llvm_config(&self, target: &str) -> PathBuf {
|
||||
let target_config = self.config.target_config.get(target);
|
||||
fn llvm_config(&self, target: Interned<String>) -> PathBuf {
|
||||
let target_config = self.config.target_config.get(&target);
|
||||
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
|
||||
s.clone()
|
||||
} else {
|
||||
self.llvm_out(&self.config.build).join("bin")
|
||||
.join(exe("llvm-config", target))
|
||||
self.llvm_out(self.config.build).join("bin")
|
||||
.join(exe("llvm-config", &*target))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to `FileCheck` binary for the specified target
|
||||
fn llvm_filecheck(&self, target: &str) -> PathBuf {
|
||||
let target_config = self.config.target_config.get(target);
|
||||
fn llvm_filecheck(&self, target: Interned<String>) -> PathBuf {
|
||||
let target_config = self.config.target_config.get(&target);
|
||||
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
|
||||
let llvm_bindir = output(Command::new(s).arg("--bindir"));
|
||||
Path::new(llvm_bindir.trim()).join(exe("FileCheck", target))
|
||||
Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target))
|
||||
} else {
|
||||
let base = self.llvm_out(&self.config.build).join("build");
|
||||
let exe = exe("FileCheck", target);
|
||||
let base = self.llvm_out(self.config.build).join("build");
|
||||
let exe = exe("FileCheck", &*target);
|
||||
if !self.config.ninja && self.config.build.contains("msvc") {
|
||||
base.join("Release/bin").join(exe)
|
||||
} else {
|
||||
@ -747,29 +536,16 @@ impl Build {
|
||||
}
|
||||
|
||||
/// Directory for libraries built from C/C++ code and shared between stages.
|
||||
fn native_dir(&self, target: &str) -> PathBuf {
|
||||
self.out.join(target).join("native")
|
||||
fn native_dir(&self, target: Interned<String>) -> PathBuf {
|
||||
self.out.join(&*target).join("native")
|
||||
}
|
||||
|
||||
/// Root output directory for rust_test_helpers library compiled for
|
||||
/// `target`
|
||||
fn test_helpers_out(&self, target: &str) -> PathBuf {
|
||||
fn test_helpers_out(&self, target: Interned<String>) -> PathBuf {
|
||||
self.native_dir(target).join("rust-test-helpers")
|
||||
}
|
||||
|
||||
/// Adds the compiler's directory of dynamic libraries to `cmd`'s dynamic
|
||||
/// library lookup path.
|
||||
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);
|
||||
}
|
||||
|
||||
/// Adds the `RUST_TEST_THREADS` env var if necessary
|
||||
fn add_rust_test_threads(&self, cmd: &mut Command) {
|
||||
if env::var_os("RUST_TEST_THREADS").is_none() {
|
||||
@ -777,19 +553,6 @@ impl Build {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the compiler's libdir where it stores the dynamic libraries that
|
||||
/// it itself links against.
|
||||
///
|
||||
/// For example this returns `<sysroot>/lib` on Unix and `<sysroot>/bin` on
|
||||
/// Windows.
|
||||
fn rustc_libdir(&self, compiler: &Compiler) -> PathBuf {
|
||||
if compiler.is_snapshot(self) {
|
||||
self.rustc_snapshot_libdir()
|
||||
} else {
|
||||
self.sysroot(compiler).join(libdir(compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the libdir of the snapshot compiler.
|
||||
fn rustc_snapshot_libdir(&self) -> PathBuf {
|
||||
self.initial_rustc.parent().unwrap().parent().unwrap()
|
||||
@ -846,16 +609,16 @@ impl Build {
|
||||
}
|
||||
|
||||
/// Returns the path to the C compiler for the target specified.
|
||||
fn cc(&self, target: &str) -> &Path {
|
||||
self.cc[target].0.path()
|
||||
fn cc(&self, target: Interned<String>) -> &Path {
|
||||
self.cc[&target].0.path()
|
||||
}
|
||||
|
||||
/// Returns a list of flags to pass to the C compiler for the target
|
||||
/// specified.
|
||||
fn cflags(&self, target: &str) -> Vec<String> {
|
||||
fn cflags(&self, target: Interned<String>) -> Vec<String> {
|
||||
// Filter out -O and /O (the optimization flags) that we picked up from
|
||||
// gcc-rs because the build scripts will determine that for themselves.
|
||||
let mut base = self.cc[target].0.args().iter()
|
||||
let mut base = self.cc[&target].0.args().iter()
|
||||
.map(|s| s.to_string_lossy().into_owned())
|
||||
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
|
||||
.collect::<Vec<_>>();
|
||||
@ -871,20 +634,20 @@ impl Build {
|
||||
// Work around an apparently bad MinGW / GCC optimization,
|
||||
// See: http://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html
|
||||
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936
|
||||
if target == "i686-pc-windows-gnu" {
|
||||
if &*target == "i686-pc-windows-gnu" {
|
||||
base.push("-fno-omit-frame-pointer".into());
|
||||
}
|
||||
base
|
||||
}
|
||||
|
||||
/// Returns the path to the `ar` archive utility for the target specified.
|
||||
fn ar(&self, target: &str) -> Option<&Path> {
|
||||
self.cc[target].1.as_ref().map(|p| &**p)
|
||||
fn ar(&self, target: Interned<String>) -> Option<&Path> {
|
||||
self.cc[&target].1.as_ref().map(|p| &**p)
|
||||
}
|
||||
|
||||
/// Returns the path to the C++ compiler for the target specified.
|
||||
fn cxx(&self, target: &str) -> Result<&Path, String> {
|
||||
match self.cxx.get(target) {
|
||||
fn cxx(&self, target: Interned<String>) -> Result<&Path, String> {
|
||||
match self.cxx.get(&target) {
|
||||
Some(p) => Ok(p.path()),
|
||||
None => Err(format!(
|
||||
"target `{}` is not configured as a host, only as a target",
|
||||
@ -893,7 +656,7 @@ impl Build {
|
||||
}
|
||||
|
||||
/// Returns flags to pass to the compiler to generate code for `target`.
|
||||
fn rustc_flags(&self, target: &str) -> Vec<String> {
|
||||
fn rustc_flags(&self, target: Interned<String>) -> Vec<String> {
|
||||
// New flags should be added here with great caution!
|
||||
//
|
||||
// It's quite unfortunate to **require** flags to generate code for a
|
||||
@ -910,8 +673,8 @@ impl Build {
|
||||
}
|
||||
|
||||
/// Returns the "musl root" for this `target`, if defined
|
||||
fn musl_root(&self, target: &str) -> Option<&Path> {
|
||||
self.config.target_config.get(target)
|
||||
fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
|
||||
self.config.target_config.get(&target)
|
||||
.and_then(|t| t.musl_root.as_ref())
|
||||
.or(self.config.musl_root.as_ref())
|
||||
.map(|p| &**p)
|
||||
@ -919,7 +682,7 @@ impl Build {
|
||||
|
||||
/// Returns whether the target will be tested using the `remote-test-client`
|
||||
/// and `remote-test-server` binaries.
|
||||
fn remote_tested(&self, target: &str) -> bool {
|
||||
fn remote_tested(&self, target: Interned<String>) -> bool {
|
||||
self.qemu_rootfs(target).is_some() || target.contains("android") ||
|
||||
env::var_os("TEST_DEVICE_ADDR").is_some()
|
||||
}
|
||||
@ -929,8 +692,8 @@ impl Build {
|
||||
///
|
||||
/// If `Some` is returned then that means that tests for this target are
|
||||
/// emulated with QEMU and binaries will need to be shipped to the emulator.
|
||||
fn qemu_rootfs(&self, target: &str) -> Option<&Path> {
|
||||
self.config.target_config.get(target)
|
||||
fn qemu_rootfs(&self, target: Interned<String>) -> Option<&Path> {
|
||||
self.config.target_config.get(&target)
|
||||
.and_then(|t| t.qemu_rootfs.as_ref())
|
||||
.map(|p| &**p)
|
||||
}
|
||||
@ -958,20 +721,20 @@ impl Build {
|
||||
///
|
||||
/// When all of these conditions are met the build will lift artifacts from
|
||||
/// the previous stage forward.
|
||||
fn force_use_stage1(&self, compiler: &Compiler, target: &str) -> bool {
|
||||
fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
|
||||
!self.config.full_bootstrap &&
|
||||
compiler.stage >= 2 &&
|
||||
self.config.host.iter().any(|h| h == target)
|
||||
self.config.host.iter().any(|h| *h == target)
|
||||
}
|
||||
|
||||
/// Returns the directory that OpenSSL artifacts are compiled into if
|
||||
/// configured to do so.
|
||||
fn openssl_dir(&self, target: &str) -> Option<PathBuf> {
|
||||
fn openssl_dir(&self, target: Interned<String>) -> Option<PathBuf> {
|
||||
// OpenSSL not used on Windows
|
||||
if target.contains("windows") {
|
||||
None
|
||||
} else if self.config.openssl_static {
|
||||
Some(self.out.join(target).join("openssl"))
|
||||
Some(self.out.join(&*target).join("openssl"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -979,7 +742,7 @@ impl Build {
|
||||
|
||||
/// Returns the directory that OpenSSL artifacts are installed into if
|
||||
/// configured as such.
|
||||
fn openssl_install_dir(&self, target: &str) -> Option<PathBuf> {
|
||||
fn openssl_install_dir(&self, target: Interned<String>) -> Option<PathBuf> {
|
||||
self.openssl_dir(target).map(|p| p.join("install"))
|
||||
}
|
||||
|
||||
@ -1078,16 +841,38 @@ impl Build {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of crates from a root crate.
|
||||
///
|
||||
/// Returns Vec<(crate, path to crate, is_root_crate)>
|
||||
fn crates(&self, root: &str) -> Vec<(Interned<String>, &Path)> {
|
||||
let interned = INTERNER.intern_string(root.to_owned());
|
||||
let mut ret = Vec::new();
|
||||
let mut list = vec![interned];
|
||||
let mut visited = HashSet::new();
|
||||
while let Some(krate) = list.pop() {
|
||||
let krate = &self.crates[&krate];
|
||||
// If we can't strip prefix, then out-of-tree path
|
||||
let path = krate.path.strip_prefix(&self.src).unwrap_or(&krate.path);
|
||||
ret.push((krate.name, path));
|
||||
for dep in &krate.deps {
|
||||
if visited.insert(dep) && dep != "build_helper" {
|
||||
list.push(*dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
/// Creates a new complier for the specified stage/host
|
||||
fn new(stage: u32, host: &'a str) -> Compiler<'a> {
|
||||
Compiler { stage: stage, host: host }
|
||||
impl<'a> Compiler {
|
||||
pub fn with_stage(mut self, stage: u32) -> Compiler {
|
||||
self.stage = stage;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether this is a snapshot compiler for `build`'s configuration
|
||||
fn is_snapshot(&self, build: &Build) -> bool {
|
||||
pub fn is_snapshot(&self, build: &Build) -> bool {
|
||||
self.stage == 0 && self.host == build.build
|
||||
}
|
||||
|
||||
@ -1095,7 +880,7 @@ impl<'a> Compiler<'a> {
|
||||
/// current build session.
|
||||
/// This takes into account whether we're performing a full bootstrap or
|
||||
/// not; don't directly compare the stage with `2`!
|
||||
fn is_final_stage(&self, build: &Build) -> bool {
|
||||
pub fn is_final_stage(&self, build: &Build) -> bool {
|
||||
let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
|
||||
self.stage >= final_stage
|
||||
}
|
||||
|
@ -13,17 +13,18 @@ use std::process::Command;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use build_helper::output;
|
||||
use rustc_serialize::json;
|
||||
use serde_json;
|
||||
|
||||
use {Build, Crate};
|
||||
use cache::INTERNER;
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
#[derive(Deserialize)]
|
||||
struct Output {
|
||||
packages: Vec<Package>,
|
||||
resolve: Resolve,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
#[derive(Deserialize)]
|
||||
struct Package {
|
||||
id: String,
|
||||
name: String,
|
||||
@ -32,12 +33,12 @@ struct Package {
|
||||
manifest_path: String,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
#[derive(Deserialize)]
|
||||
struct Resolve {
|
||||
nodes: Vec<ResolveNode>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
#[derive(Deserialize)]
|
||||
struct ResolveNode {
|
||||
id: String,
|
||||
dependencies: Vec<String>,
|
||||
@ -61,19 +62,20 @@ fn build_krate(build: &mut Build, krate: &str) {
|
||||
.arg("--format-version").arg("1")
|
||||
.arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml"));
|
||||
let output = output(&mut cargo);
|
||||
let output: Output = json::decode(&output).unwrap();
|
||||
let output: Output = serde_json::from_str(&output).unwrap();
|
||||
let mut id2name = HashMap::new();
|
||||
for package in output.packages {
|
||||
if package.source.is_none() {
|
||||
id2name.insert(package.id, package.name.clone());
|
||||
let name = INTERNER.intern_string(package.name);
|
||||
id2name.insert(package.id, name);
|
||||
let mut path = PathBuf::from(package.manifest_path);
|
||||
path.pop();
|
||||
build.crates.insert(package.name.clone(), Crate {
|
||||
build_step: format!("build-crate-{}", package.name),
|
||||
doc_step: format!("doc-crate-{}", package.name),
|
||||
test_step: format!("test-crate-{}", package.name),
|
||||
bench_step: format!("bench-crate-{}", package.name),
|
||||
name: package.name,
|
||||
build.crates.insert(name, Crate {
|
||||
build_step: format!("build-crate-{}", name),
|
||||
doc_step: format!("doc-crate-{}", name),
|
||||
test_step: format!("test-crate-{}", name),
|
||||
bench_step: format!("bench-crate-{}", name),
|
||||
name: name,
|
||||
version: package.version,
|
||||
deps: Vec::new(),
|
||||
path: path,
|
||||
@ -93,7 +95,7 @@ fn build_krate(build: &mut Build, krate: &str) {
|
||||
Some(dep) => dep,
|
||||
None => continue,
|
||||
};
|
||||
krate.deps.push(dep.clone());
|
||||
krate.deps.push(*dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,174 +32,193 @@ use gcc;
|
||||
use Build;
|
||||
use util;
|
||||
use build_helper::up_to_date;
|
||||
use builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
use cache::Interned;
|
||||
|
||||
/// Compile LLVM for `target`.
|
||||
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);
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Llvm {
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Llvm {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/llvm")
|
||||
}
|
||||
|
||||
/// Compile LLVM for `target`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rebuild_trigger = build.src.join("src/rustllvm/llvm-rebuild-trigger");
|
||||
let mut rebuild_trigger_contents = String::new();
|
||||
t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));
|
||||
let rebuild_trigger = build.src.join("src/rustllvm/llvm-rebuild-trigger");
|
||||
let mut rebuild_trigger_contents = String::new();
|
||||
t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));
|
||||
|
||||
let out_dir = build.llvm_out(target);
|
||||
let done_stamp = out_dir.join("llvm-finished-building");
|
||||
if done_stamp.exists() {
|
||||
let mut done_contents = String::new();
|
||||
t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
|
||||
let out_dir = build.llvm_out(target);
|
||||
let done_stamp = out_dir.join("llvm-finished-building");
|
||||
if done_stamp.exists() {
|
||||
let mut done_contents = String::new();
|
||||
t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
|
||||
|
||||
// If LLVM was already built previously and contents of the rebuild-trigger file
|
||||
// didn't change from the previous build, then no action is required.
|
||||
if done_contents == rebuild_trigger_contents {
|
||||
return
|
||||
// If LLVM was already built previously and contents of the rebuild-trigger file
|
||||
// didn't change from the previous build, then no action is required.
|
||||
if done_contents == rebuild_trigger_contents {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if build.config.llvm_clean_rebuild {
|
||||
drop(fs::remove_dir_all(&out_dir));
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "llvm");
|
||||
println!("Building LLVM for {}", target);
|
||||
let _time = util::timeit();
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
|
||||
// http://llvm.org/docs/CMake.html
|
||||
let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
|
||||
if build.config.ninja {
|
||||
cfg.generator("Ninja");
|
||||
}
|
||||
|
||||
let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
|
||||
(false, _) => "Debug",
|
||||
(true, false) => "Release",
|
||||
(true, true) => "RelWithDebInfo",
|
||||
};
|
||||
|
||||
// NOTE: remember to also update `config.toml.example` when changing the defaults!
|
||||
let llvm_targets = match build.config.llvm_targets {
|
||||
Some(ref s) => s,
|
||||
None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon",
|
||||
};
|
||||
|
||||
let llvm_exp_targets = match build.config.llvm_experimental_targets {
|
||||
Some(ref s) => s,
|
||||
None => "",
|
||||
};
|
||||
|
||||
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
||||
|
||||
cfg.target(target)
|
||||
.host(&build.build)
|
||||
.out_dir(&out_dir)
|
||||
.profile(profile)
|
||||
.define("LLVM_ENABLE_ASSERTIONS", assertions)
|
||||
.define("LLVM_TARGETS_TO_BUILD", llvm_targets)
|
||||
.define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
|
||||
.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())
|
||||
.define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
|
||||
.define("LLVM_DEFAULT_TARGET_TRIPLE", target);
|
||||
|
||||
if target.contains("msvc") {
|
||||
cfg.define("LLVM_USE_CRT_DEBUG", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELEASE", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
|
||||
cfg.static_crt(true);
|
||||
}
|
||||
|
||||
if target.starts_with("i686") {
|
||||
cfg.define("LLVM_BUILD_32_BITS", "ON");
|
||||
}
|
||||
|
||||
if let Some(num_linkers) = build.config.llvm_link_jobs {
|
||||
if num_linkers > 0 {
|
||||
cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
|
||||
if build.config.llvm_clean_rebuild {
|
||||
drop(fs::remove_dir_all(&out_dir));
|
||||
}
|
||||
}
|
||||
|
||||
// http://llvm.org/docs/HowToCrossCompileLLVM.html
|
||||
if target != build.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.build).join("bin/llvm-tblgen");
|
||||
cfg.define("CMAKE_CROSSCOMPILING", "True")
|
||||
.define("LLVM_TABLEGEN", &host);
|
||||
}
|
||||
let _folder = build.fold_output(|| "llvm");
|
||||
println!("Building LLVM for {}", target);
|
||||
let _time = util::timeit();
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
|
||||
// http://llvm.org/docs/CMake.html
|
||||
let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
|
||||
if build.config.ninja {
|
||||
cfg.generator("Ninja");
|
||||
}
|
||||
|
||||
let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
|
||||
(false, _) => "Debug",
|
||||
(true, false) => "Release",
|
||||
(true, true) => "RelWithDebInfo",
|
||||
};
|
||||
|
||||
// NOTE: remember to also update `config.toml.example` when changing the defaults!
|
||||
let llvm_targets = match build.config.llvm_targets {
|
||||
Some(ref s) => s,
|
||||
None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon",
|
||||
};
|
||||
|
||||
let llvm_exp_targets = match build.config.llvm_experimental_targets {
|
||||
Some(ref s) => s,
|
||||
None => "",
|
||||
};
|
||||
|
||||
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
||||
|
||||
cfg.target(&target)
|
||||
.host(&build.build)
|
||||
.out_dir(&out_dir)
|
||||
.profile(profile)
|
||||
.define("LLVM_ENABLE_ASSERTIONS", assertions)
|
||||
.define("LLVM_TARGETS_TO_BUILD", llvm_targets)
|
||||
.define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
|
||||
.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())
|
||||
.define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
|
||||
.define("LLVM_DEFAULT_TARGET_TRIPLE", target);
|
||||
|
||||
let sanitize_cc = |cc: &Path| {
|
||||
if target.contains("msvc") {
|
||||
OsString::from(cc.to_str().unwrap().replace("\\", "/"))
|
||||
} else {
|
||||
cc.as_os_str().to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
let configure_compilers = |cfg: &mut cmake::Config| {
|
||||
// MSVC with CMake uses msbuild by default which doesn't respect these
|
||||
// vars that we'd otherwise configure. In that case we just skip this
|
||||
// entirely.
|
||||
if target.contains("msvc") && !build.config.ninja {
|
||||
return
|
||||
cfg.define("LLVM_USE_CRT_DEBUG", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELEASE", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
|
||||
cfg.static_crt(true);
|
||||
}
|
||||
|
||||
let cc = build.cc(target);
|
||||
let cxx = build.cxx(target).unwrap();
|
||||
|
||||
// Handle msvc + ninja + ccache specially (this is what the bots use)
|
||||
if target.contains("msvc") &&
|
||||
build.config.ninja &&
|
||||
build.config.ccache.is_some() {
|
||||
let mut cc = env::current_exe().expect("failed to get cwd");
|
||||
cc.set_file_name("sccache-plus-cl.exe");
|
||||
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(&cc));
|
||||
cfg.env("SCCACHE_PATH",
|
||||
build.config.ccache.as_ref().unwrap())
|
||||
.env("SCCACHE_TARGET", target);
|
||||
|
||||
// If ccache is configured we inform the build a little differently hwo
|
||||
// to invoke ccache while also invoking our compilers.
|
||||
} else if let Some(ref ccache) = build.config.ccache {
|
||||
cfg.define("CMAKE_C_COMPILER", ccache)
|
||||
.define("CMAKE_C_COMPILER_ARG1", sanitize_cc(cc))
|
||||
.define("CMAKE_CXX_COMPILER", ccache)
|
||||
.define("CMAKE_CXX_COMPILER_ARG1", sanitize_cc(cxx));
|
||||
} else {
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(cxx));
|
||||
if target.starts_with("i686") {
|
||||
cfg.define("LLVM_BUILD_32_BITS", "ON");
|
||||
}
|
||||
|
||||
cfg.build_arg("-j").build_arg(build.jobs().to_string());
|
||||
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
|
||||
cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
|
||||
};
|
||||
if let Some(num_linkers) = build.config.llvm_link_jobs {
|
||||
if num_linkers > 0 {
|
||||
cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
configure_compilers(&mut cfg);
|
||||
// http://llvm.org/docs/HowToCrossCompileLLVM.html
|
||||
if target != build.build {
|
||||
builder.ensure(Llvm { target: build.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.build).join("bin/llvm-tblgen");
|
||||
cfg.define("CMAKE_CROSSCOMPILING", "True")
|
||||
.define("LLVM_TABLEGEN", &host);
|
||||
}
|
||||
|
||||
if env::var_os("SCCACHE_ERROR_LOG").is_some() {
|
||||
cfg.env("RUST_LOG", "sccache=warn");
|
||||
let sanitize_cc = |cc: &Path| {
|
||||
if target.contains("msvc") {
|
||||
OsString::from(cc.to_str().unwrap().replace("\\", "/"))
|
||||
} else {
|
||||
cc.as_os_str().to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
let configure_compilers = |cfg: &mut cmake::Config| {
|
||||
// MSVC with CMake uses msbuild by default which doesn't respect these
|
||||
// vars that we'd otherwise configure. In that case we just skip this
|
||||
// entirely.
|
||||
if target.contains("msvc") && !build.config.ninja {
|
||||
return
|
||||
}
|
||||
|
||||
let cc = build.cc(target);
|
||||
let cxx = build.cxx(target).unwrap();
|
||||
|
||||
// Handle msvc + ninja + ccache specially (this is what the bots use)
|
||||
if target.contains("msvc") &&
|
||||
build.config.ninja &&
|
||||
build.config.ccache.is_some() {
|
||||
let mut cc = env::current_exe().expect("failed to get cwd");
|
||||
cc.set_file_name("sccache-plus-cl.exe");
|
||||
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(&cc));
|
||||
cfg.env("SCCACHE_PATH",
|
||||
build.config.ccache.as_ref().unwrap())
|
||||
.env("SCCACHE_TARGET", target);
|
||||
|
||||
// If ccache is configured we inform the build a little differently hwo
|
||||
// to invoke ccache while also invoking our compilers.
|
||||
} else if let Some(ref ccache) = build.config.ccache {
|
||||
cfg.define("CMAKE_C_COMPILER", ccache)
|
||||
.define("CMAKE_C_COMPILER_ARG1", sanitize_cc(cc))
|
||||
.define("CMAKE_CXX_COMPILER", ccache)
|
||||
.define("CMAKE_CXX_COMPILER_ARG1", sanitize_cc(cxx));
|
||||
} else {
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(cxx));
|
||||
}
|
||||
|
||||
cfg.build_arg("-j").build_arg(build.jobs().to_string());
|
||||
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
|
||||
cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
|
||||
};
|
||||
|
||||
configure_compilers(&mut cfg);
|
||||
|
||||
if env::var_os("SCCACHE_ERROR_LOG").is_some() {
|
||||
cfg.env("RUST_LOG", "sccache=warn");
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
|
||||
}
|
||||
|
||||
fn check_llvm_version(build: &Build, llvm_config: &Path) {
|
||||
@ -216,161 +235,196 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) {
|
||||
panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
|
||||
}
|
||||
|
||||
/// Compiles the `rust_test_helpers.c` library which we used in various
|
||||
/// `run-pass` test suites for ABI testing.
|
||||
pub fn test_helpers(build: &Build, target: &str) {
|
||||
let dst = build.test_helpers_out(target);
|
||||
let src = build.src.join("src/rt/rust_test_helpers.c");
|
||||
if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
|
||||
return
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "build_test_helpers");
|
||||
println!("Building test helpers");
|
||||
t!(fs::create_dir_all(&dst));
|
||||
let mut cfg = gcc::Config::new();
|
||||
|
||||
// We may have found various cross-compilers a little differently due to our
|
||||
// extra configuration, so inform gcc of these compilers. Note, though, that
|
||||
// on MSVC we still need gcc's detection of env vars (ugh).
|
||||
if !target.contains("msvc") {
|
||||
if let Some(ar) = build.ar(target) {
|
||||
cfg.archiver(ar);
|
||||
}
|
||||
cfg.compiler(build.cc(target));
|
||||
}
|
||||
|
||||
cfg.cargo_metadata(false)
|
||||
.out_dir(&dst)
|
||||
.target(target)
|
||||
.host(&build.build)
|
||||
.opt_level(0)
|
||||
.debug(false)
|
||||
.file(build.src.join("src/rt/rust_test_helpers.c"))
|
||||
.compile("librust_test_helpers.a");
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TestHelpers {
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for TestHelpers {
|
||||
type Output = ();
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/rt/rust_test_helpers.c")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(TestHelpers { target: run.target })
|
||||
}
|
||||
|
||||
/// Compiles the `rust_test_helpers.c` library which we used in various
|
||||
/// `run-pass` test suites for ABI testing.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let dst = build.test_helpers_out(target);
|
||||
let src = build.src.join("src/rt/rust_test_helpers.c");
|
||||
if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
|
||||
return
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "build_test_helpers");
|
||||
println!("Building test helpers");
|
||||
t!(fs::create_dir_all(&dst));
|
||||
let mut cfg = gcc::Config::new();
|
||||
|
||||
// We may have found various cross-compilers a little differently due to our
|
||||
// extra configuration, so inform gcc of these compilers. Note, though, that
|
||||
// on MSVC we still need gcc's detection of env vars (ugh).
|
||||
if !target.contains("msvc") {
|
||||
if let Some(ar) = build.ar(target) {
|
||||
cfg.archiver(ar);
|
||||
}
|
||||
cfg.compiler(build.cc(target));
|
||||
}
|
||||
|
||||
cfg.cargo_metadata(false)
|
||||
.out_dir(&dst)
|
||||
.target(&target)
|
||||
.host(&build.build)
|
||||
.opt_level(0)
|
||||
.debug(false)
|
||||
.file(build.src.join("src/rt/rust_test_helpers.c"))
|
||||
.compile("librust_test_helpers.a");
|
||||
}
|
||||
}
|
||||
|
||||
const OPENSSL_VERS: &'static str = "1.0.2k";
|
||||
const OPENSSL_SHA256: &'static str =
|
||||
"6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0";
|
||||
|
||||
pub fn openssl(build: &Build, target: &str) {
|
||||
let out = match build.openssl_dir(target) {
|
||||
Some(dir) => dir,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let stamp = out.join(".stamp");
|
||||
let mut contents = String::new();
|
||||
drop(File::open(&stamp).and_then(|mut f| f.read_to_string(&mut contents)));
|
||||
if contents == OPENSSL_VERS {
|
||||
return
|
||||
}
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let name = format!("openssl-{}.tar.gz", OPENSSL_VERS);
|
||||
let tarball = out.join(&name);
|
||||
if !tarball.exists() {
|
||||
let tmp = tarball.with_extension("tmp");
|
||||
// originally from https://www.openssl.org/source/...
|
||||
let url = format!("https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/{}",
|
||||
name);
|
||||
let mut ok = false;
|
||||
for _ in 0..3 {
|
||||
let status = Command::new("curl")
|
||||
.arg("-o").arg(&tmp)
|
||||
.arg(&url)
|
||||
.status()
|
||||
.expect("failed to spawn curl");
|
||||
if status.success() {
|
||||
ok = true;
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
panic!("failed to download openssl source")
|
||||
}
|
||||
let mut shasum = if target.contains("apple") {
|
||||
let mut cmd = Command::new("shasum");
|
||||
cmd.arg("-a").arg("256");
|
||||
cmd
|
||||
} else {
|
||||
Command::new("sha256sum")
|
||||
};
|
||||
let output = output(&mut shasum.arg(&tmp));
|
||||
let found = output.split_whitespace().next().unwrap();
|
||||
if found != OPENSSL_SHA256 {
|
||||
panic!("downloaded openssl sha256 different\n\
|
||||
expected: {}\n\
|
||||
found: {}\n", OPENSSL_SHA256, found);
|
||||
}
|
||||
t!(fs::rename(&tmp, &tarball));
|
||||
}
|
||||
let obj = out.join(format!("openssl-{}", OPENSSL_VERS));
|
||||
let dst = build.openssl_install_dir(target).unwrap();
|
||||
drop(fs::remove_dir_all(&obj));
|
||||
drop(fs::remove_dir_all(&dst));
|
||||
build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));
|
||||
|
||||
let mut configure = Command::new(obj.join("Configure"));
|
||||
configure.arg(format!("--prefix={}", dst.display()));
|
||||
configure.arg("no-dso");
|
||||
configure.arg("no-ssl2");
|
||||
configure.arg("no-ssl3");
|
||||
|
||||
let os = match target {
|
||||
"aarch64-linux-android" => "linux-aarch64",
|
||||
"aarch64-unknown-linux-gnu" => "linux-aarch64",
|
||||
"arm-linux-androideabi" => "android",
|
||||
"arm-unknown-linux-gnueabi" => "linux-armv4",
|
||||
"arm-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"armv7-linux-androideabi" => "android-armv7",
|
||||
"armv7-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"i686-apple-darwin" => "darwin-i386-cc",
|
||||
"i686-linux-android" => "android-x86",
|
||||
"i686-unknown-freebsd" => "BSD-x86-elf",
|
||||
"i686-unknown-linux-gnu" => "linux-elf",
|
||||
"i686-unknown-linux-musl" => "linux-elf",
|
||||
"mips-unknown-linux-gnu" => "linux-mips32",
|
||||
"mips64-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
"mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
"mipsel-unknown-linux-gnu" => "linux-mips32",
|
||||
"powerpc-unknown-linux-gnu" => "linux-ppc",
|
||||
"powerpc64-unknown-linux-gnu" => "linux-ppc64",
|
||||
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
|
||||
"s390x-unknown-linux-gnu" => "linux64-s390x",
|
||||
"x86_64-apple-darwin" => "darwin64-x86_64-cc",
|
||||
"x86_64-linux-android" => "linux-x86_64",
|
||||
"x86_64-unknown-freebsd" => "BSD-x86_64",
|
||||
"x86_64-unknown-linux-gnu" => "linux-x86_64",
|
||||
"x86_64-unknown-linux-musl" => "linux-x86_64",
|
||||
"x86_64-unknown-netbsd" => "BSD-x86_64",
|
||||
_ => panic!("don't know how to configure OpenSSL for {}", target),
|
||||
};
|
||||
configure.arg(os);
|
||||
configure.env("CC", build.cc(target));
|
||||
for flag in build.cflags(target) {
|
||||
configure.arg(flag);
|
||||
}
|
||||
// There is no specific os target for android aarch64 or x86_64,
|
||||
// so we need to pass some extra cflags
|
||||
if target == "aarch64-linux-android" || target == "x86_64-linux-android" {
|
||||
configure.arg("-mandroid");
|
||||
configure.arg("-fomit-frame-pointer");
|
||||
}
|
||||
// Make PIE binaries
|
||||
// Non-PIE linker support was removed in Lollipop
|
||||
// https://source.android.com/security/enhancements/enhancements50
|
||||
if target == "i686-linux-android" {
|
||||
configure.arg("no-asm");
|
||||
}
|
||||
configure.current_dir(&obj);
|
||||
println!("Configuring openssl for {}", target);
|
||||
build.run_quiet(&mut configure);
|
||||
println!("Building openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
|
||||
println!("Installing openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("install").current_dir(&obj));
|
||||
|
||||
let mut f = t!(File::create(&stamp));
|
||||
t!(f.write_all(OPENSSL_VERS.as_bytes()));
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Openssl {
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Openssl {
|
||||
type Output = ();
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let out = match build.openssl_dir(target) {
|
||||
Some(dir) => dir,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let stamp = out.join(".stamp");
|
||||
let mut contents = String::new();
|
||||
drop(File::open(&stamp).and_then(|mut f| f.read_to_string(&mut contents)));
|
||||
if contents == OPENSSL_VERS {
|
||||
return
|
||||
}
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let name = format!("openssl-{}.tar.gz", OPENSSL_VERS);
|
||||
let tarball = out.join(&name);
|
||||
if !tarball.exists() {
|
||||
let tmp = tarball.with_extension("tmp");
|
||||
// originally from https://www.openssl.org/source/...
|
||||
let url = format!("https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/{}",
|
||||
name);
|
||||
let mut ok = false;
|
||||
for _ in 0..3 {
|
||||
let status = Command::new("curl")
|
||||
.arg("-o").arg(&tmp)
|
||||
.arg(&url)
|
||||
.status()
|
||||
.expect("failed to spawn curl");
|
||||
if status.success() {
|
||||
ok = true;
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
panic!("failed to download openssl source")
|
||||
}
|
||||
let mut shasum = if target.contains("apple") {
|
||||
let mut cmd = Command::new("shasum");
|
||||
cmd.arg("-a").arg("256");
|
||||
cmd
|
||||
} else {
|
||||
Command::new("sha256sum")
|
||||
};
|
||||
let output = output(&mut shasum.arg(&tmp));
|
||||
let found = output.split_whitespace().next().unwrap();
|
||||
if found != OPENSSL_SHA256 {
|
||||
panic!("downloaded openssl sha256 different\n\
|
||||
expected: {}\n\
|
||||
found: {}\n", OPENSSL_SHA256, found);
|
||||
}
|
||||
t!(fs::rename(&tmp, &tarball));
|
||||
}
|
||||
let obj = out.join(format!("openssl-{}", OPENSSL_VERS));
|
||||
let dst = build.openssl_install_dir(target).unwrap();
|
||||
drop(fs::remove_dir_all(&obj));
|
||||
drop(fs::remove_dir_all(&dst));
|
||||
build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));
|
||||
|
||||
let mut configure = Command::new(obj.join("Configure"));
|
||||
configure.arg(format!("--prefix={}", dst.display()));
|
||||
configure.arg("no-dso");
|
||||
configure.arg("no-ssl2");
|
||||
configure.arg("no-ssl3");
|
||||
|
||||
let os = match &*target {
|
||||
"aarch64-linux-android" => "linux-aarch64",
|
||||
"aarch64-unknown-linux-gnu" => "linux-aarch64",
|
||||
"arm-linux-androideabi" => "android",
|
||||
"arm-unknown-linux-gnueabi" => "linux-armv4",
|
||||
"arm-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"armv7-linux-androideabi" => "android-armv7",
|
||||
"armv7-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"i686-apple-darwin" => "darwin-i386-cc",
|
||||
"i686-linux-android" => "android-x86",
|
||||
"i686-unknown-freebsd" => "BSD-x86-elf",
|
||||
"i686-unknown-linux-gnu" => "linux-elf",
|
||||
"i686-unknown-linux-musl" => "linux-elf",
|
||||
"mips-unknown-linux-gnu" => "linux-mips32",
|
||||
"mips64-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
"mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
"mipsel-unknown-linux-gnu" => "linux-mips32",
|
||||
"powerpc-unknown-linux-gnu" => "linux-ppc",
|
||||
"powerpc64-unknown-linux-gnu" => "linux-ppc64",
|
||||
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
|
||||
"s390x-unknown-linux-gnu" => "linux64-s390x",
|
||||
"x86_64-apple-darwin" => "darwin64-x86_64-cc",
|
||||
"x86_64-linux-android" => "linux-x86_64",
|
||||
"x86_64-unknown-freebsd" => "BSD-x86_64",
|
||||
"x86_64-unknown-linux-gnu" => "linux-x86_64",
|
||||
"x86_64-unknown-linux-musl" => "linux-x86_64",
|
||||
"x86_64-unknown-netbsd" => "BSD-x86_64",
|
||||
_ => panic!("don't know how to configure OpenSSL for {}", target),
|
||||
};
|
||||
configure.arg(os);
|
||||
configure.env("CC", build.cc(target));
|
||||
for flag in build.cflags(target) {
|
||||
configure.arg(flag);
|
||||
}
|
||||
// There is no specific os target for android aarch64 or x86_64,
|
||||
// so we need to pass some extra cflags
|
||||
if target == "aarch64-linux-android" || target == "x86_64-linux-android" {
|
||||
configure.arg("-mandroid");
|
||||
configure.arg("-fomit-frame-pointer");
|
||||
}
|
||||
// Make PIE binaries
|
||||
// Non-PIE linker support was removed in Lollipop
|
||||
// https://source.android.com/security/enhancements/enhancements50
|
||||
if target == "i686-linux-android" {
|
||||
configure.arg("no-asm");
|
||||
}
|
||||
configure.current_dir(&obj);
|
||||
println!("Configuring openssl for {}", target);
|
||||
build.run_quiet(&mut configure);
|
||||
println!("Building openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
|
||||
println!("Installing openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("install").current_dir(&obj));
|
||||
|
||||
let mut f = t!(File::create(&stamp));
|
||||
t!(f.write_all(OPENSSL_VERS.as_bytes()));
|
||||
}
|
||||
}
|
||||
|
@ -122,14 +122,14 @@ pub fn check(build: &mut Build) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd_finder.must_have(build.cc(target));
|
||||
if let Some(ar) = build.ar(target) {
|
||||
cmd_finder.must_have(build.cc(*target));
|
||||
if let Some(ar) = build.ar(*target) {
|
||||
cmd_finder.must_have(ar);
|
||||
}
|
||||
}
|
||||
|
||||
for host in build.config.host.iter() {
|
||||
cmd_finder.must_have(build.cxx(host).unwrap());
|
||||
cmd_finder.must_have(build.cxx(*host).unwrap());
|
||||
|
||||
// The msvc hosts don't use jemalloc, turn it off globally to
|
||||
// avoid packaging the dummy liballoc_jemalloc on that platform.
|
||||
@ -139,7 +139,7 @@ pub fn check(build: &mut Build) {
|
||||
}
|
||||
|
||||
// Externally configured LLVM requires FileCheck to exist
|
||||
let filecheck = build.llvm_filecheck(&build.build);
|
||||
let filecheck = build.llvm_filecheck(build.build);
|
||||
if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
|
||||
panic!("FileCheck executable {:?} does not exist", filecheck);
|
||||
}
|
||||
@ -153,7 +153,7 @@ pub fn check(build: &mut Build) {
|
||||
|
||||
// Make sure musl-root is valid if specified
|
||||
if target.contains("musl") && !target.contains("mips") {
|
||||
match build.musl_root(target) {
|
||||
match build.musl_root(*target) {
|
||||
Some(root) => {
|
||||
if fs::metadata(root.join("lib/libc.a")).is_err() {
|
||||
panic!("couldn't find libc.a in musl dir: {}",
|
||||
|
File diff suppressed because it is too large
Load Diff
353
src/bootstrap/tool.rs
Normal file
353
src/bootstrap/tool.rs
Normal file
@ -0,0 +1,353 @@
|
||||
// Copyright 2017 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::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use Mode;
|
||||
use Compiler;
|
||||
use builder::{Step, RunConfig, ShouldRun, Builder};
|
||||
use util::{exe, add_lib_path};
|
||||
use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
|
||||
use native;
|
||||
use channel::GitInfo;
|
||||
use cache::Interned;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct CleanTools {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
impl Step for CleanTools {
|
||||
type Output = ();
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
/// Build a tool in `src/tools`
|
||||
///
|
||||
/// This will build the specified tool with the specified `host` compiler in
|
||||
/// `stage` into the normal cargo output directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
let mode = self.mode;
|
||||
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
|
||||
let stamp = match mode {
|
||||
Mode::Libstd => libstd_stamp(build, compiler, target),
|
||||
Mode::Libtest => libtest_stamp(build, compiler, target),
|
||||
Mode::Librustc => librustc_stamp(build, compiler, target),
|
||||
_ => panic!(),
|
||||
};
|
||||
let out_dir = build.cargo_out(compiler, Mode::Tool, target);
|
||||
build.clear_if_dirty(&out_dir, &stamp);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct ToolBuild {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
pub tool: &'static str,
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
impl Step for ToolBuild {
|
||||
type Output = PathBuf;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.never()
|
||||
}
|
||||
|
||||
/// Build a tool in `src/tools`
|
||||
///
|
||||
/// This will build the specified tool with the specified `host` compiler in
|
||||
/// `stage` into the normal cargo output directory.
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
let tool = self.tool;
|
||||
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
builder.ensure(CleanTools { stage, target, mode: self.mode });
|
||||
match self.mode {
|
||||
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
||||
Mode::Libtest => builder.ensure(compile::Test { compiler, target }),
|
||||
Mode::Librustc => builder.ensure(compile::Rustc { compiler, target }),
|
||||
Mode::Tool => panic!("unexpected Mode::Tool for tool build")
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-{}", stage, tool));
|
||||
println!("Building stage{} tool {} ({})", stage, tool, target);
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build");
|
||||
let dir = build.src.join("src/tools").join(tool);
|
||||
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
|
||||
|
||||
// We don't want to build tools dynamically as they'll be running across
|
||||
// stages and such and it's just easier if they're not dynamically linked.
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
|
||||
if let Some(dir) = build.openssl_install_dir(target) {
|
||||
cargo.env("OPENSSL_STATIC", "1");
|
||||
cargo.env("OPENSSL_DIR", dir);
|
||||
cargo.env("LIBZ_SYS_STATIC", "1");
|
||||
}
|
||||
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel);
|
||||
|
||||
let info = GitInfo::new(&dir);
|
||||
if let Some(sha) = info.sha() {
|
||||
cargo.env("CFG_COMMIT_HASH", sha);
|
||||
}
|
||||
if let Some(sha_short) = info.sha_short() {
|
||||
cargo.env("CFG_SHORT_COMMIT_HASH", sha_short);
|
||||
}
|
||||
if let Some(date) = info.commit_date() {
|
||||
cargo.env("CFG_COMMIT_DATE", date);
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tool {
|
||||
($($name:ident, $path:expr, $tool_name:expr, $mode:expr;)+) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Tool {
|
||||
$(
|
||||
$name,
|
||||
)+
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub fn tool_exe(&self, tool: Tool) -> PathBuf {
|
||||
match tool {
|
||||
$(Tool::$name =>
|
||||
self.ensure($name {
|
||||
stage: 0,
|
||||
target: self.build.build,
|
||||
}),
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct $name {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for $name {
|
||||
type Output = PathBuf;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path($path)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
builder.ensure(ToolBuild {
|
||||
stage: self.stage,
|
||||
target: self.target,
|
||||
tool: $tool_name,
|
||||
mode: $mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
tool!(
|
||||
Rustbook, "src/tools/rustbook", "rustbook", Mode::Librustc;
|
||||
ErrorIndex, "src/tools/error_index_generator", "error_index_generator", Mode::Librustc;
|
||||
UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen", Mode::Libstd;
|
||||
Tidy, "src/tools/tidy", "tidy", Mode::Libstd;
|
||||
Linkchecker, "src/tools/linkchecker", "linkchecker", Mode::Libstd;
|
||||
CargoTest, "src/tools/cargotest", "cargotest", Mode::Libstd;
|
||||
Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest;
|
||||
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Librustc;
|
||||
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::Libstd;
|
||||
RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::Libstd;
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct RemoteTestServer {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for RemoteTestServer {
|
||||
type Output = PathBuf;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/tools/remote-test-server")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(RemoteTestServer {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
builder.ensure(ToolBuild {
|
||||
stage: self.stage,
|
||||
target: self.target,
|
||||
tool: "remote-test-server",
|
||||
mode: Mode::Libstd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Cargo {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Cargo {
|
||||
type Output = PathBuf;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/cargo").default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Cargo {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
builder.ensure(native::Openssl {
|
||||
target: self.target,
|
||||
});
|
||||
// Cargo depends on procedural macros, which requires a full host
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: builder.compiler(self.stage, builder.build.build),
|
||||
target: builder.build.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
stage: self.stage,
|
||||
target: self.target,
|
||||
tool: "cargo",
|
||||
mode: Mode::Libstd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rls {
|
||||
pub stage: u32,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rls {
|
||||
type Output = PathBuf;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/rls").default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rls {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
builder.ensure(native::Openssl {
|
||||
target: self.target,
|
||||
});
|
||||
// RLS depends on procedural macros, which requires a full host
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: builder.compiler(self.stage, builder.build.build),
|
||||
target: builder.build.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
stage: self.stage,
|
||||
target: self.target,
|
||||
tool: "rls",
|
||||
mode: Mode::Librustc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
/// Get a `Command` which is ready to run `tool` in `stage` built for
|
||||
/// `host`.
|
||||
pub fn tool_cmd(&self, tool: Tool) -> Command {
|
||||
let mut cmd = Command::new(self.tool_exe(tool));
|
||||
let compiler = self.compiler(0, self.build.build);
|
||||
self.prepare_tool_cmd(compiler, &mut cmd);
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Prepares the `cmd` provided to be able to run the `compiler` provided.
|
||||
///
|
||||
/// Notably this munges the dynamic library lookup path to point to the
|
||||
/// right location to run `compiler`.
|
||||
fn prepare_tool_cmd(&self, compiler: Compiler, cmd: &mut Command) {
|
||||
let host = &compiler.host;
|
||||
let mut paths: Vec<PathBuf> = vec![
|
||||
PathBuf::from(&self.sysroot_libdir(compiler, compiler.host)),
|
||||
self.cargo_out(compiler, Mode::Tool, *host).join("deps"),
|
||||
];
|
||||
|
||||
// On MSVC a tool may invoke a C compiler (e.g. compiletest in run-make
|
||||
// mode) and that C compiler may need some extra PATH modification. Do
|
||||
// so here.
|
||||
if compiler.host.contains("msvc") {
|
||||
let curpaths = env::var_os("PATH").unwrap_or_default();
|
||||
let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
|
||||
for &(ref k, ref v) in self.cc[&compiler.host].0.env() {
|
||||
if k != "PATH" {
|
||||
continue
|
||||
}
|
||||
for path in env::split_paths(v) {
|
||||
if !curpaths.contains(&path) {
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_lib_path(paths, cmd);
|
||||
}
|
||||
}
|
@ -4,5 +4,6 @@ version = "0.1.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
|
||||
[dependencies]
|
||||
toml = "0.1"
|
||||
rustc-serialize = "0.3"
|
||||
toml = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
@ -9,7 +9,9 @@
|
||||
// except according to those terms.
|
||||
|
||||
extern crate toml;
|
||||
extern crate rustc_serialize;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
@ -99,19 +101,21 @@ static MINGW: &'static [&'static str] = &[
|
||||
"x86_64-pc-windows-gnu",
|
||||
];
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct Manifest {
|
||||
manifest_version: String,
|
||||
date: String,
|
||||
pkg: BTreeMap<String, Package>,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable)]
|
||||
#[derive(Serialize)]
|
||||
struct Package {
|
||||
version: String,
|
||||
target: BTreeMap<String, Target>,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable)]
|
||||
#[derive(Serialize)]
|
||||
struct Target {
|
||||
available: bool,
|
||||
url: Option<String>,
|
||||
@ -136,7 +140,7 @@ impl Target {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable)]
|
||||
#[derive(Serialize)]
|
||||
struct Component {
|
||||
pkg: String,
|
||||
target: String,
|
||||
@ -199,28 +203,16 @@ impl Builder {
|
||||
self.rls_version = self.version("rls", "x86_64-unknown-linux-gnu");
|
||||
|
||||
self.digest_and_sign();
|
||||
let Manifest { manifest_version, date, pkg } = self.build_manifest();
|
||||
|
||||
// Unfortunately we can't use derive(RustcEncodable) here because the
|
||||
// version field is called `manifest-version`, not `manifest_version`.
|
||||
// In lieu of that just create the table directly here with a `BTreeMap`
|
||||
// and wrap it up in a `Value::Table`.
|
||||
let mut manifest = BTreeMap::new();
|
||||
manifest.insert("manifest-version".to_string(),
|
||||
toml::Value::String(manifest_version));
|
||||
manifest.insert("date".to_string(), toml::Value::String(date.clone()));
|
||||
manifest.insert("pkg".to_string(), toml::encode(&pkg));
|
||||
let manifest = toml::Value::Table(manifest).to_string();
|
||||
|
||||
let manifest = self.build_manifest();
|
||||
let filename = format!("channel-rust-{}.toml", self.rust_release);
|
||||
self.write_manifest(&manifest, &filename);
|
||||
self.write_manifest(&toml::to_string(&manifest).unwrap(), &filename);
|
||||
|
||||
let filename = format!("channel-rust-{}-date.txt", self.rust_release);
|
||||
self.write_date_stamp(&date, &filename);
|
||||
self.write_date_stamp(&manifest.date, &filename);
|
||||
|
||||
if self.rust_release != "beta" && self.rust_release != "nightly" {
|
||||
self.write_manifest(&manifest, "channel-rust-stable.toml");
|
||||
self.write_date_stamp(&date, "channel-rust-stable-date.txt");
|
||||
self.write_manifest(&toml::to_string(&manifest).unwrap(), "channel-rust-stable.toml");
|
||||
self.write_date_stamp(&manifest.date, "channel-rust-stable-date.txt");
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user