diff options
| author | Mark Simulacrum <mark.simulacrum@gmail.com> | 2018-03-27 16:06:47 +0200 |
|---|---|---|
| committer | Mark Simulacrum <mark.simulacrum@gmail.com> | 2018-04-03 11:43:14 -0600 |
| commit | a727447f5927ac0a186af4504f8a11014f42b1da (patch) | |
| tree | 15c5889a504c938586d745540df5917bfdb0009b /src | |
| parent | b0dbc7c15d4f3027410b3836154da1ae63e0a7d3 (diff) | |
| download | rust-a727447f5927ac0a186af4504f8a11014f42b1da.tar.gz rust-a727447f5927ac0a186af4504f8a11014f42b1da.zip | |
Refactor to use a dry-run config instead of cfg(test)
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/bootstrap/builder.rs | 10 | ||||
| -rw-r--r-- | src/bootstrap/check.rs | 6 | ||||
| -rw-r--r-- | src/bootstrap/compile.rs | 49 | ||||
| -rw-r--r-- | src/bootstrap/config.rs | 10 | ||||
| -rw-r--r-- | src/bootstrap/dist.rs | 236 | ||||
| -rw-r--r-- | src/bootstrap/doc.rs | 35 | ||||
| -rw-r--r-- | src/bootstrap/flags.rs | 4 | ||||
| -rw-r--r-- | src/bootstrap/lib.rs | 201 | ||||
| -rw-r--r-- | src/bootstrap/native.rs | 14 | ||||
| -rw-r--r-- | src/bootstrap/test.rs | 9 | ||||
| -rw-r--r-- | src/bootstrap/tool.rs | 6 | ||||
| -rw-r--r-- | src/bootstrap/util.rs | 106 |
12 files changed, 393 insertions, 293 deletions
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 86a51c8e26b..3f5ec4933d0 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -413,8 +413,9 @@ impl<'a> Builder<'a> { 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 @@ impl<'a> Builder<'a> { #[cfg(test)] mod __test { use config::Config; + use std::thread; use super::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -917,6 +919,12 @@ mod __test { // 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<_>>(); diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index a9dccea827b..a39fad67ebe 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 daf25a36d47..872c2908528 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; @@ -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 @@ impl Step for StdLink { 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 @@ 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,7 +309,7 @@ impl Step for StartupObjects { build.cc(target), target, obj); - copy(&src, &sysroot_dir.join(obj)); + build.copy(&src, &sysroot_dir.join(obj)); } } } @@ -420,7 +420,7 @@ impl Step for TestLink { &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 @@ impl Step for RustcLink { &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 @@ impl Step for CodegenBackend { 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 @@ impl Step for Assemble { 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 @@ 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 } @@ -967,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())); } } @@ -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") diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 0848590a9bc..76672df5c57 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<u32>, pub cmd: Subcommand, pub incremental: bool, + pub dry_run: bool, // llvm codegen options pub llvm_enabled: bool, @@ -362,8 +363,15 @@ impl 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()); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index cfa0cdecca6..86ef5c35cd7 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; @@ -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)) } @@ -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,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 @@ 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, &[("<INSERT DATE HERE>", &month_year), ("<INSERT VERSION HERE>", 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"); @@ -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)) } } @@ -738,7 +731,7 @@ impl Step for 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.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,7 +789,7 @@ 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)); } } @@ -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,7 +880,7 @@ 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)) } } @@ -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,7 +983,7 @@ 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"); let mut cmd = rust_installer(builder); @@ -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,11 +1016,6 @@ 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, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Cargo { pub stage: u32, @@ -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); @@ -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); @@ -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); @@ -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,7 +1477,7 @@ 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"))); @@ -1526,10 +1494,10 @@ 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); + 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 @@ 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); @@ -1666,8 +1634,8 @@ impl Step for Extended { } 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 @@ 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 44073a5b075..eeea6f0d01d 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;)+) => { @@ -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); 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 @@ 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 } @@ -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); } } @@ -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); } } @@ -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); } } @@ -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); @@ -806,8 +808,8 @@ impl Step for UnstableBookGen { 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 @@ 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 60b22e35832..cd304fb26e0 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; @@ -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 @@ To learn more about a subcommand, run `./x.py <subcommand> -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"); @@ -365,6 +366,7 @@ 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()), diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 5b13fa27fbf..7b37c9cefa5 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] @@ -143,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}; @@ -404,13 +406,36 @@ impl Build { 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 @@ impl Build { } 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 { diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index f95f8e01dae..c49811d839a 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -60,9 +60,6 @@ impl Step for Llvm { /// 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 @@ 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())); @@ -339,7 +341,7 @@ impl Step for Lld { /// 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 @@ 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 cfg!(test) { + if builder.config.dry_run { return; } let build = builder.build; @@ -450,7 +452,7 @@ impl Step for Openssl { } fn run(self, builder: &Builder) { - if cfg!(test) { + if builder.config.dry_run { return; } let build = builder.build; diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 0b70a50f67d..2fb84db4d89 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1042,6 +1042,7 @@ impl Step for DocTest { 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 @@ 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 diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 2bb46cc5171..0b1616b9b4f 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; @@ -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) } } @@ -443,7 +443,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 f3f4278d329..8a64b07496c 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -15,13 +15,13 @@ 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 @@ impl Drop for TimeIt { /// 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); |
