diff --git a/.travis.yml b/.travis.yml index ea8c94af80b..dd5fe7cd043 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ matrix: - env: IMAGE=i686-gnu-nopt - env: IMAGE=x86_64-freebsd - env: IMAGE=x86_64-gnu + - env: IMAGE=x86_64-gnu-full-bootstrap - env: IMAGE=x86_64-gnu-cargotest - env: IMAGE=x86_64-gnu-debug - env: IMAGE=x86_64-gnu-nopt diff --git a/configure b/configure index 4f1e8f656ae..5094e5a764b 100755 --- a/configure +++ b/configure @@ -693,6 +693,7 @@ opt_nosave manage-submodules 1 "let the build manage the git submodules" opt_nosave clang 0 "prefer clang to gcc for building the runtime" opt_nosave jemalloc 1 "build liballoc with jemalloc" opt elf-tls 1 "elf thread local storage on platforms where supported" +opt full-bootstrap 0 "build three compilers instead of two" valopt_nosave prefix "/usr/local" "set installation prefix" valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary" diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index e7b0afeb8ce..ec0243908ed 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -341,12 +341,22 @@ pub fn krate(build: &Build, println!("{} {} stage{} ({} -> {})", test_kind, name, compiler.stage, compiler.host, target); + // If we're not doing a full bootstrap but we're testing a stage2 version of + // libstd, then what we're actually testing is the libstd produced in + // stage1. Reflect that here by updating the compiler that we're working + // with automatically. + let compiler = if build.force_use_stage1(compiler, target) { + Compiler::new(1, compiler.host) + } else { + compiler.clone() + }; + // Build up the base `cargo test` command. // // Pass in some standard flags then iterate over the graph we've discovered // in `cargo metadata` with the maps above and figure out what `-p` // arguments need to get passed. - let mut cargo = build.cargo(compiler, mode, target, test_kind.subcommand()); + let mut cargo = build.cargo(&compiler, mode, target, test_kind.subcommand()); cargo.arg("--manifest-path") .arg(build.src.join(path).join("Cargo.toml")) .arg("--features").arg(features); @@ -380,7 +390,7 @@ pub fn krate(build: &Build, // Note that to run the compiler we need to run with the *host* libraries, // but our wrapper scripts arrange for that to be the case anyway. let mut dylib_path = dylib_path(); - dylib_path.insert(0, build.sysroot_libdir(compiler, target)); + dylib_path.insert(0, build.sysroot_libdir(&compiler, target)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); if target.contains("android") { @@ -399,10 +409,10 @@ pub fn krate(build: &Build, if target.contains("android") { build.run(&mut cargo); - krate_android(build, compiler, target, mode); + krate_android(build, &compiler, target, mode); } else if target.contains("emscripten") { build.run(&mut cargo); - krate_emscripten(build, compiler, target, mode); + krate_emscripten(build, &compiler, target, mode); } else { cargo.args(&build.flags.cmd.test_args()); build.run(&mut cargo); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index b268686ca6c..24e29225c6e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -33,14 +33,14 @@ use {Build, Compiler, Mode}; /// This will build the standard library for a particular stage of the build /// using the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. -pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { - println!("Building stage{} std artifacts ({} -> {})", compiler.stage, - compiler.host, target); - +pub fn std(build: &Build, target: &str, compiler: &Compiler) { let libdir = build.sysroot_libdir(compiler, target); let _ = fs::remove_dir_all(&libdir); t!(fs::create_dir_all(&libdir)); + println!("Building stage{} std artifacts ({} -> {})", compiler.stage, + compiler.host, target); + // Some platforms have startup objects that may be required to produce the // libstd dynamic library, for example. build_startup_objects(build, target, &libdir); @@ -65,29 +65,30 @@ pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { build.run(&mut cargo); update_mtime(&libstd_stamp(build, &compiler, target)); - std_link(build, target, compiler.stage, compiler.host); } /// Link all libstd rlibs/dylibs into the sysroot location. /// -/// Links those artifacts generated in the given `stage` for `target` produced -/// by `compiler` into `host`'s sysroot. +/// Links those artifacts generated by `compiler` to a the `stage` compiler's +/// sysroot for the specified `host` and `target`. +/// +/// Note that this assumes that `compiler` has already generated the libstd +/// libraries for `target`, and this method will find them in the relevant +/// output directory. pub fn std_link(build: &Build, - target: &str, - stage: u32, - host: &str) { - let compiler = Compiler::new(stage, &build.config.build); - let target_compiler = Compiler::new(compiler.stage, host); + compiler: &Compiler, + target_compiler: &Compiler, + target: &str) { + println!("Copying stage{} std from stage{} ({} -> {} / {})", + target_compiler.stage, + compiler.stage, + compiler.host, + target_compiler.host, + target); let libdir = build.sysroot_libdir(&target_compiler, target); let out_dir = build.cargo_out(&compiler, Mode::Libstd, target); - // If we're linking one compiler host's output into another, then we weren't - // called from the `std` method above. In that case we clean out what's - // already there. - if host != compiler.host { - let _ = fs::remove_dir_all(&libdir); - t!(fs::create_dir_all(&libdir)); - } + t!(fs::create_dir_all(&libdir)); add_to_sysroot(&out_dir, &libdir); if target.contains("musl") && !target.contains("mips") { @@ -137,7 +138,7 @@ fn build_startup_objects(build: &Build, target: &str, into: &Path) { /// This will build libtest and supporting libraries for a particular stage of /// the build using the `compiler` targeting the `target` architecture. The /// artifacts created will also be linked into the sysroot directory. -pub fn test<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { +pub fn test(build: &Build, target: &str, compiler: &Compiler) { println!("Building stage{} test artifacts ({} -> {})", compiler.stage, compiler.host, target); let out_dir = build.cargo_out(compiler, Mode::Libtest, target); @@ -147,19 +148,13 @@ pub fn test<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { .arg(build.src.join("src/rustc/test_shim/Cargo.toml")); build.run(&mut cargo); update_mtime(&libtest_stamp(build, compiler, target)); - test_link(build, target, compiler.stage, compiler.host); } -/// Link all libtest rlibs/dylibs into the sysroot location. -/// -/// Links those artifacts generated in the given `stage` for `target` produced -/// by `compiler` into `host`'s sysroot. +/// Same as `std_link`, only for libtest pub fn test_link(build: &Build, - target: &str, - stage: u32, - host: &str) { - let compiler = Compiler::new(stage, &build.config.build); - let target_compiler = Compiler::new(compiler.stage, host); + compiler: &Compiler, + target_compiler: &Compiler, + target: &str) { let libdir = build.sysroot_libdir(&target_compiler, target); let out_dir = build.cargo_out(&compiler, Mode::Libtest, target); add_to_sysroot(&out_dir, &libdir); @@ -170,7 +165,7 @@ pub fn test_link(build: &Build, /// This will build the compiler for a particular stage of the build using /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. -pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { +pub fn rustc(build: &Build, target: &str, compiler: &Compiler) { println!("Building stage{} compiler artifacts ({} -> {})", compiler.stage, compiler.host, target); @@ -222,20 +217,13 @@ pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { cargo.env("CFG_DEFAULT_AR", s); } build.run(&mut cargo); - - rustc_link(build, target, compiler.stage, compiler.host); } -/// Link all librustc rlibs/dylibs into the sysroot location. -/// -/// Links those artifacts generated in the given `stage` for `target` produced -/// by `compiler` into `host`'s sysroot. +/// Same as `std_link`, only for librustc pub fn rustc_link(build: &Build, - target: &str, - stage: u32, - host: &str) { - let compiler = Compiler::new(stage, &build.config.build); - let target_compiler = Compiler::new(compiler.stage, host); + compiler: &Compiler, + target_compiler: &Compiler, + target: &str) { let libdir = build.sysroot_libdir(&target_compiler, target); let out_dir = build.cargo_out(&compiler, Mode::Librustc, target); add_to_sysroot(&out_dir, &libdir); @@ -259,6 +247,17 @@ fn compiler_file(compiler: &Path, file: &str) -> PathBuf { PathBuf::from(out.trim()) } +pub fn create_sysroot(build: &Build, compiler: &Compiler) { + // nothing to do in stage0 + if compiler.stage == 0 { + return + } + + let sysroot = build.sysroot(compiler); + let _ = fs::remove_dir_all(&sysroot); + t!(fs::create_dir_all(&sysroot)); +} + /// Prepare a new compiler from the artifacts in `stage` /// /// This will assemble a compiler in `build/$host/stage$stage`. The compiler @@ -269,18 +268,17 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) { if stage == 0 { return } + + println!("Copying stage{} compiler ({})", stage, host); + // The compiler that we're assembling 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); - // Clear out old files - let sysroot = build.sysroot(&target_compiler); - let _ = fs::remove_dir_all(&sysroot); - t!(fs::create_dir_all(&sysroot)); - // Link in all dylibs to the libdir + let sysroot = build.sysroot(&target_compiler); let sysroot_libdir = sysroot.join(libdir(host)); t!(fs::create_dir_all(&sysroot_libdir)); let src_libdir = build.sysroot_libdir(&build_compiler, host); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 6b86e537b7d..93fe39ab41b 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -46,6 +46,7 @@ pub struct Config { pub docs: bool, pub vendor: bool, pub target_config: HashMap, + pub full_bootstrap: bool, // llvm codegen options pub llvm_assertions: bool, @@ -134,6 +135,7 @@ struct Build { vendor: Option, nodejs: Option, python: Option, + full_bootstrap: Option, } /// TOML representation of various global install decisions. @@ -264,6 +266,7 @@ impl Config { set(&mut config.docs, build.docs); set(&mut config.submodules, build.submodules); set(&mut config.vendor, build.vendor); + set(&mut config.full_bootstrap, build.full_bootstrap); if let Some(ref install) = toml.install { config.prefix = install.prefix.clone(); @@ -393,6 +396,7 @@ impl Config { ("NINJA", self.ninja), ("CODEGEN_TESTS", self.codegen_tests), ("VENDOR", self.vendor), + ("FULL_BOOTSTRAP", self.full_bootstrap), } match key { diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example index 5fc095137c7..7c8d3870e92 100644 --- a/src/bootstrap/config.toml.example +++ b/src/bootstrap/config.toml.example @@ -100,6 +100,13 @@ # Indicate whether the vendored sources are used for Rust dependencies or not #vendor = false +# Typically the build system will build the rust compiler twice. The second +# compiler, however, will simply use its own libraries to link against. If you +# would rather to perform a full bootstrap, compiling the compiler three times, +# then you can set this option to true. You shouldn't ever need to set this +# option to true. +#full-bootstrap = false + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 30c7fefad87..4c4462bf122 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -137,6 +137,11 @@ pub fn std(build: &Build, stage: u32, target: &str) { let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = Compiler::new(stage, &build.config.build); + let compiler = if build.force_use_stage1(&compiler, target) { + Compiler::new(1, compiler.host) + } else { + compiler + }; let out_dir = build.stage_out(&compiler, Mode::Libstd) .join(target).join("doc"); let rustdoc = build.rustdoc(&compiler); @@ -160,6 +165,11 @@ pub fn test(build: &Build, stage: u32, target: &str) { let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = Compiler::new(stage, &build.config.build); + let compiler = if build.force_use_stage1(&compiler, target) { + Compiler::new(1, compiler.host) + } else { + compiler + }; let out_dir = build.stage_out(&compiler, Mode::Libtest) .join(target).join("doc"); let rustdoc = build.rustdoc(&compiler); @@ -182,6 +192,11 @@ pub fn rustc(build: &Build, stage: u32, target: &str) { let out = build.doc_out(target); t!(fs::create_dir_all(&out)); let compiler = Compiler::new(stage, &build.config.build); + let compiler = if build.force_use_stage1(&compiler, target) { + Compiler::new(1, compiler.host) + } else { + compiler + }; let out_dir = build.stage_out(&compiler, Mode::Librustc) .join(target).join("doc"); let rustdoc = build.rustdoc(&compiler); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 665e0c67b7f..49eaed4c67a 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -572,9 +572,7 @@ impl Build { let mut cmd = Command::new(self.tool(&compiler, tool)); let host = compiler.host; let mut paths = vec![ - self.cargo_out(compiler, Mode::Libstd, host).join("deps"), - self.cargo_out(compiler, Mode::Libtest, host).join("deps"), - self.cargo_out(compiler, Mode::Librustc, host).join("deps"), + self.sysroot_libdir(compiler, compiler.host), self.cargo_out(compiler, Mode::Tool, host).join("deps"), ]; @@ -880,6 +878,30 @@ impl Build { fn python(&self) -> &Path { self.config.python.as_ref().unwrap() } + + /// Tests whether the `compiler` compiling for `target` should be forced to + /// use a stage1 compiler instead. + /// + /// Currently, by default, the build system does not perform a "full + /// bootstrap" by default where we compile the compiler three times. + /// Instead, we compile the compiler two times. The final stage (stage2) + /// just copies the libraries from the previous stage, which is what this + /// method detects. + /// + /// Here we return `true` if: + /// + /// * The build isn't performing a full bootstrap + /// * The `compiler` is in the final stage, 2 + /// * We're not cross-compiling, so the artifacts are already available in + /// stage1 + /// + /// When all of these conditions are met the build will lift artifacts from + /// the previous stage forward. + fn force_use_stage1(&self, compiler: &Compiler, target: &str) -> bool { + !self.config.full_bootstrap && + compiler.stage >= 2 && + self.config.host.iter().any(|h| h == target) + } } impl<'a> Compiler<'a> { diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs index c5898c1119a..ad188897387 100644 --- a/src/bootstrap/step.rs +++ b/src/bootstrap/step.rs @@ -44,7 +44,7 @@ pub fn run(build: &Build) { rules.run(&steps); } -pub fn build_rules(build: &Build) -> Rules { +pub fn build_rules<'a>(build: &'a Build) -> Rules { let mut rules = Rules::new(build); // This is the first rule that we're going to define for rustbuild, which is @@ -117,6 +117,7 @@ pub fn build_rules(build: &Build) -> Rules { // the compiler with no target libraries ready to go rules.build("rustc", "src/rustc") + .dep(|s| s.name("create-sysroot").target(s.host)) .dep(move |s| { if s.stage == 0 { Step::noop() @@ -151,69 +152,131 @@ pub fn build_rules(build: &Build) -> Rules { // Crate compilations // // Tools used during the build system but not shipped + rules.build("create-sysroot", "path/to/nowhere") + .run(move |s| compile::create_sysroot(build, &s.compiler())); + + // These rules are "pseudo rules" that don't actually do any work + // themselves, but represent a complete sysroot with the relevant compiler + // linked into place. + // + // That is, depending on "libstd" means that when the rule is completed then + // the `stage` sysroot for the compiler `host` will be available with a + // standard library built for `target` linked in place. Not all rules need + // the compiler itself to be available, just the standard library, so + // there's a distinction between the two. rules.build("libstd", "src/libstd") - .dep(|s| s.name("build-crate-std_shim")); + .dep(|s| s.name("rustc").target(s.host)) + .dep(|s| s.name("libstd-link")); rules.build("libtest", "src/libtest") - .dep(|s| s.name("build-crate-test_shim")); + .dep(|s| s.name("libstd")) + .dep(|s| s.name("libtest-link")) + .default(true); rules.build("librustc", "src/librustc") - .dep(|s| s.name("build-crate-rustc-main")); + .dep(|s| s.name("libtest")) + .dep(|s| s.name("librustc-link")) + .host(true) + .default(true); + + // Helper method to define the rules to link a crate into its place in the + // sysroot. + // + // The logic here is a little subtle as there's a few cases to consider. + // Not all combinations of (stage, host, target) actually require something + // to be compiled, but rather libraries could get propagated from a + // different location. For example: + // + // * Any crate with a `host` that's not the build triple will not actually + // compile something. A different `host` means that the build triple will + // actually compile the libraries, and then we'll copy them over from the + // build triple to the `host` directory. + // + // * Some crates aren't even compiled by the build triple, but may be copied + // from previous stages. For example if we're not doing a full bootstrap + // then we may just depend on the stage1 versions of libraries to be + // available to get linked forward. + // + // * Finally, there are some cases, however, which do indeed comiple crates + // and link them into place afterwards. + // + // The rule definition below mirrors these three cases. The `dep` method + // calculates the correct dependency which either comes from stage1, a + // different compiler, or from actually building the crate itself (the `dep` + // rule). The `run` rule then mirrors these three cases and links the cases + // forward into the compiler sysroot specified from the correct location. + fn crate_rule<'a, 'b>(build: &'a Build, + rules: &'b mut Rules<'a>, + krate: &'a str, + dep: &'a str, + link: fn(&Build, &Compiler, &Compiler, &str)) + -> RuleBuilder<'a, 'b> { + 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.name(dep) + } else { + s.host(&build.config.build) + } + }) + .run(move |s| { + if build.force_use_stage1(&s.compiler(), s.target) { + link(build, + &s.stage(1).host(&build.config.build).compiler(), + &s.compiler(), + s.target) + } else if s.host == build.config.build { + link(build, &s.compiler(), &s.compiler(), s.target) + } else { + link(build, + &s.host(&build.config.build).compiler(), + &s.compiler(), + s.target) + } + }); + return rule + } + + // Similar to the `libstd`, `libtest`, and `librustc` rules above, except + // these rules only represent the libraries being available in the sysroot, + // not the compiler itself. This is done as not all rules need a compiler in + // the sysroot, but may just need the libraries. + // + // All of these rules use the helper definition above. + crate_rule(build, + &mut rules, + "libstd-link", + "build-crate-std_shim", + compile::std_link) + .dep(|s| s.name("create-sysroot").target(s.host)); + crate_rule(build, + &mut rules, + "libtest-link", + "build-crate-test_shim", + compile::test_link) + .dep(|s| s.name("libstd-link")); + crate_rule(build, + &mut rules, + "librustc-link", + "build-crate-rustc-main", + compile::rustc_link) + .dep(|s| s.name("libtest-link")); + for (krate, path, _default) in krates("std_shim") { rules.build(&krate.build_step, path) .dep(move |s| s.name("rustc").host(&build.config.build).target(s.host)) - .dep(move |s| { - if s.host == build.config.build { - Step::noop() - } else { - s.host(&build.config.build) - } - }) - .run(move |s| { - if s.host == build.config.build { - compile::std(build, s.target, &s.compiler()) - } else { - compile::std_link(build, s.target, s.stage, s.host) - } - }); + .run(move |s| compile::std(build, s.target, &s.compiler())); } - for (krate, path, default) in krates("test_shim") { + for (krate, path, _default) in krates("test_shim") { rules.build(&krate.build_step, path) - .dep(|s| s.name("libstd")) - .dep(move |s| { - if s.host == build.config.build { - Step::noop() - } else { - s.host(&build.config.build) - } - }) - .default(default) - .run(move |s| { - if s.host == build.config.build { - compile::test(build, s.target, &s.compiler()) - } else { - compile::test_link(build, s.target, s.stage, s.host) - } - }); + .dep(|s| s.name("libstd-link")) + .run(move |s| compile::test(build, s.target, &s.compiler())); } - for (krate, path, default) in krates("rustc-main") { + for (krate, path, _default) in krates("rustc-main") { rules.build(&krate.build_step, path) - .dep(|s| s.name("libtest")) + .dep(|s| s.name("libtest-link")) .dep(move |s| s.name("llvm").host(&build.config.build).stage(0)) - .dep(move |s| { - if s.host == build.config.build { - Step::noop() - } else { - s.host(&build.config.build) - } - }) - .host(true) - .default(default) - .run(move |s| { - if s.host == build.config.build { - compile::rustc(build, s.target, &s.compiler()) - } else { - compile::rustc_link(build, s.target, s.stage, s.host) - } - }); + .run(move |s| compile::rustc(build, s.target, &s.compiler())); } // ======================================================================== @@ -444,25 +507,25 @@ pub fn build_rules(build: &Build) -> Rules { .run(move |s| doc::standalone(build, s.stage, s.target)); rules.doc("doc-error-index", "src/tools/error_index_generator") .dep(move |s| s.name("tool-error-index").target(&build.config.build)) - .dep(move |s| s.name("librustc")) + .dep(move |s| s.name("librustc-link")) .default(build.config.docs) .host(true) .run(move |s| doc::error_index(build, s.stage, s.target)); for (krate, path, default) in krates("std_shim") { rules.doc(&krate.doc_step, path) - .dep(|s| s.name("libstd")) + .dep(|s| s.name("libstd-link")) .default(default && build.config.docs) .run(move |s| doc::std(build, s.stage, s.target)); } for (krate, path, default) in krates("test_shim") { rules.doc(&krate.doc_step, path) - .dep(|s| s.name("libtest")) + .dep(|s| s.name("libtest-link")) .default(default && build.config.compiler_docs) .run(move |s| doc::test(build, s.stage, s.target)); } for (krate, path, default) in krates("rustc-main") { rules.doc(&krate.doc_step, path) - .dep(|s| s.name("librustc")) + .dep(|s| s.name("librustc-link")) .host(true) .default(default && build.config.compiler_docs) .run(move |s| doc::rustc(build, s.stage, s.target)); @@ -481,9 +544,9 @@ pub fn build_rules(build: &Build) -> Rules { // for the `rust-std` package, so if this is a host target we // depend on librustc and otherwise we just depend on libtest. if build.config.host.iter().any(|t| t == s.target) { - s.name("librustc") + s.name("librustc-link") } else { - s.name("libtest") + s.name("libtest-link") } }) .default(true) diff --git a/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile new file mode 100644 index 00000000000..7c079e45751 --- /dev/null +++ b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python2.7 \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] + +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --enable-full-bootstrap +ENV RUST_CHECK_TARGET ""