diff --git a/.gitignore b/.gitignore index ff839c34df1..b54bab177d0 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ .hg/ .hgignore .idea +*.iml __pycache__/ *.py[cod] *$py.class diff --git a/README.md b/README.md index 0b209fd3e94..a1f01861075 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,9 @@ Read ["Installation"] from [The Book]. > ***Note:*** Install locations can be adjusted by copying the config file > from `./src/bootstrap/config.toml.example` to `./config.toml`, and - > adjusting the `prefix` option under `[install]`. Various other options are - > also supported, and are documented in the config file. + > adjusting the `prefix` option under `[install]`. Various other options, such + > as enabling debug information, are also supported, and are documented in + > the config file. When complete, `sudo ./x.py install` will place several programs into `/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 8c6eaee24f2..7232208b522 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -75,16 +75,11 @@ fn main() { Err(_) => 0, }; - // Build scripts always use the snapshot compiler which is guaranteed to be - // able to produce an executable, whereas intermediate compilers may not - // have the standard library built yet and may not be able to produce an - // executable. Otherwise we just use the standard compiler we're - // bootstrapping with. - // - // Also note that cargo will detect the version of the compiler to trigger - // a rebuild when the compiler changes. If this happens, we want to make - // sure to use the actual compiler instead of the snapshot compiler becase - // that's the one that's actually changing. + // Use a different compiler for build scripts, since there may not yet be a + // libstd for the real compiler to use. However, if Cargo is attempting to + // determine the version of the compiler, the real compiler needs to be + // used. Currently, these two states are differentiated based on whether + // --target and -vV is/isn't passed. let (rustc, libdir) = if target.is_none() && version.is_none() { ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR") } else { diff --git a/src/bootstrap/cc.rs b/src/bootstrap/cc.rs index 54c8194678e..7c7161916ee 100644 --- a/src/bootstrap/cc.rs +++ b/src/bootstrap/cc.rs @@ -42,10 +42,13 @@ use config::Target; pub fn find(build: &mut Build) { // For all targets we're going to need a C compiler for building some shims // and such as well as for being a linker for Rust code. - for target in build.config.target.iter() { + // + // This includes targets that aren't necessarily passed on the commandline + // (FIXME: Perhaps it shouldn't?) + for target in &build.config.target { let mut cfg = gcc::Config::new(); cfg.cargo_metadata(false).opt_level(0).debug(false) - .target(target).host(&build.config.build); + .target(target).host(&build.build); let config = build.config.target_config.get(target); if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { @@ -64,10 +67,13 @@ pub fn find(build: &mut Build) { } // For all host triples we need to find a C++ compiler as well - for host in build.config.host.iter() { + // + // This includes hosts that aren't necessarily passed on the commandline + // (FIXME: Perhaps it shouldn't?) + for host in &build.config.host { let mut cfg = gcc::Config::new(); cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true) - .target(host).host(&build.config.build); + .target(host).host(&build.build); let config = build.config.target_config.get(host); if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) { cfg.compiler(cxx); diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 4664b1f765e..1153acfa57d 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -23,12 +23,12 @@ use build_helper::output; use Build; // The version number -pub const CFG_RELEASE_NUM: &'static str = "1.20.0"; +pub const CFG_RELEASE_NUM: &str = "1.20.0"; // An optional number to put after the label, e.g. '.2' -> '-beta.2' // Be sure to make this starts with a dot to conform to semver pre-release // versions (section 9) -pub const CFG_PRERELEASE_VERSION: &'static str = ".1"; +pub const CFG_PRERELEASE_VERSION: &str = ".1"; pub struct GitInfo { inner: Option, @@ -99,6 +99,10 @@ impl GitInfo { version.push_str(&inner.commit_date); version.push_str(")"); } - return version + version + } + + pub fn is_git(&self) -> bool { + self.inner.is_some() } } diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 277728b90b7..b3b5ae8d67d 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -13,23 +13,22 @@ //! This file implements the various regression test suites that we execute on //! our CI. -extern crate build_helper; - use std::collections::HashSet; use std::env; +use std::iter; use std::fmt; use std::fs::{self, File}; use std::path::{PathBuf, Path}; use std::process::Command; use std::io::Read; -use build_helper::output; +use build_helper::{self, output}; use {Build, Compiler, Mode}; use dist; use util::{self, dylib_path, dylib_path_var, exe}; -const ADB_TEST_DIR: &'static str = "/data/tmp/work"; +const ADB_TEST_DIR: &str = "/data/tmp/work"; /// The two modes of the test runner; tests or benchmarks. #[derive(Copy, Clone)] @@ -60,7 +59,7 @@ impl fmt::Display for TestKind { } fn try_run(build: &Build, cmd: &mut Command) { - if build.flags.cmd.no_fail_fast() { + if !build.fail_fast { if !build.try_run(cmd) { let failures = build.delayed_failures.get(); build.delayed_failures.set(failures + 1); @@ -71,7 +70,7 @@ fn try_run(build: &Build, cmd: &mut Command) { } fn try_run_quiet(build: &Build, cmd: &mut Command) { - if build.flags.cmd.no_fail_fast() { + if !build.fail_fast { if !build.try_run_quiet(cmd) { let failures = build.delayed_failures.get(); build.delayed_failures.set(failures + 1); @@ -99,7 +98,7 @@ pub fn linkcheck(build: &Build, host: &str) { /// This tool in `src/tools` will check out a few Rust projects and run `cargo /// test` to ensure that we don't regress the test suites there. pub fn cargotest(build: &Build, stage: u32, host: &str) { - let ref compiler = Compiler::new(stage, host); + let compiler = Compiler::new(stage, host); // Note that this is a short, cryptic, and not scoped directory name. This // is currently to minimize the length of path on Windows where we otherwise @@ -109,11 +108,11 @@ pub fn cargotest(build: &Build, stage: u32, host: &str) { let _time = util::timeit(); let mut cmd = Command::new(build.tool(&Compiler::new(0, host), "cargotest")); - build.prepare_tool_cmd(compiler, &mut cmd); - try_run(build, cmd.arg(&build.cargo) + build.prepare_tool_cmd(&compiler, &mut cmd); + try_run(build, cmd.arg(&build.initial_cargo) .arg(&out_dir) - .env("RUSTC", build.compiler_path(compiler)) - .env("RUSTDOC", build.rustdoc(compiler))); + .env("RUSTC", build.compiler_path(&compiler)) + .env("RUSTDOC", build.rustdoc(&compiler))); } /// Runs `cargo test` for `cargo` packaged with Rust. @@ -124,13 +123,12 @@ pub fn cargo(build: &Build, stage: u32, host: &str) { // and not RUSTC because the Cargo test suite has tests that will // fail if rustc is not spelled `rustc`. let path = build.sysroot(compiler).join("bin"); - let old_path = ::std::env::var("PATH").expect(""); - let sep = if cfg!(windows) { ";" } else {":" }; - let ref newpath = format!("{}{}{}", path.display(), sep, old_path); + let old_path = env::var_os("PATH").unwrap_or_default(); + let newpath = env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect(""); let mut cargo = build.cargo(compiler, Mode::Tool, host, "test"); cargo.arg("--manifest-path").arg(build.src.join("src/tools/cargo/Cargo.toml")); - if build.flags.cmd.no_fail_fast() { + if !build.fail_fast { cargo.arg("--no-fail-fast"); } @@ -198,9 +196,9 @@ pub fn compiletest(build: &Build, cmd.arg("--mode").arg(mode); cmd.arg("--target").arg(target); cmd.arg("--host").arg(compiler.host); - cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.config.build)); + cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.build)); - if let Some(nodejs) = build.config.nodejs.as_ref() { + if let Some(ref nodejs) = build.config.nodejs { cmd.arg("--nodejs").arg(nodejs); } @@ -224,7 +222,7 @@ pub fn compiletest(build: &Build, cmd.arg("--docck-python").arg(build.python()); - if build.config.build.ends_with("apple-darwin") { + if build.build.ends_with("apple-darwin") { // Force /usr/bin/python on macOS for LLDB tests because we're loading the // LLDB plugin's compiled module which only works with the system python // (namely not Homebrew-installed python) @@ -251,7 +249,7 @@ pub fn compiletest(build: &Build, cmd.args(&build.flags.cmd.test_args()); - if build.config.verbose() || build.flags.verbose() { + if build.is_verbose() { cmd.arg("--verbose"); } @@ -279,7 +277,7 @@ pub fn compiletest(build: &Build, if build.remote_tested(target) { cmd.arg("--remote-test-client") - .arg(build.tool(&Compiler::new(0, &build.config.build), + .arg(build.tool(&Compiler::new(0, &build.build), "remote-test-client")); } @@ -368,7 +366,7 @@ pub fn error_index(build: &Build, compiler: &Compiler) { "error_index_generator") .arg("markdown") .arg(&output) - .env("CFG_BUILD", &build.config.build)); + .env("CFG_BUILD", &build.build)); markdown_test(build, compiler, &output); } @@ -450,7 +448,7 @@ pub fn krate(build: &Build, cargo.arg("--manifest-path") .arg(build.src.join(path).join("Cargo.toml")) .arg("--features").arg(features); - if test_kind.subcommand() == "test" && build.flags.cmd.no_fail_fast() { + if test_kind.subcommand() == "test" && !build.fail_fast { cargo.arg("--no-fail-fast"); } @@ -520,16 +518,14 @@ fn krate_emscripten(build: &Build, compiler: &Compiler, target: &str, mode: Mode) { - let mut tests = Vec::new(); let out_dir = build.cargo_out(compiler, mode, target); - find_tests(&out_dir.join("deps"), target, &mut tests); + let tests = find_tests(&out_dir.join("deps"), target); + let nodejs = build.config.nodejs.as_ref().expect("nodejs not configured"); for test in tests { - let test_file_name = test.to_string_lossy().into_owned(); - println!("running {}", test_file_name); - let nodejs = build.config.nodejs.as_ref().expect("nodejs not configured"); + println!("running {}", test.display()); let mut cmd = Command::new(nodejs); - cmd.arg(&test_file_name); + cmd.arg(&test); if build.config.quiet_tests { cmd.arg("--quiet"); } @@ -541,11 +537,10 @@ fn krate_remote(build: &Build, compiler: &Compiler, target: &str, mode: Mode) { - let mut tests = Vec::new(); let out_dir = build.cargo_out(compiler, mode, target); - find_tests(&out_dir.join("deps"), target, &mut tests); + let tests = find_tests(&out_dir.join("deps"), target); - let tool = build.tool(&Compiler::new(0, &build.config.build), + let tool = build.tool(&Compiler::new(0, &build.build), "remote-test-client"); for test in tests { let mut cmd = Command::new(&tool); @@ -559,9 +554,8 @@ fn krate_remote(build: &Build, } } -fn find_tests(dir: &Path, - target: &str, - dst: &mut Vec) { +fn find_tests(dir: &Path, target: &str) -> Vec { + let mut dst = Vec::new(); for e in t!(dir.read_dir()).map(|e| t!(e)) { let file_type = t!(e.file_type()); if !file_type.is_file() { @@ -576,6 +570,7 @@ fn find_tests(dir: &Path, dst.push(e.path()); } } + dst } pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) { @@ -590,7 +585,7 @@ pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) { .join(exe("remote-test-server", target)); // Spawn the emulator and wait for it to come online - let tool = build.tool(&Compiler::new(0, &build.config.build), + let tool = build.tool(&Compiler::new(0, &build.build), "remote-test-client"); let mut cmd = Command::new(&tool); cmd.arg("spawn-emulator") @@ -616,7 +611,7 @@ pub fn remote_copy_libs(build: &Build, compiler: &Compiler, target: &str) { /// Run "distcheck", a 'make check' from a tarball pub fn distcheck(build: &Build) { - if build.config.build != "x86_64-unknown-linux-gnu" { + if build.build != "x86_64-unknown-linux-gnu" { return } if !build.config.host.iter().any(|s| s == "x86_64-unknown-linux-gnu") { @@ -641,7 +636,7 @@ pub fn distcheck(build: &Build) { .args(&build.config.configure_args) .arg("--enable-vendor") .current_dir(&dir)); - build.run(Command::new(build_helper::make(&build.config.build)) + build.run(Command::new(build_helper::make(&build.build)) .arg("check") .current_dir(&dir)); @@ -659,7 +654,7 @@ pub fn distcheck(build: &Build) { build.run(&mut cmd); let toml = dir.join("rust-src/lib/rustlib/src/rust/src/libstd/Cargo.toml"); - build.run(Command::new(&build.cargo) + build.run(Command::new(&build.initial_cargo) .arg("generate-lockfile") .arg("--manifest-path") .arg(&toml) @@ -668,13 +663,13 @@ pub fn distcheck(build: &Build) { /// Test the build system itself pub fn bootstrap(build: &Build) { - let mut cmd = Command::new(&build.cargo); + let mut cmd = Command::new(&build.initial_cargo); cmd.arg("test") .current_dir(build.src.join("src/bootstrap")) .env("CARGO_TARGET_DIR", build.out.join("bootstrap")) .env("RUSTC_BOOTSTRAP", "1") - .env("RUSTC", &build.rustc); - if build.flags.cmd.no_fail_fast() { + .env("RUSTC", &build.initial_rustc); + if !build.fail_fast { cmd.arg("--no-fail-fast"); } cmd.arg("--").args(&build.flags.cmd.test_args()); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index b2bd792e93b..5a3106c7d5e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -50,7 +50,7 @@ pub fn std(build: &Build, target: &str, compiler: &Compiler) { let mut cargo = build.cargo(compiler, Mode::Libstd, target, "build"); let mut features = build.std_features(); - if let Ok(target) = env::var("MACOSX_STD_DEPLOYMENT_TARGET") { + if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { cargo.env("MACOSX_DEPLOYMENT_TARGET", target); } @@ -158,7 +158,7 @@ pub fn build_startup_objects(build: &Build, for_compiler: &Compiler, target: &st return } - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let compiler_path = build.compiler_path(&compiler); let src_dir = &build.src.join("src/rtstartup"); let dst_dir = &build.native_dir(target).join("rtstartup"); @@ -199,7 +199,7 @@ pub fn test(build: &Build, target: &str, compiler: &Compiler) { let out_dir = build.cargo_out(compiler, Mode::Libtest, target); build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target)); let mut cargo = build.cargo(compiler, Mode::Libtest, target, "build"); - if let Ok(target) = env::var("MACOSX_STD_DEPLOYMENT_TARGET") { + if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { cargo.env("MACOSX_DEPLOYMENT_TARGET", target); } cargo.arg("--manifest-path") @@ -247,7 +247,7 @@ pub fn rustc(build: &Build, target: &str, compiler: &Compiler) { cargo.env("CFG_RELEASE", build.rust_release()) .env("CFG_RELEASE_CHANNEL", &build.config.channel) .env("CFG_VERSION", build.rust_version()) - .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or(PathBuf::new())); + .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default()); if compiler.stage == 0 { cargo.env("CFG_LIBDIR_RELATIVE", "lib"); @@ -351,7 +351,7 @@ pub fn create_sysroot(build: &Build, compiler: &Compiler) { /// Prepare a new compiler from the artifacts in `stage` /// /// This will assemble a compiler in `build/$host/stage$stage`. The compiler -/// must have been previously produced by the `stage - 1` build.config.build +/// must have been previously produced by the `stage - 1` build.build /// compiler. pub fn assemble_rustc(build: &Build, stage: u32, host: &str) { // nothing to do in stage0 @@ -365,7 +365,7 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) { let target_compiler = Compiler::new(stage, host); // The compiler that compiled the compiler we're assembling - let build_compiler = Compiler::new(stage - 1, &build.config.build); + let build_compiler = Compiler::new(stage - 1, &build.build); // Link in all dylibs to the libdir let sysroot = build.sysroot(&target_compiler); @@ -385,7 +385,7 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) { let rustc = out_dir.join(exe("rustc", host)); let bindir = sysroot.join("bin"); t!(fs::create_dir_all(&bindir)); - let compiler = build.compiler_path(&Compiler::new(stage, host)); + let compiler = build.compiler_path(&target_compiler); let _ = fs::remove_file(&compiler); copy(&rustc, &compiler); @@ -407,6 +407,8 @@ fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) { t!(fs::create_dir_all(&sysroot_dst)); 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 this file). for part in contents.split(|b| *b == 0) { if part.is_empty() { continue @@ -421,7 +423,7 @@ fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) { /// This will build the specified tool with the specified `host` compiler in /// `stage` into the normal cargo output directory. pub fn maybe_clean_tools(build: &Build, stage: u32, target: &str, mode: Mode) { - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let stamp = match mode { Mode::Libstd => libstd_stamp(build, &compiler, target), @@ -441,7 +443,7 @@ pub fn tool(build: &Build, stage: u32, target: &str, tool: &str) { let _folder = build.fold_output(|| format!("stage{}-{}", stage, tool)); println!("Building stage{} tool {} ({})", stage, tool, target); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let mut cargo = build.cargo(&compiler, Mode::Tool, target, "build"); let dir = build.src.join("src/tools").join(tool); @@ -557,23 +559,24 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) { // If this was an output file in the "host dir" we don't actually // worry about it, it's not relevant for us. if filename.starts_with(&host_root_dir) { - continue + continue; + } // If this was output in the `deps` dir then this is a precise file // name (hash included) so we start tracking it. - } else if filename.starts_with(&target_deps_dir) { + if filename.starts_with(&target_deps_dir) { deps.push(filename.to_path_buf()); + continue; + } // Otherwise this was a "top level artifact" which right now doesn't // have a hash in the name, but there's a version of this file in // the `deps` folder which *does* have a hash in the name. That's // the one we'll want to we'll probe for it later. - } else { - toplevel.push((filename.file_stem().unwrap() - .to_str().unwrap().to_string(), - filename.extension().unwrap().to_owned() - .to_str().unwrap().to_string())); - } + toplevel.push((filename.file_stem().unwrap() + .to_str().unwrap().to_string(), + filename.extension().unwrap().to_owned() + .to_str().unwrap().to_string())); } } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 3ada846e382..34628852ab3 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -81,8 +81,6 @@ pub struct Config { pub build: String, pub host: Vec, pub target: Vec, - pub rustc: Option, - pub cargo: Option, pub local_rebuild: bool, // dist misc @@ -114,11 +112,18 @@ pub struct Config { pub python: Option, pub configure_args: Vec, pub openssl_static: bool, + + + // These are either the stage0 downloaded binaries or the locally installed ones. + pub initial_cargo: PathBuf, + pub initial_rustc: PathBuf, + } /// Per-target configuration stored in the global configuration structure. #[derive(Default)] pub struct Target { + /// Some(path to llvm-config) if using an external LLVM. pub llvm_config: Option, pub jemalloc: Option, pub cc: Option, @@ -307,8 +312,6 @@ impl Config { config.target.push(target.clone()); } } - config.rustc = build.rustc.map(PathBuf::from); - config.cargo = build.cargo.map(PathBuf::from); config.nodejs = build.nodejs.map(PathBuf::from); config.gdb = build.gdb.map(PathBuf::from); config.python = build.python.map(PathBuf::from); @@ -410,13 +413,25 @@ impl Config { set(&mut config.rust_dist_src, t.src_tarball); } + let cwd = t!(env::current_dir()); + let out = cwd.join("build"); + + let stage0_root = out.join(&config.build).join("stage0/bin"); + config.initial_rustc = match build.rustc { + Some(s) => PathBuf::from(s), + None => stage0_root.join(exe("rustc", &config.build)), + }; + config.initial_cargo = match build.cargo { + Some(s) => PathBuf::from(s), + None => stage0_root.join(exe("cargo", &config.build)), + }; // compat with `./configure` while we're still using that if fs::metadata("config.mk").is_ok() { config.update_with_config_mk(); } - return config + config } /// "Temporary" routine to parse `config.mk` into this configuration. @@ -609,8 +624,8 @@ impl Config { } "CFG_LOCAL_RUST_ROOT" if value.len() > 0 => { let path = parse_configure_path(value); - self.rustc = Some(push_exe_path(path.clone(), &["bin", "rustc"])); - self.cargo = Some(push_exe_path(path, &["bin", "cargo"])); + self.initial_rustc = push_exe_path(path.clone(), &["bin", "rustc"]); + self.initial_cargo = push_exe_path(path, &["bin", "cargo"]); } "CFG_PYTHON" if value.len() > 0 => { let path = parse_configure_path(value); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 753bd1df0d8..efc33bcee9b 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -50,7 +50,7 @@ pub fn tmpdir(build: &Build) -> PathBuf { } fn rust_installer(build: &Build) -> Command { - build.tool_cmd(&Compiler::new(0, &build.config.build), "rust-installer") + build.tool_cmd(&Compiler::new(0, &build.build), "rust-installer") } /// Builds the `rust-docs` installer component. @@ -89,7 +89,7 @@ pub fn docs(build: &Build, stage: u32, host: &str) { // As part of this step, *also* copy the docs directory to a directory which // buildbot typically uploads. - if host == build.config.build { + if host == build.build { let dst = distdir(build).join("doc").join(build.rust_package_vers()); t!(fs::create_dir_all(&dst)); cp_r(&src, &dst); @@ -97,7 +97,7 @@ pub fn docs(build: &Build, stage: u32, host: &str) { } fn find_files(files: &[&str], path: &[PathBuf]) -> Vec { - let mut found = Vec::new(); + let mut found = Vec::with_capacity(files.len()); for file in files { let file_path = @@ -119,17 +119,9 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: //Ask gcc where it keeps its stuff let mut cmd = Command::new(build.cc(target_triple)); cmd.arg("-print-search-dirs"); - build.run_quiet(&mut cmd); - let gcc_out = - String::from_utf8( - cmd - .output() - .expect("failed to execute gcc") - .stdout).expect("gcc.exe output was not utf8"); + let gcc_out = output(&mut cmd); - let mut bin_path: Vec<_> = - env::split_paths(&env::var_os("PATH").unwrap_or_default()) - .collect(); + let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); let mut lib_path = Vec::new(); for line in gcc_out.lines() { @@ -140,7 +132,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: line[(idx + 1)..] .trim_left_matches(trim_chars) .split(';') - .map(|s| PathBuf::from(s)); + .map(PathBuf::from); if key == "programs" { bin_path.extend(value); @@ -149,7 +141,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: } } - let target_tools = vec!["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe", "libwinpthread-1.dll"]; + let target_tools = ["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe", "libwinpthread-1.dll"]; let mut rustc_dlls = vec!["libstdc++-6.dll", "libwinpthread-1.dll"]; if target_triple.starts_with("i686-") { rustc_dlls.push("libgcc_s_dw2-1.dll"); @@ -157,7 +149,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: rustc_dlls.push("libgcc_s_seh-1.dll"); } - let target_libs = vec![ //MinGW libs + let target_libs = [ //MinGW libs "libgcc.a", "libgcc_eh.a", "libgcc_s.a", @@ -203,7 +195,7 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: 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().to_os_string(); + let file_name = src.file_name().unwrap(); let dest = dest_folder.join(file_name); copy(src, &dest); } @@ -234,8 +226,6 @@ fn make_win_dist(rust_root: &Path, plat_root: &Path, target_triple: &str, build: /// /// This contains all the bits and pieces to run the MinGW Windows targets /// without any extra installed software (e.g. we bundle gcc, libraries, etc). -/// Currently just shells out to a python script, but that should be rewritten -/// in Rust. pub fn mingw(build: &Build, host: &str) { println!("Dist mingw ({})", host); let name = pkgname(build, "rust-mingw"); @@ -366,9 +356,9 @@ pub fn rustc(build: &Build, stage: u32, host: &str) { pub fn debugger_scripts(build: &Build, sysroot: &Path, host: &str) { + let dst = sysroot.join("lib/rustlib/etc"); + t!(fs::create_dir_all(&dst)); let cp_debugger_script = |file: &str| { - let dst = sysroot.join("lib/rustlib/etc"); - t!(fs::create_dir_all(&dst)); install(&build.src.join("src/etc/").join(file), &dst, 0o644); }; if host.contains("windows-msvc") { @@ -404,7 +394,7 @@ pub fn std(build: &Build, compiler: &Compiler, target: &str) { // The only true set of target libraries came from the build triple, so // let's reduce redundant work by only producing archives from that host. - if compiler.host != build.config.build { + if compiler.host != build.build { println!("\tskipping, not a build host"); return } @@ -450,7 +440,7 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) { assert!(build.config.extended); println!("Dist analysis"); - if compiler.host != build.config.build { + if compiler.host != build.build { println!("\tskipping, not a build host"); return; } @@ -498,12 +488,11 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di if spath.ends_with("~") || spath.ends_with(".pyc") { return false } - if spath.contains("llvm/test") || spath.contains("llvm\\test") { - if spath.ends_with(".ll") || - spath.ends_with(".td") || - spath.ends_with(".s") { - return false - } + if (spath.contains("llvm/test") || spath.contains("llvm\\test")) && + (spath.ends_with(".ll") || + spath.ends_with(".td") || + spath.ends_with(".s")) { + return false } let full_path = Path::new(dir).join(path); @@ -595,7 +584,7 @@ pub fn rust_src(build: &Build) { t!(fs::remove_dir_all(&image)); } -const CARGO_VENDOR_VERSION: &'static str = "0.1.4"; +const CARGO_VENDOR_VERSION: &str = "0.1.4"; /// Creates the plain source tarball pub fn plain_source_tarball(build: &Build) { @@ -634,26 +623,26 @@ pub fn plain_source_tarball(build: &Build) { write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes()); // If we're building from git sources, we need to vendor a complete distribution. - if build.src_is_git { + if build.rust_info.is_git() { // Get cargo-vendor installed, if it isn't already. let mut has_cargo_vendor = false; - let mut cmd = Command::new(&build.cargo); + let mut cmd = Command::new(&build.initial_cargo); for line in output(cmd.arg("install").arg("--list")).lines() { has_cargo_vendor |= line.starts_with("cargo-vendor "); } if !has_cargo_vendor { - let mut cmd = Command::new(&build.cargo); + let mut cmd = Command::new(&build.initial_cargo); cmd.arg("install") .arg("--force") .arg("--debug") .arg("--vers").arg(CARGO_VENDOR_VERSION) .arg("cargo-vendor") - .env("RUSTC", &build.rustc); + .env("RUSTC", &build.initial_rustc); build.run(&mut cmd); } // Vendor all Cargo dependencies - let mut cmd = Command::new(&build.cargo); + let mut cmd = Command::new(&build.initial_cargo); cmd.arg("vendor") .current_dir(&plain_dst_src.join("src")); build.run(&mut cmd); @@ -716,7 +705,7 @@ fn write_file(path: &Path, data: &[u8]) { pub fn cargo(build: &Build, stage: u32, target: &str) { println!("Dist cargo stage{} ({})", stage, target); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let src = build.src.join("src/tools/cargo"); let etc = src.join("src/etc"); @@ -777,7 +766,7 @@ pub fn cargo(build: &Build, stage: u32, target: &str) { pub fn rls(build: &Build, stage: u32, target: &str) { assert!(build.config.extended); println!("Dist RLS stage{} ({})", stage, target); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let src = build.src.join("src/tools/rls"); let release_num = build.release_num("rls"); @@ -1209,7 +1198,7 @@ fn add_env(build: &Build, cmd: &mut Command, target: &str) { } pub fn hash_and_sign(build: &Build) { - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let mut cmd = build.tool_cmd(&compiler, "build-manifest"); let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| { panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n") diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 30f631ca2df..7dbc3e55539 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -45,7 +45,7 @@ pub fn rustbook_src(build: &Build, target: &str, name: &str, src: &Path) { t!(fs::create_dir_all(&out)); let out = out.join(name); - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let src = src.join(name); let index = out.join("index.html"); let rustbook = build.tool(&compiler, "rustbook"); @@ -95,7 +95,7 @@ pub fn book(build: &Build, target: &str, name: &str) { fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) { let out = build.doc_out(target); - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let path = build.src.join("src/doc").join(markdown); @@ -150,7 +150,7 @@ pub fn standalone(build: &Build, target: &str) { let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let favicon = build.src.join("src/doc/favicon.inc"); let footer = build.src.join("src/doc/footer.inc"); @@ -217,7 +217,7 @@ pub fn std(build: &Build, stage: u32, target: &str) { println!("Documenting stage{} std ({})", stage, target); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let compiler = if build.force_use_stage1(&compiler, target) { Compiler::new(1, compiler.host) } else { @@ -276,7 +276,7 @@ pub fn test(build: &Build, stage: u32, target: &str) { println!("Documenting stage{} test ({})", stage, target); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let compiler = if build.force_use_stage1(&compiler, target) { Compiler::new(1, compiler.host) } else { @@ -306,7 +306,7 @@ pub fn rustc(build: &Build, stage: u32, target: &str) { println!("Documenting stage{} compiler ({})", stage, target); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(stage, &build.build); let compiler = if build.force_use_stage1(&compiler, target) { Compiler::new(1, compiler.host) } else { @@ -351,13 +351,13 @@ pub fn error_index(build: &Build, target: &str) { println!("Documenting error index ({})", target); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let mut index = build.tool_cmd(&compiler, "error_index_generator"); index.arg("html"); index.arg(out.join("error-index.html")); // FIXME: shouldn't have to pass this env var - index.env("CFG_BUILD", &build.config.build); + index.env("CFG_BUILD", &build.build); build.run(&mut index); } @@ -367,7 +367,7 @@ pub fn unstable_book_gen(build: &Build, target: &str) { let out = build.md_doc_out(target).join("unstable-book"); t!(fs::create_dir_all(&out)); t!(fs::remove_dir_all(&out)); - let compiler = Compiler::new(0, &build.config.build); + let compiler = Compiler::new(0, &build.build); let mut cmd = build.tool_cmd(&compiler, "unstable-book-gen"); cmd.arg(build.src.join("src")); cmd.arg(out); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index dc9dac73627..5804df34e8b 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -35,22 +35,12 @@ pub struct Flags { pub host: Vec, pub target: Vec, pub config: Option, - pub src: Option, + pub src: PathBuf, pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, } -impl Flags { - pub fn verbose(&self) -> bool { - self.verbose > 0 - } - - pub fn very_verbose(&self) -> bool { - self.verbose > 1 - } -} - pub enum Subcommand { Build { paths: Vec, @@ -61,7 +51,7 @@ pub enum Subcommand { Test { paths: Vec, test_args: Vec, - no_fail_fast: bool, + fail_fast: bool, }, Bench { paths: Vec, @@ -122,16 +112,15 @@ To learn more about a subcommand, run `./x.py -h`"); // the subcommand. Therefore we must manually identify the subcommand first, so that we can // complete the definition of the options. Then we can use the getopt::Matches object from // there on out. - let mut possible_subcommands = args.iter().collect::>(); - possible_subcommands.retain(|&s| - (s == "build") - || (s == "test") - || (s == "bench") - || (s == "doc") - || (s == "clean") - || (s == "dist") - || (s == "install")); - let subcommand = match possible_subcommands.first() { + let subcommand = args.iter().find(|&s| + (s == "build") + || (s == "test") + || (s == "bench") + || (s == "doc") + || (s == "clean") + || (s == "dist") + || (s == "install")); + let subcommand = match subcommand { Some(s) => s, None => { // No subcommand -- show the general usage and subcommand help @@ -164,7 +153,7 @@ To learn more about a subcommand, run `./x.py -h`"); let mut pass_sanity_check = true; match matches.free.get(0) { Some(check_subcommand) => { - if &check_subcommand != subcommand { + if check_subcommand != subcommand { pass_sanity_check = false; } }, @@ -279,7 +268,7 @@ Arguments: Subcommand::Test { paths: paths, test_args: matches.opt_strs("test-args"), - no_fail_fast: matches.opt_present("no-fail-fast"), + fail_fast: !matches.opt_present("no-fail-fast"), } } "bench" => { @@ -316,12 +305,15 @@ Arguments: let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap()); - if matches.opt_present("incremental") { - if stage.is_none() { - stage = Some(1); - } + if matches.opt_present("incremental") && stage.is_none() { + stage = Some(1); } + let cwd = t!(env::current_dir()); + let src = matches.opt_str("src").map(PathBuf::from) + .or_else(|| env::var_os("SRC").map(PathBuf::from)) + .unwrap_or(cwd); + Flags { verbose: matches.opt_count("verbose"), stage: stage, @@ -333,7 +325,7 @@ Arguments: host: split(matches.opt_strs("host")), target: split(matches.opt_strs("target")), config: cfg_file, - src: matches.opt_str("src").map(PathBuf::from), + src: src, jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()), cmd: cmd, incremental: matches.opt_present("incremental"), @@ -352,9 +344,9 @@ impl Subcommand { } } - pub fn no_fail_fast(&self) -> bool { + pub fn fail_fast(&self) -> bool { match *self { - Subcommand::Test { no_fail_fast, .. } => no_fail_fast, + Subcommand::Test { fail_fast, .. } => fail_fast, _ => false, } } diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 21e21628dc9..8e2ef527b16 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -146,5 +146,5 @@ fn add_destdir(path: &Path, destdir: &Option) -> PathBuf { _ => {} } } - return ret + ret } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 86180077b82..69b0c4a2756 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -161,25 +161,35 @@ pub struct Build { flags: Flags, // Derived properties from the above two configurations - cargo: PathBuf, - rustc: PathBuf, src: PathBuf, out: PathBuf, rust_info: channel::GitInfo, cargo_info: channel::GitInfo, rls_info: channel::GitInfo, local_rebuild: bool, + fail_fast: bool, + verbosity: usize, + + // Targets for which to build. + build: String, + hosts: Vec, + targets: Vec, + + // Stage 0 (downloaded) compiler and cargo or their local rust equivalents. + initial_rustc: PathBuf, + initial_cargo: PathBuf, // Probed tools at runtime lldb_version: Option, lldb_python_dir: Option, // Runtime state filled in later on + // target -> (cc, ar) cc: HashMap)>, + // host -> (cc, ar) cxx: HashMap, crates: HashMap, is_sudo: bool, - src_is_git: bool, ci_env: CiEnv, delayed_failures: Cell, } @@ -202,20 +212,16 @@ struct Crate { /// build system, with each mod generating output in a different directory. #[derive(Clone, Copy, PartialEq, Eq)] pub enum Mode { - /// This cargo is going to build the standard library, placing output in the - /// "stageN-std" directory. + /// Build the standard library, placing output in the "stageN-std" directory. Libstd, - /// This cargo is going to build libtest, placing output in the - /// "stageN-test" directory. + /// Build libtest, placing output in the "stageN-test" directory. Libtest, - /// This cargo is going to build librustc and compiler libraries, placing - /// output in the "stageN-rustc" directory. + /// Build librustc and compiler libraries, placing output in the "stageN-rustc" directory. Librustc, - /// This cargo is going to build some tool, placing output in the - /// "stageN-tools" directory. + /// Build some tool, placing output in the "stageN-tools" directory. Tool, } @@ -226,22 +232,9 @@ impl Build { /// By default all build output will be placed in the current directory. pub fn new(flags: Flags, config: Config) -> Build { let cwd = t!(env::current_dir()); - let src = flags.src.clone().or_else(|| { - env::var_os("SRC").map(|x| x.into()) - }).unwrap_or(cwd.clone()); + let src = flags.src.clone(); let out = cwd.join("build"); - let stage0_root = out.join(&config.build).join("stage0/bin"); - let rustc = match config.rustc { - Some(ref s) => PathBuf::from(s), - None => stage0_root.join(exe("rustc", &config.build)), - }; - let cargo = match config.cargo { - Some(ref s) => PathBuf::from(s), - None => stage0_root.join(exe("cargo", &config.build)), - }; - let local_rebuild = config.local_rebuild; - let is_sudo = match env::var_os("SUDO_USER") { Some(sudo_user) => { match env::var_os("USER") { @@ -254,32 +247,64 @@ impl Build { let rust_info = channel::GitInfo::new(&src); let cargo_info = channel::GitInfo::new(&src.join("src/tools/cargo")); let rls_info = channel::GitInfo::new(&src.join("src/tools/rls")); - let src_is_git = src.join(".git").exists(); + + let hosts = if !flags.host.is_empty() { + for host in flags.host.iter() { + if !config.host.contains(host) { + panic!("specified host `{}` is not in configuration", host); + } + } + flags.host.clone() + } else { + config.host.clone() + }; + let targets = if !flags.target.is_empty() { + for target in flags.target.iter() { + if !config.target.contains(target) { + panic!("specified target `{}` is not in configuration", target); + } + } + flags.target.clone() + } else { + config.target.clone() + }; Build { + initial_rustc: config.initial_rustc.clone(), + initial_cargo: config.initial_cargo.clone(), + local_rebuild: config.local_rebuild, + fail_fast: flags.cmd.fail_fast(), + verbosity: cmp::max(flags.verbose, config.verbose), + + build: config.host[0].clone(), + hosts: hosts, + targets: targets, + flags: flags, config: config, - cargo: cargo, - rustc: rustc, src: src, out: out, rust_info: rust_info, cargo_info: cargo_info, rls_info: rls_info, - local_rebuild: local_rebuild, cc: HashMap::new(), cxx: HashMap::new(), crates: HashMap::new(), lldb_version: None, lldb_python_dir: None, is_sudo: is_sudo, - src_is_git: src_is_git, ci_env: CiEnv::current(), delayed_failures: Cell::new(0), } } + fn build_slice(&self) -> &[String] { + unsafe { + std::slice::from_raw_parts(&self.build, 1) + } + } + /// Executes the entire build, as configured by the flags and configuration. pub fn build(&mut self) { unsafe { @@ -296,7 +321,7 @@ impl Build { sanity::check(self); // If local-rust is the same major.minor as the current version, then force a local-rebuild let local_version_verbose = output( - Command::new(&self.rustc).arg("--version").arg("--verbose")); + Command::new(&self.initial_rustc).arg("--version").arg("--verbose")); let local_release = local_version_verbose .lines().filter(|x| x.starts_with("release:")) .next().unwrap().trim_left_matches("release:").trim(); @@ -338,7 +363,7 @@ impl Build { mode: Mode, target: &str, cmd: &str) -> Command { - let mut cargo = Command::new(&self.cargo); + let mut cargo = Command::new(&self.initial_cargo); let out_dir = self.stage_out(compiler, mode); cargo.env("CARGO_TARGET_DIR", out_dir) .arg(cmd) @@ -422,7 +447,7 @@ impl Build { // library up and running, so we can use the normal compiler to compile // build scripts in that situation. if mode == Mode::Libstd { - cargo.env("RUSTC_SNAPSHOT", &self.rustc) + cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc) .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); } else { cargo.env("RUSTC_SNAPSHOT", self.compiler_path(compiler)) @@ -441,8 +466,7 @@ impl Build { cargo.env("RUSTC_ON_FAIL", on_fail); } - let verbose = cmp::max(self.config.verbose, self.flags.verbose); - cargo.env("RUSTC_VERBOSE", format!("{}", verbose)); + cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity)); // Specify some various options for build scripts used throughout // the build. @@ -480,7 +504,7 @@ impl Build { // FIXME: should update code to not require this env var cargo.env("CFG_COMPILER_HOST_TRIPLE", target); - if self.config.verbose() || self.flags.verbose() { + if self.is_verbose() { cargo.arg("-v"); } // FIXME: cargo bench does not accept `--release` @@ -496,13 +520,13 @@ impl Build { self.ci_env.force_coloring_in_ci(&mut cargo); - return cargo + cargo } /// Get a path to the compiler specified. fn compiler_path(&self, compiler: &Compiler) -> PathBuf { if compiler.is_snapshot(self) { - self.rustc.clone() + self.initial_rustc.clone() } else { self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host)) } @@ -519,7 +543,7 @@ impl Build { let mut rustdoc = self.compiler_path(compiler); rustdoc.pop(); rustdoc.push(exe("rustdoc", compiler.host)); - return rustdoc + rustdoc } /// Get a `Command` which is ready to run `tool` in `stage` built for @@ -527,7 +551,7 @@ impl Build { fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command { let mut cmd = Command::new(self.tool(&compiler, tool)); self.prepare_tool_cmd(compiler, &mut cmd); - return cmd + cmd } /// Prepares the `cmd` provided to be able to run the `compiler` provided. @@ -578,7 +602,7 @@ impl Build { if self.config.profiler { features.push_str(" profiler"); } - return features + features } /// Get the space-separated set of activated features for the compiler. @@ -587,7 +611,7 @@ impl Build { if self.config.use_jemalloc { features.push_str(" jemalloc"); } - return features + features } /// Component directory that Cargo will produce output into (e.g. @@ -760,7 +784,7 @@ impl Build { /// Returns the libdir of the snapshot compiler. fn rustc_snapshot_libdir(&self) -> PathBuf { - self.rustc.parent().unwrap().parent().unwrap() + self.initial_rustc.parent().unwrap().parent().unwrap() .join(libdir(&self.config.build)) } @@ -792,9 +816,17 @@ impl Build { try_run_suppressed(cmd) } + pub fn is_verbose(&self) -> bool { + self.verbosity > 0 + } + + pub fn is_very_verbose(&self) -> bool { + self.verbosity > 1 + } + /// Prints a message if this build is configured in verbose mode. fn verbose(&self, msg: &str) { - if self.flags.verbose() || self.config.verbose() { + if self.is_verbose() { println!("{}", msg); } } @@ -802,7 +834,7 @@ impl Build { /// Returns the number of parallel jobs that have been configured for this /// build. fn jobs(&self) -> u32 { - self.flags.jobs.unwrap_or(num_cpus::get() as u32) + self.flags.jobs.unwrap_or_else(|| num_cpus::get() as u32) } /// Returns the path to the C compiler for the target specified. @@ -834,7 +866,7 @@ impl Build { if target == "i686-pc-windows-gnu" { base.push("-fno-omit-frame-pointer".into()); } - return base + base } /// Returns the path to the `ar` archive utility for the target specified. @@ -866,7 +898,7 @@ impl Build { !target.contains("emscripten") { base.push(format!("-Clinker={}", self.cc(target).display())); } - return base + base } /// Returns the "musl root" for this `target`, if defined @@ -1047,7 +1079,7 @@ impl<'a> Compiler<'a> { /// Returns whether this is a snapshot compiler for `build`'s configuration fn is_snapshot(&self, build: &Build) -> bool { - self.stage == 0 && self.host == build.config.build + self.stage == 0 && self.host == build.build } /// Returns if this compiler should be treated as a final stage one in the diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index 7b6b01655df..9326bb7129a 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -56,7 +56,7 @@ fn build_krate(build: &mut Build, krate: &str) { // of packages we're going to have to know what `-p` arguments to pass it // to know what crates to test. Here we run `cargo metadata` to learn about // the dependency graph and what `-p` arguments there are. - let mut cargo = Command::new(&build.cargo); + let mut cargo = Command::new(&build.initial_cargo); cargo.arg("metadata") .arg("--format-version").arg("1") .arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml")); diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index f150df6cdcd..20eec97d8e5 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -94,7 +94,7 @@ pub fn llvm(build: &Build, target: &str) { let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"}; cfg.target(target) - .host(&build.config.build) + .host(&build.build) .out_dir(&out_dir) .profile(profile) .define("LLVM_ENABLE_ASSERTIONS", assertions) @@ -129,11 +129,11 @@ pub fn llvm(build: &Build, target: &str) { } // http://llvm.org/docs/HowToCrossCompileLLVM.html - if target != build.config.build { + if target != build.build { // FIXME: if the llvm root for the build triple is overridden then we // should use llvm-tblgen from there, also should verify that it // actually exists most of the time in normal installs of LLVM. - let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen"); + let host = build.llvm_out(&build.build).join("bin/llvm-tblgen"); cfg.define("CMAKE_CROSSCOMPILING", "True") .define("LLVM_TABLEGEN", &host); } @@ -243,7 +243,7 @@ pub fn test_helpers(build: &Build, target: &str) { cfg.cargo_metadata(false) .out_dir(&dst) .target(target) - .host(&build.config.build) + .host(&build.build) .opt_level(0) .debug(false) .file(build.src.join("src/rt/rust_test_helpers.c")) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 46d047bb015..a9c1b023dd4 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -18,9 +18,9 @@ //! In theory if we get past this phase it's a bug if a build fails, but in //! practice that's likely not true! -use std::collections::HashSet; +use std::collections::HashMap; use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::{OsString, OsStr}; use std::fs; use std::process::Command; use std::path::PathBuf; @@ -29,45 +29,59 @@ use build_helper::output; use Build; +struct Finder { + cache: HashMap>, + path: OsString, +} + +impl Finder { + fn new() -> Self { + Self { + cache: HashMap::new(), + path: env::var_os("PATH").unwrap_or_default() + } + } + + fn maybe_have>(&mut self, cmd: S) -> Option { + let cmd: OsString = cmd.as_ref().into(); + let path = self.path.clone(); + self.cache.entry(cmd.clone()).or_insert_with(|| { + for path in env::split_paths(&path) { + let target = path.join(&cmd); + let mut cmd_alt = cmd.clone(); + cmd_alt.push(".exe"); + if target.is_file() || // some/path/git + target.with_extension("exe").exists() || // some/path/git.exe + target.join(&cmd_alt).exists() { // some/path/git/git.exe + return Some(target); + } + } + None + }).clone() + } + + fn must_have>(&mut self, cmd: S) -> PathBuf { + self.maybe_have(&cmd).unwrap_or_else(|| { + panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref()); + }) + } +} + pub fn check(build: &mut Build) { - let mut checked = HashSet::new(); - let path = env::var_os("PATH").unwrap_or(OsString::new()); + let path = env::var_os("PATH").unwrap_or_default(); // On Windows, quotes are invalid characters for filename paths, and if // one is present as part of the PATH then that can lead to the system // being unable to identify the files properly. See // https://github.com/rust-lang/rust/issues/34959 for more details. - if cfg!(windows) { - if path.to_string_lossy().contains("\"") { - panic!("PATH contains invalid character '\"'"); - } + if cfg!(windows) && path.to_string_lossy().contains("\"") { + panic!("PATH contains invalid character '\"'"); } - let have_cmd = |cmd: &OsStr| { - for path in env::split_paths(&path) { - let target = path.join(cmd); - let mut cmd_alt = cmd.to_os_string(); - cmd_alt.push(".exe"); - if target.is_file() || - target.with_extension("exe").exists() || - target.join(cmd_alt).exists() { - return Some(target); - } - } - return None; - }; - - let mut need_cmd = |cmd: &OsStr| { - if !checked.insert(cmd.to_owned()) { - return - } - if have_cmd(cmd).is_none() { - panic!("\n\ncouldn't find required command: {:?}\n\n", cmd); - } - }; + let mut cmd_finder = Finder::new(); // If we've got a git directory we're gona need git to update // submodules and learn about various other aspects. - if build.src_is_git { - need_cmd("git".as_ref()); + if build.rust_info.is_git() { + cmd_finder.must_have("git"); } // We need cmake, but only if we're actually building LLVM or sanitizers. @@ -75,57 +89,32 @@ pub fn check(build: &mut Build) { .filter_map(|host| build.config.target_config.get(host)) .any(|config| config.llvm_config.is_none()); if building_llvm || build.config.sanitizers { - need_cmd("cmake".as_ref()); + cmd_finder.must_have("cmake"); } // Ninja is currently only used for LLVM itself. - if building_llvm && build.config.ninja { - // Some Linux distros rename `ninja` to `ninja-build`. - // CMake can work with either binary name. - if have_cmd("ninja-build".as_ref()).is_none() { - need_cmd("ninja".as_ref()); - } + // Some Linux distros rename `ninja` to `ninja-build`. + // CMake can work with either binary name. + if building_llvm && build.config.ninja && cmd_finder.maybe_have("ninja-build").is_none() { + cmd_finder.must_have("ninja"); } - if build.config.python.is_none() { - // set by bootstrap.py - if let Some(v) = env::var_os("BOOTSTRAP_PYTHON") { - build.config.python = Some(PathBuf::from(v)); - } - } - if build.config.python.is_none() { - build.config.python = have_cmd("python2.7".as_ref()); - } - if build.config.python.is_none() { - build.config.python = have_cmd("python2".as_ref()); - } - if build.config.python.is_none() { - need_cmd("python".as_ref()); - build.config.python = Some("python".into()); - } - need_cmd(build.config.python.as_ref().unwrap().as_ref()); + build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p)) + .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py + .or_else(|| cmd_finder.maybe_have("python2.7")) + .or_else(|| cmd_finder.maybe_have("python2")) + .or_else(|| Some(cmd_finder.must_have("python"))); + build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p)) + .or_else(|| cmd_finder.maybe_have("node")) + .or_else(|| cmd_finder.maybe_have("nodejs")); - if let Some(ref s) = build.config.nodejs { - need_cmd(s.as_ref()); - } else { - // Look for the nodejs command, needed for emscripten testing - if let Some(node) = have_cmd("node".as_ref()) { - build.config.nodejs = Some(node); - } else if let Some(node) = have_cmd("nodejs".as_ref()) { - build.config.nodejs = Some(node); - } - } - - if let Some(ref gdb) = build.config.gdb { - need_cmd(gdb.as_ref()); - } else { - build.config.gdb = have_cmd("gdb".as_ref()); - } + build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p)) + .or_else(|| cmd_finder.maybe_have("gdb")); // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. - for target in build.config.target.iter() { + for target in &build.config.target { // On emscripten we don't actually need the C compiler to just // build the target artifacts, only for testing. For the sake // of easier bot configuration, just skip detection. @@ -133,33 +122,32 @@ pub fn check(build: &mut Build) { continue; } - need_cmd(build.cc(target).as_ref()); + cmd_finder.must_have(build.cc(target)); if let Some(ar) = build.ar(target) { - need_cmd(ar.as_ref()); + cmd_finder.must_have(ar); } } - for host in build.config.host.iter() { - need_cmd(build.cxx(host).unwrap().as_ref()); - } - // The msvc hosts don't use jemalloc, turn it off globally to - // avoid packaging the dummy liballoc_jemalloc on that platform. for host in build.config.host.iter() { + cmd_finder.must_have(build.cxx(host).unwrap()); + + // The msvc hosts don't use jemalloc, turn it off globally to + // avoid packaging the dummy liballoc_jemalloc on that platform. if host.contains("msvc") { build.config.use_jemalloc = false; } } // Externally configured LLVM requires FileCheck to exist - let filecheck = build.llvm_filecheck(&build.config.build); + let filecheck = build.llvm_filecheck(&build.build); if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { panic!("FileCheck executable {:?} does not exist", filecheck); } - for target in build.config.target.iter() { + for target in &build.config.target { // Can't compile for iOS unless we're on macOS if target.contains("apple-ios") && - !build.config.build.contains("apple-darwin") { + !build.build.contains("apple-darwin") { panic!("the iOS target is only supported on macOS"); } @@ -206,18 +194,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake } } - for host in build.flags.host.iter() { - if !build.config.host.contains(host) { - panic!("specified host `{}` is not in the ./configure list", host); - } - } - for target in build.flags.target.iter() { - if !build.config.target.contains(target) { - panic!("specified target `{}` is not in the ./configure list", - target); - } - } - let run = |cmd: &mut Command| { cmd.output().map(|output| { String::from_utf8_lossy(&output.stdout) @@ -231,6 +207,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake } if let Some(ref s) = build.config.ccache { - need_cmd(s.as_ref()); + cmd_finder.must_have(s); } } diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs index 5f0724c6577..c221d707683 100644 --- a/src/bootstrap/step.rs +++ b/src/bootstrap/step.rs @@ -104,10 +104,10 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.build("llvm", "src/llvm") .host(true) .dep(move |s| { - if s.target == build.config.build { + if s.target == build.build { Step::noop() } else { - s.target(&build.config.build) + s.target(&build.build) } }) .run(move |s| native::llvm(build, s.target)); @@ -124,7 +124,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { Step::noop() } else { s.name("librustc") - .host(&build.config.build) + .host(&build.build) .stage(s.stage - 1) } }) @@ -148,7 +148,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { } } } - return ret + ret }; // ======================================================================== @@ -215,29 +215,29 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { let mut rule = rules.build(&krate, "path/to/nowhere"); rule.dep(move |s| { if build.force_use_stage1(&s.compiler(), s.target) { - s.host(&build.config.build).stage(1) - } else if s.host == build.config.build { + s.host(&build.build).stage(1) + } else if s.host == build.build { s.name(dep) } else { - s.host(&build.config.build) + s.host(&build.build) } }) .run(move |s| { if build.force_use_stage1(&s.compiler(), s.target) { link(build, - &s.stage(1).host(&build.config.build).compiler(), + &s.stage(1).host(&build.build).compiler(), &s.compiler(), s.target) - } else if s.host == build.config.build { + } else if s.host == build.build { link(build, &s.compiler(), &s.compiler(), s.target) } else { link(build, - &s.host(&build.config.build).compiler(), + &s.host(&build.build).compiler(), &s.compiler(), s.target) } }); - return rule + rule } // Similar to the `libstd`, `libtest`, and `librustc` rules above, except @@ -269,7 +269,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { for (krate, path, _default) in krates("std") { rules.build(&krate.build_step, path) .dep(|s| s.name("startup-objects")) - .dep(move |s| s.name("rustc").host(&build.config.build).target(s.host)) + .dep(move |s| s.name("rustc").host(&build.build).target(s.host)) .run(move |s| compile::std(build, s.target, &s.compiler())); } for (krate, path, _default) in krates("test") { @@ -280,7 +280,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { for (krate, path, _default) in krates("rustc-main") { rules.build(&krate.build_step, path) .dep(|s| s.name("libtest-link")) - .dep(move |s| s.name("llvm").host(&build.config.build).stage(0)) + .dep(move |s| s.name("llvm").host(&build.build).stage(0)) .dep(|s| s.name("may-run-build-script")) .run(move |s| compile::rustc(build, s.target, &s.compiler())); } @@ -291,8 +291,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.build("may-run-build-script", "path/to/nowhere") .dep(move |s| { s.name("libstd-link") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) }); rules.build("startup-objects", "src/rtstartup") .dep(|s| s.name("create-sysroot").target(s.host)) @@ -332,7 +332,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { "incremental"); } - if build.config.build.contains("msvc") { + if build.build.contains("msvc") { // nothing to do for debuginfo tests } else { rules.test("check-debuginfo-lldb", "src/test/debuginfo-lldb") @@ -352,7 +352,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { "debuginfo-gdb", "debuginfo")); let mut rule = rules.test("check-debuginfo", "src/test/debuginfo"); rule.default(true); - if build.config.build.contains("apple") { + if build.build.contains("apple") { rule.dep(|s| s.name("check-debuginfo-lldb")); } else { rule.dep(|s| s.name("check-debuginfo-gdb")); @@ -594,8 +594,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { // Cargo depends on procedural macros, which requires a full host // compiler to be available, so we need to depend on that. s.name("librustc-link") - .target(&build.config.build) - .host(&build.config.build) + .target(&build.build) + .host(&build.build) }) .run(move |s| compile::tool(build, s.stage, s.target, "cargo")); rules.build("tool-rls", "src/tools/rls") @@ -606,8 +606,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { .dep(move |s| { // rls, like cargo, uses procedural macros s.name("librustc-link") - .target(&build.config.build) - .host(&build.config.build) + .target(&build.build) + .host(&build.build) }) .run(move |s| compile::tool(build, s.stage, s.target, "rls")); @@ -635,8 +635,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-book", "src/doc/book") .dep(move |s| { s.name("tool-rustbook") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .default(build.config.docs) @@ -644,8 +644,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-nomicon", "src/doc/nomicon") .dep(move |s| { s.name("tool-rustbook") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .default(build.config.docs) @@ -653,8 +653,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-reference", "src/doc/reference") .dep(move |s| { s.name("tool-rustbook") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .default(build.config.docs) @@ -662,8 +662,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-unstable-book", "src/doc/unstable-book") .dep(move |s| { s.name("tool-rustbook") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .dep(move |s| s.name("doc-unstable-book-gen")) @@ -675,14 +675,14 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-standalone", "src/doc") .dep(move |s| { s.name("rustc") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .default(build.config.docs) .run(move |s| doc::standalone(build, s.target)); rules.doc("doc-error-index", "src/tools/error_index_generator") - .dep(move |s| s.name("tool-error-index").target(&build.config.build).stage(0)) + .dep(move |s| s.name("tool-error-index").target(&build.build).stage(0)) .dep(move |s| s.name("librustc-link")) .default(build.config.docs) .host(true) @@ -690,8 +690,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen") .dep(move |s| { s.name("tool-unstable-book-gen") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) }) .dep(move |s| s.name("libstd-link")) @@ -725,7 +725,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { // ======================================================================== // Distribution targets rules.dist("dist-rustc", "src/librustc") - .dep(move |s| s.name("rustc").host(&build.config.build)) + .dep(move |s| s.name("rustc").host(&build.build)) .host(true) .only_host_build(true) .default(true) @@ -811,7 +811,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { .host(true) .only_build(true) .only_host_build(true) - .dep(move |s| s.name("tool-build-manifest").target(&build.config.build).stage(0)) + .dep(move |s| s.name("tool-build-manifest").target(&build.build).stage(0)) .run(move |_| dist::hash_and_sign(build)); rules.install("install-docs", "src/doc") @@ -861,8 +861,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { /// Helper to depend on a stage0 build-only rust-installer tool. fn tool_rust_installer<'a>(build: &'a Build, step: &Step<'a>) -> Step<'a> { step.name("tool-rust-installer") - .host(&build.config.build) - .target(&build.config.build) + .host(&build.build) + .target(&build.build) .stage(0) } } @@ -1058,8 +1058,8 @@ impl<'a> Rules<'a> { build: build, sbuild: Step { stage: build.flags.stage.unwrap_or(2), - target: &build.config.build, - host: &build.config.build, + target: &build.build, + host: &build.build, name: "", }, rules: BTreeMap::new(), @@ -1218,16 +1218,9 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? rules.into_iter().flat_map(|(rule, _)| { let hosts = if rule.only_host_build || rule.only_build { - &self.build.config.host[..1] - } else if self.build.flags.host.len() > 0 { - &self.build.flags.host + self.build.build_slice() } else { - &self.build.config.host - }; - let targets = if self.build.flags.target.len() > 0 { - &self.build.flags.target - } else { - &self.build.config.target + &self.build.hosts }; // Determine the actual targets participating in this rule. // NOTE: We should keep the full projection from build triple to @@ -1236,19 +1229,18 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? // the original non-shadowed hosts array is used below. let arr = if rule.host { // If --target was specified but --host wasn't specified, - // don't run any host-only tests. Also, respect any `--host` - // overrides as done for `hosts`. + // don't run any host-only tests. if self.build.flags.host.len() > 0 { - &self.build.flags.host[..] + &self.build.hosts } else if self.build.flags.target.len() > 0 { &[] } else if rule.only_build { - &self.build.config.host[..1] + self.build.build_slice() } else { - &self.build.config.host[..] + &self.build.hosts } } else { - targets + &self.build.targets }; hosts.iter().flat_map(move |host| { @@ -1326,7 +1318,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? for idx in 0..nodes.len() { self.topo_sort(idx, &idx_to_node, &edges, &mut visited, &mut order); } - return order + order } /// Builds the dependency graph rooted at `step`. @@ -1365,7 +1357,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? } edges.entry(idx).or_insert(HashSet::new()).extend(deps); - return idx + idx } /// Given a dependency graph with a finished list of `nodes`, fill out more @@ -1494,8 +1486,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(plan.contains(&step.name("dist-docs"))); @@ -1517,8 +1509,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(plan.contains(&step.name("dist-docs"))); @@ -1545,8 +1537,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(!plan.iter().any(|s| s.host == "B")); @@ -1575,8 +1567,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(!plan.iter().any(|s| s.host == "B")); @@ -1612,8 +1604,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(!plan.iter().any(|s| s.target == "A")); @@ -1639,8 +1631,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; assert!(!plan.iter().any(|s| s.target == "A")); @@ -1683,8 +1675,8 @@ mod tests { let step = super::Step { name: "", stage: 2, - host: &build.config.build, - target: &build.config.build, + host: &build.build, + target: &build.build, }; // rustc built for all for of (A, B) x (A, B) diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index 61bd85e76c5..092fb04637b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -14,7 +14,6 @@ //! not a lot of interesting happenings here unfortunately. use std::env; -use std::ffi::OsString; use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -32,16 +31,9 @@ pub fn staticlib(name: &str, target: &str) -> String { } } -/// Copies a file from `src` to `dst`, attempting to use hard links and then -/// falling back to an actually filesystem copy if necessary. +/// Copies a file from `src` to `dst` pub fn copy(src: &Path, dst: &Path) { - // A call to `hard_link` will fail if `dst` exists, so remove it if it - // already exists so we can try to help `hard_link` succeed. 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. - // let res = fs::hard_link(src, dst); let res = fs::copy(src, dst); if let Err(e) = res { panic!("failed to copy `{}` to `{}`: {}", src.display(), @@ -149,8 +141,7 @@ pub fn dylib_path_var() -> &'static str { /// Parses the `dylib_path_var()` environment variable, returning a list of /// paths that are members of this lookup path. pub fn dylib_path() -> Vec { - env::split_paths(&env::var_os(dylib_path_var()).unwrap_or(OsString::new())) - .collect() + env::split_paths(&env::var_os(dylib_path_var()).unwrap_or_default()).collect() } /// `push` all components to `buf`. On windows, append `.exe` to the last component. @@ -422,4 +413,4 @@ impl CiEnv { cmd.env("TERM", "xterm").args(&["--color", "always"]); } } -} \ No newline at end of file +} diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 79d1ccf637d..622cc68964b 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2008,10 +2008,10 @@ impl From> for String { } } -#[stable(feature = "box_from_str", since = "1.18.0")] -impl Into> for String { - fn into(self) -> Box { - self.into_boxed_str() +#[stable(feature = "box_from_str", since = "1.20.0")] +impl From for Box { + fn from(s: String) -> Box { + s.into_boxed_str() } } diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index fdf453b39cf..17f1229c206 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -274,6 +274,11 @@ fn test_dedup_by() { vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); assert_eq!(vec, ["foo", "bar", "baz", "bar"]); + + let mut vec = vec![("foo", 1), ("foo", 2), ("bar", 3), ("bar", 4), ("bar", 5)]; + vec.dedup_by(|a, b| a.0 == b.0 && { b.1 += a.1; true }); + + assert_eq!(vec, [("foo", 3), ("bar", 12)]); } #[test] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 5d1999a4262..1a5975686df 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -823,7 +823,8 @@ impl Vec { } } - /// Removes consecutive elements in the vector that resolve to the same key. + /// Removes all but the first of consecutive elements in the vector that resolve to the same + /// key. /// /// If the vector is sorted, this removes all duplicates. /// @@ -842,11 +843,13 @@ impl Vec { self.dedup_by(|a, b| key(a) == key(b)) } - /// Removes consecutive elements in the vector according to a predicate. + /// Removes all but the first of consecutive elements in the vector satisfying a given equality + /// relation. /// /// The `same_bucket` function is passed references to two elements from the vector, and - /// returns `true` if the elements compare equal, or `false` if they do not. Only the first - /// of adjacent equal items is kept. + /// returns `true` if the elements compare equal, or `false` if they do not. The elements are + /// passed in opposite order from their order in the vector, so if `same_bucket(a, b)` returns + /// `true`, `a` is removed. /// /// If the vector is sorted, this removes all duplicates. /// diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 7882a8ce5c8..f133bd93c91 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -343,7 +343,7 @@ impl Ordering { /// ``` #[derive(PartialEq, Eq, Debug)] #[stable(feature = "reverse_cmp_key", since = "1.19.0")] -pub struct Reverse(pub T); +pub struct Reverse(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T); #[stable(feature = "reverse_cmp_key", since = "1.19.0")] impl PartialOrd for Reverse { diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index 102e3c0bd7b..322df6e5b47 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -49,9 +49,37 @@ impl<'a, 'b: 'a> fmt::Write for PadAdapter<'a, 'b> { } } -/// A struct to help with `fmt::Debug` implementations. +/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. /// -/// Constructed by the `Formatter::debug_struct` method. +/// This is useful when you wish to output a formatted struct as a part of your +/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// +/// This can be constructed by the +/// [`Formatter::debug_struct`](struct.Formatter.html#method.debug_struct) +/// method. +/// +/// # Example +/// +/// ``` +/// use std::fmt; +/// +/// struct Foo { +/// bar: i32, +/// baz: String, +/// } +/// +/// impl fmt::Debug for Foo { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// fmt.debug_struct("Foo") +/// .field("bar", &self.bar) +/// .field("baz", &self.baz) +/// .finish() +/// } +/// } +/// +/// // prints "Foo { bar: 10, baz: "Hello World" }" +/// println!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }); +/// ``` #[must_use] #[allow(missing_debug_implementations)] #[stable(feature = "debug_builders", since = "1.2.0")] @@ -116,9 +144,34 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { } } -/// A struct to help with `fmt::Debug` implementations. +/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. /// -/// Constructed by the `Formatter::debug_tuple` method. +/// This is useful when you wish to output a formatted tuple as a part of your +/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// +/// This can be constructed by the +/// [`Formatter::debug_tuple`](struct.Formatter.html#method.debug_tuple) +/// method. +/// +/// # Example +/// +/// ``` +/// use std::fmt; +/// +/// struct Foo(i32, String); +/// +/// impl fmt::Debug for Foo { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// fmt.debug_tuple("Foo") +/// .field(&self.0) +/// .field(&self.1) +/// .finish() +/// } +/// } +/// +/// // prints "Foo(10, "Hello World")" +/// println!("{:?}", Foo(10, "Hello World".to_string())); +/// ``` #[must_use] #[allow(missing_debug_implementations)] #[stable(feature = "debug_builders", since = "1.2.0")] @@ -228,9 +281,31 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { } } -/// A struct to help with `fmt::Debug` implementations. +/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. /// -/// Constructed by the `Formatter::debug_set` method. +/// This is useful when you wish to output a formatted set of items as a part +/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// +/// This can be constructed by the +/// [`Formatter::debug_set`](struct.Formatter.html#method.debug_set) +/// method. +/// +/// # Example +/// +/// ``` +/// use std::fmt; +/// +/// struct Foo(Vec); +/// +/// impl fmt::Debug for Foo { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// fmt.debug_set().entries(self.0.iter()).finish() +/// } +/// } +/// +/// // prints "{10, 11}" +/// println!("{:?}", Foo(vec![10, 11])); +/// ``` #[must_use] #[allow(missing_debug_implementations)] #[stable(feature = "debug_builders", since = "1.2.0")] @@ -277,9 +352,31 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { } } -/// A struct to help with `fmt::Debug` implementations. +/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. /// -/// Constructed by the `Formatter::debug_list` method. +/// This is useful when you wish to output a formatted list of items as a part +/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// +/// This can be constructed by the +/// [`Formatter::debug_list`](struct.Formatter.html#method.debug_list) +/// method. +/// +/// # Example +/// +/// ``` +/// use std::fmt; +/// +/// struct Foo(Vec); +/// +/// impl fmt::Debug for Foo { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// fmt.debug_list().entries(self.0.iter()).finish() +/// } +/// } +/// +/// // prints "[10, 11]" +/// println!("{:?}", Foo(vec![10, 11])); +/// ``` #[must_use] #[allow(missing_debug_implementations)] #[stable(feature = "debug_builders", since = "1.2.0")] @@ -326,9 +423,31 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { } } -/// A struct to help with `fmt::Debug` implementations. +/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. /// -/// Constructed by the `Formatter::debug_map` method. +/// This is useful when you wish to output a formatted map as a part of your +/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// +/// This can be constructed by the +/// [`Formatter::debug_map`](struct.Formatter.html#method.debug_map) +/// method. +/// +/// # Example +/// +/// ``` +/// use std::fmt; +/// +/// struct Foo(Vec<(String, i32)>); +/// +/// impl fmt::Debug for Foo { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// fmt.debug_map().entries(self.0.iter().map(|&(ref k, ref v)| (k, v))).finish() +/// } +/// } +/// +/// // prints "{"A": 10, "B": 11}" +/// println!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])); +/// ``` #[must_use] #[allow(missing_debug_implementations)] #[stable(feature = "debug_builders", since = "1.2.0")] diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 1a91417ca0e..5f0b11a616e 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -585,11 +585,11 @@ impl From> for CString { } } -#[stable(feature = "box_from_c_string", since = "1.18.0")] -impl Into> for CString { +#[stable(feature = "box_from_c_string", since = "1.20.0")] +impl From for Box { #[inline] - fn into(self) -> Box { - self.into_boxed_c_str() + fn from(s: CString) -> Box { + s.into_boxed_c_str() } } diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 42d641706a8..02a13ed7a5a 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -540,10 +540,10 @@ impl From> for OsString { } } -#[stable(feature = "box_from_os_string", since = "1.18.0")] -impl Into> for OsString { - fn into(self) -> Box { - self.into_boxed_os_str() +#[stable(feature = "box_from_os_string", since = "1.20.0")] +impl From for Box { + fn from(s: OsString) -> Box { + s.into_boxed_os_str() } } diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 472ce6bc4fe..e7c7be981d2 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1348,10 +1348,10 @@ impl From> for PathBuf { } } -#[stable(feature = "box_from_path_buf", since = "1.18.0")] -impl Into> for PathBuf { - fn into(self) -> Box { - self.into_boxed_path() +#[stable(feature = "box_from_path_buf", since = "1.20.0")] +impl From for Box { + fn from(p: PathBuf) -> Box { + p.into_boxed_path() } } diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs index 48d9cdcb2c9..c5a19e8debe 100644 --- a/src/libstd/sys/redox/fs.rs +++ b/src/libstd/sys/redox/fs.rs @@ -420,12 +420,19 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result { - canonicalize(p) + let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_SYMLINK | syscall::O_RDONLY))?; + let mut buf: [u8; 4096] = [0; 4096]; + let count = cvt(syscall::read(fd, &mut buf))?; + cvt(syscall::close(fd))?; + Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) } -pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { - ::sys_common::util::dumb_print(format_args!("Symlink\n")); - unimplemented!(); +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let fd = cvt(syscall::open(dst.to_str().unwrap(), + syscall::O_SYMLINK | syscall::O_CREAT | syscall::O_WRONLY | 0o777))?; + cvt(syscall::write(fd, src.to_str().unwrap().as_bytes()))?; + cvt(syscall::close(fd))?; + Ok(()) } pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs index 9f0d3e6f779..bd603cfe6ef 100644 --- a/src/libstd/sys/redox/syscall/flag.rs +++ b/src/libstd/sys/redox/syscall/flag.rs @@ -33,6 +33,7 @@ pub const MAP_WRITE_COMBINE: usize = 2; pub const MODE_TYPE: u16 = 0xF000; pub const MODE_DIR: u16 = 0x4000; pub const MODE_FILE: u16 = 0x8000; +pub const MODE_SYMLINK: u16 = 0xA000; pub const MODE_PERM: u16 = 0x0FFF; pub const MODE_SETUID: u16 = 0o4000; @@ -53,6 +54,7 @@ pub const O_TRUNC: usize = 0x0400_0000; pub const O_EXCL: usize = 0x0800_0000; pub const O_DIRECTORY: usize = 0x1000_0000; pub const O_STAT: usize = 0x2000_0000; +pub const O_SYMLINK: usize = 0x4000_0000; pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR; pub const SEEK_SET: usize = 0;