mirror of
https://git.proxmox.com/git/rustc
synced 2025-06-06 01:51:15 +00:00
548 lines
18 KiB
Rust
548 lines
18 KiB
Rust
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::fs::{self, File};
|
|
use std::path::{PathBuf, Path};
|
|
use std::process::Command;
|
|
|
|
use build_helper::{run_silent, output};
|
|
use gcc;
|
|
use num_cpus;
|
|
|
|
use build::util::{exe, mtime, libdir, add_lib_path};
|
|
|
|
macro_rules! t {
|
|
($e:expr) => (match $e {
|
|
Ok(e) => e,
|
|
Err(e) => panic!("{} failed with {}", stringify!($e), e),
|
|
})
|
|
}
|
|
|
|
mod cc;
|
|
mod channel;
|
|
mod check;
|
|
mod clean;
|
|
mod compile;
|
|
mod config;
|
|
mod dist;
|
|
mod doc;
|
|
mod flags;
|
|
mod native;
|
|
mod sanity;
|
|
mod step;
|
|
mod util;
|
|
|
|
#[cfg(windows)]
|
|
mod job;
|
|
|
|
#[cfg(not(windows))]
|
|
mod job {
|
|
pub unsafe fn setup() {}
|
|
}
|
|
|
|
pub use build::config::Config;
|
|
pub use build::flags::Flags;
|
|
|
|
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
|
|
pub struct Compiler<'a> {
|
|
stage: u32,
|
|
host: &'a str,
|
|
}
|
|
|
|
pub struct Build {
|
|
// User-specified configuration via config.toml
|
|
config: Config,
|
|
|
|
// User-specified configuration via CLI flags
|
|
flags: Flags,
|
|
|
|
// Derived properties from the above two configurations
|
|
cargo: PathBuf,
|
|
rustc: PathBuf,
|
|
src: PathBuf,
|
|
out: PathBuf,
|
|
release: String,
|
|
unstable_features: bool,
|
|
ver_hash: Option<String>,
|
|
short_ver_hash: Option<String>,
|
|
ver_date: Option<String>,
|
|
version: String,
|
|
package_vers: String,
|
|
bootstrap_key: String,
|
|
|
|
// Runtime state filled in later on
|
|
cc: HashMap<String, (gcc::Tool, PathBuf)>,
|
|
cxx: HashMap<String, gcc::Tool>,
|
|
compiler_rt_built: RefCell<HashMap<String, PathBuf>>,
|
|
}
|
|
|
|
pub enum Mode {
|
|
Libstd,
|
|
Libtest,
|
|
Librustc,
|
|
Tool,
|
|
}
|
|
|
|
impl Build {
|
|
pub fn new(flags: Flags, config: Config) -> Build {
|
|
let cwd = t!(env::current_dir());
|
|
let src = flags.src.clone().unwrap_or(cwd.clone());
|
|
let out = cwd.join("build");
|
|
|
|
let stage0_root = out.join(&config.build).join("stage0/bin");
|
|
let rustc = match config.rustc {
|
|
Some(ref s) => PathBuf::from(s),
|
|
None => stage0_root.join(exe("rustc", &config.build)),
|
|
};
|
|
let cargo = match config.cargo {
|
|
Some(ref s) => PathBuf::from(s),
|
|
None => stage0_root.join(exe("cargo", &config.build)),
|
|
};
|
|
|
|
Build {
|
|
flags: flags,
|
|
config: config,
|
|
cargo: cargo,
|
|
rustc: rustc,
|
|
src: src,
|
|
out: out,
|
|
|
|
release: String::new(),
|
|
unstable_features: false,
|
|
ver_hash: None,
|
|
short_ver_hash: None,
|
|
ver_date: None,
|
|
version: String::new(),
|
|
bootstrap_key: String::new(),
|
|
package_vers: String::new(),
|
|
cc: HashMap::new(),
|
|
cxx: HashMap::new(),
|
|
compiler_rt_built: RefCell::new(HashMap::new()),
|
|
}
|
|
}
|
|
|
|
pub fn build(&mut self) {
|
|
use build::step::Source::*;
|
|
|
|
unsafe {
|
|
job::setup();
|
|
}
|
|
|
|
if self.flags.clean {
|
|
return clean::clean(self);
|
|
}
|
|
|
|
self.verbose("finding compilers");
|
|
cc::find(self);
|
|
self.verbose("running sanity check");
|
|
sanity::check(self);
|
|
self.verbose("collecting channel variables");
|
|
channel::collect(self);
|
|
self.verbose("updating submodules");
|
|
self.update_submodules();
|
|
|
|
for target in step::all(self) {
|
|
let doc_out = self.out.join(&target.target).join("doc");
|
|
match target.src {
|
|
Llvm { _dummy } => {
|
|
native::llvm(self, target.target);
|
|
}
|
|
CompilerRt { _dummy } => {
|
|
native::compiler_rt(self, target.target);
|
|
}
|
|
Libstd { compiler } => {
|
|
compile::std(self, target.target, &compiler);
|
|
}
|
|
Libtest { compiler } => {
|
|
compile::test(self, target.target, &compiler);
|
|
}
|
|
Librustc { compiler } => {
|
|
compile::rustc(self, target.target, &compiler);
|
|
}
|
|
LibstdLink { compiler, host } => {
|
|
compile::std_link(self, target.target, &compiler, host);
|
|
}
|
|
LibtestLink { compiler, host } => {
|
|
compile::test_link(self, target.target, &compiler, host);
|
|
}
|
|
LibrustcLink { compiler, host } => {
|
|
compile::rustc_link(self, target.target, &compiler, host);
|
|
}
|
|
Rustc { stage: 0 } => {
|
|
// nothing to do...
|
|
}
|
|
Rustc { stage } => {
|
|
compile::assemble_rustc(self, stage, target.target);
|
|
}
|
|
ToolLinkchecker { stage } => {
|
|
compile::tool(self, stage, target.target, "linkchecker");
|
|
}
|
|
ToolRustbook { stage } => {
|
|
compile::tool(self, stage, target.target, "rustbook");
|
|
}
|
|
ToolErrorIndex { stage } => {
|
|
compile::tool(self, stage, target.target,
|
|
"error_index_generator");
|
|
}
|
|
ToolCargoTest { stage } => {
|
|
compile::tool(self, stage, target.target, "cargotest");
|
|
}
|
|
DocBook { stage } => {
|
|
doc::rustbook(self, stage, target.target, "book", &doc_out);
|
|
}
|
|
DocNomicon { stage } => {
|
|
doc::rustbook(self, stage, target.target, "nomicon",
|
|
&doc_out);
|
|
}
|
|
DocStyle { stage } => {
|
|
doc::rustbook(self, stage, target.target, "style",
|
|
&doc_out);
|
|
}
|
|
DocStandalone { stage } => {
|
|
doc::standalone(self, stage, target.target, &doc_out);
|
|
}
|
|
DocStd { stage } => {
|
|
doc::std(self, stage, target.target, &doc_out);
|
|
}
|
|
DocTest { stage } => {
|
|
doc::test(self, stage, target.target, &doc_out);
|
|
}
|
|
DocRustc { stage } => {
|
|
doc::rustc(self, stage, target.target, &doc_out);
|
|
}
|
|
DocErrorIndex { stage } => {
|
|
doc::error_index(self, stage, target.target, &doc_out);
|
|
}
|
|
|
|
CheckLinkcheck { stage } => {
|
|
check::linkcheck(self, stage, target.target);
|
|
}
|
|
CheckCargoTest { stage } => {
|
|
check::cargotest(self, stage, target.target);
|
|
}
|
|
|
|
DistDocs { stage } => dist::docs(self, stage, target.target),
|
|
DistMingw { _dummy } => dist::mingw(self, target.target),
|
|
DistRustc { stage } => dist::rustc(self, stage, target.target),
|
|
DistStd { compiler } => dist::std(self, &compiler, target.target),
|
|
|
|
Dist { .. } |
|
|
Doc { .. } | // pseudo-steps
|
|
Check { .. } => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update_submodules(&self) {
|
|
if !self.config.submodules {
|
|
return
|
|
}
|
|
if fs::metadata(self.src.join(".git")).is_err() {
|
|
return
|
|
}
|
|
let git_submodule = || {
|
|
let mut cmd = Command::new("git");
|
|
cmd.current_dir(&self.src).arg("submodule");
|
|
return cmd
|
|
};
|
|
let out = output(git_submodule().arg("status"));
|
|
if !out.lines().any(|l| l.starts_with("+") || l.starts_with("-")) {
|
|
return
|
|
}
|
|
|
|
self.run(git_submodule().arg("sync"));
|
|
self.run(git_submodule().arg("init"));
|
|
self.run(git_submodule().arg("update"));
|
|
self.run(git_submodule().arg("update").arg("--recursive"));
|
|
self.run(git_submodule().arg("status").arg("--recursive"));
|
|
self.run(git_submodule().arg("foreach").arg("--recursive")
|
|
.arg("git").arg("clean").arg("-fdx"));
|
|
self.run(git_submodule().arg("foreach").arg("--recursive")
|
|
.arg("git").arg("checkout").arg("."));
|
|
}
|
|
|
|
/// Clear out `dir` if our build has been flagged as dirty, and also set
|
|
/// ourselves as dirty if `file` changes when `f` is executed.
|
|
fn clear_if_dirty(&self, dir: &Path, input: &Path) {
|
|
let stamp = dir.join(".stamp");
|
|
if mtime(&stamp) < mtime(input) {
|
|
self.verbose(&format!("Dirty - {}", dir.display()));
|
|
let _ = fs::remove_dir_all(dir);
|
|
}
|
|
t!(fs::create_dir_all(dir));
|
|
t!(File::create(stamp));
|
|
}
|
|
|
|
/// Prepares an invocation of `cargo` to be run.
|
|
///
|
|
/// This will create a `Command` that represents a pending execution of
|
|
/// Cargo for the specified stage, whether or not the standard library is
|
|
/// being built, and using the specified compiler targeting `target`.
|
|
fn cargo(&self,
|
|
compiler: &Compiler,
|
|
mode: Mode,
|
|
target: &str,
|
|
cmd: &str) -> Command {
|
|
let mut cargo = Command::new(&self.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);
|
|
|
|
// Customize the compiler we're running. Specify the compiler to cargo
|
|
// as our shim and then pass it some various options used to configure
|
|
// how the actual compiler itself is called.
|
|
cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
|
|
.env("RUSTC_REAL", self.compiler_path(compiler))
|
|
.env("RUSTC_STAGE", compiler.stage.to_string())
|
|
.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
|
|
.env("RUSTC_CODEGEN_UNITS",
|
|
self.config.rust_codegen_units.to_string())
|
|
.env("RUSTC_DEBUG_ASSERTIONS",
|
|
self.config.rust_debug_assertions.to_string())
|
|
.env("RUSTC_SNAPSHOT", &self.rustc)
|
|
.env("RUSTC_SYSROOT", self.sysroot(compiler))
|
|
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir())
|
|
.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(" "));
|
|
|
|
// 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))
|
|
.env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
|
|
}
|
|
|
|
// If we're building for OSX, inform the compiler and the linker that
|
|
// we want to build a compiler runnable on 10.7
|
|
if target.contains("apple-darwin") {
|
|
cargo.env("MACOSX_DEPLOYMENT_TARGET", "10.7");
|
|
}
|
|
|
|
// Environment variables *required* needed throughout the build
|
|
//
|
|
// FIXME: should update code to not require this env vars
|
|
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
|
|
|
|
if self.config.verbose || self.flags.verbose {
|
|
cargo.arg("-v");
|
|
}
|
|
if self.config.rust_optimize {
|
|
cargo.arg("--release");
|
|
}
|
|
self.add_rustc_lib_path(compiler, &mut cargo);
|
|
return cargo
|
|
}
|
|
|
|
/// Get a path to the compiler specified.
|
|
fn compiler_path(&self, compiler: &Compiler) -> PathBuf {
|
|
if compiler.is_snapshot(self) {
|
|
self.rustc.clone()
|
|
} else {
|
|
self.sysroot(compiler).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));
|
|
return 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));
|
|
let host = compiler.host;
|
|
let paths = vec![
|
|
self.cargo_out(compiler, Mode::Libstd, host).join("deps"),
|
|
self.cargo_out(compiler, Mode::Libtest, host).join("deps"),
|
|
self.cargo_out(compiler, Mode::Librustc, host).join("deps"),
|
|
];
|
|
add_lib_path(paths, &mut cmd);
|
|
return cmd
|
|
}
|
|
|
|
/// Get the space-separated set of activated features for the standard
|
|
/// library.
|
|
fn std_features(&self) -> String {
|
|
let mut features = String::new();
|
|
if self.config.debug_jemalloc {
|
|
features.push_str(" debug-jemalloc");
|
|
}
|
|
if self.config.use_jemalloc {
|
|
features.push_str(" jemalloc");
|
|
}
|
|
return features
|
|
}
|
|
|
|
/// Get the space-separated set of activated features for the compiler.
|
|
fn rustc_features(&self) -> String {
|
|
let mut features = String::new();
|
|
if self.config.use_jemalloc {
|
|
features.push_str(" jemalloc");
|
|
}
|
|
return features
|
|
}
|
|
|
|
/// Component directory that Cargo will produce output into (e.g.
|
|
/// release/debug)
|
|
fn cargo_dir(&self) -> &'static str {
|
|
if self.config.rust_optimize {"release"} else {"debug"}
|
|
}
|
|
|
|
fn sysroot(&self, 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))
|
|
}
|
|
}
|
|
|
|
fn sysroot_libdir(&self, compiler: &Compiler, target: &str) -> PathBuf {
|
|
self.sysroot(compiler).join("lib").join("rustlib")
|
|
.join(target).join("lib")
|
|
}
|
|
|
|
/// Returns the root directory for all output generated in a particular
|
|
/// stage when running with a particular host compiler.
|
|
///
|
|
/// The mode indicates what the root directory is for.
|
|
fn stage_out(&self, compiler: &Compiler, mode: Mode) -> PathBuf {
|
|
let suffix = match mode {
|
|
Mode::Libstd => "-std",
|
|
Mode::Libtest => "-test",
|
|
Mode::Tool | Mode::Librustc => "-rustc",
|
|
};
|
|
self.out.join(compiler.host)
|
|
.join(format!("stage{}{}", compiler.stage, suffix))
|
|
}
|
|
|
|
/// Returns the root output directory for all Cargo output in a given stage,
|
|
/// running a particular comipler, wehther or not we're building the
|
|
/// standard library, and targeting the specified architecture.
|
|
fn cargo_out(&self,
|
|
compiler: &Compiler,
|
|
mode: Mode,
|
|
target: &str) -> PathBuf {
|
|
self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
|
|
}
|
|
|
|
/// Root output directory for LLVM compiled for `target`
|
|
fn llvm_out(&self, target: &str) -> PathBuf {
|
|
self.out.join(target).join("llvm")
|
|
}
|
|
|
|
/// Root output directory for compiler-rt compiled for `target`
|
|
fn compiler_rt_out(&self, target: &str) -> PathBuf {
|
|
self.out.join(target).join("compiler-rt")
|
|
}
|
|
|
|
fn add_rustc_lib_path(&self, compiler: &Compiler, cmd: &mut Command) {
|
|
// Windows doesn't need dylib path munging because the dlls for the
|
|
// compiler live next to the compiler and the system will find them
|
|
// automatically.
|
|
if cfg!(windows) { return }
|
|
|
|
add_lib_path(vec![self.rustc_libdir(compiler)], cmd);
|
|
}
|
|
|
|
fn rustc_libdir(&self, compiler: &Compiler) -> PathBuf {
|
|
if compiler.is_snapshot(self) {
|
|
self.rustc_snapshot_libdir()
|
|
} else {
|
|
self.sysroot(compiler).join(libdir(compiler.host))
|
|
}
|
|
}
|
|
|
|
fn rustc_snapshot_libdir(&self) -> PathBuf {
|
|
self.rustc.parent().unwrap().parent().unwrap()
|
|
.join(libdir(&self.config.build))
|
|
}
|
|
|
|
fn run(&self, cmd: &mut Command) {
|
|
self.verbose(&format!("running: {:?}", cmd));
|
|
run_silent(cmd)
|
|
}
|
|
|
|
fn verbose(&self, msg: &str) {
|
|
if self.flags.verbose || self.config.verbose {
|
|
println!("{}", msg);
|
|
}
|
|
}
|
|
|
|
fn jobs(&self) -> u32 {
|
|
self.flags.jobs.unwrap_or(num_cpus::get() as u32)
|
|
}
|
|
|
|
fn cc(&self, target: &str) -> &Path {
|
|
self.cc[target].0.path()
|
|
}
|
|
|
|
fn cflags(&self, target: &str) -> Vec<String> {
|
|
let mut base = self.cc[target].0.args().iter()
|
|
.map(|s| s.to_string_lossy().into_owned())
|
|
.collect::<Vec<_>>();
|
|
|
|
// If we're compiling on OSX then we add a few unconditional flags
|
|
// indicating that we want libc++ (more filled out than libstdc++) and
|
|
// we want to compile for 10.7. This way we can ensure that
|
|
// LLVM/jemalloc/etc are all properly compiled.
|
|
if target.contains("apple-darwin") {
|
|
base.push("-stdlib=libc++".into());
|
|
base.push("-mmacosx-version-min=10.7".into());
|
|
}
|
|
return base
|
|
}
|
|
|
|
fn ar(&self, target: &str) -> &Path {
|
|
&self.cc[target].1
|
|
}
|
|
|
|
fn cxx(&self, target: &str) -> &Path {
|
|
self.cxx[target].path()
|
|
}
|
|
|
|
fn rustc_flags(&self, target: &str) -> Vec<String> {
|
|
let mut base = Vec::new();
|
|
if target != self.config.build && !target.contains("msvc") {
|
|
base.push(format!("-Clinker={}", self.cc(target).display()));
|
|
}
|
|
return base
|
|
}
|
|
}
|
|
|
|
impl<'a> Compiler<'a> {
|
|
fn new(stage: u32, host: &'a str) -> Compiler<'a> {
|
|
Compiler { stage: stage, host: host }
|
|
}
|
|
|
|
fn is_snapshot(&self, build: &Build) -> bool {
|
|
self.stage == 0 && self.host == build.config.build
|
|
}
|
|
}
|