96a6a34
From 9d8fc50c4a989c934f9473e28022b86a07556dca Mon Sep 17 00:00:00 2001
96a6a34
From: Josh Stone <jistone@redhat.com>
96a6a34
Date: Tue, 6 Jun 2017 12:59:04 -0700
96a6a34
Subject: [PATCH] rustbuild: Add `./x.py test --no-fail-fast`
96a6a34
96a6a34
This option forwards to each `cargo test` invocation, and applies the
96a6a34
same logic across all test steps to keep going after failures.  At the
96a6a34
end, a brief summary line reports how many commands failed, if any.
96a6a34
96a6a34
Note that if a test program fails to even start at all, or if an
96a6a34
auxiliary build command related to testing fails, these are still left
96a6a34
to stop everything right away.
96a6a34
96a6a34
Fixes #40219.
96a6a34
---
96a6a34
 src/bootstrap/check.rs  | 53 ++++++++++++++++++++++++++++++++++++++-----------
96a6a34
 src/bootstrap/flags.rs  | 14 ++++++++++++-
96a6a34
 src/bootstrap/lib.rs    | 22 +++++++++++++++++++-
96a6a34
 src/bootstrap/step.rs   | 12 +++++++++--
96a6a34
 src/build_helper/lib.rs | 38 ++++++++++++++++++++++++-----------
96a6a34
 5 files changed, 111 insertions(+), 28 deletions(-)
96a6a34
96a6a34
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
96a6a34
index f8f641060c44..f39a7ba61213 100644
96a6a34
--- a/src/bootstrap/check.rs
96a6a34
+++ b/src/bootstrap/check.rs
96a6a34
@@ -58,6 +58,29 @@ impl fmt::Display for TestKind {
96a6a34
     }
96a6a34
 }
96a6a34
 
96a6a34
+fn try_run(build: &Build, cmd: &mut Command) {
96a6a34
+    if build.flags.cmd.no_fail_fast() {
96a6a34
+        if !build.try_run(cmd) {
96a6a34
+            let failures = build.delayed_failures.get();
96a6a34
+            build.delayed_failures.set(failures + 1);
96a6a34
+        }
96a6a34
+    } else {
96a6a34
+        build.run(cmd);
96a6a34
+    }
96a6a34
+}
96a6a34
+
96a6a34
+#[allow(unused)]
96a6a34
+fn try_run_quiet(build: &Build, cmd: &mut Command) {
96a6a34
+    if build.flags.cmd.no_fail_fast() {
96a6a34
+        if !build.try_run_quiet(cmd) {
96a6a34
+            let failures = build.delayed_failures.get();
96a6a34
+            build.delayed_failures.set(failures + 1);
96a6a34
+        }
96a6a34
+    } else {
96a6a34
+        build.run_quiet(cmd);
96a6a34
+    }
96a6a34
+}
96a6a34
+
96a6a34
 /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
96a6a34
 ///
96a6a34
 /// This tool in `src/tools` will verify the validity of all our links in the
96a6a34
@@ -67,8 +90,8 @@ pub fn linkcheck(build: &Build, host: &str) {
96a6a34
     let compiler = Compiler::new(0, host);
96a6a34
 
96a6a34
     let _time = util::timeit();
96a6a34
-    build.run(build.tool_cmd(&compiler, "linkchecker")
96a6a34
-                   .arg(build.out.join(host).join("doc")));
96a6a34
+    try_run(build, build.tool_cmd(&compiler, "linkchecker")
96a6a34
+                        .arg(build.out.join(host).join("doc")));
96a6a34
 }
96a6a34
 
96a6a34
 /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.
96a6a34
@@ -95,9 +118,9 @@ pub fn cargotest(build: &Build, stage: u32, host: &str) {
96a6a34
     let _time = util::timeit();
96a6a34
     let mut cmd = Command::new(build.tool(&Compiler::new(0, host), "cargotest"));
96a6a34
     build.prepare_tool_cmd(compiler, &mut cmd);
96a6a34
-    build.run(cmd.env("PATH", newpath)
96a6a34
-                 .arg(&build.cargo)
96a6a34
-                 .arg(&out_dir));
96a6a34
+    try_run(build, cmd.env("PATH", newpath)
96a6a34
+                      .arg(&build.cargo)
96a6a34
+                      .arg(&out_dir));
96a6a34
 }
96a6a34
 
96a6a34
 /// Runs the `tidy` tool as compiled in `stage` by the `host` compiler.
96a6a34
@@ -113,7 +136,7 @@ pub fn tidy(build: &Build, host: &str) {
96a6a34
     if !build.config.vendor {
96a6a34
         cmd.arg("--no-vendor");
96a6a34
     }
96a6a34
-    build.run(&mut cmd);
96a6a34
+    try_run(build, &mut cmd);
96a6a34
 }
96a6a34
 
96a6a34
 fn testdir(build: &Build, host: &str) -> PathBuf {
96a6a34
@@ -261,7 +284,7 @@ pub fn compiletest(build: &Build,
96a6a34
     }
96a6a34
 
96a6a34
     let _time = util::timeit();
96a6a34
-    build.run(&mut cmd);
96a6a34
+    try_run(build, &mut cmd);
96a6a34
 }
96a6a34
 
96a6a34
 /// Run `rustdoc --test` for all documentation in `src/doc`.
96a6a34
@@ -337,7 +360,7 @@ fn markdown_test(build: &Build, compiler: &Compiler, markdown: &Path) {
96a6a34
     }
96a6a34
     cmd.arg("--test-args").arg(test_args);
96a6a34
 
96a6a34
-    build.run(&mut cmd);
96a6a34
+    try_run(build, &mut cmd);
96a6a34
 }
96a6a34
 
96a6a34
 /// Run all unit tests plus documentation tests for an entire crate DAG defined
96a6a34
@@ -388,6 +411,9 @@ pub fn krate(build: &Build,
96a6a34
     cargo.arg("--manifest-path")
96a6a34
          .arg(build.src.join(path).join("Cargo.toml"))
96a6a34
          .arg("--features").arg(features);
96a6a34
+    if test_kind.subcommand() == "test" && build.flags.cmd.no_fail_fast() {
96a6a34
+        cargo.arg("--no-fail-fast");
96a6a34
+    }
96a6a34
 
96a6a34
     match krate {
96a6a34
         Some(krate) => {
96a6a34
@@ -452,7 +478,7 @@ pub fn krate(build: &Build,
96a6a34
         krate_qemu(build, &compiler, target, mode);
96a6a34
     } else {
96a6a34
         cargo.args(&build.flags.cmd.test_args());
96a6a34
-        build.run(&mut cargo);
96a6a34
+        try_run(build, &mut cargo);
96a6a34
     }
96a6a34
 }
96a6a34
 
96a6a34
@@ -521,7 +547,7 @@ fn krate_emscripten(build: &Build,
96a6a34
         if build.config.quiet_tests {
96a6a34
             cmd.arg("--quiet");
96a6a34
         }
96a6a34
-        build.run(&mut cmd);
96a6a34
+        try_run(build, &mut cmd);
96a6a34
     }
96a6a34
 }
96a6a34
 
96a6a34
@@ -544,7 +570,7 @@ fn krate_qemu(build: &Build,
96a6a34
             cmd.arg("--quiet");
96a6a34
         }
96a6a34
         cmd.args(&build.flags.cmd.test_args());
96a6a34
-        build.run(&mut cmd);
96a6a34
+        try_run(build, &mut cmd);
96a6a34
     }
96a6a34
 }
96a6a34
 
96a6a34
@@ -671,6 +697,9 @@ pub fn bootstrap(build: &Build) {
96a6a34
        .current_dir(build.src.join("src/bootstrap"))
96a6a34
        .env("CARGO_TARGET_DIR", build.out.join("bootstrap"))
96a6a34
        .env("RUSTC", &build.rustc);
96a6a34
+    if build.flags.cmd.no_fail_fast() {
96a6a34
+        cmd.arg("--no-fail-fast");
96a6a34
+    }
96a6a34
     cmd.arg("--").args(&build.flags.cmd.test_args());
96a6a34
-    build.run(&mut cmd);
96a6a34
+    try_run(build, &mut cmd);
96a6a34
 }
96a6a34
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
96a6a34
index a1466d68a135..1ed759d24c54 100644
96a6a34
--- a/src/bootstrap/flags.rs
96a6a34
+++ b/src/bootstrap/flags.rs
96a6a34
@@ -61,6 +61,7 @@ pub enum Subcommand {
96a6a34
     Test {
96a6a34
         paths: Vec<PathBuf>,
96a6a34
         test_args: Vec<String>,
96a6a34
+        no_fail_fast: bool,
96a6a34
     },
96a6a34
     Bench {
96a6a34
         paths: Vec<PathBuf>,
96a6a34
@@ -137,7 +138,10 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
96a6a34
 
96a6a34
         // Some subcommands get extra options
96a6a34
         match subcommand.as_str() {
96a6a34
-            "test"  => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
96a6a34
+            "test"  => {
96a6a34
+                opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
96a6a34
+                opts.optmulti("", "test-args", "extra arguments", "ARGS");
96a6a34
+            },
96a6a34
             "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
96a6a34
             "dist"  => { opts.optflag("", "install", "run installer as well"); },
96a6a34
             _ => { },
96a6a34
@@ -260,6 +264,7 @@ Arguments:
96a6a34
                 Subcommand::Test {
96a6a34
                     paths: paths,
96a6a34
                     test_args: matches.opt_strs("test-args"),
96a6a34
+                    no_fail_fast: matches.opt_present("no-fail-fast"),
96a6a34
                 }
96a6a34
             }
96a6a34
             "bench" => {
96a6a34
@@ -335,6 +340,13 @@ impl Subcommand {
96a6a34
             _ => Vec::new(),
96a6a34
         }
96a6a34
     }
96a6a34
+
96a6a34
+    pub fn no_fail_fast(&self) -> bool {
96a6a34
+        match *self {
96a6a34
+            Subcommand::Test { no_fail_fast, .. } => no_fail_fast,
96a6a34
+            _ => false,
96a6a34
+        }
96a6a34
+    }
96a6a34
 }
96a6a34
 
96a6a34
 fn split(s: Vec<String>) -> Vec<String> {
96a6a34
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
96a6a34
index 5e046f41673e..d2303c96d201 100644
96a6a34
--- a/src/bootstrap/lib.rs
96a6a34
+++ b/src/bootstrap/lib.rs
96a6a34
@@ -76,6 +76,7 @@ extern crate num_cpus;
96a6a34
 extern crate rustc_serialize;
96a6a34
 extern crate toml;
96a6a34
 
96a6a34
+use std::cell::Cell;
96a6a34
 use std::cmp;
96a6a34
 use std::collections::HashMap;
96a6a34
 use std::env;
96a6a34
@@ -85,7 +86,7 @@ use std::io::Read;
96a6a34
 use std::path::{Component, PathBuf, Path};
96a6a34
 use std::process::Command;
96a6a34
 
96a6a34
-use build_helper::{run_silent, run_suppressed, output, mtime};
96a6a34
+use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
96a6a34
 
96a6a34
 use util::{exe, libdir, add_lib_path};
96a6a34
 
96a6a34
@@ -164,6 +165,7 @@ pub struct Build {
96a6a34
     crates: HashMap<String, Crate>,
96a6a34
     is_sudo: bool,
96a6a34
     src_is_git: bool,
96a6a34
+    delayed_failures: Cell<usize>,
96a6a34
 }
96a6a34
 
96a6a34
 #[derive(Debug)]
96a6a34
@@ -257,6 +259,7 @@ impl Build {
96a6a34
             lldb_python_dir: None,
96a6a34
             is_sudo: is_sudo,
96a6a34
             src_is_git: src_is_git,
96a6a34
+            delayed_failures: Cell::new(0),
96a6a34
         }
96a6a34
     }
96a6a34
 
96a6a34
@@ -847,6 +850,23 @@ impl Build {
96a6a34
         run_suppressed(cmd)
96a6a34
     }
96a6a34
 
96a6a34
+    /// Runs a command, printing out nice contextual information if it fails.
96a6a34
+    /// Exits if the command failed to execute at all, otherwise returns its
96a6a34
+    /// `status.success()`.
96a6a34
+    fn try_run(&self, cmd: &mut Command) -> bool {
96a6a34
+        self.verbose(&format!("running: {:?}", cmd));
96a6a34
+        try_run_silent(cmd)
96a6a34
+    }
96a6a34
+
96a6a34
+    /// Runs a command, printing out nice contextual information if it fails.
96a6a34
+    /// Exits if the command failed to execute at all, otherwise returns its
96a6a34
+    /// `status.success()`.
96a6a34
+    #[allow(unused)]
96a6a34
+    fn try_run_quiet(&self, cmd: &mut Command) -> bool {
96a6a34
+        self.verbose(&format!("running: {:?}", cmd));
96a6a34
+        try_run_suppressed(cmd)
96a6a34
+    }
96a6a34
+
96a6a34
     /// Prints a message if this build is configured in verbose mode.
96a6a34
     fn verbose(&self, msg: &str) {
96a6a34
         if self.flags.verbose() || self.config.verbose() {
96a6a34
diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs
96a6a34
index 17902a39df1e..f0522035127c 100644
96a6a34
--- a/src/bootstrap/step.rs
96a6a34
+++ b/src/bootstrap/step.rs
96a6a34
@@ -28,6 +28,7 @@
96a6a34
 
96a6a34
 use std::collections::{BTreeMap, HashSet, HashMap};
96a6a34
 use std::mem;
96a6a34
+use std::process;
96a6a34
 
96a6a34
 use check::{self, TestKind};
96a6a34
 use compile;
96a6a34
@@ -1092,8 +1093,8 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
96a6a34
         let (kind, paths) = match self.build.flags.cmd {
96a6a34
             Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
96a6a34
             Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]),
96a6a34
-            Subcommand::Test { ref paths, test_args: _ } => (Kind::Test, &paths[..]),
96a6a34
-            Subcommand::Bench { ref paths, test_args: _ } => (Kind::Bench, &paths[..]),
96a6a34
+            Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]),
96a6a34
+            Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
96a6a34
             Subcommand::Dist { ref paths, install } => {
96a6a34
                 if install {
96a6a34
                     return vec![self.sbuild.name("install")]
96a6a34
@@ -1191,6 +1192,13 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
96a6a34
             self.build.verbose(&format!("executing step {:?}", step));
96a6a34
             (self.rules[step.name].run)(step);
96a6a34
         }
96a6a34
+
96a6a34
+        // Check for postponed failures from `test --no-fail-fast`.
96a6a34
+        let failures = self.build.delayed_failures.get();
96a6a34
+        if failures > 0 {
96a6a34
+            println!("\n{} command(s) did not execute successfully.\n", failures);
96a6a34
+            process::exit(1);
96a6a34
+        }
96a6a34
     }
96a6a34
 
96a6a34
     /// From the top level targets `steps` generate a topological ordering of
96a6a34
diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs
96a6a34
index cb58a916fb79..10b0f19d6274 100644
96a6a34
--- a/src/build_helper/lib.rs
96a6a34
+++ b/src/build_helper/lib.rs
96a6a34
@@ -42,35 +42,49 @@ pub fn run(cmd: &mut Command) {
96a6a34
 }
96a6a34
 
96a6a34
 pub fn run_silent(cmd: &mut Command) {
96a6a34
+    if !try_run_silent(cmd) {
96a6a34
+        std::process::exit(1);
96a6a34
+    }
96a6a34
+}
96a6a34
+
96a6a34
+pub fn try_run_silent(cmd: &mut Command) -> bool {
96a6a34
     let status = match cmd.status() {
96a6a34
         Ok(status) => status,
96a6a34
         Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
96a6a34
                                 cmd, e)),
96a6a34
     };
96a6a34
     if !status.success() {
96a6a34
-        fail(&format!("command did not execute successfully: {:?}\n\
96a6a34
-                       expected success, got: {}",
96a6a34
-                      cmd,
96a6a34
-                      status));
96a6a34
+        println!("\n\ncommand did not execute successfully: {:?}\n\
96a6a34
+                  expected success, got: {}\n\n",
96a6a34
+                 cmd,
96a6a34
+                 status);
96a6a34
     }
96a6a34
+    status.success()
96a6a34
 }
96a6a34
 
96a6a34
 pub fn run_suppressed(cmd: &mut Command) {
96a6a34
+    if !try_run_suppressed(cmd) {
96a6a34
+        std::process::exit(1);
96a6a34
+    }
96a6a34
+}
96a6a34
+
96a6a34
+pub fn try_run_suppressed(cmd: &mut Command) -> bool {
96a6a34
     let output = match cmd.output() {
96a6a34
         Ok(status) => status,
96a6a34
         Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
96a6a34
                                 cmd, e)),
96a6a34
     };
96a6a34
     if !output.status.success() {
96a6a34
-        fail(&format!("command did not execute successfully: {:?}\n\
96a6a34
-                       expected success, got: {}\n\n\
96a6a34
-                       stdout ----\n{}\n\
96a6a34
-                       stderr ----\n{}\n",
96a6a34
-                      cmd,
96a6a34
-                      output.status,
96a6a34
-                      String::from_utf8_lossy(&output.stdout),
96a6a34
-                      String::from_utf8_lossy(&output.stderr)));
96a6a34
+        println!("\n\ncommand did not execute successfully: {:?}\n\
96a6a34
+                  expected success, got: {}\n\n\
96a6a34
+                  stdout ----\n{}\n\
96a6a34
+                  stderr ----\n{}\n\n",
96a6a34
+                 cmd,
96a6a34
+                 output.status,
96a6a34
+                 String::from_utf8_lossy(&output.stdout),
96a6a34
+                 String::from_utf8_lossy(&output.stderr));
96a6a34
     }
96a6a34
+    output.status.success()
96a6a34
 }
96a6a34
 
96a6a34
 pub fn gnu_target(target: &str) -> String {
96a6a34
-- 
96a6a34
2.13.0
96a6a34