diff --git a/build_rust/.gitignore b/build_rust/.gitignore new file mode 100644 index 0000000000000..36b6ef17a527e --- /dev/null +++ b/build_rust/.gitignore @@ -0,0 +1,9 @@ +target +*racertmp +x86_64* +*~ +dl +*# +*.o +*.a +unpack diff --git a/build_rust/Cargo.lock b/build_rust/Cargo.lock new file mode 100644 index 0000000000000..6de74a2acb15a --- /dev/null +++ b/build_rust/Cargo.lock @@ -0,0 +1,34 @@ +[root] +name = "build-rust" +version = "0.1.0" +dependencies = [ + "getopts 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getopts" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/build_rust/Cargo.toml b/build_rust/Cargo.toml new file mode 100644 index 0000000000000..db7443f1a59c8 --- /dev/null +++ b/build_rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "build-rust" +version = "0.1.0" +authors = ["Chang Liu "] + +build = "build.rs" + +[[bin]] +name = "build-rust" +path = "src/main.rs" + +[dependencies] +getopts = "*" +regex = "*" diff --git a/build_rust/README.md b/build_rust/README.md new file mode 100644 index 0000000000000..cc1406d5b3370 --- /dev/null +++ b/build_rust/README.md @@ -0,0 +1,36 @@ +The Rust Build System +===================== + +The Rust Build System is written in Rust and is managed as a Cargo package. +Building the latest Rust compiler is as simple as running: + +```sh +$ cargo run +``` + +under this directory (where the build system is). The Rust Build System will +automatically build all supporting libraries (including LLVM) and bootstrap +a working stage2 compiler. + +To speed up the build process by running parallel jobs, use `--nproc`: + +```sh +$ cargo run -- --nproc=4 +``` + +This will run 4 parallel jobs when building LLVM. + +To show the command output during the build process, use `--verbose`: + +```sh +$ cargo run -- --verbose +``` + +You can use `--no-bootstrap`, `--no-rebuild-llvm`, etc to control the build +process. + +This build system supports out-of-tree build. Use `--rustc-root=` to +specify the location of the source repo. Use `--build-dir=` to specify +the root build directory. + +Use `--help` to see a list of supported command line arguments. diff --git a/build_rust/build.rs b/build_rust/build.rs new file mode 100644 index 0000000000000..dfd4a6236b9c0 --- /dev/null +++ b/build_rust/build.rs @@ -0,0 +1,84 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Collect build environment info and pass on to configure.rs +//! +//! The only two pieces of information we currently collect are +//! the triple spec (arch-vendor-os-abi) of the build machine +//! and the manifest directory of this Cargo package. The latter +//! is used to determine where the Rust source code is. +//! +//! Once finished collecting said variables this script will +//! write them into the file build_env.rs which configure.rs +//! will then include it (at compile time). + +struct BuildEnv { + build_triple : String, + manifest_dir : String +} + +/// On error, we will convert all errors into an error string, +/// display the error string and then panic. +struct ErrMsg { + msg : String +} + +impl std::convert::From for ErrMsg { + fn from(err : T) -> ErrMsg { + ErrMsg { msg : err.description().to_string() } + } +} + +type Result = std::result::Result; + +/// We use the host triple of the compiler used to compile +/// this build script as the triple spec of the build machine. +/// This script must be compiled as a native executable for it +/// runs on the same machine as it is compiled. +fn get_build_env_info() -> Result { + let host_triple = try!(std::env::var("HOST")); + let target_triple = try!(std::env::var("TARGET")); + if host_triple != target_triple { + return Err(ErrMsg { msg : "The Rust Build System must be built as a native executable".to_string() }); + } + let manifest_dir = try!(std::env::var("CARGO_MANIFEST_DIR")); + Ok(BuildEnv { + build_triple : host_triple.to_string(), + manifest_dir : manifest_dir + }) +} + +fn write_to_build_env_rs(info : &BuildEnv) -> Result<()> { + use std::io::Write; + let out_dir = env!("OUT_DIR"); + let dest_path = std::path::Path::new(&out_dir).join("build_env.rs"); + let mut f = try!(std::fs::File::create(&dest_path)); + try!(write!(&mut f, + "const BUILD_TRIPLE : &'static str = \"{}\"; +const MANIFEST_DIR : &'static str = r\"{}\";", + info.build_triple, + info.manifest_dir)); + Ok(()) +} + +fn run() -> Result<()> { + let info = try!(get_build_env_info()); + write_to_build_env_rs(&info) +} + +fn main() { + match run() { + Err(e) => { + println!("Failed to collect build environment information: {}", e.msg); + std::process::exit(1); + }, + _ => {} + } +} diff --git a/build_rust/src/build_state.rs b/build_rust/src/build_state.rs new file mode 100644 index 0000000000000..36387b30f0292 --- /dev/null +++ b/build_rust/src/build_state.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Indicate the state of the build process. +//! +//! `BuildState` wraps around the `Result` type and is used +//! to indicate the state of the build process for functions that may +//! fail at runtime. Functions can continue the build by returning +//! `continue_build()` or fail by returning one of the fail states. +//! Use the `try!` macro to chain functions that return `BuildState`. + +pub enum ExitStatus { + SuccessStop, // Build is successful + MsgStop(String), // Build is terminated with a (non-error) message + ErrStop(String) // Build is stopped due to an error +} + +pub type BuildState = Result; + +impl From for ExitStatus { + fn from(e : T) -> ExitStatus { + ExitStatus::ErrStop(format!("{}", e)) + } +} + +pub fn continue_build() -> BuildState<()> { + Ok(()) +} + +pub fn continue_with(v : T) -> BuildState { + Ok(v) +} + +pub fn success_stop() -> BuildState { + Err(ExitStatus::SuccessStop) +} + +pub fn msg_stop, V>(s : S) -> BuildState { + Err(ExitStatus::MsgStop(s.into())) +} + +pub fn err_stop, V>(s : S) -> BuildState { + Err(ExitStatus::ErrStop(s.into())) +} + +macro_rules! err_stop { + ( $( $x:expr ),* ) => { return err_stop(format!( $( $x ),* )) } +} diff --git a/build_rust/src/cc.rs b/build_rust/src/cc.rs new file mode 100644 index 0000000000000..91d79f5830033 --- /dev/null +++ b/build_rust/src/cc.rs @@ -0,0 +1,451 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Invoke the appropriate toolchain to build and assemble a static +//! library. Different flavours of the toolchain (gnu vs. msvc) are +//! abstracted behind the trait `Toolchain`, and the build system +//! will select the appropriate one based on the build configuration +//! and the platform. + +use std::fmt; +use std::ffi::{OsStr, OsString}; +use std::path::{PathBuf, Path}; +use std::process::Command; +use llvm::LLVMTools; +use build_state::*; +use configure::ConfigArgs; +use log::{Tee, Logger}; + +/// Specify a target triple, in the format `arch-vendor-os-abi`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Triple { + triple : String +} + +impl Triple { + pub fn new(triple : &str) -> Result { + let v : Vec<&str>= triple.split('-').map(|s| s).collect(); + if v.len() < 3 { + Err(format!("Invalid target triple {}.", triple)) + } else { + Ok(Triple { triple : triple.into() }) + } + } + + pub fn arch(&self) -> &str { + self.triple.split('-').nth(0).unwrap() + } + + pub fn os(&self) -> &str { + self.triple.split('-').nth(2).unwrap() + } + + pub fn abi(&self) -> Option<&str> { + self.triple.split('-').nth(3) + } + + pub fn is_i686(&self) -> bool { + self.arch() == "i686" + } + + pub fn is_x86_64(&self) -> bool { + self.arch() == "x86_64" + } + + pub fn is_windows(&self) -> bool { + self.os() == "windows" + } + + pub fn is_mingw(&self) -> bool { + self.is_windows() && self.abi() == Some("gnu") + } + + pub fn is_msvc(&self) -> bool { + self.abi() == Some("msvc") + } + + pub fn is_linux(&self) -> bool { + self.os() == "linux" + } + + pub fn is_darwin(&self) -> bool { + self.os() == "darwin" + } + + /// Append the extension for the executables in this platform + pub fn with_exe_ext(&self, name : &str) -> String { + if self.is_windows() { + format!("{}.exe", name) + } else { + format!("{}", name) + } + } + + /// Append the extension for the executables in this platform + pub fn with_lib_ext(&self, name : &str) -> String { + if self.is_msvc() { + format!("{}.lib", name) + } else { + format!("lib{}.a", name) + } + } + + /// Get the file extension for the dynamic libraries + /// in this platform. + pub fn dylib_ext(&self) -> &'static str { + if self.is_windows() { + "dll" + } else if self.is_darwin() { + "dylib" + } else { + "so" + } + } +} + +impl fmt::Display for Triple { + fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.triple) + } +} + +impl AsRef for Triple { + fn as_ref(&self) -> &OsStr { + &OsStr::new(&self.triple) + } +} + +impl AsRef for Triple { + fn as_ref(&self) -> &Path { + &Path::new(&self.triple) + } +} + +impl<'a> From<&'a Triple> for &'a str { + fn from(t : &'a Triple) -> &'a str { + &t.triple + } +} + +impl<'a> From<&'a Triple> for String { + fn from(t : &'a Triple) -> String { + t.triple.clone() + } +} + +/// Compile and assemble a static library. This will invoke the +/// appropriate toolchain command (or LLVM tool) to compile the +/// given list of files into object files and then invoke ar (or +/// equivalent) to assemble them into a static library. +/// +/// If a directory is given instead of a file, all supported files +/// under that directory will be compiled. +/// +/// Supported file types are: +/// +/// `*.ll` --- LLVM byte code, will invoke llc +/// +/// `*.S`, `*.c`, `*.cc`, `*.cpp` --- will invoke appropriate +/// toolchain command +pub struct StaticLib { + toolchain : Box, + llvm_tools : LLVMTools, + include_directories: Vec, + files: Vec, + root_src_dir : PathBuf, + root_build_dir : PathBuf, + llvm_cxxflags : bool +} + +impl StaticLib { + /// This builder is finished with the `compile` function. + fn new(toolchain : Box, llvm_tools : LLVMTools) + -> StaticLib { + StaticLib { + toolchain : toolchain, + llvm_tools : llvm_tools, + include_directories: Vec::new(), + files: Vec::new(), + root_src_dir : PathBuf::from("."), + root_build_dir : PathBuf::from("."), + llvm_cxxflags : false + } + } + + /// Set the source directory + pub fn set_src_dir

>(&mut self, dir : P) + -> &mut StaticLib { + self.root_src_dir = dir.as_ref().into(); + self + } + + /// Set the destination directory where the final artifact is written + pub fn set_build_dir

>(&mut self, dir : P) + -> &mut StaticLib { + self.root_build_dir = dir.as_ref().into(); + self + } + + /// Add a directory to the `-I` or include path for headers + pub fn include_dirs>(&mut self, dir: &[P]) + -> &mut StaticLib { + let _ : Vec<()> = dir.iter().map(|p| p.as_ref().into()) + .map(|p| self.include_directories.push(p)) + .collect(); + self + } + + /// Add a group of files which will be compiled + pub fn files

>(&mut self, files : &[P]) + -> &mut StaticLib { + let _ : Vec<()> = files.iter().map(|p| p.as_ref().into()) + .map(|p| self.files.push(p)) + .collect(); + self + } + + /// If set, will invoke llvm-config to add the appropriate cxxflags + pub fn set_llvm_cxxflags(&mut self) -> &mut StaticLib { + self.llvm_cxxflags = true; + self + } + + /// Run the compiler, generating the file `output` + pub fn compile(&self, out_lib: &str, logger : &Logger) + -> BuildState<()> { + use std::fs::{read_dir, create_dir_all}; + let mut src_files : Vec = vec![]; + for path in &self.files { + if let Ok(dir) = read_dir(self.root_src_dir.join(path)) { + let _ : Vec<_> = dir.map(|ent| ent.map(|f| { + if let Some(ext) = f.path().extension() { + if ext == "ll" || ext == "cc" || ext == "cpp" + || ext == "c" || ext == "S" { + let file = path.join( + f.path().file_name().unwrap()); + src_files.push(file); + } + } + })).collect(); + } else { + src_files.push(path.into()); + } + } + + let mut objects = Vec::new(); + for file in &src_files { + let src = self.root_src_dir.join(file); + let obj = self.root_build_dir.join(file).with_extension("o"); + let _ = create_dir_all(&obj.parent().unwrap()); // ignore errors + let mut cmd = self.compiler_cmd(&src, &obj); + if self.llvm_cxxflags { + let cxxflags = try!(self.llvm_tools.get_llvm_cxxflags()); + cmd.args(&cxxflags); + } + try!(cmd.tee(logger)); + objects.push(obj); + } + + let output = self.root_build_dir + .join(self.toolchain.target_triple().with_lib_ext(out_lib)); + self.toolchain.ar_cmd(&objects, &output).tee(logger) + } + + fn compiler_cmd(&self, src : &Path, obj : &Path) -> Command { + let inc_dirs = &self.include_directories; + let ext = src.extension().expect( + &format!("Source {:?} file has no extension.", src)); + if ext == "ll" { + self.llvm_tools.llc_cmd( + self.toolchain.target_triple(), src, obj) + } else if ext == "cc" || ext == "cpp" { + self.toolchain.cxx_cmd(src, obj, inc_dirs) + } else { + self.toolchain.cc_cmd(src, obj, inc_dirs) + } + } +} + +/// Define the abstract interface that a toolchain implemenation +/// should support. +pub trait Toolchain { + fn target_triple(&self) -> &Triple; + fn cc_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command; + fn cxx_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command; + fn ar_cmd(&self, obj_files : &[PathBuf], output : &Path) -> Command; +} + +/// Gnu-flavoured toolchain. Supports both gcc and clang. +struct GccishToolchain { + target_triple : Triple, + cc_cmd : String, + cxx_cmd : String, + ar_cmd : String +} + +impl GccishToolchain { + fn cross_gcc(triple : &Triple) -> GccishToolchain { + GccishToolchain { + target_triple : triple.clone(), + cc_cmd : format!("{}-gcc", triple), + cxx_cmd : format!("{}-g++", triple), + ar_cmd : "ar".into() + } + } + + fn native_gcc(triple : &Triple) -> GccishToolchain { + GccishToolchain { + target_triple : triple.clone(), + cc_cmd : "gcc".into(), + cxx_cmd : "g++".into(), + ar_cmd : "ar".into() + } + } + + fn clang(triple : &Triple) -> GccishToolchain { + GccishToolchain { + target_triple : triple.clone(), + cc_cmd : "clang".into(), + cxx_cmd : "clang++".into(), + ar_cmd : "ar".into() + } + } + + fn add_args(&self, cmd : &mut Command, src : &Path, obj : &Path, + inc_dirs : &[PathBuf]) { + cmd.arg("-c").arg("-ffunction-sections").arg("-fdata-sections"); + + let target = &self.target_triple; + if target.is_windows() { + cmd.arg("-mwin32"); + } + + if target.is_i686() { + cmd.arg("-m32"); + } else if target.is_x86_64() { + cmd.arg("-m64"); + } + + if !target.is_i686() { + cmd.arg("-fPIC"); + } + + if target.is_darwin() { + // for some reason clang on darwin doesn't seem to define this + cmd.arg("-DCHAR_BIT=8"); + } + + for directory in inc_dirs { + cmd.arg("-I").arg(directory); + } + + cmd.arg("-o").arg(obj).arg(src); + } +} + +impl Toolchain for GccishToolchain { + fn target_triple(&self) -> &Triple { + &self.target_triple + } + + fn cc_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command { + let mut cmd = Command::new(&self.cc_cmd); + self.add_args(&mut cmd, src_file, obj_file, inc_dirs); + return cmd; + } + + fn cxx_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command { + let mut cmd = Command::new(&self.cxx_cmd); + self.add_args(&mut cmd, src_file, obj_file, inc_dirs); + cmd.arg("-fno-rtti"); + return cmd; + } + + fn ar_cmd(&self, obj_files : &[PathBuf], output : &Path) -> Command { + let mut cmd = Command::new(&self.ar_cmd); + cmd.arg("crus"); + cmd.arg(&output); + cmd.args(obj_files); + cmd + } +} + +/// MSVC toolchain +struct MsvcToolchain { + target_triple : Triple +} + +impl MsvcToolchain { + fn new(triple : &Triple) -> MsvcToolchain { + MsvcToolchain { + target_triple : triple.clone() + } + } +} + +impl Toolchain for MsvcToolchain { + fn target_triple(&self) -> &Triple { + &self.target_triple + } + + fn cc_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command { + let mut cmd = Command::new("cl"); + + cmd.arg("-nologo").arg("-c").arg(src_file); + let _ : Vec<_> = inc_dirs.iter().map(|p| { + let mut s = OsString::new(); + s.push("-I"); + s.push(&p); + cmd.arg(&s); + }).collect(); + + let mut out = OsString::new(); + out.push("-Fo"); + out.push(obj_file); + cmd.arg(&out); + + cmd + } + + fn cxx_cmd(&self, src_file : &Path, obj_file : &Path, + inc_dirs : &[PathBuf]) -> Command { + self.cc_cmd(src_file, obj_file, inc_dirs) + } + + fn ar_cmd(&self, obj_files : &[PathBuf], output : &Path) -> Command { + let mut cmd = Command::new("lib"); + cmd.arg("-nologo"); + let mut s = OsString::new(); + s.push("-OUT:"); + s.push(&output); + cmd.arg(&s); + cmd.args(obj_files); + cmd + } +} + +pub fn build_static_lib(cfg : &ConfigArgs, triple : &Triple) + -> StaticLib { + let toolchain : Box = if triple.is_darwin() { + Box::new(GccishToolchain::clang(triple)) + } else if triple.is_mingw() { + Box::new(GccishToolchain::native_gcc(triple)) + } else if triple.is_msvc() { + Box::new(MsvcToolchain::new(triple)) + } else { + Box::new(GccishToolchain::cross_gcc(triple)) + }; + StaticLib::new(toolchain, cfg.llvm_tools(triple)) +} diff --git a/build_rust/src/cmd_args.rs b/build_rust/src/cmd_args.rs new file mode 100644 index 0000000000000..16614d9adebce --- /dev/null +++ b/build_rust/src/cmd_args.rs @@ -0,0 +1,109 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Use `getopts` to parse the command line arguments. Note this +//! module does minimal processing and it is up to `mod configure` +//! to interpret the command line arguments and set the configure +//! variables accordingly. + +extern crate getopts; + +use build_state::*; + +pub struct CmdArgs { + pub host_triple : Option, + pub target_triples : Vec, + pub build_dir : Option, + pub rustc_root_dir : Option, + pub nproc : Option, + pub no_clean_build : bool, + pub use_local_rustc : bool, + pub no_reconfigure_llvm : bool, + pub no_rebuild_llvm : bool, + pub no_bootstrap : bool, + pub enable_debug_build : bool, + pub enable_llvm_assertions : bool, + pub disable_llvm_assertions : bool, + pub verbose : bool +} + +fn get_all_options() -> getopts::Options { + let mut opts = getopts::Options::new(); + opts.optflag("h", "help", "Print this help menu."); + opts.optopt("", "build-dir", + "Specify the build directory where all build artifacts are written into.", + "DIR"); + opts.optopt("", "rustc-root", + "Specify the root directory for the rustc source code.", + "DIR"); + opts.optopt("", "host", + "Specify the host triple, ie. the triple on which the compiler runs.", + "HOST"); + opts.optopt("", "target", + "Specify the tearget triples that the compiler can generate code for.", + "TARGET1[,TARGET2,...]"); + opts.optopt("", "nproc", + "Specify the number of parallel jobs the build can use. Note that for MSVC builds, this parameter is ignored and all available cores are used.", + "NUM"); + opts.optflag("", "no-clean-build", + "Do not clean the build directories."); + opts.optflag("", "use-local-rustc", + "Do not download the snapshot. Use local rustc to bootstrap stage0 build."); + opts.optflag("", "no-reconfigure-llvm", + "Do not re-configure llvm. Build will fail if llvm has not been configured previously. This implies --no-clean-build."); + opts.optflag("", "no-rebuild-llvm", + "Do not rebuild llvm. Build will fail if llvm has not been built previously. This implies --no-clean-build."); + opts.optflag("", "no-bootstrap", + "Do not bootstrap. Build stage2 binaries only. Build will fail if not already bootstrapped."); + opts.optflag("", "enable-debug-build", + "Build the compiler in debug mode. The default is a release build with optimizations on."); + opts.optflag("", "enable-llvm-assertions", + "Build LLVM with assertions on. This is implied by --enable-debug-build."); + opts.optflag("", "disable-llvm-assertions", + "Build LLVM with assertions off. This is the default when building in release mode."); + opts.optflag("v", "verbose", + "Show extra information during build."); + opts +} + +fn get_usage_string(prog : &str, opts: &getopts::Options) -> String { + let brief = format!("Usage: {}", prog); + opts.usage(&brief) +} + +pub fn parse_cmd_args() -> BuildState { + let opts = get_all_options(); + let args : Vec = ::std::env::args().collect(); + let program = &args[0]; + let matches = try!(opts.parse(&args[1..])); + if matches.opt_present("h") { + return msg_stop(get_usage_string(program, &opts)); + } + + let cmd_args = CmdArgs { + host_triple : matches.opt_str("host"), + target_triples : matches.opt_strs("target"), + build_dir : matches.opt_str("build-dir"), + rustc_root_dir : matches.opt_str("rustc-root"), + nproc : matches.opt_str("nproc").and_then(|s| s.parse::().ok()), + no_clean_build : matches.opt_present("no-clean-build"), + use_local_rustc : matches.opt_present("use-local-rustc"), + no_reconfigure_llvm : matches.opt_present("no-reconfigure-llvm"), + no_rebuild_llvm : matches.opt_present("no-rebuild-llvm"), + no_bootstrap : matches.opt_present("no-bootstrap"), + enable_debug_build : matches.opt_present("enable-debug-build"), + enable_llvm_assertions : matches.opt_present("enable-llvm-assertions"), + disable_llvm_assertions : matches.opt_present( + "disable-llvm-assertions"), + verbose : matches.opt_present("verbose") + }; + + continue_with(cmd_args) +} diff --git a/build_rust/src/configure.rs b/build_rust/src/configure.rs new file mode 100644 index 0000000000000..b615ce5b7cbbd --- /dev/null +++ b/build_rust/src/configure.rs @@ -0,0 +1,285 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Set build variables from the command line and inspect build +//! environment. +//! +//! For a general description of the build pipeline, see main.rs + +// Include the build_env.rs file generated by build.rs +include!(concat!(env!("OUT_DIR"), "/build_env.rs")); + +use std::process::Command; +use std::path::PathBuf; +use std::ffi::OsString; +use std::env; +use std::str::from_utf8; +use build_state::*; +use cmd_args::*; +use llvm::{LLVMBuildType, LLVMTools}; +use cc::Triple; +use log::Logger; + +/// Build variables collected from the command line arguments +/// and the build environment. +pub struct ConfigArgs { + build_triple : Triple, + host_triple : Triple, + target_triples : Vec, + llvm_build_type : LLVMBuildType, + cmd_args : CmdArgs +} + +impl ConfigArgs { + /// Get the git hash for the HEAD commit in the source repo. + /// This is used to append the `extra-filename` argument to rustc. + /// TODO: For build from a tarball release, we should supply + /// a fixed git hash. + pub fn get_git_hash(&self) -> String { + Command::new("git").current_dir(self.rustc_root_dir()) + .arg("rev-parse").arg("--short").arg("HEAD").output() + .map_err(|e| format!("{}", e)) + .and_then(|output| from_utf8(&output.stdout) + .map(|s| s.trim().into()) + .map_err(|e| format!("{}", e))) + .unwrap_or("0000000".into()) + } + + /// Root directory for the rustc repo. This can be overridden + /// from the command line. Note user may provide a relative path, + /// in which case we prepend the current dir to it. + pub fn rustc_root_dir(&self) -> PathBuf { + if let Some(ref dir) = self.cmd_args.rustc_root_dir { + let mut src_dir = env::current_dir().unwrap(); + src_dir.push(&dir); + src_dir + } else { + PathBuf::from(MANIFEST_DIR).parent() + .expect("MANIFEST_DIR has no parent directory.").into() + } + } + + pub fn src_dir(&self) -> PathBuf { + self.rustc_root_dir().join("src") + } + + /// All build artifacts are written into the toplevel build + /// directory, which by default is the same directory as the + /// Cargo manifest of this package. + pub fn toplevel_build_dir(&self) -> PathBuf { + if let Some(ref dir) = self.cmd_args.build_dir { + let mut build_dir = env::current_dir().unwrap(); + build_dir.push(&dir); // see comment in rustc_root_dir() + build_dir + } else { + PathBuf::from(MANIFEST_DIR) + } + } + + /// All build artifacts belonging to a particular target triple + /// are written into / + pub fn target_build_dir(&self, triple : &Triple) -> PathBuf { + self.toplevel_build_dir().join(triple) + } + + /// Triple of the build machine. + pub fn build_triple(&self) -> &Triple { + &self.build_triple + } + + /// Host triple of the stage2 compiler + pub fn host_triple(&self) -> &Triple { + &self.host_triple + } + + /// Target triples for which the stage2 libraries will be built + pub fn target_triples(&self) -> &[Triple] { + &self.target_triples + } + + /// See `mod llvm`. + pub fn llvm_build_type(&self) -> LLVMBuildType { + self.llvm_build_type + } + + /// See `mod llvm`. + pub fn llvm_tools(&self, target : &Triple) -> LLVMTools { + LLVMTools::new(self, target) + } + + pub fn is_debug_build(&self) -> bool { + self.cmd_args.enable_debug_build + } + + /// Use the rustc in the local $PATH as the stage0 compiler + /// instead of the stage0 snapshot. + pub fn use_local_rustc(&self) -> bool { + self.cmd_args.use_local_rustc + } + + /// Indicate whether we should clean the build directory. + /// We won't if either --no-reconfigure-llvm, + /// --no-rebuild-llvm or --no-bootstrap is supplied. + pub fn no_clean_build(&self) -> bool { + self.cmd_args.no_clean_build || self.no_reconfigure_llvm() + || self.no_rebuild_llvm() || self.no_bootstrap() + } + + pub fn no_reconfigure_llvm(&self) -> bool { + self.cmd_args.no_reconfigure_llvm + || self.no_rebuild_llvm() || self.no_bootstrap() + } + + pub fn no_rebuild_llvm(&self) -> bool { + self.cmd_args.no_rebuild_llvm || self.no_bootstrap() + } + + /// See `mod rust` + pub fn no_bootstrap(&self) -> bool { + self.cmd_args.no_bootstrap + } + + /// Number of parallel jobs to run during build. Default to 1. + pub fn jnproc(&self) -> OsString { + if let Some(n) = self.cmd_args.nproc { + format!("-j{}", n).into() + } else { + "-j1".into() + } + } + + /// See `mod log`. + pub fn get_logger(&self, triple : &Triple, prog : &str) -> Logger { + let build_dir = self.target_build_dir(triple); + let stdout = build_dir.join("log") + .join(format!("{}.stdout.log", prog)); + let stderr = build_dir.join("log") + .join(format!("{}.stderr.log", prog)); + Logger::new(stdout, stderr, triple.into(), prog.into(), + self.cmd_args.verbose) + } +} + +fn get_config_args(cmd_args : CmdArgs) -> BuildState { + let build_triple = try!(Triple::new(BUILD_TRIPLE)); + let host_triple = match cmd_args.host_triple { + None => build_triple.clone(), + Some(ref t) => try!(Triple::new(t)) + }; + let mut target_triples = vec![ host_triple.clone() ]; + for triple in &cmd_args.target_triples { + target_triples.push(try!(Triple::new(triple))); + } + let llvm_build_type = + if cmd_args.enable_debug_build { + if cmd_args.disable_llvm_assertions { + LLVMBuildType::DebugNoAsserts + } else { + LLVMBuildType::Debug + } + } else if cmd_args.enable_llvm_assertions { + LLVMBuildType::ReleaseAsserts + } else { + LLVMBuildType::Release + }; + continue_with(ConfigArgs { + build_triple : build_triple, + host_triple : host_triple, + target_triples : target_triples, + llvm_build_type : llvm_build_type, + cmd_args : cmd_args + }) +} + +fn check_prog(prog : &str) -> BuildState<()> { + println!("Looking for {}...", prog); + let _ = try!(Command::new(prog).output()); + continue_build() +} + +fn check_python_version() -> BuildState<()> { + use regex::Regex; + println!("Checking if python --version >= 2.7 ..."); + let output = try!(Command::new("python").arg("--version").output()); + let out_str = try!(String::from_utf8(output.stderr)); + let re = try!(Regex::new(r"Python (\d+)\.(\d+)\.(\d+)")); + let err_msg = "Python 2.7 or newer is required."; + let cap = try!(re.captures(&out_str).ok_or(err_msg.to_string())); + let major_version = try!(try!( + cap.at(1).ok_or(err_msg.to_string())).parse::()); + let minor_version = try!(try!( + cap.at(2).ok_or(err_msg.to_string())).parse::()); + if (major_version == 2 && minor_version >= 7) || major_version >= 3 { + continue_build() + } else { + err_stop(err_msg) + } +} + +fn check_build_prereq(_ : &ConfigArgs) -> BuildState<()> { + println!("Checking build prerequisite..."); + try!(check_prog("cmake")); + try!(check_prog("git")); + try!(check_prog("curl")); + try!(check_prog("tar")); + try!(check_prog("bzip2")); + try!(check_prog("python")); + try!(check_python_version()); + // TODO: Check toolchain availability + continue_build() +} + +fn clean_build_dirs(args : &ConfigArgs) -> BuildState<()> { + use std::fs::remove_dir_all; + println!("Removing old build directories..."); + for triple in args.target_triples() { + let _ = remove_dir_all(args.target_build_dir(triple)); + } + continue_build() +} + +fn create_build_dirs(args : &ConfigArgs) -> BuildState<()> { + use std::fs::create_dir_all; + println!("Creating build directories..."); + let dirs = vec![ "llvm", "log", "rt" ]; + for d in &dirs { + let bld = args.target_build_dir(args.build_triple()); + let hst = args.target_build_dir(args.host_triple()); + let _ = create_dir_all(hst.join(d)); + let _ = create_dir_all(bld.join(d)); + } + continue_build() +} + +fn check_src_dir(args : &ConfigArgs) -> BuildState<()> { + use std::fs::read_dir; + let libsyntax_dir = args.src_dir().join("libsyntax"); + if read_dir(&libsyntax_dir).is_err() { + err_stop!("{:?} does not appear to contain rustc source code. Use --rustc-root=DIR to specify the rustc root directory.", + &args.src_dir()); + } + continue_build() +} + +/// Perform the configure step and return the build variables. +pub fn configure() -> BuildState { + let cmd_args = try!(parse_cmd_args()); + if !cmd_args.verbose { + println!("Building in quiet mode. Use --verbose for more loggings."); + } + let config_args = try!(get_config_args(cmd_args)); + try!(check_build_prereq(&config_args)); + try!(check_src_dir(&config_args)); + if !config_args.no_clean_build() { + try!(clean_build_dirs(&config_args)); + } + try!(create_build_dirs(&config_args)); + continue_with(config_args) +} diff --git a/build_rust/src/llvm.rs b/build_rust/src/llvm.rs new file mode 100644 index 0000000000000..2840b0065b0be --- /dev/null +++ b/build_rust/src/llvm.rs @@ -0,0 +1,180 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Configure and compile LLVM. Also provide paths to the LLVM tools +//! for other parts of the build system. + +use std::process::Command; +use std::path::{Path, PathBuf}; +use std::ffi::OsString; +use build_state::*; +use configure::ConfigArgs; +use cc::Triple; +use log::Tee; + +/// Type of the LLVM build. +#[derive(Clone, Copy)] +pub enum LLVMBuildType { + Release, // implies no assertions + ReleaseAsserts, + Debug, // implies assertions on + DebugNoAsserts +} + +impl LLVMBuildType { + fn to_cmake_build_type(self) -> &'static str { + match self { + LLVMBuildType::Release | LLVMBuildType::ReleaseAsserts + => "-DCMAKE_BUILD_TYPE=Release", + LLVMBuildType::Debug | LLVMBuildType::DebugNoAsserts + => "-DCMAKE_BUILD_TYPE=Debug" + } + } + + fn to_cmake_assert_var(self) -> &'static str { + match self { + LLVMBuildType::ReleaseAsserts => "-DLLVM_ENABLE_ASSERTIONS=ON", + LLVMBuildType::DebugNoAsserts => "-DLLVM_ENABLE_ASSERTIONS=OFF", + _ => "" // use the default + } + } +} + +/// Provides paths to the LLVM tools and other related information +pub struct LLVMTools { + llvm_build_artifacts_dir : PathBuf +} + +impl LLVMTools { + pub fn new(args : &ConfigArgs, triple : &Triple) -> LLVMTools { + // msvc build seems to put all build artifacts under Debug + // regardless of build type + let build_dir = if triple.is_msvc() { + llvm_build_dir(args, triple).join("Debug") + } else { + llvm_build_dir(args, triple) + }; + LLVMTools { + llvm_build_artifacts_dir : build_dir + } + } + + pub fn path_to_llvm_config(&self) -> PathBuf { + self.llvm_build_artifacts_dir.join("bin").join("llvm-config") + } + + fn path_to_llc(&self) -> PathBuf { + self.llvm_build_artifacts_dir.join("bin").join("llc") + } + + pub fn path_to_llvm_libs(&self) -> PathBuf { + self.llvm_build_artifacts_dir.join("lib") + } + + pub fn llc_cmd(&self, target : &Triple, src : &Path, obj : &Path) + -> Command { + let mut cmd = Command::new(&self.path_to_llc()); + cmd.arg("-filetype=obj") + .arg(&format!("-mtriple={}", target)) + .arg("-relocation-model=pic") + .arg("-o").arg(obj).arg(src); + cmd + } + + pub fn get_llvm_cxxflags(&self) -> BuildState> { + let output = try!(Command::new(&self.path_to_llvm_config()) + .arg("--cxxflags").output()); + let cxxflags = try!(String::from_utf8(output.stdout)); + continue_with(cxxflags.trim().split(' ') + .filter(|s| *s != "") + .map(|s| s.into()).collect()) + } +} + +fn llvm_src_dir(args : &ConfigArgs) -> PathBuf { + args.src_dir().join("llvm") +} + +fn llvm_build_dir(args : &ConfigArgs, triple : &Triple) -> PathBuf { + args.target_build_dir(triple).join("llvm") +} + +fn cmake_makefile_target(triple : &Triple) -> &'static str { + if triple.is_msvc() { + "Visual Studio 12" + } else if triple.is_mingw() { + "MinGW Makefiles" + } else { + "Unix Makefiles" + } +} + +// TODO : Add cross-compile +fn llvm_config_args(cfg : &ConfigArgs, target : &Triple) -> Vec { + vec![ + "-G".into(), + cmake_makefile_target(target).into(), + cfg.llvm_build_type().to_cmake_build_type().into(), + cfg.llvm_build_type().to_cmake_assert_var().into(), + "-DLLVM_ENABLE_TERMINFO=OFF".into(), + "-DLLVM_ENABLE_ZLIB=OFF".into(), + "-DLLVM_ENABLE_FFI=OFF".into(), + "-DLLVM_BUILD_DOCS=OFF".into(), + llvm_src_dir(cfg).into_os_string() ] +} + +fn configure_llvm_for_target(args : &ConfigArgs, + target : &Triple) -> BuildState<()> { + println!("Configuring llvm for target triple {}...", target); + let build_dir = llvm_build_dir(args, target); + let logger = args.get_logger(target, "configure_llvm"); + Command::new("cmake") + .args(&llvm_config_args(args, target)) + .current_dir(&build_dir) + .tee(&logger) +} + +fn build_llvm_for_target(cfg : &ConfigArgs, + target : &Triple) -> BuildState<()> { + println!("Building llvm for target triple {}...", target); + let logger = cfg.get_logger(target, "make_llvm"); + let mut arg : Vec = vec![ "--build".into(), ".".into() ]; + // msbuild doesn't support -jnproc and will use all cores by default + if !target.is_msvc() { + arg.push("--".into()); + arg.push(cfg.jnproc()); + } + Command::new("cmake") + .args(&arg) + .current_dir(&llvm_build_dir(cfg, target)) + .tee(&logger) +} + +pub fn configure_llvm(cfg : &ConfigArgs) -> BuildState<()> { + let build = cfg.build_triple(); + let host = cfg.host_triple(); + if build == host { + configure_llvm_for_target(cfg, build) + } else { + try!(configure_llvm_for_target(cfg, build)); + configure_llvm_for_target(cfg, host) + } +} + +pub fn build_llvm(cfg : &ConfigArgs) -> BuildState<()> { + let build = cfg.build_triple(); + let host = cfg.host_triple(); + if build == host { + build_llvm_for_target(cfg, build) + } else { + try!(build_llvm_for_target(cfg, build)); + build_llvm_for_target(cfg, host) + } +} diff --git a/build_rust/src/log.rs b/build_rust/src/log.rs new file mode 100644 index 0000000000000..15c1de1dc7616 --- /dev/null +++ b/build_rust/src/log.rs @@ -0,0 +1,131 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module implements a simple logger that will pipe the output +//! of a `Command` to both the standard output and a log file. +//! +//! Both the stdout and stderr will be logged. + +extern crate std; + +use std::process::Command; +use std::process::Stdio; +use std::thread; +use std::path::{Path, PathBuf}; +use std::io::{Read, BufRead, BufReader, Write}; +use std::sync::mpsc::{channel, Receiver}; +use build_state::*; + +/// Log to /log/.{stdout,stderr}.log +pub struct Logger { + stdout : PathBuf, + stderr : PathBuf, + triple : String, + prog : String, + verbose : bool +} + +impl Logger { + pub fn new(stdout : PathBuf, stderr : PathBuf, + triple : String, prog : String, verbose : bool) -> Logger { + Logger { stdout : stdout, stderr : stderr, + triple : triple, prog : prog, verbose : verbose } + } +} + +/// Tee pipes the output of a command to the stdio and a logger +pub trait Tee { + fn tee(&mut self, logger : &Logger) -> BuildState<()>; +} + +#[derive(Clone, Copy)] +enum OutputType { + Stdout, + Stderr +} + +impl Tee for Command { + fn tee(&mut self, logger : &Logger) -> BuildState<()> { + let cmd = format!{"{:?}", self}; + if logger.verbose { + println!("Running {}", cmd); + } + let mut child = try!(self + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()); + let stdout = + try!(child.stdout.take() + .ok_or(format!("{} failed: no stdout", logger.prog))); + let stderr = + try!(child.stderr.take() + .ok_or(format!("{} failed: no stderr", logger.prog))); + let stdout_ch = read_async(&logger.stdout, &cmd, OutputType::Stdout, + stdout, logger.verbose); + let stderr_ch = read_async(&logger.stderr, &cmd, OutputType::Stderr, + stderr, logger.verbose); + let status = try!(child.wait()); + let _ = stdout_ch.recv(); // errors ignored + let _ = stderr_ch.recv(); // errors ignored + if !status.success() { + err_stop!("{} failed. See log files under {}/log.", + logger.prog, logger.triple); + } + continue_build() + } +} + +fn read_async(path : &Path, + cmd : &str, + out_type : OutputType, + reader : R, + verbose : bool) + -> Receiver> { + let top_line = match out_type { + OutputType::Stdout => format!("# stdout {}\n", cmd), + OutputType::Stderr => format!("# stderr {}\n", cmd) + }; + let (tx, rx) = channel(); + let pathbuf = path.to_path_buf(); + thread::spawn(move || { + tx.send(log_to_file(pathbuf, top_line, out_type, + reader, verbose)).unwrap(); + }); + rx +} + +fn log_to_file(path : PathBuf, + top_line : String, + out_type : OutputType, + reader : R, + verbose : bool) + -> BuildState<()> { + use std::io; + use std::fs::OpenOptions; + let mut buf_reader = BufReader::new(reader); + let mut line : Vec = Vec::new(); + let mut file = try!(OpenOptions::new().create(true) + .write(true).append(true).open(&path) + .map_err(|e| format!( + "Failed to open file {:?}: {}", path, e))); + try!(file.write_all(top_line.as_bytes())); + while try!(buf_reader.read_until(0xA, &mut line)) > 0 { + if verbose { + try!(match out_type { + OutputType::Stdout => io::stdout().write_all(&line), + OutputType::Stderr => io::stderr().write_all(&line) + }); + } + try!(file.write_all(&line)); + line.clear(); + } + continue_build() +} diff --git a/build_rust/src/main.rs b/build_rust/src/main.rs new file mode 100644 index 0000000000000..a67d0ebccd739 --- /dev/null +++ b/build_rust/src/main.rs @@ -0,0 +1,96 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! General comments on the build system: +//! +//! The Rust Build System is an alternative build system for building +//! the Rust compiler. It is written in Rust and is managed as a +//! Cargo package, `build-rust`. The user will initiate the build +//! process by running `cargo run`, under its manifest directory. +//! +//! The build process starts with Cargo invoking a small build script, +//! `build.rs`, located at the same directory as the package manifest. +//! This script will collect the triple of the build machine and +//! the path to the manifest directory, and then generate a file +//! `build_env.rs`, which will be included into `configure.rs`. +//! +//! The next stage of the build process starts in module `configure`. +//! The build system will invoke `configure::configure()`, which will +//! parse the command line arguments, inspect the build environment +//! (for instance, check the availability of the required build +//! programs), and then return a ConfigArgs object which encapsulates +//! the information collected. Future build processes will read this +//! object instead of poking the build environment directly. +//! +//! Because the configure step may fail (for instance, it may be +//! unable to find the required build program), the `configure()` +//! returns type `BuildState` where `T` equals `ConfigArgs`. +//! `BuildState` is a wrapper around the `Result` which is +//! used to indicate the success/failure state of a function. +//! For details, see `mod build_state`. +//! +//! The build system will then download the stage0 snapshot, +//! configure and build LLVM, invoke the appropriate toolchain to +//! build runtime libraries, and then finally boostrap a working stage2 +//! rustc. For details of these steps, see the respective modules for +//! more comments. + +extern crate regex; + +#[macro_use] +mod build_state; +mod cmd_args; +mod configure; +mod snapshot; +mod llvm; +mod rt; +mod rust; +mod cc; +mod log; + +use build_state::*; +use configure::{ConfigArgs, configure}; +use llvm::{build_llvm, configure_llvm}; +use rt::build_native_libs; +use rust::build_rust; +use snapshot::download_stage0_snapshot; + +fn make(args : &ConfigArgs) -> BuildState<()> { + let dl = download_stage0_snapshot(args); + if !args.no_reconfigure_llvm() { + try!(configure_llvm(args)); + } + if !args.no_rebuild_llvm() { + try!(build_llvm(args)); + } + if !args.no_bootstrap() { + try!(build_native_libs(args)); + } + try!(dl.recv().unwrap()); // we need to wait for stage0 download + try!(build_rust(args)); + continue_build() +} + +fn run() -> BuildState<()> { + let config_args = try!(configure()); + + try!(make(&config_args)); + + success_stop() +} + +fn main() { + let result : BuildState<()> = run(); + match result { + Err(ExitStatus::MsgStop(e)) => println!("{}", e), + Err(ExitStatus::ErrStop(e)) => println!("Build failed: {}", e), + _ => println!("Build successful."), + } +} diff --git a/build_rust/src/rt.rs b/build_rust/src/rt.rs new file mode 100644 index 0000000000000..458a70314b95d --- /dev/null +++ b/build_rust/src/rt.rs @@ -0,0 +1,321 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Build the runtime libraries for the compiler. There are two types +//! of runtime libraries, `RtLib` and `ExtRtLib`. `ExtRtLib`s have +//! external build system which we will simply run to build them. +//! For `RtLib`s we invoke the toolchain directly (through `mod cc`) +//! and ignore its build system even if it has one (for compiler-rt). +//! We also generate the `llvmdeps.rs` and `rustc_llvm.def` file. + +use std::process::Command; +use std::fs::{create_dir_all, copy}; +use std::path::PathBuf; +use std::ffi::OsString; +use build_state::*; +use configure::*; +use cc::{Triple, build_static_lib}; +use log::Tee; + +fn rt_src_dir(args : &ConfigArgs) -> PathBuf { + args.src_dir().join("rt") +} + +pub fn rt_build_dir(args : &ConfigArgs, triple : &Triple) -> PathBuf { + args.target_build_dir(triple).join("rt") +} + +struct RtLib { + name : &'static str, + needed : bool, + src_files : Vec<&'static str>, + inc_dirs : Vec<&'static str> +} + +fn runtime_libraries(target : &Triple) -> Vec { + vec![ + RtLib { + name : "hoedown", + needed : true, + src_files : vec!["hoedown/src"], + inc_dirs : vec!["hoedown/src"], + }, + RtLib { + name : "miniz", + needed : true, + src_files : vec!["miniz.c"], + inc_dirs : vec![], + }, + RtLib { + name : "rust_builtin", + needed : true, + src_files : vec!["rust_builtin.c", "rust_android_dummy.c"], + inc_dirs : vec![], + }, + RtLib { + name : "rustrt_native", + needed : true, + src_files : { + let mut v = vec!["rust_try.ll"]; + if target.is_linux() { + v.push("arch/{arch}/record_sp.S"); + } + v + }, + inc_dirs : vec![], + }, + RtLib { + name : "rust_test_helpers", + needed : true, + src_files : vec!["rust_test_helpers.c"], + inc_dirs : vec![], + }, + RtLib { + name : "morestack", + needed : !target.is_windows(), + src_files : vec!["arch/{arch}/morestack.S"], + inc_dirs : vec![], + }, + RtLib { + name : "compiler-rt", + needed : !target.is_msvc(), + src_files : vec!["../compiler-rt/lib/builtins", + "../compiler-rt/lib/builtins/{arch}"], + inc_dirs : vec!["../compiler-rt/lib/builtins", + "../compiler-rt/SDKS/{os}/usr/include"], + } + ] +} + +fn parse_dir(s : &str, tgt : &Triple) -> PathBuf { + let arch = if tgt.is_i686() { + "i386" + } else { + tgt.arch() + }; + let os = if tgt.is_windows() { + "win" + } else { + tgt.os() + }; + PathBuf::from(s).iter().map(|d| { + if d == "{arch}" { + PathBuf::from(arch) + } else if d == "{os}" { + PathBuf::from(os) + } else { + PathBuf::from(d) + } + }).collect() +} + +fn build_rt_lib(args : &ConfigArgs, + triple : &Triple, + rtlib : &RtLib) + -> BuildState<()> { + if !rtlib.needed { + return continue_build(); + } + let name = format!("lib{}", rtlib.name); + println!("Building {} for target triple {}...", name, triple); + let src_dir = rt_src_dir(args); + let build_dir = rt_build_dir(args, triple); + let src_files : Vec = rtlib.src_files.iter() + .map(|d| parse_dir(d, triple)).collect(); + let inc_dirs : Vec = rtlib.inc_dirs.iter() + .map(|d| parse_dir(d, triple)) + .map(|d| src_dir.join(d)).collect(); + let logger = args.get_logger(triple, &name); + build_static_lib(args, triple) + .set_src_dir(&src_dir) + .set_build_dir(&build_dir) + .files(&src_files) + .include_dirs(&inc_dirs) + .compile(&rtlib.name, &logger) +} + +struct ExtRtLib { + name : &'static str, + needed : bool, + env_vars : Vec<(OsString, OsString)>, + config_cmd : &'static str, + config_args : Vec, + make_cmd : &'static str, + make_args : Vec, + build_artefact_src : PathBuf, + build_artefact_dest : PathBuf +} + +fn jemalloc_config_args(cfg : &ConfigArgs, target : &Triple) + -> Vec { + vec![ cfg.src_dir().join("jemalloc").join("configure").into(), + "--with-jemalloc-prefix=je_".into(), + "--disable-fill".into(), + format!("--build={}", cfg.build_triple()).into(), + format!("--host={}", target).into() ] +} + +fn libbacktrace_src_dir(cfg : &ConfigArgs) -> PathBuf { + cfg.src_dir().join("libbacktrace") +} + +fn libbacktrace_config_args(cfg : &ConfigArgs, + target : &Triple) -> Vec { + vec![ libbacktrace_src_dir(cfg).join("configure").into(), + format!("--host={}", cfg.build_triple()).into(), + format!("--target={}", target).into() ] +} + +fn external_rt_libs(cfg : &ConfigArgs, triple : &Triple) -> Vec { + vec![ + ExtRtLib { + name : "jemalloc", + needed : !triple.is_windows(), + env_vars : vec![], + config_cmd : "bash", + config_args : jemalloc_config_args(cfg, triple), + make_cmd : "make", + make_args : vec![cfg.jnproc()], + build_artefact_src : PathBuf::from("lib") + .join("libjemalloc_pic.a"), + build_artefact_dest : PathBuf::from("libjemalloc.a") + }, + ExtRtLib { + name : "libbacktrace", + needed : triple.is_linux(), + env_vars : vec![("CFLAGS".into(), + "-fPIC -fno-stack-protector".into())], + config_cmd : "bash", + config_args : libbacktrace_config_args(cfg, triple), + make_cmd : "make", + make_args : vec![cfg.jnproc(), + { let mut s = OsString::new(); + s.push("INCDIR="); + s.push(libbacktrace_src_dir(cfg)); + s + }], + build_artefact_src : PathBuf::from(".libs").join("libbacktrace.a"), + build_artefact_dest : PathBuf::from("libbacktrace.a") + } + ] +} + +fn build_external_rt_lib(args : &ConfigArgs, + triple : &Triple, + rtlib : &ExtRtLib) + -> BuildState<()> { + let name = rtlib.name; + let build_dir = rt_build_dir(args, triple).join(name); + let logger = args.get_logger(triple, name); + let _ = create_dir_all(&build_dir); // errors ignored + if rtlib.config_cmd != "" { + println!("Configuring {} for target triple {}...", name, triple); + let mut cfg_cmd = Command::new(rtlib.config_cmd); + cfg_cmd.args(&rtlib.config_args) + .current_dir(&build_dir); + for &(ref k, ref v) in &rtlib.env_vars { + cfg_cmd.env(&k, &v); + } + try!(cfg_cmd.tee(&logger)); + } + if rtlib.make_cmd != "" { + println!("Building {} for target triple {}...", name, triple); + try!(Command::new(rtlib.make_cmd) + .args(&rtlib.make_args) + .current_dir(&build_dir) + .tee(&logger)); + } + try!(copy(&build_dir.join(&rtlib.build_artefact_src), + &rt_build_dir(args, triple).join(&rtlib.build_artefact_dest)) + .map_err(|e| format!("Failed to copy build artefact for {}: {}", + name, e))); + continue_build() +} + +fn build_rustllvm(cfg : &ConfigArgs, target : &Triple) -> BuildState<()> { + println!("Building librustllvm for target triple {}...", target); + let logger = cfg.get_logger(target, "rustllvm"); + let build_dir = rt_build_dir(cfg, target); + let src_dir = cfg.src_dir().join("rustllvm"); + let src_files = vec!["ExecutionEngineWrapper.cpp", + "PassWrapper.cpp", "RustWrapper.cpp"]; + build_static_lib(cfg, target) + .set_src_dir(&src_dir) + .set_build_dir(&build_dir) + .files(&src_files) + .set_llvm_cxxflags() + .compile("rustllvm", &logger) +} + +pub fn llvmdeps_path(cfg : &ConfigArgs, target : &Triple) -> PathBuf { + rt_build_dir(cfg, target).join("llvmdeps.rs") +} + +fn generate_llvmdeps(cfg : &ConfigArgs, target : &Triple) + -> BuildState<()> { + println!("Generating llvmdeps.rs for target triple {}...", target); + let logger = cfg.get_logger(target, "llvmdeps"); + let script = cfg.src_dir().join("etc").join("mklldeps.py"); + let dest = llvmdeps_path(cfg, target); + let llvm_components = "x86 arm aarch64 mips powerpc ipo bitreader bitwriter linker asmparser mcjit interpreter instrumentation"; + let llvm_enable_static_libcpp = ""; // FIXME : add support + Command::new("python") + .arg(&script) + .arg(&dest) + .arg(llvm_components) + .arg(llvm_enable_static_libcpp) + .arg(&cfg.llvm_tools(target).path_to_llvm_config()) + .tee(&logger) +} + +pub fn llvmdef_path(cfg : &ConfigArgs, target : &Triple) -> PathBuf { + rt_build_dir(cfg, target).join("rustc_llvm.def") +} + +fn generate_llvmdef(cfg : &ConfigArgs, target : &Triple) + -> BuildState<()> { + println!("Generating rustc_llvm.def for target triple {}...", target); + let logger = cfg.get_logger(target, "llvmdef"); + let script = cfg.src_dir().join("etc").join("mklldef.py"); + let src = cfg.src_dir().join("librustc_llvm").join("lib.rs"); + let dest = llvmdef_path(cfg, target); + let arg = format!("rustc_llvm-{}", cfg.get_git_hash()); + Command::new("python") + .arg(&script) + .arg(&src) + .arg(&dest) + .arg(&arg) + .tee(&logger) +} + +pub fn build_native_libs(args : &ConfigArgs) -> BuildState<()> { + let mut triples : Vec = vec![]; + let _ : Vec<_> = args.target_triples().iter() + .map(|t| triples.push(t.clone())).collect(); + if triples.iter().filter(|&t| t == args.build_triple()).count() == 0 { + triples.push(args.build_triple().clone()); + } + for triple in &triples { + for extlib in &external_rt_libs(args, triple) { + if extlib.needed { + try!(build_external_rt_lib(args, triple, extlib)); + } + } + for rtlib in &runtime_libraries(triple) { + try!(build_rt_lib(args, triple, rtlib)); + } + try!(build_rustllvm(args, triple)); + try!(generate_llvmdeps(args, triple)); + if triple.is_msvc() { + try!(generate_llvmdef(args, triple)); + } + } + continue_build() +} diff --git a/build_rust/src/rust.rs b/build_rust/src/rust.rs new file mode 100644 index 0000000000000..0392d33be4577 --- /dev/null +++ b/build_rust/src/rust.rs @@ -0,0 +1,367 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Bootstrap a working stage2 compiler from the stage0 snapshot. +//! This means that for a given `i` where `i` runs from 0 to 2, we +//! use the existing stage`i` compiler to compile the stage`i+1` +//! compiler. The build artifacts are then promoted into the +//! stage`i+1` directory which will then be used to compile the next +//! stage. + +use std::process::Command; +use std::ffi::{OsStr, OsString}; +use std::path::{Path, PathBuf}; +use build_state::*; +use configure::ConfigArgs; +use rt::{rt_build_dir, llvmdeps_path, llvmdef_path}; +use cc::Triple; +use log::Tee; + +const RUST_LIBS : &'static [&'static str] + = &["core", "libc", "rand", "alloc", "rustc_unicode", "collections", + "rustc_bitflags", "std", "arena", "flate", "getopts", "graphviz", + "log", "term", "serialize", "fmt_macros", "syntax", "rbml", + "rustc_llvm", "rustc_back", "rustc_data_structures", "rustc", + "rustc_borrowck", "rustc_typeck", "rustc_resolve", "rustc_trans", + "rustc_privacy", "rustc_lint", "rustc_driver"]; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Stage { + Stage0, Stage1, Stage2 +} + +impl Stage { + fn to_str(self) -> &'static str { + match self { + Stage::Stage0 => "stage0", + Stage::Stage1 => "stage1", + Stage::Stage2 => "stage2" + } + } +} + +/// Specifies the host and target triples and build directories +/// of a given stage +struct StageInfo { + stage : Stage, + compiler_host : Triple, + target_triple : Triple, + host_build_dir : PathBuf +} + +impl StageInfo { + fn stage0(cfg : &ConfigArgs) -> StageInfo { + let build = cfg.build_triple(); + StageInfo { + stage : Stage::Stage0, + compiler_host : build.clone(), + target_triple : build.clone(), + host_build_dir : cfg.target_build_dir(build) + } + } + + fn stage1(cfg : &ConfigArgs) -> StageInfo { + let build = cfg.build_triple(); + let host = cfg.host_triple(); + StageInfo { + stage : Stage::Stage1, + compiler_host : build.clone(), + target_triple : host.clone(), + host_build_dir : cfg.target_build_dir(build) + } + } + + fn stage2(cfg : &ConfigArgs, target : &Triple) -> StageInfo { + let host = cfg.host_triple(); + StageInfo { + stage : Stage::Stage2, + compiler_host : host.clone(), + target_triple : target.clone(), + host_build_dir : cfg.target_build_dir(host) + } + } + + fn is_stage0(&self) -> bool { + self.stage == Stage::Stage0 + } + + fn is_stage1(&self) -> bool { + self.stage == Stage::Stage1 + } + + fn is_stage2(&self) -> bool { + self.stage == Stage::Stage2 + } + + fn build_dir(&self) -> PathBuf { + self.host_build_dir.join(self.stage.to_str()) + } + + fn bin_dir(&self) -> PathBuf { + self.build_dir().join("bin") + } + + fn lib_dir(&self) -> PathBuf { + if self.compiler_host.is_windows() { + self.build_dir().join("bin") + } else { + self.build_dir().join("lib") + } + } + + fn rustlib_dir(&self) -> PathBuf { + self.lib_dir().join("rustlib").join(&self.target_triple) + } + + fn rustlib_bin(&self) -> PathBuf { + self.rustlib_dir().join("bin") + } + + fn rustlib_lib(&self) -> PathBuf { + self.rustlib_dir().join("lib") + } +} + +/// Invoke the rustc compiler to compile a library or an executable +struct RustBuilder { + rustc_path : PathBuf, + src_dir : PathBuf, + llvm_deps_file : PathBuf, + llvm_def_file : PathBuf, + ld_library_path : PathBuf, + cfgs : Vec<&'static str>, + prefer_dynamic : bool, + no_landing_pads : bool, + target_triple : Triple, + link_dirs : Vec, + git_hash : String, + extra_args : Vec<&'static str> +} + +impl RustBuilder { + fn new(cfg : &ConfigArgs, sinfo : &StageInfo) -> RustBuilder { + let mut cfgs : Vec<&'static str> = vec![]; + if !cfg.is_debug_build() { + cfgs.push("rtopt"); + cfgs.push("ndebug"); + } + if !sinfo.target_triple.is_windows() { + cfgs.push("jemalloc"); + } + cfgs.push(sinfo.stage.to_str()); + + let rustc_path = + if cfg.use_local_rustc() && sinfo.is_stage0() { + PathBuf::from("rustc") + } else { + sinfo.bin_dir().join("rustc") + }; + + let link_dirs = vec![ + rt_build_dir(cfg, &sinfo.compiler_host), + cfg.llvm_tools(&sinfo.compiler_host).path_to_llvm_libs()]; + + RustBuilder { + rustc_path : rustc_path, + src_dir : cfg.src_dir(), + llvm_deps_file : llvmdeps_path(cfg, &sinfo.compiler_host), + llvm_def_file : llvmdef_path(cfg, &sinfo.compiler_host), + ld_library_path : sinfo.lib_dir(), + cfgs : cfgs, + prefer_dynamic : true, + no_landing_pads : true, + target_triple : sinfo.target_triple.clone(), + link_dirs : link_dirs, + git_hash : cfg.get_git_hash(), + extra_args : vec![ "-O", "-W", "warnings" ] + } + } + + fn compile_cmd(&self) -> Command { + let mut cmd = Command::new(&self.rustc_path); + cmd.env("CFG_COMPILER_HOST_TRIPLE", &self.target_triple); + cmd.env("CFG_LLVM_LINKAGE_FILE", &self.llvm_deps_file); + cmd.env("LD_LIBRARY_PATH", &self.ld_library_path); + cmd.args(&self.extra_args); + for s in &self.cfgs { + cmd.arg("--cfg").arg(s); + } + if self.prefer_dynamic { + cmd.arg("-C").arg("prefer-dynamic"); + } + if self.no_landing_pads { + cmd.arg("-Z").arg("no-landing-pads"); + } + cmd.arg("--target").arg(&self.target_triple); + for d in &self.link_dirs { + cmd.arg("-L").arg(d); + } + cmd.arg("-C").arg(&format!("extra-filename=-{}", self.git_hash)); + cmd + } + + fn rustc_lib_cmd(&self, lib : &str, out_dir : &Path) -> Command { + let src_path = self.src_dir + .join(&format!("lib{}", lib)).join("lib.rs"); + let mut cmd = self.compile_cmd(); + if lib == "rustc_llvm" && self.target_triple.is_msvc() { + cmd.arg("-C"); + let mut s = OsString::new(); + s.push("link-args=-DEF:"); + s.push(&self.llvm_def_file); + cmd.arg(&s); + } + cmd.arg("--out-dir").arg(out_dir); + cmd.arg(&src_path); + cmd + } + + fn compile_exe_cmd(&self, out : &Path) -> Command { + let mut cmd = self.compile_cmd(); + cmd.arg("-o").arg(out); + let src = self.src_dir.join("driver").join("driver.rs"); + cmd.arg(&src); + cmd + } + + fn rustc_exe_cmd(&self, out : &Path) -> Command { + let mut cmd = self.compile_exe_cmd(out); + cmd.arg("--cfg").arg("rustc"); + cmd + } + + fn rustdoc_exe_cmd(&self, out : &Path) -> Command { + let mut cmd = self.compile_exe_cmd(out); + cmd.arg("--cfg").arg("rustdoc"); + cmd + } +} + +// Copy the runtime libraries from the /rt directory into +// stagei/lib/rustlib//lib +fn copy_rt_libraries(cfg : &ConfigArgs, sinfo : &StageInfo) + -> BuildState<()> { + use std::fs::copy; + let libs = if sinfo.target_triple.is_windows() { + vec!["compiler-rt"] + } else { + vec!["compiler-rt", "morestack"] + }; + let from_dir = rt_build_dir(cfg, &sinfo.target_triple); + let to_dir = sinfo.rustlib_lib(); + for lib in &libs { + let filename = sinfo.target_triple.with_lib_ext(lib); + let from = from_dir.join(&filename); + let to = to_dir.join(&filename); + try!(copy(&from, &to).map_err(|e| { + format!("Failed to copy {:?} to {:?}: {}", from, to, e) + })); + } + continue_build() +} + +fn build_stage(cfg : &ConfigArgs, sinfo : &StageInfo) -> BuildState<()> { + try!(copy_rt_libraries(cfg, sinfo)); + let compiler = RustBuilder::new(cfg, sinfo); + let out_dir = sinfo.rustlib_lib(); + let logger = cfg.get_logger(&sinfo.compiler_host, + sinfo.stage.to_str()); + for lib in RUST_LIBS { + println!("Building {} library lib{} for target triple {}...", + sinfo.stage.to_str(), lib, sinfo.target_triple); + try!(compiler.rustc_lib_cmd(lib, &out_dir).tee(&logger)); + } + if !sinfo.is_stage2() { + println!("Building {} rustc for target triple {}...", + sinfo.stage.to_str(), sinfo.target_triple); + let rustc_exe = sinfo.rustlib_bin() + .join(sinfo.target_triple.with_exe_ext("rustc")); + try!(compiler.rustc_exe_cmd(&rustc_exe).tee(&logger)); + } + if sinfo.is_stage1() { + let rustdoc_libs = vec!["test", "rustdoc"]; + for lib in &rustdoc_libs { + println!("Building stage1 library lib{} for target triple {}...", + lib, sinfo.target_triple); + try!(compiler.rustc_lib_cmd(lib, &out_dir).tee(&logger)); + } + println!("Building stage1 rustdoc for target triple {}...", + sinfo.target_triple); + let rustdoc_exe = sinfo.rustlib_bin() + .join(sinfo.target_triple.with_exe_ext("rustdoc")); + try!(compiler.rustdoc_exe_cmd(&rustdoc_exe).tee(&logger)); + } + continue_build() +} + +// Promote the stagei artifacts built under +// stage`i`/lib/rustlib//lib directory into stage`i+1`/lib +fn promote_to_next_stage(sinfo : &StageInfo, snext : &StageInfo) + -> BuildState<()> { + use std::fs::{read_dir, copy}; + println!("Promoting {} to {}...", + sinfo.stage.to_str(), snext.stage.to_str()); + let mut copylist : Vec<(PathBuf, PathBuf)> = vec![]; + let exe = sinfo.target_triple.with_exe_ext("rustc"); + copylist.push((sinfo.rustlib_bin().join(&exe), + snext.bin_dir().join(&exe))); + if sinfo.is_stage1() { + let rustdoc = sinfo.target_triple.with_exe_ext("rustdoc"); + copylist.push((sinfo.rustlib_bin().join(&rustdoc), + snext.bin_dir().join(&rustdoc))); + } + for entry in try!(read_dir(&sinfo.rustlib_lib()) + .map_err(|e| format!("Failed to read dir {:?}: {}", + sinfo.rustlib_lib(), e))) { + let entry = try!(entry).path(); + if let Some(ext) = entry.extension() { + if OsStr::new(sinfo.target_triple.dylib_ext()) == ext { + let filename = entry.file_name().unwrap(); + let to = snext.lib_dir().join(filename); + copylist.push((entry.clone(), to)); + } + } + } + for &(ref from, ref to) in ©list { + try!(copy(&from, &to).map_err(|e| { + format!("Failed to copy {:?} to {:?}: {}", from, to, e) + })); + } + continue_build() +} + +fn create_dirs(sinfo : &StageInfo) { + use std::fs::create_dir_all; + let _ = create_dir_all(sinfo.bin_dir()); + let _ = create_dir_all(sinfo.lib_dir()); + let _ = create_dir_all(sinfo.rustlib_bin()); + let _ = create_dir_all(sinfo.rustlib_lib()); +} + +pub fn build_rust(cfg : &ConfigArgs) -> BuildState<()> { + let stage0 = StageInfo::stage0(cfg); + let stage1 = StageInfo::stage1(cfg); + let stage2 : Vec<_> = cfg.target_triples().iter() + .map(|target| StageInfo::stage2(cfg, target)).collect(); + create_dirs(&stage0); + create_dirs(&stage1); + let _ : Vec<_> = stage2.iter().map(|sinfo| create_dirs(&sinfo)).collect(); + + if !cfg.no_bootstrap() { + try!(build_stage(cfg, &stage0)); + try!(promote_to_next_stage(&stage0, &stage1)); + } + try!(build_stage(cfg, &stage1)); + try!(promote_to_next_stage(&stage1, &stage2[0])); + for sinfo in &stage2 { + try!(build_stage(cfg, &sinfo)); + } + continue_build() +} diff --git a/build_rust/src/snapshot.rs b/build_rust/src/snapshot.rs new file mode 100644 index 0000000000000..dec15014d2991 --- /dev/null +++ b/build_rust/src/snapshot.rs @@ -0,0 +1,49 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Invoke the external tools to download and unpack the stage0 +//! snapshot. This is done in an background thread so that it does +//! not block the current compilation process. + +use std::fs::create_dir_all; +use std::process::Command; +use std::sync::mpsc::{channel, Receiver}; +use std::thread; +use build_state::*; +use configure::ConfigArgs; +use log::Tee; + +pub fn download_stage0_snapshot(args : &ConfigArgs) + -> Receiver> { + let (tx, rx) = channel(); + if args.use_local_rustc() || args.no_bootstrap() { + tx.send(continue_build()).unwrap(); + } else { + let build_triple = args.build_triple().clone(); + let host_triple = args.host_triple().clone(); + let logger = args.get_logger(&host_triple, "download_stage0_snapshot"); + let rustc_root_dir = args.rustc_root_dir(); + let build_dir = args.toplevel_build_dir(); + let script = args.src_dir().join("etc").join("get-snapshot.py"); + println!("Downloading stage0 snapshot in the background..."); + let _ = create_dir_all(args.target_build_dir(&build_triple) + .join("stage0").join("bin")); + let _ = create_dir_all(build_dir.join("dl")); + thread::spawn(move || { + tx.send(Command::new("python") + .arg(&script) + .arg(build_triple) + .env("CFG_SRC_DIR", &rustc_root_dir) + .current_dir(&build_dir) + .tee(&logger)).unwrap(); + }); + } + rx +} diff --git a/src/compiler-rt b/src/compiler-rt index 58ab642c30d9f..7001c98423038 160000 --- a/src/compiler-rt +++ b/src/compiler-rt @@ -1 +1 @@ -Subproject commit 58ab642c30d9f97735d5745b5d01781ee199c6ae +Subproject commit 7001c984230382b8476a79512f0ec6c0522c48fb