diff --git a/src/Cargo.lock b/src/Cargo.lock index e4c16b92ceba5..5b918a063b07c 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -124,6 +124,8 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,6 +530,11 @@ name = "diff" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dlmalloc" version = "0.0.0" @@ -635,6 +642,11 @@ version = "0.1.0" name = "find_impls" version = "0.1.0" +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "flate2" version = "1.0.1" @@ -1162,6 +1174,11 @@ dependencies = [ "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "owning_ref" version = "0.3.3" @@ -1228,11 +1245,29 @@ dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "petgraph" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pretty_assertions" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.2.3" @@ -2683,6 +2718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e49c7125131f5afaded06944d6888b55cbdf8eba05dae73c954019b907961" "checksum derive-new 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fcb923bab47a948f1b01cec2f758fdebba95c9ebc255458654b2b88efe59d71" "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum ena 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b449f3b18c89d2dbe40548d2ee4fa58ea0a08b761992da6ecb9788e4688834" @@ -2693,6 +2729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" "checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "714653f3e34871534de23771ac7b26e999651a0a228f47beb324dfdf1dd4b10f" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -2748,13 +2785,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1636c9f1d78af9cbcc50e523bfff4a30274108aad5e86761afd4d31e4e184fa7" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdc5c4a02e69ce65046f1763a0181107038e02176233acb0b3351d7cc588f9" +"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" "checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" "checksum pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab94faafeb93f4c5e3ce81ca0e5a779529a602ad5d09ae6d21996bfb8b6a52bf" +"checksum petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8b30dc85588cd02b9b76f5e386535db546d21dc68506cff2abebee0b6445e8e4" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" +"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" "checksum proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "388d7ea47318c5ccdeb9ba6312cee7d3f65dd2804be8580a170fce410d50b786" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 2d47834131784..2f9c4e148a6ba 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -42,3 +42,7 @@ serde_json = "1.0.2" toml = "0.4" lazy_static = "0.2" time = "0.1" +petgraph = "0.4.12" + +[dev-dependencies] +pretty_assertions = "0.5" diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index eeac4436e6454..cf54591f25cd5 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -314,7 +314,7 @@ def __init__(self): self.build_dir = os.path.join(os.getcwd(), "build") self.clean = False self.config_toml = '' - self.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) + self.rust_root = '' self.use_locked_deps = '' self.use_vendored_sources = '' self.verbose = False @@ -710,6 +710,7 @@ def bootstrap(help_triggered): parser = argparse.ArgumentParser(description='Build rust') parser.add_argument('--config') parser.add_argument('--build') + parser.add_argument('--src') parser.add_argument('--clean', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0) @@ -718,6 +719,7 @@ def bootstrap(help_triggered): # Configure initial bootstrap build = RustBuild() + build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..')) build.verbose = args.verbose build.clean = args.clean @@ -788,6 +790,7 @@ def bootstrap(help_triggered): env["SRC"] = build.rust_root env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) env["BOOTSTRAP_PYTHON"] = sys.executable + env["BUILD_DIR"] = build.build_dir run(args, env=env, verbose=build.verbose) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 0464840c3e818..3f5ec4933d02b 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -19,6 +19,7 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Instant, Duration}; +use std::collections::HashMap; use compile; use install; @@ -35,6 +36,9 @@ use native; pub use Compiler; +use petgraph::Graph; +use petgraph::graph::NodeIndex; + pub struct Builder<'a> { pub build: &'a Build, pub top_stage: u32, @@ -42,6 +46,10 @@ pub struct Builder<'a> { cache: Cache, stack: RefCell>>, time_spent_on_dependencies: Cell, + pub paths: Vec, + graph_nodes: RefCell>, + graph: RefCell>, + parent: Cell>, } impl<'a> Deref for Builder<'a> { @@ -351,6 +359,10 @@ impl<'a> Builder<'a> { cache: Cache::new(), stack: RefCell::new(Vec::new()), time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), + paths: vec![], + graph_nodes: RefCell::new(HashMap::new()), + graph: RefCell::new(Graph::new()), + parent: Cell::new(None), }; let builder = &builder; @@ -367,7 +379,7 @@ impl<'a> Builder<'a> { Some(help) } - pub fn run(build: &Build) { + pub fn new(build: &Build) -> Builder { let (kind, paths) = match build.config.cmd { Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), @@ -379,12 +391,6 @@ impl<'a> Builder<'a> { Subcommand::Clean { .. } => panic!(), }; - if let Some(path) = paths.get(0) { - if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { - return; - } - } - let builder = Builder { build, top_stage: build.config.stage.unwrap_or(2), @@ -392,20 +398,33 @@ impl<'a> Builder<'a> { cache: Cache::new(), stack: RefCell::new(Vec::new()), time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), + paths: paths.to_owned(), + graph_nodes: RefCell::new(HashMap::new()), + graph: RefCell::new(Graph::new()), + parent: Cell::new(None), }; if kind == Kind::Dist { - assert!(!build.config.test_miri, "Do not distribute with miri enabled.\n\ + assert!(!builder.config.test_miri, "Do not distribute with miri enabled.\n\ The distributed libraries would include all MIR (increasing binary size). The distributed MIR would include validation statements."); } - StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths); + builder + } + + pub fn execute_cli(&self) -> Graph { + self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths); + self.graph.borrow().clone() } pub fn default_doc(&self, paths: Option<&[PathBuf]>) { let paths = paths.unwrap_or(&[]); - StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths); + self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths); + } + + fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) { + StepDescription::run(v, self, paths); } /// Obtain a compiler at a given stage and for a given host. Explicitly does @@ -828,12 +847,37 @@ impl<'a> Builder<'a> { if let Some(out) = self.cache.get(&step) { self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step)); + { + let mut graph = self.graph.borrow_mut(); + let parent = self.parent.get(); + let us = *self.graph_nodes.borrow_mut() + .entry(format!("{:?}", step)) + .or_insert_with(|| graph.add_node(format!("{:?}", step))); + if let Some(parent) = parent { + graph.add_edge(parent, us, false); + } + } + return out; } self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step)); stack.push(Box::new(step.clone())); } + let prev_parent = self.parent.get(); + + { + let mut graph = self.graph.borrow_mut(); + let parent = self.parent.get(); + let us = *self.graph_nodes.borrow_mut() + .entry(format!("{:?}", step)) + .or_insert_with(|| graph.add_node(format!("{:?}", step))); + self.parent.set(Some(us)); + if let Some(parent) = parent { + graph.add_edge(parent, us, true); + } + } + let (out, dur) = { let start = Instant::now(); let zero = Duration::new(0, 0); @@ -844,6 +888,8 @@ impl<'a> Builder<'a> { (out, dur - deps) }; + self.parent.set(prev_parent); + if self.build.config.print_step_timings && dur > Duration::from_millis(100) { println!("[TIMING] {:?} -- {}.{:03}", step, @@ -861,3 +907,483 @@ impl<'a> Builder<'a> { out } } + +#[cfg(test)] +mod __test { + use config::Config; + use std::thread; + use super::*; + + fn configure(host: &[&str], target: &[&str]) -> Config { + let mut config = Config::default_opts(); + // don't save toolstates + config.save_toolstates = None; + config.run_host_only = true; + config.dry_run = true; + // try to avoid spurious failures in dist where we create/delete each others file + let dir = config.out.join("tmp-rustbuild-tests") + .join(&thread::current().name().unwrap_or("unknown").replace(":", "-")); + t!(fs::create_dir_all(&dir)); + config.out = dir; + config.build = INTERNER.intern_str("A"); + config.hosts = vec![config.build].clone().into_iter() + .chain(host.iter().map(|s| INTERNER.intern_str(s))).collect::>(); + config.targets = config.hosts.clone().into_iter() + .chain(target.iter().map(|s| INTERNER.intern_str(s))).collect::>(); + config + } + + fn first(v: Vec<(A, B)>) -> Vec { + v.into_iter().map(|(a, _)| a).collect::>() + } + + #[test] + fn dist_baseline() { + let build = Build::new(configure(&[], &[])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_targets() { + let build = Build::new(configure(&[], &["B"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + dist::Docs { stage: 2, host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + dist::Mingw { host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_hosts() { + let build = Build::new(configure(&["B"], &[])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + dist::Docs { stage: 2, host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + dist::Mingw { host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_targets_and_hosts() { + let build = Build::new(configure(&["B"], &["C"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + let c = INTERNER.intern_str("C"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + dist::Docs { stage: 2, host: b }, + dist::Docs { stage: 2, host: c }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + dist::Mingw { host: b }, + dist::Mingw { host: c }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: c, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_target_flag() { + let mut config = configure(&["B"], &["C"]); + config.run_host_only = false; // as-if --target=C was passed + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + let c = INTERNER.intern_str("C"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + dist::Docs { stage: 2, host: b }, + dist::Docs { stage: 2, host: c }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + dist::Mingw { host: b }, + dist::Mingw { host: c }, + ]); + assert_eq!(first(builder.cache.all::()), &[]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: c, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[]); + } + + #[test] + fn dist_with_same_targets_and_hosts() { + let build = Build::new(configure(&["B"], &["B"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + + assert_eq!(first(builder.cache.all::()), &[ + dist::Docs { stage: 2, host: a }, + dist::Docs { stage: 2, host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Mingw { host: a }, + dist::Mingw { host: b }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + dist::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + assert_eq!(first(builder.cache.all::()), &[ + compile::Std { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Std { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Std { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + compile::Std { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + compile::Std { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + compile::Test { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + compile::Assemble { + target_compiler: Compiler { host: a, stage: 0 }, + }, + compile::Assemble { + target_compiler: Compiler { host: a, stage: 1 }, + }, + compile::Assemble { + target_compiler: Compiler { host: a, stage: 2 }, + }, + compile::Assemble { + target_compiler: Compiler { host: b, stage: 2 }, + }, + ]); + } + + #[test] + fn build_default() { + let build = Build::new(configure(&["B"], &["C"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + let c = INTERNER.intern_str("C"); + + assert!(!builder.cache.all::().is_empty()); + assert!(!builder.cache.all::().is_empty()); + assert_eq!(first(builder.cache.all::()), &[ + compile::Rustc { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: b, stage: 2 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 0 }, + target: b, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + compile::Rustc { + compiler: Compiler { host: b, stage: 2 }, + target: b, + }, + ]); + + assert_eq!(first(builder.cache.all::()), &[ + compile::Test { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 0 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: c, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: c, + }, + ]); + } + + #[test] + fn build_with_target_flag() { + let mut config = configure(&["B"], &["C"]); + config.run_host_only = false; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = INTERNER.intern_str("A"); + let b = INTERNER.intern_str("B"); + let c = INTERNER.intern_str("C"); + + assert!(!builder.cache.all::().is_empty()); + assert_eq!(first(builder.cache.all::()), &[ + compile::Assemble { + target_compiler: Compiler { host: a, stage: 0 }, + }, + compile::Assemble { + target_compiler: Compiler { host: a, stage: 1 }, + }, + compile::Assemble { + target_compiler: Compiler { host: b, stage: 1 }, + }, + compile::Assemble { + target_compiler: Compiler { host: a, stage: 2 }, + }, + compile::Assemble { + target_compiler: Compiler { host: b, stage: 2 }, + }, + ]); + assert_eq!(first(builder.cache.all::()), &[ + compile::Rustc { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 0 }, + target: b, + }, + compile::Rustc { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + ]); + + assert_eq!(first(builder.cache.all::()), &[ + compile::Test { + compiler: Compiler { host: a, stage: 0 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: a, + }, + compile::Test { + compiler: Compiler { host: a, stage: 0 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 1 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: b, + }, + compile::Test { + compiler: Compiler { host: a, stage: 2 }, + target: c, + }, + compile::Test { + compiler: Compiler { host: b, stage: 2 }, + target: c, + }, + ]); + } +} diff --git a/src/bootstrap/cache.rs b/src/bootstrap/cache.rs index c27493158826c..d81c6bc28e527 100644 --- a/src/bootstrap/cache.rs +++ b/src/bootstrap/cache.rs @@ -21,6 +21,7 @@ use std::mem; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::Mutex; +use std::cmp::{PartialOrd, Ord, Ordering}; use builder::Step; @@ -154,6 +155,19 @@ impl AsRef for Interned { } } +impl PartialOrd> for Interned { + fn partial_cmp(&self, other: &Self) -> Option { + let l = INTERNER.strs.lock().unwrap(); + l.get(*self).partial_cmp(l.get(*other)) + } +} + +impl Ord for Interned { + fn cmp(&self, other: &Self) -> Ordering { + let l = INTERNER.strs.lock().unwrap(); + l.get(*self).cmp(l.get(*other)) + } +} struct TyIntern { items: Vec, @@ -264,4 +278,16 @@ impl Cache { .expect("invalid type mapped"); stepcache.get(step).cloned() } + + #[cfg(test)] + pub fn all(&mut self) -> Vec<(S, S::Output)> { + let cache = self.0.get_mut(); + let type_id = TypeId::of::(); + let mut v = cache.remove(&type_id) + .map(|b| b.downcast::>().expect("correct type")) + .map(|m| m.into_iter().collect::>()) + .unwrap_or_default(); + v.sort_by_key(|&(a, _)| a); + v + } } diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index a9dccea827b6e..a39fad67ebea4 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -53,7 +53,7 @@ impl Step for Std { true); let libdir = builder.sysroot_libdir(compiler, target); - add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target)); + add_to_sysroot(&build, &libdir, &libstd_stamp(build, compiler, target)); } } @@ -102,7 +102,7 @@ impl Step for Rustc { true); let libdir = builder.sysroot_libdir(compiler, target); - add_to_sysroot(&libdir, &librustc_stamp(build, compiler, target)); + add_to_sysroot(&build, &libdir, &librustc_stamp(build, compiler, target)); } } @@ -143,7 +143,7 @@ impl Step for Test { true); let libdir = builder.sysroot_libdir(compiler, target); - add_to_sysroot(&libdir, &libtest_stamp(build, compiler, target)); + add_to_sysroot(&build, &libdir, &libtest_stamp(build, compiler, target)); } } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index e6aa78fba52ff..da57881202d17 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -30,7 +30,7 @@ use build_helper::{output, mtime, up_to_date}; use filetime::FileTime; use serde_json; -use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv}; +use util::{exe, libdir, is_dylib, CiEnv}; use {Build, Compiler, Mode}; use native; use tool; @@ -38,7 +38,7 @@ use tool; use cache::{INTERNER, Interned}; use builder::{Step, RunConfig, ShouldRun, Builder}; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Std { pub target: Interned, pub compiler: Compiler, @@ -77,7 +77,7 @@ impl Step for Std { compiler: from, target, }); - println!("Uplifting stage1 std ({} -> {})", from.host, target); + builder.info(&format!("Uplifting stage1 std ({} -> {})", from.host, target)); // Even if we're not building std this stage, the new sysroot must // still contain the musl startup objects. @@ -105,8 +105,8 @@ impl Step for Std { std_cargo(builder, &compiler, target, &mut cargo); let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage)); - println!("Building stage{} std artifacts ({} -> {})", compiler.stage, - &compiler.host, target); + build.info(&format!("Building stage{} std artifacts ({} -> {})", compiler.stage, + &compiler.host, target)); run_cargo(build, &mut cargo, &libstd_stamp(build, compiler, target), @@ -130,7 +130,7 @@ fn copy_musl_third_party_objects(build: &Build, target: Interned, into: &Path) { for &obj in &["crt1.o", "crti.o", "crtn.o"] { - copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj)); + build.copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj)); } } @@ -213,20 +213,20 @@ impl Step for StdLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - println!("Copying stage{} std from stage{} ({} -> {} / {})", + build.info(&format!("Copying stage{} std from stage{} ({} -> {} / {})", target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, - target); + target)); let libdir = builder.sysroot_libdir(target_compiler, target); - add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target)); + add_to_sysroot(&build, &libdir, &libstd_stamp(build, compiler, target)); if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" { // The sanitizers are only built in stage1 or above, so the dylibs will // be missing in stage0 and causes panic. See the `std()` function above // for reason why the sanitizers are not built in stage0. - copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir); + copy_apple_sanitizer_dylibs(&build, &build.native_dir(target), "osx", &libdir); } builder.ensure(tool::CleanTools { @@ -237,7 +237,7 @@ impl Step for StdLink { } } -fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) { +fn copy_apple_sanitizer_dylibs(build: &Build, native_dir: &Path, platform: &str, into: &Path) { for &sanitizer in &["asan", "tsan"] { let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform); let mut src_path = native_dir.join(sanitizer); @@ -245,7 +245,7 @@ fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) { src_path.push("lib"); src_path.push("darwin"); src_path.push(&filename); - copy(&src_path, &into.join(filename)); + build.copy(&src_path, &into.join(filename)); } } @@ -301,7 +301,7 @@ impl Step for StartupObjects { .arg(src_file)); } - copy(dst_file, &sysroot_dir.join(file.to_string() + ".o")); + build.copy(dst_file, &sysroot_dir.join(file.to_string() + ".o")); } for obj in ["crt2.o", "dllcrt2.o"].iter() { @@ -309,15 +309,15 @@ impl Step for StartupObjects { build.cc(target), target, obj); - copy(&src, &sysroot_dir.join(obj)); + build.copy(&src, &sysroot_dir.join(obj)); } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Test { - pub compiler: Compiler, pub target: Interned, + pub compiler: Compiler, } impl Step for Test { @@ -352,7 +352,7 @@ impl Step for Test { compiler: builder.compiler(1, build.build), target, }); - println!("Uplifting stage1 test ({} -> {})", &build.build, target); + build.info(&format!("Uplifting stage1 test ({} -> {})", &build.build, target)); builder.ensure(TestLink { compiler: builder.compiler(1, build.build), target_compiler: compiler, @@ -367,8 +367,8 @@ impl Step for Test { test_cargo(build, &compiler, target, &mut cargo); let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage)); - println!("Building stage{} test artifacts ({} -> {})", compiler.stage, - &compiler.host, target); + build.info(&format!("Building stage{} test artifacts ({} -> {})", compiler.stage, + &compiler.host, target)); run_cargo(build, &mut cargo, &libtest_stamp(build, compiler, target), @@ -414,13 +414,13 @@ impl Step for TestLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - println!("Copying stage{} test from stage{} ({} -> {} / {})", + build.info(&format!("Copying stage{} test from stage{} ({} -> {} / {})", target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, - target); - add_to_sysroot(&builder.sysroot_libdir(target_compiler, target), + target)); + add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target), &libtest_stamp(build, compiler, target)); builder.ensure(tool::CleanTools { compiler: target_compiler, @@ -430,10 +430,10 @@ impl Step for TestLink { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Rustc { - pub compiler: Compiler, pub target: Interned, + pub compiler: Compiler, } impl Step for Rustc { @@ -469,7 +469,7 @@ impl Step for Rustc { compiler: builder.compiler(1, build.build), target, }); - println!("Uplifting stage1 rustc ({} -> {})", &build.build, target); + build.info(&format!("Uplifting stage1 rustc ({} -> {})", &build.build, target)); builder.ensure(RustcLink { compiler: builder.compiler(1, build.build), target_compiler: compiler, @@ -491,8 +491,8 @@ impl Step for Rustc { rustc_cargo(build, &mut cargo); let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage)); - println!("Building stage{} compiler artifacts ({} -> {})", - compiler.stage, &compiler.host, target); + build.info(&format!("Building stage{} compiler artifacts ({} -> {})", + compiler.stage, &compiler.host, target)); run_cargo(build, &mut cargo, &librustc_stamp(build, compiler, target), @@ -569,13 +569,13 @@ impl Step for RustcLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - println!("Copying stage{} rustc from stage{} ({} -> {} / {})", + build.info(&format!("Copying stage{} rustc from stage{} ({} -> {} / {})", target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, - target); - add_to_sysroot(&builder.sysroot_libdir(target_compiler, target), + target)); + add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target), &librustc_stamp(build, compiler, target)); builder.ensure(tool::CleanTools { compiler: target_compiler, @@ -648,8 +648,8 @@ impl Step for CodegenBackend { features.push_str(" emscripten"); } - println!("Building stage{} codegen artifacts ({} -> {}, {})", - compiler.stage, &compiler.host, target, self.backend); + build.info(&format!("Building stage{} codegen artifacts ({} -> {}, {})", + compiler.stage, &compiler.host, target, self.backend)); // Pass down configuration from the LLVM build into the build of // librustc_llvm and librustc_trans. @@ -690,6 +690,9 @@ impl Step for CodegenBackend { cargo.arg("--features").arg(features), &tmp_stamp, false); + if builder.config.dry_run { + return; + } let mut files = files.into_iter() .filter(|f| { let filename = f.file_name().unwrap().to_str().unwrap(); @@ -733,6 +736,10 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder, let dst = builder.sysroot_codegen_backends(target_compiler); t!(fs::create_dir_all(&dst)); + if builder.config.dry_run { + return; + } + for backend in builder.config.rust_codegen_backends.iter() { let stamp = codegen_backend_stamp(build, compiler, target, *backend); let mut dylib = String::new(); @@ -748,7 +755,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder, backend, &filename[dot..]) }; - copy(&file, &dst.join(target_filename)); + build.copy(&file, &dst.join(target_filename)); } } @@ -764,7 +771,7 @@ fn copy_lld_to_sysroot(builder: &Builder, t!(fs::create_dir_all(&dst)); let exe = exe("lld", &target); - copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe)); + builder.copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe)); } /// Cargo's output path for the standard library in a given stage, compiled @@ -836,7 +843,7 @@ impl Step for Sysroot { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Assemble { /// The compiler which we will produce in this step. Assemble itself will /// take care of ensuring that the necessary prerequisites to do so exist, @@ -926,17 +933,17 @@ impl Step for Assemble { let stage = target_compiler.stage; let host = target_compiler.host; - println!("Assembling stage{} compiler ({})", stage, host); + build.info(&format!("Assembling stage{} compiler ({})", stage, host)); // Link in all dylibs to the libdir let sysroot = builder.sysroot(target_compiler); let sysroot_libdir = sysroot.join(libdir(&*host)); t!(fs::create_dir_all(&sysroot_libdir)); let src_libdir = builder.sysroot_libdir(build_compiler, host); - for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) { + for f in builder.read_dir(&src_libdir) { let filename = f.file_name().into_string().unwrap(); if is_dylib(&filename) { - copy(&f.path(), &sysroot_libdir.join(&filename)); + builder.copy(&f.path(), &sysroot_libdir.join(&filename)); } } @@ -954,7 +961,7 @@ impl Step for Assemble { t!(fs::create_dir_all(&bindir)); let compiler = builder.rustc(target_compiler); let _ = fs::remove_file(&compiler); - copy(&rustc, &compiler); + builder.copy(&rustc, &compiler); target_compiler } @@ -964,10 +971,10 @@ impl Step for Assemble { /// /// For a particular stage this will link the file listed in `stamp` into the /// `sysroot_dst` provided. -pub fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) { +pub fn add_to_sysroot(build: &Build, sysroot_dst: &Path, stamp: &Path) { t!(fs::create_dir_all(&sysroot_dst)); - for path in read_stamp_file(stamp) { - copy(&path, &sysroot_dst.join(path.file_name().unwrap())); + for path in build.read_stamp_file(stamp) { + build.copy(&path, &sysroot_dst.join(path.file_name().unwrap())); } } @@ -997,6 +1004,10 @@ fn stderr_isatty() -> bool { pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool) -> Vec { + if build.config.dry_run { + return Vec::new(); + } + // `target_root_dir` looks like $dir/$target/release let target_root_dir = stamp.parent().unwrap(); // `target_deps_dir` looks like $dir/$target/release/deps @@ -1138,6 +1149,9 @@ pub fn stream_cargo( cargo: &mut Command, cb: &mut FnMut(CargoMessage), ) -> bool { + if build.config.dry_run { + return true; + } // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. cargo.arg("--message-format").arg("json") @@ -1172,7 +1186,7 @@ pub fn stream_cargo( // Make sure Cargo actually succeeded after we read all of its stdout. let status = t!(child.wait()); if !status.success() { - println!("command did not execute successfully: {:?}\n\ + eprintln!("command did not execute successfully: {:?}\n\ expected success, got: {}", cargo, status); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 33850debd3bdb..76672df5c570d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -15,7 +15,7 @@ use std::collections::{HashMap, HashSet}; use std::env; -use std::fs::File; +use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process; @@ -69,6 +69,7 @@ pub struct Config { pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, + pub dry_run: bool, // llvm codegen options pub llvm_enabled: bool, @@ -143,6 +144,7 @@ pub struct Config { // These are either the stage0 downloaded binaries or the locally installed ones. pub initial_cargo: PathBuf, pub initial_rustc: PathBuf, + pub out: PathBuf, } /// Per-target configuration stored in the global configuration structure. @@ -317,11 +319,8 @@ struct TomlTarget { } impl Config { - pub fn parse(args: &[String]) -> Config { - let flags = Flags::parse(&args); - let file = flags.config.clone(); + pub fn default_opts() -> Config { let mut config = Config::default(); - config.exclude = flags.exclude; config.llvm_enabled = true; config.llvm_optimize = true; config.llvm_version_check = true; @@ -341,15 +340,38 @@ impl Config { config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.rust_codegen_backends_dir = "codegen-backends".to_owned(); + // set by bootstrap.py + config.src = env::var_os("SRC").map(PathBuf::from).expect("'SRC' to be set"); + config.build = INTERNER.intern_str(&env::var("BUILD").expect("'BUILD' to be set")); + config.out = env::var_os("BUILD_DIR").map(PathBuf::from).expect("'BUILD_DIR' set"); + + let stage0_root = config.out.join(&config.build).join("stage0/bin"); + config.initial_rustc = stage0_root.join(exe("rustc", &config.build)); + config.initial_cargo = stage0_root.join(exe("cargo", &config.build)); + + config + } + + pub fn parse(args: &[String]) -> Config { + let flags = Flags::parse(&args); + let file = flags.config.clone(); + let mut config = Config::default_opts(); + config.exclude = flags.exclude; config.rustc_error_format = flags.rustc_error_format; config.on_fail = flags.on_fail; config.stage = flags.stage; - config.src = flags.src; config.jobs = flags.jobs; config.cmd = flags.cmd; config.incremental = flags.incremental; + config.dry_run = flags.dry_run; config.keep_stage = flags.keep_stage; + if config.dry_run { + let dir = config.out.join("tmp-dry-run"); + t!(fs::create_dir_all(&dir)); + config.out = dir; + } + // If --target was specified but --host wasn't specified, don't run any host-only tests. config.run_host_only = !(flags.host.is_empty() && !flags.target.is_empty()); @@ -368,12 +390,7 @@ impl Config { }).unwrap_or_else(|| TomlConfig::default()); let build = toml.build.clone().unwrap_or(Build::default()); - set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x))); - set(&mut config.build, flags.build); - if config.build.is_empty() { - // set by bootstrap.py - config.build = INTERNER.intern_str(&env::var("BUILD").unwrap()); - } + // set by bootstrap.py config.hosts.push(config.build.clone()); for host in build.host.iter() { let host = INTERNER.intern_str(host); @@ -514,13 +531,13 @@ impl Config { let mut target = Target::default(); if let Some(ref s) = cfg.llvm_config { - target.llvm_config = Some(env::current_dir().unwrap().join(s)); + target.llvm_config = Some(config.src.join(s)); } if let Some(ref s) = cfg.jemalloc { - target.jemalloc = Some(env::current_dir().unwrap().join(s)); + target.jemalloc = Some(config.src.join(s)); } if let Some(ref s) = cfg.android_ndk { - target.ndk = Some(env::current_dir().unwrap().join(s)); + target.ndk = Some(config.src.join(s)); } target.cc = cfg.cc.clone().map(PathBuf::from); target.cxx = cfg.cxx.clone().map(PathBuf::from); @@ -541,22 +558,12 @@ impl Config { set(&mut config.rust_dist_src, t.src_tarball); } - let cwd = t!(env::current_dir()); - let out = cwd.join("build"); - - let stage0_root = out.join(&config.build).join("stage0/bin"); - config.initial_rustc = match build.rustc { - Some(s) => PathBuf::from(s), - None => stage0_root.join(exe("rustc", &config.build)), - }; - config.initial_cargo = match build.cargo { - Some(s) => PathBuf::from(s), - None => stage0_root.join(exe("cargo", &config.build)), - }; - // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. + set(&mut config.initial_rustc, build.rustc.map(PathBuf::from)); + set(&mut config.initial_rustc, build.cargo.map(PathBuf::from)); + let default = false; config.llvm_assertions = llvm_assertions.unwrap_or(default); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 23b7b265a94be..c9be17ff1ad2d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -20,7 +20,7 @@ use std::env; use std::fs::{self, File}; -use std::io::{self, Read, Write}; +use std::io::{Read, Write}; use std::path::{PathBuf, Path}; use std::process::{Command, Stdio}; @@ -28,7 +28,7 @@ use build_helper::output; use {Build, Compiler, Mode}; use channel; -use util::{cp_r, libdir, is_dylib, cp_filtered, copy, replace_in_file, exe}; +use util::{libdir, is_dylib, exe}; use builder::{Builder, RunConfig, ShouldRun, Step}; use compile; use native; @@ -61,7 +61,7 @@ fn rust_installer(builder: &Builder) -> Command { builder.tool_cmd(Tool::RustInstaller) } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Docs { pub stage: u32, pub host: Interned, @@ -89,9 +89,9 @@ impl Step for Docs { let name = pkgname(build, "rust-docs"); - println!("Dist docs ({})", host); + build.info(&format!("Dist docs ({})", host)); if !build.config.docs { - println!("\tskipping - docs disabled"); + build.info(&format!("\tskipping - docs disabled")); return distdir(build).join(format!("{}-{}.tar.gz", name, host)); } @@ -103,7 +103,7 @@ impl Step for Docs { let dst = image.join("share/doc/rust/html"); t!(fs::create_dir_all(&dst)); let src = build.doc_out(host); - cp_r(&src, &dst); + build.cp_r(&src, &dst); let mut cmd = rust_installer(builder); cmd.arg("generate") @@ -118,7 +118,7 @@ impl Step for Docs { .arg("--legacy-manifest-dirs=rustlib,cargo") .arg("--bulk-dirs=share/doc/rust/html"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); + build.remove_dir(&image); distdir(build).join(format!("{}-{}.tar.gz", name, host)) } @@ -152,9 +152,9 @@ impl Step for RustcDocs { let name = pkgname(build, "rustc-docs"); - println!("Dist compiler docs ({})", host); + build.info(&format!("Dist compiler docs ({})", host)); if !build.config.compiler_docs { - println!("\tskipping - compiler docs disabled"); + build.info(&format!("\tskipping - compiler docs disabled")); return distdir(build).join(format!("{}-{}.tar.gz", name, host)); } @@ -166,7 +166,7 @@ impl Step for RustcDocs { let dst = image.join("share/doc/rust/html"); t!(fs::create_dir_all(&dst)); let src = build.compiler_doc_out(host); - cp_r(&src, &dst); + build.cp_r(&src, &dst); let mut cmd = rust_installer(builder); cmd.arg("generate") @@ -181,7 +181,7 @@ impl Step for RustcDocs { .arg("--legacy-manifest-dirs=rustlib,cargo") .arg("--bulk-dirs=share/doc/rust/html"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); + build.remove_dir(&image); distdir(build).join(format!("{}-{}.tar.gz", name, host)) } @@ -292,37 +292,31 @@ fn make_win_dist( let rustc_dlls = find_files(&rustc_dlls, &bin_path); let target_libs = find_files(&target_libs, &lib_path); - fn copy_to_folder(src: &Path, dest_folder: &Path) { - let file_name = src.file_name().unwrap(); - let dest = dest_folder.join(file_name); - copy(src, &dest); - } - - //Copy runtime dlls next to rustc.exe + // Copy runtime dlls next to rustc.exe let dist_bin_dir = rust_root.join("bin/"); fs::create_dir_all(&dist_bin_dir).expect("creating dist_bin_dir failed"); for src in rustc_dlls { - copy_to_folder(&src, &dist_bin_dir); + build.copy_to_folder(&src, &dist_bin_dir); } //Copy platform tools to platform-specific bin directory let target_bin_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("bin"); fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed"); for src in target_tools { - copy_to_folder(&src, &target_bin_dir); + build.copy_to_folder(&src, &target_bin_dir); } //Copy platform libs to platform-specific lib directory let target_lib_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("lib"); fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed"); for src in target_libs { - copy_to_folder(&src, &target_lib_dir); + build.copy_to_folder(&src, &target_lib_dir); } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Mingw { - host: Interned, + pub host: Interned, } impl Step for Mingw { @@ -349,7 +343,7 @@ impl Step for Mingw { return None; } - println!("Dist mingw ({})", host); + build.info(&format!("Dist mingw ({})", host)); let name = pkgname(build, "rust-mingw"); let image = tmpdir(build).join(format!("{}-{}-image", name, host)); let _ = fs::remove_dir_all(&image); @@ -378,7 +372,7 @@ impl Step for Mingw { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rustc { pub compiler: Compiler, } @@ -404,7 +398,7 @@ impl Step for Rustc { let compiler = self.compiler; let host = self.compiler.host; - println!("Dist rustc stage{} ({})", compiler.stage, compiler.host); + build.info(&format!("Dist rustc stage{} ({})", compiler.stage, compiler.host)); let name = pkgname(build, "rustc"); let image = tmpdir(build).join(format!("{}-{}-image", name, host)); let _ = fs::remove_dir_all(&image); @@ -417,7 +411,7 @@ impl Step for Rustc { // Prepare the overlay which is part of the tarball but won't actually be // installed let cp = |file: &str| { - install(&build.src.join(file), &overlay, 0o644); + build.install(&build.src.join(file), &overlay, 0o644); }; cp("COPYRIGHT"); cp("LICENSE-APACHE"); @@ -425,9 +419,9 @@ impl Step for Rustc { cp("README.md"); // tiny morsel of metadata is used by rust-packaging let version = build.rust_version(); - t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + build.create(&overlay.join("version"), &version); if let Some(sha) = build.rust_sha() { - t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes())); + build.create(&overlay.join("git-commit-hash"), &sha); } // On MinGW we've got a few runtime DLL dependencies that we need to @@ -445,7 +439,7 @@ impl Step for Rustc { let dst = image.join("share/doc"); t!(fs::create_dir_all(&dst)); - cp_r(&build.src.join("src/etc/third-party"), &dst); + build.cp_r(&build.src.join("src/etc/third-party"), &dst); } // Finally, wrap everything up in a nice tarball! @@ -462,8 +456,8 @@ impl Step for Rustc { .arg("--component-name=rustc") .arg("--legacy-manifest-dirs=rustlib,cargo"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); - t!(fs::remove_dir_all(&overlay)); + build.remove_dir(&image); + build.remove_dir(&overlay); return distdir(build).join(format!("{}-{}.tar.gz", name, host)); @@ -475,17 +469,17 @@ impl Step for Rustc { // Copy rustc/rustdoc binaries t!(fs::create_dir_all(image.join("bin"))); - cp_r(&src.join("bin"), &image.join("bin")); + build.cp_r(&src.join("bin"), &image.join("bin")); - install(&builder.rustdoc(compiler.host), &image.join("bin"), 0o755); + build.install(&builder.rustdoc(compiler.host), &image.join("bin"), 0o755); // Copy runtime DLLs needed by the compiler if libdir != "bin" { - for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) { + for entry in build.read_dir(&src.join(libdir)) { let name = entry.file_name(); if let Some(s) = name.to_str() { if is_dylib(s) { - install(&entry.path(), &image.join(libdir), 0o644); + build.install(&entry.path(), &image.join(libdir), 0o644); } } } @@ -496,7 +490,7 @@ impl Step for Rustc { let backends_rel = backends_src.strip_prefix(&src).unwrap(); let backends_dst = image.join(&backends_rel); t!(fs::create_dir_all(&backends_dst)); - cp_r(&backends_src, &backends_dst); + build.cp_r(&backends_src, &backends_dst); // Copy over lld if it's there if builder.config.lld_enabled { @@ -511,7 +505,7 @@ impl Step for Rustc { .join("bin") .join(&exe); t!(fs::create_dir_all(&dst.parent().unwrap())); - copy(&src, &dst); + build.copy(&src, &dst); } // Man pages @@ -521,13 +515,12 @@ impl Step for Rustc { let month_year = t!(time::strftime("%B %Y", &time::now())); // don't use our `bootstrap::util::{copy, cp_r}`, because those try // to hardlink, and we don't want to edit the source templates - for entry_result in t!(fs::read_dir(man_src)) { - let file_entry = t!(entry_result); + for file_entry in build.read_dir(&man_src) { let page_src = file_entry.path(); let page_dst = man_dst.join(file_entry.file_name()); t!(fs::copy(&page_src, &page_dst)); // template in month/year and version number - replace_in_file(&page_dst, + build.replace_in_file(&page_dst, &[("", &month_year), ("", channel::CFG_RELEASE_NUM)]); } @@ -540,7 +533,7 @@ impl Step for Rustc { // Misc license info let cp = |file: &str| { - install(&build.src.join(file), &image.join("share/doc/rust"), 0o644); + build.install(&build.src.join(file), &image.join("share/doc/rust"), 0o644); }; cp("COPYRIGHT"); cp("LICENSE-APACHE"); @@ -578,11 +571,11 @@ impl Step for DebuggerScripts { let dst = sysroot.join("lib/rustlib/etc"); t!(fs::create_dir_all(&dst)); let cp_debugger_script = |file: &str| { - install(&build.src.join("src/etc/").join(file), &dst, 0o644); + build.install(&build.src.join("src/etc/").join(file), &dst, 0o644); }; if host.contains("windows-msvc") { // windbg debugger scripts - install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"), + build.install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"), 0o755); cp_debugger_script("natvis/intrinsic.natvis"); @@ -592,14 +585,14 @@ impl Step for DebuggerScripts { cp_debugger_script("debugger_pretty_printers_common.py"); // gdb debugger scripts - install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), + build.install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_rust_pretty_printing.py"); // lldb debugger scripts - install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), + build.install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); cp_debugger_script("lldb_rust_formatters.py"); @@ -607,7 +600,7 @@ impl Step for DebuggerScripts { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Std { pub compiler: Compiler, pub target: Interned, @@ -634,12 +627,12 @@ impl Step for Std { let target = self.target; let name = pkgname(build, "rust-std"); - println!("Dist std stage{} ({} -> {})", compiler.stage, &compiler.host, target); + build.info(&format!("Dist std stage{} ({} -> {})", compiler.stage, &compiler.host, target)); // The only true set of target libraries came from the build triple, so // let's reduce redundant work by only producing archives from that host. if compiler.host != build.build { - println!("\tskipping, not a build host"); + build.info(&format!("\tskipping, not a build host")); return distdir(build).join(format!("{}-{}.tar.gz", name, target)); } @@ -659,7 +652,7 @@ impl Step for Std { t!(fs::create_dir_all(&dst)); let mut src = builder.sysroot_libdir(compiler, target).to_path_buf(); src.pop(); // Remove the trailing /lib folder from the sysroot_libdir - cp_filtered(&src, &dst, &|path| { + build.cp_filtered(&src, &dst, &|path| { let name = path.file_name().and_then(|s| s.to_str()); name != Some(build.config.rust_codegen_backends_dir.as_str()) && name != Some("bin") @@ -678,7 +671,7 @@ impl Step for Std { .arg(format!("--component-name=rust-std-{}", target)) .arg("--legacy-manifest-dirs=rustlib,cargo"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); + build.remove_dir(&image); distdir(build).join(format!("{}-{}.tar.gz", name, target)) } } @@ -711,11 +704,11 @@ impl Step for Analysis { let compiler = self.compiler; let target = self.target; assert!(build.config.extended); - println!("Dist analysis"); + build.info(&format!("Dist analysis")); let name = pkgname(build, "rust-analysis"); if &compiler.host != build.build { - println!("\tskipping, not a build host"); + build.info(&format!("\tskipping, not a build host")); return distdir(build).join(format!("{}-{}.tar.gz", name, target)); } @@ -737,8 +730,8 @@ impl Step for Analysis { let image_src = src.join("save-analysis"); let dst = image.join("lib/rustlib").join(target).join("analysis"); t!(fs::create_dir_all(&dst)); - println!("image_src: {:?}, dst: {:?}", image_src, dst); - cp_r(&image_src, &dst); + build.info(&format!("image_src: {:?}, dst: {:?}", image_src, dst)); + build.cp_r(&image_src, &dst); let mut cmd = rust_installer(builder); cmd.arg("generate") @@ -752,7 +745,7 @@ impl Step for Analysis { .arg(format!("--component-name=rust-analysis-{}", target)) .arg("--legacy-manifest-dirs=rustlib,cargo"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); + build.remove_dir(&image); distdir(build).join(format!("{}-{}.tar.gz", name, target)) } } @@ -796,11 +789,11 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di for item in src_dirs { let dst = &dst_dir.join(item); t!(fs::create_dir_all(dst)); - cp_filtered(&build.src.join(item), dst, &|path| filter_fn(exclude_dirs, item, path)); + build.cp_filtered(&build.src.join(item), dst, &|path| filter_fn(exclude_dirs, item, path)); } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Src; impl Step for Src { @@ -820,7 +813,7 @@ impl Step for Src { /// Creates the `rust-src` installer component fn run(self, builder: &Builder) -> PathBuf { let build = builder.build; - println!("Dist src"); + build.info(&format!("Dist src")); let name = pkgname(build, "rust-src"); let image = tmpdir(build).join(format!("{}-image", name)); @@ -870,7 +863,7 @@ impl Step for Src { copy_src_dirs(build, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src); for file in src_files.iter() { - copy(&build.src.join(file), &dst_src.join(file)); + build.copy(&build.src.join(file), &dst_src.join(file)); } // Create source tarball in rust-installer format @@ -887,14 +880,14 @@ impl Step for Src { .arg("--legacy-manifest-dirs=rustlib,cargo"); build.run(&mut cmd); - t!(fs::remove_dir_all(&image)); + build.remove_dir(&image); distdir(build).join(&format!("{}.tar.gz", name)) } } const CARGO_VENDOR_VERSION: &str = "0.1.4"; -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct PlainSourceTarball; impl Step for PlainSourceTarball { @@ -915,7 +908,7 @@ impl Step for PlainSourceTarball { /// Creates the plain source tarball fn run(self, builder: &Builder) -> PathBuf { let build = builder.build; - println!("Create plain source tarball"); + build.info(&format!("Create plain source tarball")); // Make sure that the root folder of tarball has the correct name let plain_name = format!("{}-src", pkgname(build, "rustc")); @@ -943,13 +936,13 @@ impl Step for PlainSourceTarball { // Copy the files normally for item in &src_files { - copy(&build.src.join(item), &plain_dst_src.join(item)); + build.copy(&build.src.join(item), &plain_dst_src.join(item)); } // Create the version file - write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes()); + build.create(&plain_dst_src.join("version"), &build.rust_version()); if let Some(sha) = build.rust_sha() { - write_file(&plain_dst_src.join("git-commit-hash"), sha.as_bytes()); + build.create(&plain_dst_src.join("git-commit-hash"), &sha); } // If we're building from git sources, we need to vendor a complete distribution. @@ -990,9 +983,9 @@ impl Step for PlainSourceTarball { tarball.set_extension(""); // strip .gz tarball.set_extension(""); // strip .tar if let Some(dir) = tarball.parent() { - t!(fs::create_dir_all(dir)); + build.create_dir(&dir); } - println!("running installer"); + build.info(&format!("running installer")); let mut cmd = rust_installer(builder); cmd.arg("tarball") .arg("--input").arg(&plain_name) @@ -1004,26 +997,6 @@ impl Step for PlainSourceTarball { } } -fn install(src: &Path, dstdir: &Path, perms: u32) { - let dst = dstdir.join(src.file_name().unwrap()); - t!(fs::create_dir_all(dstdir)); - drop(fs::remove_file(&dst)); - { - let mut s = t!(fs::File::open(&src)); - let mut d = t!(fs::File::create(&dst)); - io::copy(&mut s, &mut d).expect("failed to copy"); - } - chmod(&dst, perms); -} - -#[cfg(unix)] -fn chmod(path: &Path, perms: u32) { - use std::os::unix::fs::*; - t!(fs::set_permissions(path, fs::Permissions::from_mode(perms))); -} -#[cfg(windows)] -fn chmod(_path: &Path, _perms: u32) {} - // We have to run a few shell scripts, which choke quite a bit on both `\` // characters and on `C:\` paths, so normalize both of them away. pub fn sanitize_sh(path: &Path) -> String { @@ -1043,12 +1016,7 @@ pub fn sanitize_sh(path: &Path) -> String { } } -fn write_file(path: &Path, data: &[u8]) { - let mut vf = t!(fs::File::create(path)); - t!(vf.write_all(data)); -} - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Cargo { pub stage: u32, pub target: Interned, @@ -1074,7 +1042,7 @@ impl Step for Cargo { let stage = self.stage; let target = self.target; - println!("Dist cargo stage{} ({})", stage, target); + build.info(&format!("Dist cargo stage{} ({})", stage, target)); let src = build.src.join("src/tools/cargo"); let etc = src.join("src/etc"); let release_num = build.release_num("cargo"); @@ -1084,38 +1052,38 @@ impl Step for Cargo { let tmp = tmpdir(build); let image = tmp.join("cargo-image"); drop(fs::remove_dir_all(&image)); - t!(fs::create_dir_all(&image)); + build.create_dir(&image); // Prepare the image directory - t!(fs::create_dir_all(image.join("share/zsh/site-functions"))); - t!(fs::create_dir_all(image.join("etc/bash_completion.d"))); + build.create_dir(&image.join("share/zsh/site-functions")); + build.create_dir(&image.join("etc/bash_completion.d")); let cargo = builder.ensure(tool::Cargo { compiler: builder.compiler(stage, build.build), target }); - install(&cargo, &image.join("bin"), 0o755); + build.install(&cargo, &image.join("bin"), 0o755); for man in t!(etc.join("man").read_dir()) { let man = t!(man); - install(&man.path(), &image.join("share/man/man1"), 0o644); + build.install(&man.path(), &image.join("share/man/man1"), 0o644); } - install(&etc.join("_cargo"), &image.join("share/zsh/site-functions"), 0o644); - copy(&etc.join("cargo.bashcomp.sh"), + build.install(&etc.join("_cargo"), &image.join("share/zsh/site-functions"), 0o644); + build.copy(&etc.join("cargo.bashcomp.sh"), &image.join("etc/bash_completion.d/cargo")); let doc = image.join("share/doc/cargo"); - install(&src.join("README.md"), &doc, 0o644); - install(&src.join("LICENSE-MIT"), &doc, 0o644); - install(&src.join("LICENSE-APACHE"), &doc, 0o644); - install(&src.join("LICENSE-THIRD-PARTY"), &doc, 0o644); + build.install(&src.join("README.md"), &doc, 0o644); + build.install(&src.join("LICENSE-MIT"), &doc, 0o644); + build.install(&src.join("LICENSE-APACHE"), &doc, 0o644); + build.install(&src.join("LICENSE-THIRD-PARTY"), &doc, 0o644); // Prepare the overlay let overlay = tmp.join("cargo-overlay"); drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - install(&src.join("README.md"), &overlay, 0o644); - install(&src.join("LICENSE-MIT"), &overlay, 0o644); - install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - install(&src.join("LICENSE-THIRD-PARTY"), &overlay, 0o644); - t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + build.create_dir(&overlay); + build.install(&src.join("README.md"), &overlay, 0o644); + build.install(&src.join("LICENSE-MIT"), &overlay, 0o644); + build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); + build.install(&src.join("LICENSE-THIRD-PARTY"), &overlay, 0o644); + build.create(&overlay.join("version"), &version); // Generate the installer tarball let mut cmd = rust_installer(builder); @@ -1135,7 +1103,7 @@ impl Step for Cargo { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rls { pub stage: u32, pub target: Interned, @@ -1162,7 +1130,7 @@ impl Step for Rls { let target = self.target; assert!(build.config.extended); - println!("Dist RLS stage{} ({})", stage, target); + build.info(&format!("Dist RLS stage{} ({})", stage, target)); let src = build.src.join("src/tools/rls"); let release_num = build.release_num("rls"); let name = pkgname(build, "rls"); @@ -1181,20 +1149,20 @@ impl Step for Rls { target, extra_features: Vec::new() }).or_else(|| { println!("Unable to build RLS, skipping dist"); None })?; - install(&rls, &image.join("bin"), 0o755); + build.install(&rls, &image.join("bin"), 0o755); let doc = image.join("share/doc/rls"); - install(&src.join("README.md"), &doc, 0o644); - install(&src.join("LICENSE-MIT"), &doc, 0o644); - install(&src.join("LICENSE-APACHE"), &doc, 0o644); + build.install(&src.join("README.md"), &doc, 0o644); + build.install(&src.join("LICENSE-MIT"), &doc, 0o644); + build.install(&src.join("LICENSE-APACHE"), &doc, 0o644); // Prepare the overlay let overlay = tmp.join("rls-overlay"); drop(fs::remove_dir_all(&overlay)); t!(fs::create_dir_all(&overlay)); - install(&src.join("README.md"), &overlay, 0o644); - install(&src.join("LICENSE-MIT"), &overlay, 0o644); - install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + build.install(&src.join("README.md"), &overlay, 0o644); + build.install(&src.join("LICENSE-MIT"), &overlay, 0o644); + build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); + build.create(&overlay.join("version"), &version); // Generate the installer tarball let mut cmd = rust_installer(builder); @@ -1216,7 +1184,7 @@ impl Step for Rls { } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rustfmt { pub stage: u32, pub target: Interned, @@ -1242,7 +1210,7 @@ impl Step for Rustfmt { let stage = self.stage; let target = self.target; - println!("Dist Rustfmt stage{} ({})", stage, target); + build.info(&format!("Dist Rustfmt stage{} ({})", stage, target)); let src = build.src.join("src/tools/rustfmt"); let release_num = build.release_num("rustfmt"); let name = pkgname(build, "rustfmt"); @@ -1251,7 +1219,7 @@ impl Step for Rustfmt { let tmp = tmpdir(build); let image = tmp.join("rustfmt-image"); drop(fs::remove_dir_all(&image)); - t!(fs::create_dir_all(&image)); + build.create_dir(&image); // Prepare the image directory let rustfmt = builder.ensure(tool::Rustfmt { @@ -1263,21 +1231,21 @@ impl Step for Rustfmt { target, extra_features: Vec::new() }).or_else(|| { println!("Unable to build Cargofmt, skipping dist"); None })?; - install(&rustfmt, &image.join("bin"), 0o755); - install(&cargofmt, &image.join("bin"), 0o755); + build.install(&rustfmt, &image.join("bin"), 0o755); + build.install(&cargofmt, &image.join("bin"), 0o755); let doc = image.join("share/doc/rustfmt"); - install(&src.join("README.md"), &doc, 0o644); - install(&src.join("LICENSE-MIT"), &doc, 0o644); - install(&src.join("LICENSE-APACHE"), &doc, 0o644); + build.install(&src.join("README.md"), &doc, 0o644); + build.install(&src.join("LICENSE-MIT"), &doc, 0o644); + build.install(&src.join("LICENSE-APACHE"), &doc, 0o644); // Prepare the overlay let overlay = tmp.join("rustfmt-overlay"); drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - install(&src.join("README.md"), &overlay, 0o644); - install(&src.join("LICENSE-MIT"), &overlay, 0o644); - install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + build.create_dir(&overlay); + build.install(&src.join("README.md"), &overlay, 0o644); + build.install(&src.join("LICENSE-MIT"), &overlay, 0o644); + build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); + build.create(&overlay.join("version"), &version); // Generate the installer tarball let mut cmd = rust_installer(builder); @@ -1298,7 +1266,7 @@ impl Step for Rustfmt { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Extended { stage: u32, host: Interned, @@ -1329,7 +1297,7 @@ impl Step for Extended { let stage = self.stage; let target = self.target; - println!("Dist extended stage{} ({})", stage, target); + build.info(&format!("Dist extended stage{} ({})", stage, target)); let rustc_installer = builder.ensure(Rustc { compiler: builder.compiler(stage, target), @@ -1355,15 +1323,15 @@ impl Step for Extended { let work = tmp.join("work"); let _ = fs::remove_dir_all(&overlay); - install(&build.src.join("COPYRIGHT"), &overlay, 0o644); - install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644); - install(&build.src.join("LICENSE-MIT"), &overlay, 0o644); + build.install(&build.src.join("COPYRIGHT"), &overlay, 0o644); + build.install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644); + build.install(&build.src.join("LICENSE-MIT"), &overlay, 0o644); let version = build.rust_version(); - t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + build.create(&overlay.join("version"), &version); if let Some(sha) = build.rust_sha() { - t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes())); + build.create(&overlay.join("git-commit-hash"), &sha); } - install(&etc.join("README.md"), &overlay, 0o644); + build.install(&etc.join("README.md"), &overlay, 0o644); // When rust-std package split from rustc, we needed to ensure that during // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering @@ -1402,11 +1370,11 @@ impl Step for Extended { build.run(&mut cmd); let mut license = String::new(); - t!(t!(File::open(build.src.join("COPYRIGHT"))).read_to_string(&mut license)); + license += &build.read(&build.src.join("COPYRIGHT")); + license += &build.read(&build.src.join("LICENSE-APACHE")); + license += &build.read(&build.src.join("LICENSE-MIT")); license.push_str("\n"); - t!(t!(File::open(build.src.join("LICENSE-APACHE"))).read_to_string(&mut license)); license.push_str("\n"); - t!(t!(File::open(build.src.join("LICENSE-MIT"))).read_to_string(&mut license)); let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18"; let mut rtf = rtf.to_string(); @@ -1463,10 +1431,10 @@ impl Step for Extended { }; let prepare = |name: &str| { - t!(fs::create_dir_all(pkg.join(name))); - cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)), + build.create_dir(&pkg.join(name)); + build.cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)), &pkg.join(name)); - install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); + build.install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); pkgbuild(name); }; prepare("rustc"); @@ -1480,12 +1448,12 @@ impl Step for Extended { } // create an 'uninstall' package - install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); + build.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); pkgbuild("uninstall"); - t!(fs::create_dir_all(pkg.join("res"))); - t!(t!(File::create(pkg.join("res/LICENSE.txt"))).write_all(license.as_bytes())); - install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); + build.create_dir(&pkg.join("res")); + build.create(&pkg.join("res/LICENSE.txt"), &license); + build.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); let mut cmd = Command::new("productbuild"); cmd.arg("--distribution").arg(xform(&etc.join("pkg/Distribution.xml"))) .arg("--resources").arg(pkg.join("res")) @@ -1501,7 +1469,7 @@ impl Step for Extended { let _ = fs::remove_dir_all(&exe); let prepare = |name: &str| { - t!(fs::create_dir_all(exe.join(name))); + build.create_dir(&exe.join(name)); let dir = if name == "rust-std" || name == "rust-analysis" { format!("{}-{}", name, target) } else if name == "rls" { @@ -1509,10 +1477,10 @@ impl Step for Extended { } else { name.to_string() }; - cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)) + build.cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)) .join(dir), &exe.join(name)); - t!(fs::remove_file(exe.join(name).join("manifest.in"))); + build.remove(&exe.join(name).join("manifest.in")); }; prepare("rustc"); prepare("cargo"); @@ -1526,11 +1494,11 @@ impl Step for Extended { prepare("rust-mingw"); } - install(&xform(&etc.join("exe/rust.iss")), &exe, 0o644); - install(&etc.join("exe/modpath.iss"), &exe, 0o644); - install(&etc.join("exe/upgrade.iss"), &exe, 0o644); - install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); - t!(t!(File::create(exe.join("LICENSE.txt"))).write_all(license.as_bytes())); + build.install(&xform(&etc.join("exe/rust.iss")), &exe, 0o644); + build.install(&etc.join("exe/modpath.iss"), &exe, 0o644); + build.install(&etc.join("exe/upgrade.iss"), &exe, 0o644); + build.install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); + build.create(&exe.join("LICENSE.txt"), &license); // Generate exe installer let mut cmd = Command::new("iscc"); @@ -1541,7 +1509,7 @@ impl Step for Extended { } add_env(build, &mut cmd, target); build.run(&mut cmd); - install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)), + build.install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)), &distdir(build), 0o755); @@ -1665,9 +1633,9 @@ impl Step for Extended { candle("GccGroup.wxs".as_ref()); } - t!(t!(File::create(exe.join("LICENSE.rtf"))).write_all(rtf.as_bytes())); - install(&etc.join("gfx/banner.bmp"), &exe, 0o644); - install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644); + build.create(&exe.join("LICENSE.rtf"), &rtf); + build.install(&etc.join("gfx/banner.bmp"), &exe, 0o644); + build.install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644); let filename = format!("{}-{}.msi", pkgname(build, "rust"), target); let mut cmd = Command::new(&light); @@ -1697,7 +1665,9 @@ impl Step for Extended { build.run(&mut cmd); - t!(fs::rename(exe.join(&filename), distdir(build).join(&filename))); + if !build.config.dry_run { + t!(fs::rename(exe.join(&filename), distdir(build).join(&filename))); + } } } } @@ -1731,7 +1701,7 @@ fn add_env(build: &Build, cmd: &mut Command, target: Interned) { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct HashSign; impl Step for HashSign { @@ -1749,6 +1719,9 @@ impl Step for HashSign { fn run(self, builder: &Builder) { let build = builder.build; let mut cmd = builder.tool_cmd(Tool::BuildManifest); + if build.config.dry_run { + return; + } let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| { panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n") }); @@ -1772,7 +1745,7 @@ impl Step for HashSign { cmd.arg(build.package_vers(&build.release_num("rustfmt"))); cmd.arg(addr); - t!(fs::create_dir_all(distdir(build))); + build.create_dir(&distdir(build)); let mut child = t!(cmd.stdin(Stdio::piped()).spawn()); t!(child.stdin.take().unwrap().write_all(pass.as_bytes())); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 44073a5b07572..f07c3e707574b 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -26,11 +26,12 @@ use std::path::{PathBuf, Path}; use {Build, Mode}; use build_helper::up_to_date; -use util::{cp_r, symlink_dir}; +use util::symlink_dir; use builder::{Builder, Compiler, RunConfig, ShouldRun, Step}; use tool::Tool; use compile; use cache::{INTERNER, Interned}; +use config::Config; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr;)+) => { @@ -168,7 +169,7 @@ impl Step for CargoBook { let out = out.join(name); - println!("Cargo Book ({}) - {}", target, name); + build.info(&format!("Cargo Book ({}) - {}", target, name)); let _ = fs::remove_dir_all(&out); @@ -210,12 +211,13 @@ impl Step for RustbookSrc { let src = src.join(name); let index = out.join("index.html"); let rustbook = builder.tool_exe(Tool::Rustbook); + let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); if up_to_date(&src, &index) && up_to_date(&rustbook, &index) { return } - println!("Rustbook ({}) - {}", target, name); + build.info(&format!("Rustbook ({}) - {}", target, name)); let _ = fs::remove_dir_all(&out); - build.run(builder.tool_cmd(Tool::Rustbook) + build.run(rustbook_cmd .arg("build") .arg(&src) .arg("-d") @@ -281,11 +283,11 @@ impl Step for TheBook { // build the index page let index = format!("{}/index.md", name); - println!("Documenting book index ({})", target); + build.info(&format!("Documenting book index ({})", target)); invoke_rustdoc(builder, compiler, target, &index); // build the redirect pages - println!("Documenting book redirect pages ({})", target); + build.info(&format!("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(); @@ -358,7 +360,7 @@ impl Step for Standalone { let build = builder.build; let target = self.target; let compiler = self.compiler; - println!("Documenting standalone ({})", target); + build.info(&format!("Documenting standalone ({})", target)); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); @@ -370,7 +372,7 @@ impl Step for Standalone { 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) { + if !build.config.dry_run && !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()) @@ -394,7 +396,7 @@ impl Step for Standalone { up_to_date(&favicon, &html) && up_to_date(&full_toc, &html) && up_to_date(&version_info, &html) && - up_to_date(&rustdoc, &html) { + (build.config.dry_run || up_to_date(&rustdoc, &html)) { continue } @@ -449,7 +451,7 @@ impl Step for Std { let build = builder.build; let stage = self.stage; let target = self.target; - println!("Documenting stage{} std ({})", stage, target); + build.info(&format!("Documenting stage{} std ({})", stage, target)); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = builder.compiler(stage, build.build); @@ -479,7 +481,7 @@ impl Step for Std { // 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)); + t!(symlink_dir_force(&build.config, &my_out, &out_dir)); let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc"); compile::std_cargo(builder, &compiler, target, &mut cargo); @@ -496,7 +498,7 @@ impl Step for Std { } build.run(&mut cargo); - cp_r(&my_out, &out); + build.cp_r(&my_out, &out); } } @@ -530,7 +532,7 @@ impl Step for Test { let build = builder.build; let stage = self.stage; let target = self.target; - println!("Documenting stage{} test ({})", stage, target); + build.info(&format!("Documenting stage{} test ({})", stage, target)); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = builder.compiler(stage, build.build); @@ -551,12 +553,12 @@ impl Step for Test { // 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)); + t!(symlink_dir_force(&builder.config, &my_out, &out_dir)); let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc"); compile::test_cargo(build, &compiler, target, &mut cargo); build.run(&mut cargo); - cp_r(&my_out, &out); + build.cp_r(&my_out, &out); } } @@ -596,7 +598,7 @@ impl Step for WhitelistedRustc { let build = builder.build; let stage = self.stage; let target = self.target; - println!("Documenting stage{} whitelisted compiler ({})", stage, target); + build.info(&format!("Documenting stage{} whitelisted compiler ({})", stage, target)); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = builder.compiler(stage, build.build); @@ -617,7 +619,7 @@ impl Step for WhitelistedRustc { // 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)); + t!(symlink_dir_force(&builder.config, &my_out, &out_dir)); let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc"); compile::rustc_cargo(build, &mut cargo); @@ -631,7 +633,7 @@ impl Step for WhitelistedRustc { } build.run(&mut cargo); - cp_r(&my_out, &out); + build.cp_r(&my_out, &out); } } @@ -668,7 +670,7 @@ impl Step for Rustc { let build = builder.build; let stage = self.stage; let target = self.target; - println!("Documenting stage{} compiler ({})", stage, target); + build.info(&format!("Documenting stage{} compiler ({})", stage, target)); let out = build.compiler_doc_out(target); t!(fs::create_dir_all(&out)); let compiler = builder.compiler(stage, build.build); @@ -680,7 +682,7 @@ impl Step for Rustc { }; if !build.config.compiler_docs { - println!("\tskipping - compiler docs disabled"); + build.info(&format!("\tskipping - compiler docs disabled")); return; } @@ -693,7 +695,7 @@ impl Step for Rustc { // We do not symlink to the same shared folder that already contains std library // documentation from previous steps as we do not want to include that. build.clear_if_dirty(&out, &rustdoc); - t!(symlink_dir_force(&out, &out_dir)); + t!(symlink_dir_force(&builder.config, &out, &out_dir)); let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc"); compile::rustc_cargo(build, &mut cargo); @@ -759,7 +761,7 @@ impl Step for ErrorIndex { let build = builder.build; let target = self.target; - println!("Documenting error index ({})", target); + build.info(&format!("Documenting error index ({})", target)); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let mut index = builder.tool_cmd(Tool::ErrorIndex); @@ -804,10 +806,10 @@ impl Step for UnstableBookGen { target, }); - println!("Generating unstable book md files ({})", target); + build.info(&format!("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)); + build.create_dir(&out); + build.remove_dir(&out); let mut cmd = builder.tool_cmd(Tool::UnstableBookGen); cmd.arg(build.src.join("src")); cmd.arg(out); @@ -816,7 +818,10 @@ impl Step for UnstableBookGen { } } -fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> { +fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> { + if config.dry_run { + return Ok(()); + } if let Ok(m) = fs::symlink_metadata(dst) { if m.file_type().is_dir() { try!(fs::remove_dir_all(dst)); @@ -829,5 +834,5 @@ fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> { } } - symlink_dir(src, dst) + symlink_dir(config, src, dst) } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index c5af0f8e2e153..cd304fb26e0bf 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -13,7 +13,6 @@ //! This module implements the command-line parsing of the build system which //! has various flags to configure how it's run. -use std::env; use std::fs; use std::path::PathBuf; use std::process; @@ -33,17 +32,16 @@ pub struct Flags { pub on_fail: Option, pub stage: Option, pub keep_stage: Option, - pub build: Option>, pub host: Vec>, pub target: Vec>, pub config: Option, - pub src: PathBuf, pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, pub exclude: Vec, pub rustc_error_format: Option, + pub dry_run: bool, } pub enum Subcommand { @@ -114,6 +112,7 @@ To learn more about a subcommand, run `./x.py -h`"); opts.optmulti("", "target", "target targets to build", "TARGET"); opts.optmulti("", "exclude", "build paths to exclude", "PATH"); opts.optopt("", "on-fail", "command to run on failure", "CMD"); + opts.optflag("", "dry-run", "dry run; don't build anything"); opts.optopt("", "stage", "stage to build", "N"); opts.optopt("", "keep-stage", "stage to keep without recompiling", "N"); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); @@ -278,10 +277,6 @@ Arguments: _ => { } }; // Get any optional paths which occur after the subcommand - let cwd = t!(env::current_dir()); - let src = matches.opt_str("src").map(PathBuf::from) - .or_else(|| env::var_os("SRC").map(PathBuf::from)) - .unwrap_or(cwd.clone()); let paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { @@ -371,10 +366,10 @@ Arguments: Flags { verbose: matches.opt_count("verbose"), stage, + dry_run: matches.opt_present("dry-run"), on_fail: matches.opt_str("on-fail"), rustc_error_format: matches.opt_str("error-format"), keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()), - build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)), host: split(matches.opt_strs("host")) .into_iter().map(|x| INTERNER.intern_string(x)).collect::>(), target: split(matches.opt_strs("target")) @@ -385,7 +380,6 @@ Arguments: incremental: matches.opt_present("incremental"), exclude: split(matches.opt_strs("exclude")) .into_iter().map(|p| p.into()).collect::>(), - src, } } } diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 0d7c1b8de6323..4b05cac1ce697 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -63,7 +63,7 @@ fn install_sh( host: Option> ) { let build = builder.build; - println!("Install {} stage{} ({:?})", package, stage, host); + build.info(&format!("Install {} stage{} ({:?})", package, stage, host)); let prefix_default = PathBuf::from("/usr/local"); let sysconfdir_default = PathBuf::from("/etc"); @@ -212,7 +212,7 @@ install!((self, builder, _config), Self::should_install(builder) { install_rls(builder, self.stage, self.target); } else { - println!("skipping Install RLS stage{} ({})", self.stage, self.target); + builder.info(&format!("skipping Install RLS stage{} ({})", self.stage, self.target)); } }; Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, { @@ -220,7 +220,8 @@ install!((self, builder, _config), Self::should_install(builder) { install_rustfmt(builder, self.stage, self.target); } else { - println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target); + builder.info( + &format!("skipping Install Rustfmt stage{} ({})", self.stage, self.target)); } }; Analysis, "analysis", Self::should_build(_config), only_hosts: false, { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 833faf3618d67..2eeb2691eaee4 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -114,7 +114,7 @@ //! also check out the `src/bootstrap/README.md` file for more information. #![deny(warnings)] -#![feature(core_intrinsics)] +#![feature(conservative_impl_trait, fs_read_write, core_intrinsics)] #![feature(slice_concat_ext)] #[macro_use] @@ -131,6 +131,11 @@ extern crate getopts; extern crate num_cpus; extern crate toml; extern crate time; +extern crate petgraph; + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; #[cfg(unix)] extern crate libc; @@ -138,13 +143,15 @@ extern crate libc; use std::cell::{RefCell, Cell}; use std::collections::{HashSet, HashMap}; use std::env; -use std::fs::{self, File}; -use std::io::Read; +use std::fs::{self, OpenOptions, File}; +use std::io::{self, Seek, SeekFrom, Write, Read}; use std::path::{PathBuf, Path}; use std::process::{self, Command}; use std::slice; +use std::str; use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime}; +use filetime::FileTime; use util::{exe, libdir, OutputFolder, CiEnv}; @@ -198,7 +205,7 @@ use toolstate::ToolState; /// Each compiler has a `stage` that it is associated with and a `host` that /// 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)] +#[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)] pub struct Compiler { stage: u32, host: Interned, @@ -309,9 +316,8 @@ impl Build { /// /// By default all build output will be placed in the current directory. pub fn new(config: Config) -> Build { - let cwd = t!(env::current_dir()); let src = config.src.clone(); - let out = cwd.join("build"); + let out = config.out.clone(); let is_sudo = match env::var_os("SUDO_USER") { Some(sudo_user) => { @@ -327,7 +333,7 @@ impl Build { let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls")); let rustfmt_info = channel::GitInfo::new(&config, &src.join("src/tools/rustfmt")); - Build { + let mut build = Build { initial_rustc: config.initial_rustc.clone(), initial_cargo: config.initial_cargo.clone(), local_rebuild: config.local_rebuild, @@ -358,7 +364,30 @@ impl Build { delayed_failures: RefCell::new(Vec::new()), prerelease_version: Cell::new(None), tool_artifacts: Default::default(), + }; + + build.verbose("finding compilers"); + cc_detect::find(&mut build); + build.verbose("running sanity check"); + sanity::check(&mut build); + + // If local-rust is the same major.minor as the current version, then force a + // local-rebuild + let local_version_verbose = output( + Command::new(&build.initial_rustc).arg("--version").arg("--verbose")); + let local_release = local_version_verbose + .lines().filter(|x| x.starts_with("release:")) + .next().unwrap().trim_left_matches("release:").trim(); + let my_version = channel::CFG_RELEASE_NUM; + if local_release.split('.').take(2).eq(my_version.split('.').take(2)) { + build.verbose(&format!("auto-detected local-rebuild {}", local_release)); + build.local_rebuild = true; } + + build.verbose("learning about cargo"); + metadata::build(&mut build); + + build } pub fn build_triple(&self) -> &[Interned] { @@ -377,25 +406,28 @@ impl Build { return clean::clean(self, all); } - self.verbose("finding compilers"); - cc_detect::find(self); - self.verbose("running sanity check"); - sanity::check(self); - // If local-rust is the same major.minor as the current version, then force a local-rebuild - let local_version_verbose = output( - Command::new(&self.initial_rustc).arg("--version").arg("--verbose")); - let local_release = local_version_verbose - .lines().filter(|x| x.starts_with("release:")) - .next().unwrap().trim_left_matches("release:").trim(); - let my_version = channel::CFG_RELEASE_NUM; - if local_release.split('.').take(2).eq(my_version.split('.').take(2)) { - self.verbose(&format!("auto-detected local-rebuild {}", local_release)); - self.local_rebuild = true; + { + let builder = builder::Builder::new(&self); + if let Some(path) = builder.paths.get(0) { + if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { + return; + } + } } - self.verbose("learning about cargo"); - metadata::build(self); - builder::Builder::run(&self); + if !self.config.dry_run { + { + self.config.dry_run = true; + let builder = builder::Builder::new(&self); + builder.execute_cli(); + } + self.config.dry_run = false; + let builder = builder::Builder::new(&self); + builder.execute_cli(); + } else { + let builder = builder::Builder::new(&self); + let _ = builder.execute_cli(); + } // Check for postponed failures from `test --no-fail-fast`. let failures = self.delayed_failures.borrow(); @@ -586,12 +618,14 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { + if self.config.dry_run { return; } self.verbose(&format!("running: {:?}", cmd)); run_silent(cmd) } /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { + if self.config.dry_run { return; } self.verbose(&format!("running: {:?}", cmd)); run_suppressed(cmd) } @@ -600,6 +634,7 @@ impl Build { /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn try_run(&self, cmd: &mut Command) -> bool { + if self.config.dry_run { return true; } self.verbose(&format!("running: {:?}", cmd)); try_run_silent(cmd) } @@ -608,6 +643,7 @@ impl Build { /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn try_run_quiet(&self, cmd: &mut Command) -> bool { + if self.config.dry_run { return true; } self.verbose(&format!("running: {:?}", cmd)); try_run_suppressed(cmd) } @@ -623,6 +659,11 @@ impl Build { } } + fn info(&self, msg: &str) { + if self.config.dry_run { return; } + println!("{}", msg); + } + /// Returns the number of parallel jobs that have been configured for this /// build. fn jobs(&self) -> u32 { @@ -930,7 +971,7 @@ impl Build { pub fn fold_output(&self, name: F) -> Option where D: Into, F: FnOnce() -> D { - if self.ci_env == CiEnv::Travis { + if !self.config.dry_run && self.ci_env == CiEnv::Travis { Some(OutputFolder::new(name().into())) } else { None @@ -978,7 +1019,172 @@ impl Build { } ret } + + fn read_stamp_file(&self, stamp: &Path) -> Vec { + if self.config.dry_run { + return Vec::new(); + } + + let mut paths = Vec::new(); + let mut contents = Vec::new(); + t!(t!(File::open(stamp)).read_to_end(&mut contents)); + // This is the method we use for extracting paths from the stamp file passed to us. See + // run_cargo for more information (in compile.rs). + for part in contents.split(|b| *b == 0) { + if part.is_empty() { + continue + } + let path = PathBuf::from(t!(str::from_utf8(part))); + paths.push(path); + } + paths + } + + /// Copies a file from `src` to `dst` + pub fn copy(&self, src: &Path, dst: &Path) { + if self.config.dry_run { return; } + let _ = fs::remove_file(&dst); + // Attempt to "easy copy" by creating a hard link (symlinks don't work on + // windows), but if that fails just fall back to a slow `copy` operation. + if let Ok(()) = fs::hard_link(src, dst) { + return + } + if let Err(e) = fs::copy(src, dst) { + panic!("failed to copy `{}` to `{}`: {}", src.display(), + dst.display(), e) + } + let metadata = t!(src.metadata()); + t!(fs::set_permissions(dst, metadata.permissions())); + let atime = FileTime::from_last_access_time(&metadata); + let mtime = FileTime::from_last_modification_time(&metadata); + t!(filetime::set_file_times(dst, atime, mtime)); + } + + /// Search-and-replaces within a file. (Not maximally efficiently: allocates a + /// new string for each replacement.) + pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) { + if self.config.dry_run { return; } + let mut contents = String::new(); + let mut file = t!(OpenOptions::new().read(true).write(true).open(path)); + t!(file.read_to_string(&mut contents)); + for &(target, replacement) in replacements { + contents = contents.replace(target, replacement); + } + t!(file.seek(SeekFrom::Start(0))); + t!(file.set_len(0)); + t!(file.write_all(contents.as_bytes())); + } + + /// Copies the `src` directory recursively to `dst`. Both are assumed to exist + /// when this function is called. + pub fn cp_r(&self, src: &Path, dst: &Path) { + if self.config.dry_run { return; } + for f in t!(fs::read_dir(src)) { + let f = t!(f); + let path = f.path(); + let name = path.file_name().unwrap(); + let dst = dst.join(name); + if t!(f.file_type()).is_dir() { + t!(fs::create_dir_all(&dst)); + self.cp_r(&path, &dst); + } else { + let _ = fs::remove_file(&dst); + self.copy(&path, &dst); + } + } + } + + /// Copies the `src` directory recursively to `dst`. Both are assumed to exist + /// when this function is called. Unwanted files or directories can be skipped + /// by returning `false` from the filter function. + pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) { + // Immediately recurse with an empty relative path + self.recurse_(src, dst, Path::new(""), filter) + } + + // Inner function does the actual work + fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) { + for f in self.read_dir(src) { + let path = f.path(); + let name = path.file_name().unwrap(); + let dst = dst.join(name); + let relative = relative.join(name); + // Only copy file or directory if the filter function returns true + if filter(&relative) { + if t!(f.file_type()).is_dir() { + let _ = fs::remove_dir_all(&dst); + self.create_dir(&dst); + self.recurse_(&path, &dst, &relative, filter); + } else { + let _ = fs::remove_file(&dst); + self.copy(&path, &dst); + } + } + } + } + + fn copy_to_folder(&self, src: &Path, dest_folder: &Path) { + let file_name = src.file_name().unwrap(); + let dest = dest_folder.join(file_name); + self.copy(src, &dest); + } + + fn install(&self, src: &Path, dstdir: &Path, perms: u32) { + if self.config.dry_run { return; } + let dst = dstdir.join(src.file_name().unwrap()); + t!(fs::create_dir_all(dstdir)); + drop(fs::remove_file(&dst)); + { + let mut s = t!(fs::File::open(&src)); + let mut d = t!(fs::File::create(&dst)); + io::copy(&mut s, &mut d).expect("failed to copy"); + } + chmod(&dst, perms); + } + + fn create(&self, path: &Path, s: &str) { + if self.config.dry_run { return; } + t!(fs::write(path, s)); + } + + fn read(&self, path: &Path) -> String { + if self.config.dry_run { return String::new(); } + t!(fs::read_string(path)) + } + + fn create_dir(&self, dir: &Path) { + if self.config.dry_run { return; } + t!(fs::create_dir_all(dir)) + } + + fn remove_dir(&self, dir: &Path) { + if self.config.dry_run { return; } + t!(fs::remove_dir_all(dir)) + } + + fn read_dir(&self, dir: &Path) -> impl Iterator { + let iter = match fs::read_dir(dir) { + Ok(v) => v, + Err(_) if self.config.dry_run => return vec![].into_iter(), + Err(err) => panic!("could not read dir {:?}: {:?}", dir, err), + }; + iter.map(|e| t!(e)).collect::>().into_iter() + } + + fn remove(&self, f: &Path) { + if self.config.dry_run { return; } + fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); + } +} + +#[cfg(unix)] +fn chmod(path: &Path, perms: u32) { + use std::os::unix::fs::*; + t!(fs::set_permissions(path, fs::Permissions::from_mode(perms))); } +#[cfg(windows)] +fn chmod(_path: &Path, _perms: u32) {} + impl<'a> Compiler { pub fn with_stage(mut self, stage: u32) -> Compiler { diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index f923ad46bcbaa..db5891afd6b1f 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -106,8 +106,8 @@ impl Step for Llvm { let _folder = build.fold_output(|| "llvm"); let descriptor = if emscripten { "Emscripten " } else { "" }; - println!("Building {}LLVM for {}", descriptor, target); - let _time = util::timeit(); + build.info(&format!("Building {}LLVM for {}", descriptor, target)); + let _time = util::timeit(&build); t!(fs::create_dir_all(&out_dir)); // http://llvm.org/docs/CMake.html @@ -217,6 +217,11 @@ impl Step for 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. + + if builder.config.dry_run { + return build_llvm_config; + } + cfg.build(); t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes())); @@ -230,6 +235,10 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) { return } + if build.config.dry_run { + return; + } + let mut cmd = Command::new(llvm_config); let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2) @@ -336,6 +345,9 @@ impl Step for Lld { /// Compile LLVM for `target`. fn run(self, builder: &Builder) -> PathBuf { + if builder.config.dry_run { + return PathBuf::from("lld-out-dir-test-gen"); + } let target = self.target; let build = builder.build; @@ -351,8 +363,8 @@ impl Step for Lld { } let _folder = build.fold_output(|| "lld"); - println!("Building LLD for {}", target); - let _time = util::timeit(); + build.info(&format!("Building LLD for {}", target)); + let _time = util::timeit(&build); t!(fs::create_dir_all(&out_dir)); let mut cfg = cmake::Config::new(build.src.join("src/tools/lld")); @@ -389,6 +401,9 @@ impl Step for TestHelpers { /// Compiles the `rust_test_helpers.c` library which we used in various /// `run-pass` test suites for ABI testing. fn run(self, builder: &Builder) { + if builder.config.dry_run { + return; + } let build = builder.build; let target = self.target; let dst = build.test_helpers_out(target); @@ -398,7 +413,7 @@ impl Step for TestHelpers { } let _folder = build.fold_output(|| "build_test_helpers"); - println!("Building test helpers"); + build.info(&format!("Building test helpers")); t!(fs::create_dir_all(&dst)); let mut cfg = cc::Build::new(); @@ -441,6 +456,9 @@ impl Step for Openssl { } fn run(self, builder: &Builder) { + if builder.config.dry_run { + return; + } let build = builder.build; let target = self.target; let out = match build.openssl_dir(target) { @@ -591,11 +609,11 @@ impl Step for Openssl { configure.arg("no-asm"); } configure.current_dir(&obj); - println!("Configuring openssl for {}", target); + build.info(&format!("Configuring openssl for {}", target)); build.run_quiet(&mut configure); - println!("Building openssl for {}", target); + build.info(&format!("Building openssl for {}", target)); build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj)); - println!("Installing openssl for {}", target); + build.info(&format!("Installing openssl for {}", target)); build.run_quiet(Command::new("make").arg("install").arg("-j1").current_dir(&obj)); let mut f = t!(File::create(&stamp)); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 76f1a4efbf014..c175d2c69016f 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -109,11 +109,11 @@ impl Step for Linkcheck { let build = builder.build; let host = self.host; - println!("Linkcheck ({})", host); + build.info(&format!("Linkcheck ({})", host)); builder.default_doc(None); - let _time = util::timeit(); + let _time = util::timeit(&build); try_run(build, builder.tool_cmd(Tool::Linkchecker) .arg(build.out.join(host).join("doc"))); } @@ -164,7 +164,7 @@ impl Step for Cargotest { let out_dir = build.out.join("ct"); t!(fs::create_dir_all(&out_dir)); - let _time = util::timeit(); + let _time = util::timeit(&build); let mut cmd = builder.tool_cmd(Tool::CargoTest); try_run(build, cmd.arg(&build.initial_cargo) .arg(&out_dir) @@ -509,7 +509,7 @@ impl Step for RustdocJS { }); builder.run(&mut command); } else { - println!("No nodejs found, skipping \"src/test/rustdoc-js\" tests"); + builder.info(&format!("No nodejs found, skipping \"src/test/rustdoc-js\" tests")); } } } @@ -541,7 +541,7 @@ impl Step for Tidy { } let _folder = build.fold_output(|| "tidy"); - println!("tidy check"); + builder.info(&format!("tidy check")); try_run(build, &mut cmd); } @@ -926,15 +926,17 @@ impl Step for Compiletest { target: build.config.build, emscripten: false, }); - let llvm_version = output(Command::new(&llvm_config).arg("--version")); - cmd.arg("--llvm-version").arg(llvm_version); + if !build.config.dry_run { + let llvm_version = output(Command::new(&llvm_config).arg("--version")); + cmd.arg("--llvm-version").arg(llvm_version); + } if !build.is_rust_llvm(target) { cmd.arg("--system-llvm"); } // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if suite == "run-make-fulldeps" { + if !build.config.dry_run && suite == "run-make-fulldeps" { let llvm_components = output(Command::new(&llvm_config).arg("--components")); let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags")); cmd.arg("--cc").arg(build.cc(target)) @@ -948,7 +950,8 @@ impl Step for Compiletest { } } if suite == "run-make-fulldeps" && !build.config.llvm_enabled { - println!("Ignoring run-make test suite as they generally don't work without LLVM"); + builder.info( + &format!("Ignoring run-make test suite as they generally don't work without LLVM")); return; } @@ -1002,9 +1005,9 @@ impl Step for Compiletest { build.ci_env.force_coloring_in_ci(&mut cmd); let _folder = build.fold_output(|| format!("test_{}", suite)); - println!("Check compiletest suite={} mode={} ({} -> {})", - suite, mode, &compiler.host, target); - let _time = util::timeit(); + builder.info(&format!("Check compiletest suite={} mode={} ({} -> {})", + suite, mode, &compiler.host, target)); + let _time = util::timeit(&build); try_run(build, &mut cmd); } } @@ -1039,9 +1042,10 @@ impl Step for DocTest { // Do a breadth-first traversal of the `src/doc` directory and just run // tests for all files that end in `*.md` let mut stack = vec![build.src.join(self.path)]; - let _time = util::timeit(); + let _time = util::timeit(&build); let _folder = build.fold_output(|| format!("test_{}", self.name)); + let mut files = Vec::new(); while let Some(p) = stack.pop() { if p.is_dir() { stack.extend(t!(p.read_dir()).map(|p| t!(p).path())); @@ -1058,7 +1062,13 @@ impl Step for DocTest { continue; } - let test_result = markdown_test(builder, compiler, &p); + files.push(p); + } + + files.sort(); + + for file in files { + let test_result = markdown_test(builder, compiler, &file); if self.is_ext_doc { let toolstate = if test_result { ToolState::TestPass @@ -1160,8 +1170,8 @@ impl Step for ErrorIndex { let _folder = build.fold_output(|| "test_error_index"); - println!("Testing error-index stage{}", compiler.stage); - let _time = util::timeit(); + build.info(&format!("Testing error-index stage{}", compiler.stage)); + let _time = util::timeit(&build); build.run(&mut tool); markdown_test(builder, compiler, &output); } @@ -1169,14 +1179,18 @@ impl Step for ErrorIndex { fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) -> bool { let build = builder.build; - let mut file = t!(File::open(markdown)); - let mut contents = String::new(); - t!(file.read_to_string(&mut contents)); - if !contents.contains("```") { - return true; + match File::open(markdown) { + Ok(mut file) => { + let mut contents = String::new(); + t!(file.read_to_string(&mut contents)); + if !contents.contains("```") { + return true; + } + } + Err(_) => {}, } - println!("doc tests for: {}", markdown.display()); + build.info(&format!("doc tests for: {}", markdown.display())); let mut cmd = builder.rustdoc_cmd(compiler.host); build.add_rust_test_threads(&mut cmd); cmd.arg("--test"); @@ -1446,8 +1460,8 @@ impl Step for Crate { // The javascript shim implements the syscall interface so that test // output can be correctly reported. if !build.config.wasm_syscall { - println!("Libstd was built without `wasm_syscall` feature enabled: \ - test output may not be visible."); + build.info(&format!("Libstd was built without `wasm_syscall` feature enabled: \ + test output may not be visible.")); } // On the wasm32-unknown-unknown target we're using LTO which is @@ -1469,9 +1483,9 @@ impl Step for Crate { let _folder = build.fold_output(|| { format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, krate) }); - println!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage, - &compiler.host, target); - let _time = util::timeit(); + build.info(&format!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage, + &compiler.host, target)); + let _time = util::timeit(&build); try_run(build, &mut cargo); } } @@ -1536,9 +1550,9 @@ impl Step for CrateRustdoc { let _folder = build.fold_output(|| { format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage) }); - println!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage, - &compiler.host, target); - let _time = util::timeit(); + build.info(&format!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage, + &compiler.host, target)); + let _time = util::timeit(&build); try_run(build, &mut cargo); } @@ -1585,7 +1599,7 @@ impl Step for RemoteCopyLibs { builder.ensure(compile::Test { compiler, target }); - println!("REMOTE copy libs to emulator ({})", target); + build.info(&format!("REMOTE copy libs to emulator ({})", target)); t!(fs::create_dir_all(build.out.join("tmp"))); let server = builder.ensure(tool::RemoteTestServer { compiler, target }); @@ -1633,7 +1647,7 @@ impl Step for Distcheck { fn run(self, builder: &Builder) { let build = builder.build; - println!("Distcheck"); + build.info(&format!("Distcheck")); let dir = build.out.join("tmp").join("distcheck"); let _ = fs::remove_dir_all(&dir); t!(fs::create_dir_all(&dir)); @@ -1657,7 +1671,7 @@ impl Step for Distcheck { .current_dir(&dir)); // Now make sure that rust-src has all of libstd's dependencies - println!("Distcheck rust-src"); + build.info(&format!("Distcheck rust-src")); let dir = build.out.join("tmp").join("distcheck-src"); let _ = fs::remove_dir_all(&dir); t!(fs::create_dir_all(&dir)); @@ -1692,6 +1706,7 @@ impl Step for Bootstrap { let mut cmd = Command::new(&build.initial_cargo); cmd.arg("test") .current_dir(build.src.join("src/bootstrap")) + .env("RUSTFLAGS", "-Cdebuginfo=2") .env("CARGO_TARGET_DIR", build.out.join("bootstrap")) .env("RUSTC_BOOTSTRAP", "1") .env("RUSTC", &build.initial_rustc); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 2bb46cc5171d6..93b6153fcb2f8 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -17,7 +17,7 @@ use std::slice::SliceConcatExt; use Mode; use Compiler; use builder::{Step, RunConfig, ShouldRun, Builder}; -use util::{copy, exe, add_lib_path}; +use util::{exe, add_lib_path}; use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp}; use native; use channel::GitInfo; @@ -116,7 +116,7 @@ impl Step for ToolBuild { cargo.arg("--features").arg(self.extra_features.join(" ")); let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool)); - println!("Building stage{} tool {} ({})", compiler.stage, tool, target); + build.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target)); let mut duplicates = Vec::new(); let is_expected = compile::stream_cargo(build, &mut cargo, &mut |msg| { // Only care about big things like the RLS/Cargo for now @@ -207,7 +207,7 @@ impl Step for ToolBuild { let cargo_out = build.cargo_out(compiler, Mode::Tool, target) .join(exe(tool, &compiler.host)); let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host)); - copy(&cargo_out, &bin); + build.copy(&cargo_out, &bin); Some(bin) } } @@ -427,7 +427,8 @@ impl Step for Rustdoc { .env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string()); let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage)); - println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host); + build.info(&format!("Building rustdoc for stage{} ({})", + target_compiler.stage, target_compiler.host)); build.run(&mut cargo); // Cargo adds a number of paths to the dylib search path on windows, which results in @@ -443,7 +444,7 @@ impl Step for Rustdoc { t!(fs::create_dir_all(&bindir)); let bin_rustdoc = bindir.join(exe("rustdoc", &*target_compiler.host)); let _ = fs::remove_file(&bin_rustdoc); - copy(&tool_rustdoc, &bin_rustdoc); + build.copy(&tool_rustdoc, &bin_rustdoc); bin_rustdoc } else { tool_rustdoc diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index 492eceef05c75..f8c7032369890 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -15,13 +15,14 @@ use std::env; use std::str; -use std::fs::{self, File, OpenOptions}; -use std::io::{self, Read, Write, Seek, SeekFrom}; +use std::fs; +use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{SystemTime, Instant}; -use filetime::{self, FileTime}; +use config::Config; +use Build; /// Returns the `name` as the filename of a static library for `target`. pub fn staticlib(name: &str, target: &str) -> String { @@ -32,102 +33,6 @@ pub fn staticlib(name: &str, target: &str) -> String { } } -/// Copies a file from `src` to `dst` -pub fn copy(src: &Path, dst: &Path) { - let _ = fs::remove_file(&dst); - // Attempt to "easy copy" by creating a hard link (symlinks don't work on - // windows), but if that fails just fall back to a slow `copy` operation. - if let Ok(()) = fs::hard_link(src, dst) { - return - } - if let Err(e) = fs::copy(src, dst) { - panic!("failed to copy `{}` to `{}`: {}", src.display(), - dst.display(), e) - } - let metadata = t!(src.metadata()); - t!(fs::set_permissions(dst, metadata.permissions())); - let atime = FileTime::from_last_access_time(&metadata); - let mtime = FileTime::from_last_modification_time(&metadata); - t!(filetime::set_file_times(dst, atime, mtime)); -} - -/// Search-and-replaces within a file. (Not maximally efficiently: allocates a -/// new string for each replacement.) -pub fn replace_in_file(path: &Path, replacements: &[(&str, &str)]) { - let mut contents = String::new(); - let mut file = t!(OpenOptions::new().read(true).write(true).open(path)); - t!(file.read_to_string(&mut contents)); - for &(target, replacement) in replacements { - contents = contents.replace(target, replacement); - } - t!(file.seek(SeekFrom::Start(0))); - t!(file.set_len(0)); - t!(file.write_all(contents.as_bytes())); -} - -pub fn read_stamp_file(stamp: &Path) -> Vec { - let mut paths = Vec::new(); - let mut contents = Vec::new(); - t!(t!(File::open(stamp)).read_to_end(&mut contents)); - // This is the method we use for extracting paths from the stamp file passed to us. See - // run_cargo for more information (in compile.rs). - for part in contents.split(|b| *b == 0) { - if part.is_empty() { - continue - } - let path = PathBuf::from(t!(str::from_utf8(part))); - paths.push(path); - } - paths -} - -/// Copies the `src` directory recursively to `dst`. Both are assumed to exist -/// when this function is called. -pub fn cp_r(src: &Path, dst: &Path) { - for f in t!(fs::read_dir(src)) { - let f = t!(f); - let path = f.path(); - let name = path.file_name().unwrap(); - let dst = dst.join(name); - if t!(f.file_type()).is_dir() { - t!(fs::create_dir_all(&dst)); - cp_r(&path, &dst); - } else { - let _ = fs::remove_file(&dst); - copy(&path, &dst); - } - } -} - -/// Copies the `src` directory recursively to `dst`. Both are assumed to exist -/// when this function is called. Unwanted files or directories can be skipped -/// by returning `false` from the filter function. -pub fn cp_filtered(src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) { - // Inner function does the actual work - fn recurse(src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) { - for f in t!(fs::read_dir(src)) { - let f = t!(f); - let path = f.path(); - let name = path.file_name().unwrap(); - let dst = dst.join(name); - let relative = relative.join(name); - // Only copy file or directory if the filter function returns true - if filter(&relative) { - if t!(f.file_type()).is_dir() { - let _ = fs::remove_dir_all(&dst); - t!(fs::create_dir(&dst)); - recurse(&path, &dst, &relative, filter); - } else { - let _ = fs::remove_file(&dst); - copy(&path, &dst); - } - } - } - } - // Immediately recurse with an empty relative path - recurse(src, dst, Path::new(""), filter) -} - /// Given an executable called `name`, return the filename for the /// executable for a particular target. pub fn exe(name: &str, target: &str) -> String { @@ -196,25 +101,28 @@ pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf { buf } -pub struct TimeIt(Instant); +pub struct TimeIt(bool, Instant); /// Returns an RAII structure that prints out how long it took to drop. -pub fn timeit() -> TimeIt { - TimeIt(Instant::now()) +pub fn timeit(build: &Build) -> TimeIt { + TimeIt(build.config.dry_run, Instant::now()) } impl Drop for TimeIt { fn drop(&mut self) { - let time = self.0.elapsed(); - println!("\tfinished in {}.{:03}", - time.as_secs(), - time.subsec_nanos() / 1_000_000); + let time = self.1.elapsed(); + if !self.0 { + println!("\tfinished in {}.{:03}", + time.as_secs(), + time.subsec_nanos() / 1_000_000); + } } } /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. -pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> { +pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> { + if config.dry_run { return Ok(()); } let _ = fs::remove_dir(dest); return symlink_dir_inner(src, dest);