]> git.lizzy.rs Git - rust.git/commitdiff
Refactor to use a dry-run config instead of cfg(test)
authorMark Simulacrum <mark.simulacrum@gmail.com>
Tue, 27 Mar 2018 14:06:47 +0000 (16:06 +0200)
committerMark Simulacrum <mark.simulacrum@gmail.com>
Tue, 3 Apr 2018 17:43:14 +0000 (11:43 -0600)
This ensures that each build will support the testing design of "dry
running" builds. It's also checked that a dry run build is equivalent
step-wise to a "wet" run build; the graphs we generate when running are
directly compared node/node and edge/edge, both for order and contents.

12 files changed:
src/bootstrap/builder.rs
src/bootstrap/check.rs
src/bootstrap/compile.rs
src/bootstrap/config.rs
src/bootstrap/dist.rs
src/bootstrap/doc.rs
src/bootstrap/flags.rs
src/bootstrap/lib.rs
src/bootstrap/native.rs
src/bootstrap/test.rs
src/bootstrap/tool.rs
src/bootstrap/util.rs

index 86a51c8e26bb83cf3f5690f86af7cb921e67d01c..3f5ec4933d02b23d31469b3ea022b46fb1f0b323 100644 (file)
@@ -413,8 +413,9 @@ pub fn new(build: &Build) -> Builder {
         builder
     }
 
-    pub fn execute_cli(&self) {
+    pub fn execute_cli(&self) -> Graph<String, bool> {
         self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
+        self.graph.borrow().clone()
     }
 
     pub fn default_doc(&self, paths: Option<&[PathBuf]>) {
@@ -910,6 +911,7 @@ pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
 #[cfg(test)]
 mod __test {
     use config::Config;
+    use std::thread;
     use super::*;
 
     fn configure(host: &[&str], target: &[&str]) -> Config {
@@ -917,6 +919,12 @@ fn configure(host: &[&str], target: &[&str]) -> Config {
         // 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::<Vec<_>>();
index a9dccea827b6e17434dc0e02b2583b099c903e49..a39fad67ebea4ac967bffe828dd8d39c7375b624 100644 (file)
@@ -53,7 +53,7 @@ fn run(self, builder: &Builder) {
                   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 @@ fn run(self, builder: &Builder) {
                   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 @@ fn run(self, builder: &Builder) {
                   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));
     }
 }
 
index daf25a36d47748066310e54f177107f44b744cc2..872c29085289a1bcfbe17c9a9bda8f31510717ac 100644 (file)
@@ -30,7 +30,7 @@
 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;
@@ -130,7 +130,7 @@ fn copy_musl_third_party_objects(build: &Build,
                                  target: Interned<String>,
                                  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));
     }
 }
 
@@ -220,13 +220,13 @@ fn run(self, builder: &Builder) {
                 target_compiler.host,
                 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 @@ fn run(self, builder: &Builder) {
     }
 }
 
-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 @@ fn run(self, builder: &Builder) {
                             .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,7 +309,7 @@ fn run(self, builder: &Builder) {
                                     build.cc(target),
                                     target,
                                     obj);
-            copy(&src, &sysroot_dir.join(obj));
+            build.copy(&src, &sysroot_dir.join(obj));
         }
     }
 }
@@ -420,7 +420,7 @@ fn run(self, builder: &Builder) {
                 &compiler.host,
                 target_compiler.host,
                 target);
-        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
+        add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target),
                     &libtest_stamp(build, compiler, target));
         builder.ensure(tool::CleanTools {
             compiler: target_compiler,
@@ -575,7 +575,7 @@ fn run(self, builder: &Builder) {
                  &compiler.host,
                  target_compiler.host,
                  target);
-        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
+        add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target),
                        &librustc_stamp(build, compiler, target));
         builder.ensure(tool::CleanTools {
             compiler: target_compiler,
@@ -690,7 +690,7 @@ fn run(self, builder: &Builder) {
                               cargo.arg("--features").arg(features),
                               &tmp_stamp,
                               false);
-        if cfg!(test) {
+        if builder.config.dry_run {
             return;
         }
         let mut files = files.into_iter()
@@ -736,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();
@@ -751,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));
     }
 }
 
@@ -767,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
@@ -936,10 +940,10 @@ fn run(self, builder: &Builder) -> 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));
             }
         }
 
@@ -957,7 +961,7 @@ fn run(self, builder: &Builder) -> Compiler {
         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
     }
@@ -967,10 +971,10 @@ fn run(self, builder: &Builder) -> Compiler {
 ///
 /// 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()));
     }
 }
 
@@ -1000,6 +1004,10 @@ fn stderr_isatty() -> bool {
 pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool)
     -> Vec<PathBuf>
 {
+    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
@@ -1141,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")
index 0848590a9bc10787ba3cdc17a0723784dad66e91..76672df5c570deeeccd747d9c809c56839dabe29 100644 (file)
@@ -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<u32>,
     pub cmd: Subcommand,
     pub incremental: bool,
+    pub dry_run: bool,
 
     // llvm codegen options
     pub llvm_enabled: bool,
@@ -362,8 +363,15 @@ pub fn parse(args: &[String]) -> Config {
         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());
 
index cfa0cdecca6b3837f216495b8a1405dd5cfed329..86ef5c35cd7f419d5a00544ea2842d8bb4e5a24d 100644 (file)
@@ -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, 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;
@@ -103,7 +103,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         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 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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))
     }
@@ -166,7 +166,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         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 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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,31 +292,25 @@ 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);
     }
 }
 
@@ -417,7 +411,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         // 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 @@ fn run(self, builder: &Builder) -> PathBuf {
         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 @@ fn run(self, builder: &Builder) -> PathBuf {
 
             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 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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 @@ fn prepare_image(builder: &Builder, compiler: Compiler, image: &Path) {
 
             // 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 @@ fn prepare_image(builder: &Builder, compiler: Compiler, image: &Path) {
             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 @@ fn prepare_image(builder: &Builder, compiler: Compiler, image: &Path) {
                     .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 @@ fn prepare_image(builder: &Builder, compiler: Compiler, image: &Path) {
             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,
                                 &[("<INSERT DATE HERE>", &month_year),
                                   ("<INSERT VERSION HERE>", channel::CFG_RELEASE_NUM)]);
             }
@@ -540,7 +533,7 @@ fn prepare_image(builder: &Builder, compiler: Compiler, image: &Path) {
 
             // 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 @@ fn run(self, builder: &Builder) {
         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 @@ fn run(self, builder: &Builder) {
             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");
@@ -659,7 +652,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         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 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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))
     }
 }
@@ -738,7 +731,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         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.cp_r(&image_src, &dst);
 
         let mut cmd = rust_installer(builder);
         cmd.arg("generate")
@@ -752,7 +745,7 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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,7 +789,7 @@ fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool {
     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));
     }
 }
 
@@ -870,7 +863,7 @@ fn run(self, builder: &Builder) -> PathBuf {
 
         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,7 +880,7 @@ fn run(self, builder: &Builder) -> PathBuf {
            .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))
     }
 }
@@ -943,13 +936,13 @@ fn run(self, builder: &Builder) -> PathBuf {
 
         // 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,7 +983,7 @@ fn run(self, builder: &Builder) -> PathBuf {
         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");
         let mut cmd = rust_installer(builder);
@@ -1004,26 +997,6 @@ fn run(self, builder: &Builder) -> PathBuf {
     }
 }
 
-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,11 +1016,6 @@ fn change_drive(s: &str) -> Option<String> {
     }
 }
 
-fn write_file(path: &Path, data: &[u8]) {
-    let mut vf = t!(fs::File::create(path));
-    t!(vf.write_all(data));
-}
-
 #[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Cargo {
     pub stage: u32,
@@ -1084,38 +1052,38 @@ fn run(self, builder: &Builder) -> PathBuf {
         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);
@@ -1181,20 +1149,20 @@ fn run(self, builder: &Builder) -> Option<PathBuf> {
             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);
@@ -1251,7 +1219,7 @@ fn run(self, builder: &Builder) -> Option<PathBuf> {
         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 @@ fn run(self, builder: &Builder) -> Option<PathBuf> {
             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);
@@ -1355,15 +1323,15 @@ fn run(self, builder: &Builder) {
         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 @@ fn run(self, builder: &Builder) {
         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 @@ fn filter(contents: &str, marker: &str) -> String {
             };
 
             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 @@ fn filter(contents: &str, marker: &str) -> String {
             }
 
             // 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 @@ fn filter(contents: &str, marker: &str) -> String {
             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,7 +1477,7 @@ fn filter(contents: &str, marker: &str) -> String {
                 } 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")));
@@ -1526,10 +1494,10 @@ fn filter(contents: &str, marker: &str) -> String {
                 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);
+            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);
             t!(t!(File::create(exe.join("LICENSE.txt"))).write_all(license.as_bytes()));
 
             // Generate exe installer
@@ -1541,7 +1509,7 @@ fn filter(contents: &str, marker: &str) -> String {
             }
             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);
 
@@ -1666,8 +1634,8 @@ fn filter(contents: &str, marker: &str) -> String {
             }
 
             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.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);
@@ -1772,7 +1740,7 @@ fn run(self, builder: &Builder) {
         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()));
index 44073a5b0757237bda2fe96abb1aa873e6432756..eeea6f0d01d2acb3cffe8fdeb21b993f4e8b3bc5 100644 (file)
 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;)+) => {
@@ -210,12 +211,13 @@ fn run(self, builder: &Builder) {
         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);
         let _ = fs::remove_dir_all(&out);
-        build.run(builder.tool_cmd(Tool::Rustbook)
+        build.run(rustbook_cmd
                        .arg("build")
                        .arg(&src)
                        .arg("-d")
@@ -370,7 +372,7 @@ fn run(self, builder: &Builder) {
         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 @@ fn run(self, builder: &Builder) {
                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
             }
 
@@ -479,7 +481,7 @@ fn run(self, builder: &Builder) {
         // 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 @@ fn run(self, builder: &Builder) {
         }
 
         build.run(&mut cargo);
-        cp_r(&my_out, &out);
+        build.cp_r(&my_out, &out);
     }
 }
 
@@ -551,12 +553,12 @@ fn run(self, builder: &Builder) {
         // 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);
     }
 }
 
@@ -617,7 +619,7 @@ fn run(self, builder: &Builder) {
         // 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 @@ fn run(self, builder: &Builder) {
         }
 
         build.run(&mut cargo);
-        cp_r(&my_out, &out);
+        build.cp_r(&my_out, &out);
     }
 }
 
@@ -693,7 +695,7 @@ fn run(self, builder: &Builder) {
         // 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);
@@ -806,8 +808,8 @@ fn run(self, builder: &Builder) {
 
         println!("Generating unstable book md files ({})", target);
         let out = build.md_doc_out(target).join("unstable-book");
-        t!(fs::create_dir_all(&out));
-        t!(fs::remove_dir_all(&out));
+        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 @@ fn run(self, builder: &Builder) {
     }
 }
 
-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)
 }
index 60b22e35832f95ecd683fc4bb5c602d7478cce1b..cd304fb26e0bfcb939cf226951a19c58c170d4f3 100644 (file)
@@ -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;
@@ -42,6 +41,7 @@ pub struct Flags {
     pub incremental: bool,
     pub exclude: Vec<PathBuf>,
     pub rustc_error_format: Option<String>,
+    pub dry_run: bool,
 }
 
 pub enum Subcommand {
@@ -112,6 +112,7 @@ pub fn parse(args: &[String]) -> Flags {
         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");
@@ -365,6 +366,7 @@ pub fn parse(args: &[String]) -> Flags {
         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()),
index 5b13fa27fbfbe2cee7af770ec844668ad22ba688..7b37c9cefa5cb5e1220a27816672c6a7b4049712 100644 (file)
 //! 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]
 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};
 
@@ -404,13 +406,36 @@ pub fn build(&mut self) {
             return clean::clean(self, all);
         }
 
-        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;
+        {
+            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;
+                }
             }
         }
-        builder.execute_cli();
+
+        if !self.config.dry_run {
+            let dry_graph = {
+                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);
+            let act_graph = builder.execute_cli();
+            assert_eq!(dry_graph.raw_nodes().iter().map(|i| &i.weight).collect::<Vec<_>>(),
+                act_graph.raw_nodes().iter().map(|i| &i.weight).collect::<Vec<_>>());
+            assert_eq!(dry_graph.raw_edges()
+                .iter().map(|i| (&dry_graph[i.source()], &dry_graph[i.target()], &i.weight))
+                .collect::<Vec<_>>(),
+                act_graph.raw_edges()
+                .iter().map(|i| (&act_graph[i.source()], &act_graph[i.target()], &i.weight))
+                .collect::<Vec<_>>());
+        } 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();
@@ -997,7 +1022,167 @@ fn in_tree_crates(&self, root: &str) -> Vec<&Crate> {
         }
         ret
     }
+
+    fn read_stamp_file(&self, stamp: &Path) -> Vec<PathBuf> {
+        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<Item=fs::DirEntry> {
+        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::<Vec<_>>().into_iter()
+    }
+}
+
+#[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 {
index f95f8e01dae50e7f152a08dfdf9841fcba5480bc..c49811d839a04dfdaf8c52ff68e7f2c2b384751d 100644 (file)
@@ -60,9 +60,6 @@ fn make_run(run: RunConfig) {
 
     /// Compile LLVM for `target`.
     fn run(self, builder: &Builder) -> PathBuf {
-        if cfg!(test) {
-            return PathBuf::from("llvm-config-test-generated");
-        }
         let build = builder.build;
         let target = self.target;
         let emscripten = self.emscripten;
@@ -220,6 +217,11 @@ fn run(self, builder: &Builder) -> PathBuf {
         //        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()));
@@ -339,7 +341,7 @@ fn make_run(run: RunConfig) {
 
     /// Compile LLVM for `target`.
     fn run(self, builder: &Builder) -> PathBuf {
-        if cfg!(test) {
+        if builder.config.dry_run {
             return PathBuf::from("lld-out-dir-test-gen");
         }
         let target = self.target;
@@ -395,7 +397,7 @@ fn make_run(run: RunConfig) {
     /// 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 cfg!(test) {
+        if builder.config.dry_run {
             return;
         }
         let build = builder.build;
@@ -450,7 +452,7 @@ fn should_run(run: ShouldRun) -> ShouldRun {
     }
 
     fn run(self, builder: &Builder) {
-        if cfg!(test) {
+        if builder.config.dry_run {
             return;
         }
         let build = builder.build;
index 0b70a50f67d57568b2810924fa64879c63e57523..2fb84db4d897e15d4193e6db4033349d03b683ae 100644 (file)
@@ -1042,6 +1042,7 @@ fn run(self, builder: &Builder) {
         let _time = util::timeit();
         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 +1059,13 @@ fn run(self, builder: &Builder) {
                 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
index 2bb46cc5171d61b0881782f5474eb2aee3dee461..0b1616b9b4f90e419170e1ee98734b2d2cc9ff3e 100644 (file)
@@ -17,7 +17,7 @@
 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;
@@ -207,7 +207,7 @@ fn run(self, builder: &Builder) -> Option<PathBuf> {
             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)
         }
     }
@@ -443,7 +443,7 @@ fn run(self, builder: &Builder) -> PathBuf {
             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
index f3f4278d3290110194163e8a40ec22753be64540..8a64b07496c96fd185fb956c09819866081780ce 100644 (file)
 
 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;
 
 /// Returns the `name` as the filename of a static library for `target`.
 pub fn staticlib(name: &str, target: &str) -> String {
@@ -32,102 +32,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<PathBuf> {
-    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 {
@@ -214,8 +118,8 @@ fn drop(&mut self) {
 
 /// Symlinks two directories, using junctions on Windows and normal symlinks on
 /// Unix.
-pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
-    if cfg!(test) { return Ok(()); }
+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);