diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 46d05b9d5d2f7..19db553021cae 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -20,6 +20,7 @@ use serde_derive::Deserialize; use tracing::span; use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair}; +use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; use crate::core::builder; @@ -1027,7 +1028,7 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { // If only `compiler` was passed, do not run this step. // Instead the `Assemble` step will take care of compiling Rustc. - if run.builder.paths == vec![PathBuf::from("compiler")] { + if run.builder.paths(IsForRerunningTests::DontCare) == vec![PathBuf::from("compiler")] { return; } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 28a7afd6c61a6..bc134fe6bc69a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -24,6 +24,7 @@ use crate::core::build_steps::compile::{ }; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::GccTargetPair; +use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, ToolTargetBuildMode, get_tool_target_compiler, }; @@ -2650,7 +2651,7 @@ impl Step for LlvmTools { // Prepare the image directory let src_bindir = builder.llvm_out(target).join("bin"); let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); - for tool in tools_to_install(&builder.paths) { + for tool in tools_to_install(builder.paths(IsForRerunningTests::DontCare)) { let exe = src_bindir.join(exe(tool, target)); // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. if !exe.exists() && builder.config.llvm_from_ci { diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index a918ae929d2e0..ddd66687829b8 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -12,6 +12,7 @@ use std::path::{Path, PathBuf}; use std::{env, fs, mem}; use crate::core::build_steps::compile; +use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; @@ -436,7 +437,9 @@ impl Step for Standalone { // We open doc/index.html as the default if invoked as `x.py doc --open` // with no particular explicit doc requested (e.g. library/core). - if builder.paths.is_empty() || builder.was_invoked_explicitly::(Kind::Doc) { + if builder.paths(IsForRerunningTests::DontCare).is_empty() + || builder.was_invoked_explicitly::(Kind::Doc) + { let index = out.join("index.html"); builder.open_in_browser(index); } @@ -720,7 +723,11 @@ impl Step for Std { // Open if the format is HTML if let DocumentationFormat::Html = self.format { - if builder.paths.iter().any(|path| path.ends_with("library")) { + if builder + .paths(IsForRerunningTests::DontCare) + .iter() + .any(|path| path.ends_with("library")) + { // For `x.py doc library --open`, open `std` by default. let index = out.join("std").join("index.html"); builder.maybe_open_in_browser::(index); @@ -991,7 +998,11 @@ impl Step for Rustc { } } - if builder.paths.iter().any(|path| path.ends_with("compiler")) { + if builder + .paths(IsForRerunningTests::DontCare) + .iter() + .any(|path| path.ends_with("compiler")) + { // For `x.py doc compiler --open`, open `rustc_middle` by default. let index = out.join("rustc_middle").join("index.html"); builder.open_in_browser(index); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c222aa2305647..bf54b2842df87 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -21,6 +21,9 @@ use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::test::compiletest::CompiletestMode; +use crate::core::build_steps::test::failed_tests::{ + IsForRerunningTests, RecordFailedTests, SetupFailedTestsFile, +}; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler, @@ -45,12 +48,14 @@ use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; use crate::{CLang, CodegenBackendKind, GitRepo, Mode, PathSet, TestTarget, envify}; mod compiletest; +pub mod failed_tests; /// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { path: PathBuf, host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateBootstrap { @@ -79,13 +84,18 @@ impl Step for CrateBootstrap { // that was selected on the command-line (or selected by default). for path in run.paths { let path = path.assert_single_path().path.clone(); - run.builder.ensure(CrateBootstrap { host: run.target, path }); + run.builder.ensure(CrateBootstrap { + host: run.target, + path, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } } fn run(self, builder: &Builder<'_>) { let bootstrap_host = builder.config.host_target; let compiler = builder.compiler(0, bootstrap_host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut path = self.path.to_str().unwrap(); // Map alias `tidyselftest` back to the actual crate path of tidy. @@ -105,7 +115,7 @@ impl Step for CrateBootstrap { ); let crate_name = path.rsplit_once('/').unwrap().1; - run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder, record_failed_tests); } fn metadata(&self) -> Option { @@ -119,6 +129,7 @@ impl Step for CrateBootstrap { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Linkcheck { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Linkcheck { @@ -134,7 +145,10 @@ impl Step for Linkcheck { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Linkcheck { host: run.target }); + run.builder.ensure(Linkcheck { + host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler. @@ -162,6 +176,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" // Test the linkchecker itself. let bootstrap_host = builder.config.host_target; let compiler = builder.compiler(0, bootstrap_host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let cargo = tool::prepare_tool_cargo( builder, @@ -173,7 +188,15 @@ You can skip linkcheck with --skip src/tools/linkchecker" SourceType::InTree, &[], ); - run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder); + run_cargo_test( + cargo, + &[], + &[], + "linkchecker self tests", + bootstrap_host, + builder, + record_failed_tests, + ); if !builder.test_target.runs_doctests() { return; @@ -209,6 +232,7 @@ fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct HtmlCheck { target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for HtmlCheck { @@ -224,7 +248,10 @@ impl Step for HtmlCheck { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(HtmlCheck { target: run.target }); + run.builder.ensure(HtmlCheck { + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -262,6 +289,7 @@ impl Step for HtmlCheck { pub struct Cargotest { build_compiler: Compiler, host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Cargotest { @@ -285,6 +313,7 @@ impl Step for Cargotest { run.builder.ensure(Cargotest { build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target), host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -335,6 +364,7 @@ impl Step for Cargotest { pub struct Cargo { build_compiler: Compiler, host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Cargo { @@ -356,6 +386,7 @@ impl Step for Cargo { ToolTargetBuildMode::Build(run.target), ), host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -365,6 +396,7 @@ impl Step for Cargo { // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then // run its tests against the stage 1 compiler (called `tested_compiler` below). builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host); builder.std(tested_compiler, self.host); @@ -429,7 +461,7 @@ impl Step for Cargo { ); let _time = helpers::timeit(builder); - add_flags_and_try_run_tests(builder, &mut cargo); + add_flags_and_try_run_tests(builder, &mut cargo, record_failed_tests); } fn metadata(&self) -> Option { @@ -440,6 +472,7 @@ impl Step for Cargo { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RustAnalyzer { compilers: RustcPrivateCompilers, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RustAnalyzer { @@ -461,6 +494,7 @@ impl Step for RustAnalyzer { run.builder.top_stage, run.builder.host_target, ), + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -468,6 +502,7 @@ impl Step for RustAnalyzer { fn run(self, builder: &Builder<'_>) { let build_compiler = self.compilers.build_compiler(); let target = self.compilers.target(); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); // NOTE: rust-analyzer repo currently (as of 2025-12-11) does not run tests against 32-bit // targets, so we also don't run them in rust-lang/rust CI (because that will just mean that @@ -482,13 +517,14 @@ impl Step for RustAnalyzer { return; } + let suite = "src/tools/rust-analyzer"; let mut cargo = tool::prepare_tool_cargo( builder, build_compiler, Mode::ToolRustcPrivate, target, Kind::Test, - "src/tools/rust-analyzer", + suite, SourceType::InTree, &["in-rust-tree".to_owned()], ); @@ -539,7 +575,15 @@ impl Step for RustAnalyzer { let skip_tests = skip_tests.iter().map(|s| s.as_str()).collect::>(); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, skip_tests.as_slice(), &[], "rust-analyzer", target, builder); + run_cargo_test( + cargo, + skip_tests.as_slice(), + &[], + "rust-analyzer", + target, + builder, + record_failed_tests, + ); } fn metadata(&self) -> Option { @@ -554,6 +598,7 @@ impl Step for RustAnalyzer { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { compilers: RustcPrivateCompilers, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Rustfmt { @@ -571,6 +616,7 @@ impl Step for Rustfmt { run.builder.top_stage, run.builder.host_target, ), + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -578,6 +624,7 @@ impl Step for Rustfmt { fn run(self, builder: &Builder<'_>) { let build_compiler = self.compilers.build_compiler(); let target = self.compilers.target(); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut cargo = tool::prepare_tool_cargo( builder, @@ -596,7 +643,7 @@ impl Step for Rustfmt { cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", target, builder); + run_cargo_test(cargo, &[], &[], "rustfmt", target, builder, record_failed_tests); } fn metadata(&self) -> Option { @@ -610,6 +657,7 @@ impl Step for Rustfmt { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Miri { @@ -663,7 +711,10 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { target: run.target }); + run.builder.ensure(Miri { + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs `cargo test` for miri. @@ -771,6 +822,7 @@ impl Step for Miri { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CargoMiri { target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CargoMiri { @@ -781,7 +833,10 @@ impl Step for CargoMiri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CargoMiri { target: run.target }); + run.builder.ensure(CargoMiri { + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Tests `cargo miri test`. @@ -851,6 +906,7 @@ impl Step for CargoMiri { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CompiletestTest { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CompiletestTest { @@ -861,12 +917,16 @@ impl Step for CompiletestTest { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CompiletestTest { host: run.target }); + run.builder.ensure(CompiletestTest { + host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs `cargo test` for compiletest. fn run(self, builder: &Builder<'_>) { let host = self.host; + let record_failed_tests = builder.ensure(SetupFailedTestsFile); // Now that compiletest uses only stable Rust, building it always uses // the stage 0 compiler. However, some of its unit tests need to be able @@ -900,13 +960,22 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // format, namely that of the staged compiler. cargo.env("TEST_RUSTC", builder.rustc(staged_compiler)); - run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder); + run_cargo_test( + cargo, + &[], + &[], + "compiletest self test", + host, + builder, + record_failed_tests, + ); } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Clippy { compilers: RustcPrivateCompilers, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Clippy { @@ -928,6 +997,7 @@ impl Step for Clippy { run.builder.top_stage, run.builder.host_target, ), + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -967,7 +1037,7 @@ impl Step for Clippy { // Collect paths of tests to run 'partially_test: { - let paths = &builder.config.paths[..]; + let paths = builder.paths(self.is_for_rerunning_tests); let mut test_names = Vec::new(); for path in paths { match helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder) { @@ -1019,6 +1089,7 @@ fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString { pub struct RustdocTheme { /// The compiler (more accurately, its rustdoc) that we test. test_compiler: Compiler, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RustdocTheme { @@ -1036,7 +1107,10 @@ impl Step for RustdocTheme { fn make_run(run: RunConfig<'_>) { let test_compiler = run.builder.compiler(run.builder.top_stage, run.target); - run.builder.ensure(RustdocTheme { test_compiler }); + run.builder.ensure(RustdocTheme { + test_compiler, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -1072,6 +1146,7 @@ pub struct RustdocJSStd { /// Compiler that will build the standary library. build_compiler: Compiler, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RustdocJSStd { @@ -1090,6 +1165,7 @@ impl Step for RustdocJSStd { run.builder.ensure(RustdocJSStd { build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target), target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -1108,7 +1184,7 @@ impl Step for RustdocJSStd { .arg("--test-folder") .arg(builder.src.join("tests/rustdoc-js-std")); - let full_suite = builder.paths.iter().any(|path| { + let full_suite = builder.paths(self.is_for_rerunning_tests).iter().any(|path| { matches!( helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder), TestFilterCategory::Fullsuite @@ -1118,7 +1194,7 @@ impl Step for RustdocJSStd { // If we have to also run the full suite, don't worry about the individual arguments. // They will be covered by running the entire suite if !full_suite { - for path in &builder.paths { + for path in builder.paths(self.is_for_rerunning_tests) { if let TestFilterCategory::Arg(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder) { @@ -1149,6 +1225,7 @@ impl Step for RustdocJSStd { pub struct RustdocJSNotStd { pub target: TargetSelection, pub compiler: Compiler, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RustdocJSNotStd { @@ -1165,7 +1242,11 @@ impl Step for RustdocJSNotStd { fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(RustdocJSNotStd { target: run.target, compiler }); + run.builder.ensure(RustdocJSNotStd { + target: run.target, + compiler, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -1176,6 +1257,7 @@ impl Step for RustdocJSNotStd { suite: "rustdoc-js", path: "tests/rustdoc-js", compare_mode: None, + is_for_rerunning_tests: self.is_for_rerunning_tests, }); } } @@ -1217,6 +1299,7 @@ pub struct RustdocGUI { /// The compiler whose rustdoc we are testing. test_compiler: Compiler, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RustdocGUI { @@ -1235,11 +1318,16 @@ impl Step for RustdocGUI { fn make_run(run: RunConfig<'_>) { let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(RustdocGUI { test_compiler, target: run.target }); + run.builder.ensure(RustdocGUI { + test_compiler, + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { builder.std(self.test_compiler, self.target); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); @@ -1269,7 +1357,7 @@ impl Step for RustdocGUI { add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No); - let full_suite = builder.paths.iter().any(|path| { + let full_suite = builder.paths(self.is_for_rerunning_tests).iter().any(|path| { matches!( helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder), TestFilterCategory::Fullsuite @@ -1279,7 +1367,7 @@ impl Step for RustdocGUI { // If we have to also run the full suite, don't worry about the individual arguments. // They will be covered by running the entire suite if !full_suite { - for path in &builder.paths { + for path in builder.paths(self.is_for_rerunning_tests) { if let TestFilterCategory::Arg(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) { @@ -1308,7 +1396,7 @@ impl Step for RustdocGUI { let _time = helpers::timeit(builder); let _guard = builder.msg_test("rustdoc-gui", self.target, self.test_compiler.stage); - try_run_tests(builder, &mut cmd, true); + try_run_tests(builder, &mut cmd, true, record_failed_tests); } fn metadata(&self) -> Option { @@ -1321,7 +1409,9 @@ impl Step for RustdocGUI { /// /// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Tidy; +pub struct Tidy { + pub is_for_rerunning_tests: IsForRerunningTests, +} impl Step for Tidy { type Output = (); @@ -1336,7 +1426,7 @@ impl Step for Tidy { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Tidy); + run.builder.ensure(Tidy { is_for_rerunning_tests: run.is_for_rerunning_tests }); } /// Runs the `tidy` tool. @@ -1458,6 +1548,7 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRunMakeSupport { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateRunMakeSupport { @@ -1469,13 +1560,17 @@ impl Step for CrateRunMakeSupport { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CrateRunMakeSupport { host: run.target }); + run.builder.ensure(CrateRunMakeSupport { + host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs `cargo test` for run-make-support. fn run(self, builder: &Builder<'_>) { let host = self.host; let compiler = builder.compiler(0, host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut cargo = tool::prepare_tool_cargo( builder, @@ -1488,13 +1583,22 @@ impl Step for CrateRunMakeSupport { &[], ); cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder); + run_cargo_test( + cargo, + &[], + &[], + "run-make-support self test", + host, + builder, + record_failed_tests, + ); } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBuildHelper { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateBuildHelper { @@ -1506,13 +1610,17 @@ impl Step for CrateBuildHelper { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CrateBuildHelper { host: run.target }); + run.builder.ensure(CrateBuildHelper { + host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs `cargo test` for build_helper. fn run(self, builder: &Builder<'_>) { let host = self.host; let compiler = builder.compiler(0, host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut cargo = tool::prepare_tool_cargo( builder, @@ -1525,7 +1633,15 @@ impl Step for CrateBuildHelper { &[], ); cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder); + run_cargo_test( + cargo, + &[], + &[], + "build_helper self test", + host, + builder, + record_failed_tests, + ); } } @@ -1552,6 +1668,7 @@ macro_rules! test { pub struct $name { test_compiler: Compiler, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for $name { @@ -1574,7 +1691,11 @@ macro_rules! test { fn make_run(run: RunConfig<'_>) { let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure($name { test_compiler, target: run.target }); + run.builder.ensure($name { + test_compiler, + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -1590,6 +1711,7 @@ macro_rules! test { $( value = $compare_mode; )? value }), + is_for_rerunning_tests: self.is_for_rerunning_tests, }) } } @@ -1706,6 +1828,7 @@ pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, pub(crate) mode: CompiletestMode, + is_for_rerunning_tests: IsForRerunningTests, } impl Coverage { @@ -1779,12 +1902,17 @@ impl Step for Coverage { // suite should also skip the `coverage-map` and `coverage-run` aliases. for mode in modes { - run.builder.ensure(Coverage { compiler, target, mode }); + run.builder.ensure(Coverage { + compiler, + target, + mode, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } } fn run(self, builder: &Builder<'_>) { - let Self { compiler, target, mode } = self; + let Self { compiler, target, mode, is_for_rerunning_tests } = self; // Like other compiletest suite test steps, delegate to an internal // compiletest task to actually run the tests. builder.ensure(Compiletest { @@ -1794,6 +1922,7 @@ impl Step for Coverage { suite: Self::SUITE, path: Self::PATH, compare_mode: None, + is_for_rerunning_tests, }); } } @@ -1811,6 +1940,7 @@ test!(CoverageRunRustdoc { pub struct MirOpt { pub compiler: Compiler, pub target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for MirOpt { @@ -1826,7 +1956,11 @@ impl Step for MirOpt { fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(MirOpt { compiler, target: run.target }); + run.builder.ensure(MirOpt { + compiler, + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -1838,6 +1972,7 @@ impl Step for MirOpt { suite: "mir-opt", path: "tests/mir-opt", compare_mode: None, + is_for_rerunning_tests: self.is_for_rerunning_tests, }) }; @@ -1881,6 +2016,7 @@ struct Compiletest { suite: &'static str, path: &'static str, compare_mode: Option<&'static str>, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Compiletest { @@ -1908,6 +2044,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let target = self.target; let mode = self.mode; let suite = self.suite; + let record_failed_tests = builder.ensure(SetupFailedTestsFile); // Path for test suite let suite_path = self.path; @@ -2221,8 +2358,14 @@ Please disable assertions with `rust.debug-assertions = false`. // Provide `rust_test_helpers` for both host and target. if suite == "ui" || suite == "incremental" { - builder.ensure(TestHelpers { target: test_compiler.host }); - builder.ensure(TestHelpers { target }); + builder.ensure(TestHelpers { + target: test_compiler.host, + is_for_rerunning_tests: self.is_for_rerunning_tests, + }); + builder.ensure(TestHelpers { + target, + is_for_rerunning_tests: self.is_for_rerunning_tests, + }); hostflags.push(format!( "-Lnative={}", builder.test_helpers_out(test_compiler.host).display() @@ -2282,7 +2425,7 @@ Please disable assertions with `rust.debug-assertions = false`. // Get paths from cmd args let mut paths = match &builder.config.cmd { - Subcommand::Test { .. } => &builder.config.paths[..], + Subcommand::Test { .. } => builder.paths(self.is_for_rerunning_tests), _ => &[], }; @@ -2552,7 +2695,7 @@ Please disable assertions with `rust.debug-assertions = false`. target, test_compiler.stage, ); - try_run_tests(builder, &mut cmd, false); + try_run_tests(builder, &mut cmd, false, record_failed_tests.clone()); if let Some(compare_mode) = compare_mode { cmd.arg("--compare-mode").arg(compare_mode); @@ -2575,7 +2718,7 @@ Please disable assertions with `rust.debug-assertions = false`. suite, mode, compare_mode, &test_compiler.host, target )); let _time = helpers::timeit(builder); - try_run_tests(builder, &mut cmd, false); + try_run_tests(builder, &mut cmd, false, record_failed_tests); } } @@ -2760,6 +2903,7 @@ macro_rules! test_book { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { test_compiler: Compiler, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for $name { @@ -2777,6 +2921,7 @@ macro_rules! test_book { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { test_compiler: run.builder.compiler(run.builder.top_stage, run.target), + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -2823,6 +2968,7 @@ test_book!( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ErrorIndex { compilers: RustcPrivateCompilers, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for ErrorIndex { @@ -2848,7 +2994,8 @@ impl Step for ErrorIndex { run.builder.top_stage, run.builder.config.host_target, ); - run.builder.ensure(ErrorIndex { compilers }); + run.builder + .ensure(ErrorIndex { compilers, is_for_rerunning_tests: run.is_for_rerunning_tests }); } /// Runs the error index generator tool to execute the tests located in the error @@ -2917,6 +3064,7 @@ pub struct CrateLibrustc { build_compiler: Compiler, target: TargetSelection, crates: Vec, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateLibrustc { @@ -2937,7 +3085,12 @@ impl Step for CrateLibrustc { let build_compiler = builder.compiler(builder.top_stage - 1, host); let crates = run.make_run_crates(Alias::Compiler); - builder.ensure(CrateLibrustc { build_compiler, target: run.target, crates }); + builder.ensure(CrateLibrustc { + build_compiler, + target: run.target, + crates, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -2949,6 +3102,7 @@ impl Step for CrateLibrustc { target: self.target, mode: Mode::Rustc, crates: self.crates, + is_for_rerunning_tests: self.is_for_rerunning_tests, }); } @@ -2967,6 +3121,7 @@ fn run_cargo_test<'a>( description: impl Into>, target: TargetSelection, builder: &Builder<'_>, + record_failed_tests: RecordFailedTests, ) -> bool { let compiler = cargo.compiler(); let stage = match cargo.mode() { @@ -2989,7 +3144,7 @@ fn run_cargo_test<'a>( }, builder, ); - add_flags_and_try_run_tests(builder, &mut cargo) + add_flags_and_try_run_tests(builder, &mut cargo, record_failed_tests) } /// Given a `cargo test` subcommand, pass it the appropriate test flags given a `builder`. @@ -3077,6 +3232,7 @@ pub struct Crate { target: TargetSelection, mode: Mode, crates: Vec, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for Crate { @@ -3100,7 +3256,13 @@ impl Step for Crate { .map(|p| builder.crate_paths[&p.assert_single_path().path].clone()) .collect(); - builder.ensure(Crate { build_compiler, target: run.target, mode: Mode::Std, crates }); + builder.ensure(Crate { + build_compiler, + target: run.target, + mode: Mode::Std, + crates, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } /// Runs all unit tests plus documentation tests for a given crate defined @@ -3119,6 +3281,7 @@ impl Step for Crate { // Prepare sysroot // See [field@compile::Std::force_recompile]. builder.ensure(Std::new(build_compiler, build_compiler.host).force_recompile(true)); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let mut cargo = if builder.kind == Kind::Miri { if builder.top_stage == 0 { @@ -3213,9 +3376,9 @@ impl Step for Crate { } if crates.iter().any(|crate_| crate_ == "alloc") { crates.push("alloctests".to_owned()); - } - - run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder); + }; + let description = crate_description(&self.crates); + run_cargo_test(cargo, &[], &crates, &*description, target, builder, record_failed_tests); } } @@ -3224,6 +3387,7 @@ impl Step for Crate { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRustdoc { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateRustdoc { @@ -3241,11 +3405,15 @@ impl Step for CrateRustdoc { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - builder.ensure(CrateRustdoc { host: run.target }); + builder.ensure(CrateRustdoc { + host: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { let target = self.host; + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let compiler = if builder.download_rustc() { builder.compiler(builder.top_stage, target) @@ -3312,7 +3480,15 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder); + run_cargo_test( + cargo, + &[], + &["rustdoc:0.0.0".to_string()], + "rustdoc", + target, + builder, + record_failed_tests, + ); } } @@ -3320,6 +3496,7 @@ impl Step for CrateRustdoc { pub struct CrateRustdocJsonTypes { build_compiler: Compiler, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CrateRustdocJsonTypes { @@ -3343,11 +3520,13 @@ impl Step for CrateRustdocJsonTypes { ToolTargetBuildMode::Build(run.target), ), target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } fn run(self, builder: &Builder<'_>) { let target = self.target; + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let cargo = tool::prepare_tool_cargo( builder, @@ -3374,6 +3553,7 @@ impl Step for CrateRustdocJsonTypes { "rustdoc-json-types", target, builder, + record_failed_tests, ); } } @@ -3436,7 +3616,9 @@ impl Step for RemoteCopyLibs { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Distcheck; +pub struct Distcheck { + is_for_rerunning_tests: IsForRerunningTests, +} impl Step for Distcheck { type Output = (); @@ -3446,7 +3628,7 @@ impl Step for Distcheck { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Distcheck); + run.builder.ensure(Distcheck { is_for_rerunning_tests: run.is_for_rerunning_tests }); } /// Runs `distcheck`, which is a collection of smoke tests: @@ -3560,7 +3742,9 @@ fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) { /// Runs unit tests in `bootstrap_test.py`, which test the Python parts of bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct BootstrapPy; +pub(crate) struct BootstrapPy { + is_for_rerunning_tests: IsForRerunningTests, +} impl Step for BootstrapPy { type Output = (); @@ -3578,7 +3762,7 @@ impl Step for BootstrapPy { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(BootstrapPy) + run.builder.ensure(BootstrapPy { is_for_rerunning_tests: run.is_for_rerunning_tests }) } fn run(self, builder: &Builder<'_>) -> Self::Output { @@ -3599,7 +3783,9 @@ impl Step for BootstrapPy { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Bootstrap; +pub struct Bootstrap { + is_for_rerunning_tests: IsForRerunningTests, +} impl Step for Bootstrap { type Output = (); @@ -3620,6 +3806,7 @@ impl Step for Bootstrap { fn run(self, builder: &Builder<'_>) { let host = builder.config.host_target; let build_compiler = builder.compiler(0, host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); // Some tests require cargo submodule to be present. builder.build.require_submodule("src/tools/cargo", None); @@ -3651,11 +3838,11 @@ impl Step for Bootstrap { cargo.env("INSTA_UPDATE", "always"); } - run_cargo_test(cargo, &[], &[], None, host, builder); + run_cargo_test(cargo, &[], &[], None, host, builder, record_failed_tests); } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Bootstrap); + run.builder.ensure(Bootstrap { is_for_rerunning_tests: run.is_for_rerunning_tests }); } } @@ -3668,6 +3855,7 @@ fn get_compiler_to_test(builder: &Builder<'_>, target: TargetSelection) -> Compi #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TierCheck { test_compiler: Compiler, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for TierCheck { @@ -3683,8 +3871,10 @@ impl Step for TierCheck { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(TierCheck { test_compiler: get_compiler_to_test(run.builder, run.target) }); + run.builder.ensure(TierCheck { + test_compiler: get_compiler_to_test(run.builder, run.target), + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -3720,6 +3910,7 @@ impl Step for TierCheck { pub struct LintDocs { build_compiler: Compiler, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for LintDocs { @@ -3748,6 +3939,7 @@ impl Step for LintDocs { run.builder.top_stage, ), target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -3766,7 +3958,9 @@ impl Step for LintDocs { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RustInstaller; +pub struct RustInstaller { + is_for_rerunning_tests: IsForRerunningTests, +} impl Step for RustInstaller { type Output = (); @@ -3781,13 +3975,14 @@ impl Step for RustInstaller { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Self); + run.builder.ensure(Self { is_for_rerunning_tests: run.is_for_rerunning_tests }); } /// Ensure the version placeholder replacement tool builds fn run(self, builder: &Builder<'_>) { let bootstrap_host = builder.config.host_target; let build_compiler = builder.compiler(0, bootstrap_host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let cargo = tool::prepare_tool_cargo( builder, build_compiler, @@ -3800,7 +3995,7 @@ impl Step for RustInstaller { ); let _guard = builder.msg_test("rust-installer", bootstrap_host, 1); - run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder, record_failed_tests); // We currently don't support running the test.sh script outside linux(?) environments. // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a @@ -3825,6 +4020,7 @@ impl Step for RustInstaller { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestHelpers { pub target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for TestHelpers { @@ -3835,7 +4031,10 @@ impl Step for TestHelpers { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(TestHelpers { target: run.target }) + run.builder.ensure(TestHelpers { + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }) } /// Compiles the `rust_test_helpers.c` library which we used in various @@ -3887,6 +4086,7 @@ impl Step for TestHelpers { pub struct CodegenCranelift { compilers: RustcPrivateCompilers, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CodegenCranelift { @@ -3934,7 +4134,11 @@ impl Step for CodegenCranelift { return; } - builder.ensure(CodegenCranelift { compilers, target: run.target }); + builder.ensure(CodegenCranelift { + compilers, + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -4008,6 +4212,7 @@ impl Step for CodegenCranelift { pub struct CodegenGCC { compilers: RustcPrivateCompilers, target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for CodegenGCC { @@ -4054,7 +4259,11 @@ impl Step for CodegenGCC { return; } - builder.ensure(CodegenGCC { compilers, target: run.target }); + builder.ensure(CodegenGCC { + compilers, + target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, + }); } fn run(self, builder: &Builder<'_>) { @@ -4139,6 +4348,7 @@ pub struct TestFloatParse { build_compiler: Compiler, /// Target for which we build std and test that std. target: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for TestFloatParse { @@ -4157,6 +4367,7 @@ impl Step for TestFloatParse { run.builder.ensure(Self { build_compiler: get_compiler_to_test(run.builder, run.target), target: run.target, + is_for_rerunning_tests: run.is_for_rerunning_tests, }); } @@ -4167,6 +4378,7 @@ impl Step for TestFloatParse { // Build the standard library that will be tested, and a stdlib for host code builder.std(build_compiler, target); builder.std(build_compiler, builder.host_target); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); // Run any unit tests in the crate let mut cargo_test = tool::prepare_tool_cargo( @@ -4181,7 +4393,15 @@ impl Step for TestFloatParse { ); cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES); - run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder); + run_cargo_test( + cargo_test, + &[], + &[], + "test-float-parse", + target, + builder, + record_failed_tests, + ); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( @@ -4208,7 +4428,9 @@ impl Step for TestFloatParse { /// which verifies that `license-metadata.json` is up-to-date and therefore /// running the tool normally would not update anything. #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct CollectLicenseMetadata; +pub struct CollectLicenseMetadata { + is_for_rerunning_tests: IsForRerunningTests, +} impl Step for CollectLicenseMetadata { type Output = PathBuf; @@ -4219,7 +4441,8 @@ impl Step for CollectLicenseMetadata { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CollectLicenseMetadata); + run.builder + .ensure(CollectLicenseMetadata { is_for_rerunning_tests: run.is_for_rerunning_tests }); } fn run(self, builder: &Builder<'_>) -> Self::Output { @@ -4242,6 +4465,7 @@ impl Step for CollectLicenseMetadata { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RemoteTestClientTests { host: TargetSelection, + is_for_rerunning_tests: IsForRerunningTests, } impl Step for RemoteTestClientTests { @@ -4257,12 +4481,14 @@ impl Step for RemoteTestClientTests { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Self { host: run.target }); + run.builder + .ensure(Self { host: run.target, is_for_rerunning_tests: run.is_for_rerunning_tests }); } fn run(self, builder: &Builder<'_>) { let bootstrap_host = builder.config.host_target; let compiler = builder.compiler(0, bootstrap_host); + let record_failed_tests = builder.ensure(SetupFailedTestsFile); let cargo = tool::prepare_tool_cargo( builder, @@ -4275,6 +4501,14 @@ impl Step for RemoteTestClientTests { &[], ); - run_cargo_test(cargo, &[], &[], "remote-test-client", bootstrap_host, builder); + run_cargo_test( + cargo, + &[], + &[], + "remote-test-client", + bootstrap_host, + builder, + record_failed_tests, + ); } } diff --git a/src/bootstrap/src/core/build_steps/test/failed_tests.rs b/src/bootstrap/src/core/build_steps/test/failed_tests.rs new file mode 100644 index 0000000000000..8b8be9225bc21 --- /dev/null +++ b/src/bootstrap/src/core/build_steps/test/failed_tests.rs @@ -0,0 +1,122 @@ +use std::collections::HashSet; +use std::fs::{self, File}; +use std::io::{BufRead, BufReader, ErrorKind}; +use std::path::{Path, PathBuf}; + +use crate::core::builder::{Builder, ShouldRun, Step}; +use crate::t; + +#[derive(Debug, Clone, Hash, PartialEq, Eq, Copy)] +pub enum IsForRerunningTests { + /// For steps that aren't part of tests, same as no + DontCare, + No, + Yes, +} + +#[derive(Clone)] +pub struct RecordFailedTests { + failed_tests_path: Option, +} + +impl RecordFailedTests { + pub fn path(&self) -> Option<&Path> { + self.failed_tests_path.as_deref() + } +} + +/// This step is run as a dependency of most testing steps. +/// Upon running, a file is created for failed tests to be recorded in if `--record` is passed on +/// the command line. +/// +/// This step is the only way to get access to a token type called [`RecordFailedTests`]. +/// Having this token type signifies the fact that a file was created to store failed tests in, +/// and is required to create a `Renderer`, the type that renders the outputs of tests. +/// +/// If `--rerun` isn't passed, or we're in dry-run mode, running this step is a no-op, +/// and the `RecordFailedTest` type doesn't (need to) signify anything. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct SetupFailedTestsFile; +impl Step for SetupFailedTestsFile { + type Output = RecordFailedTests; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + if !builder.config.cmd.record() || builder.config.dry_run() { + return RecordFailedTests { failed_tests_path: None }; + } + + let failed_tests_path = builder.config.record_failed_tests_path.clone(); + println!( + "setting up tracking of failed tests in {} (`--record` was passed)", + failed_tests_path.display() + ); + if failed_tests_path.exists() { + println!("deleting previously recorded failed tests"); + t!(fs::remove_file(&failed_tests_path)); + } + RecordFailedTests { failed_tests_path: Some(failed_tests_path) } + } +} + +pub fn collect_previously_failed_tests(failed_tests_file_path: &PathBuf) -> Vec { + let mut paths = Vec::new(); + + println!( + "`--rerun` passed so looking for failed tests in {}", + failed_tests_file_path.display() + ); + + let lines: Vec = match File::open(failed_tests_file_path) { + Ok(f) => t!(BufReader::new(f).lines().collect()), + Err(e) if e.kind() == ErrorKind::NotFound => { + println!( + "WARNING: failed tests file doesn't exist: `--rerun` only makes sense after a previous test run with `--record`" + ); + return Vec::new(); + } + Err(e) => t!(Err(e)), + }; + + let mut set_tracking_duplicates = HashSet::new(); + let mut num_printed = 0; + const MAX_RERUN_PRINTS: usize = 10; + + for line in lines { + let trimmed = line.as_str().trim(); + let without_revision = + trimmed.rsplit_once("#").map(|(before, _)| before).unwrap_or(trimmed); + let without_suite_prefix = without_revision + .strip_prefix("[") + .and_then(|rest| rest.split_once("]")) + .map(|(_, after)| after.trim()) + .unwrap_or(without_revision); + + let failed_test_path = PathBuf::from(without_suite_prefix.to_string()); + if set_tracking_duplicates.insert(failed_test_path.clone()) { + if num_printed == 0 { + println!("rerunning previously failed tests:"); + } + if num_printed < MAX_RERUN_PRINTS { + println!(" {}", failed_test_path.display()); + num_printed += 1; + } + paths.push(failed_test_path); + } + } + + if num_printed == MAX_RERUN_PRINTS && set_tracking_duplicates.len() > MAX_RERUN_PRINTS { + println!(" and {} more...", set_tracking_duplicates.len() - MAX_RERUN_PRINTS) + } + + if set_tracking_duplicates.is_empty() { + println!( + "WARNING: failed tests file doesn't contain any failed tests: `--rerun` only makes sense after a previous test run with `--record`" + ); + } + + paths +} diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index 1b0caa980e1de..3f8ba63d252e6 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -3,8 +3,9 @@ //! large and hard to navigate. use std::fmt::{self, Debug}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::builder::{Builder, Kind, PathSet, ShouldRun, StepDescription}; #[cfg(test)] @@ -109,6 +110,7 @@ pub(crate) fn match_paths_to_steps_and_run( builder: &Builder<'_>, step_descs: &[StepDescription], paths: &[PathBuf], + previously_failed_test_paths: &[PathBuf], ) { // Obtain `ShouldRun` information for each step, so that we know which // paths to match it against. @@ -135,59 +137,76 @@ pub(crate) fn match_paths_to_steps_and_run( assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name); } - if paths.is_empty() || builder.config.include_default_paths { + if (paths.is_empty() && previously_failed_test_paths.is_empty()) + || builder.config.include_default_paths + { for StepExtra { desc, should_run } in &steps { if (desc.is_default_step_fn)(builder) { - desc.maybe_run(builder, should_run.paths.iter().cloned().collect()); + desc.maybe_run( + builder, + should_run.paths.iter().cloned().collect(), + IsForRerunningTests::DontCare, + ); } } } // Attempt to resolve paths to be relative to the builder source directory. - let mut paths: Vec = paths - .iter() - .map(|original_path| { - let mut path = original_path.clone(); - - // Someone could run `x ` from a different repository than the source - // directory. - // In that case, we should not try to resolve the paths relative to the working - // directory, but rather relative to the source directory. - // So we forcefully "relocate" the path to the source directory here. - if !path.is_absolute() { - path = builder.src.join(path); - } + let remap_paths = |paths: &[PathBuf]| { + let mut paths = paths + .iter() + .map(|original_path| { + let mut path = original_path.clone(); + + // Someone could run `x ` from a different repository than the source + // directory. + // In that case, we should not try to resolve the paths relative to the working + // directory, but rather relative to the source directory. + // So we forcefully "relocate" the path to the source directory here. + if !path.is_absolute() { + path = builder.src.join(path); + } - // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` - if !path.exists() { - // Use the original path here - return original_path.clone(); - } + // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` + if !path.exists() { + // Use the original path here + return original_path.clone(); + } - // Make the path absolute, strip the prefix, and convert to a PathBuf. - match std::path::absolute(&path) { - Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), - Err(e) => { - eprintln!("ERROR: {e:?}"); - panic!("Due to the above error, failed to resolve path: {path:?}"); + // Make the path absolute, strip the prefix, and convert to a PathBuf. + match std::path::absolute(&path) { + Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), + Err(e) => { + eprintln!("ERROR: {e:?}"); + panic!("Due to the above error, failed to resolve path: {path:?}"); + } } - } - }) - .collect(); + }) + .collect(); + remap_paths(&mut paths); + paths + }; - remap_paths(&mut paths); + let mut paths = remap_paths(paths); + let previously_failed_test_paths = remap_paths(previously_failed_test_paths); // Handle all test suite paths. // (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.) - paths.retain(|path| { - for StepExtra { desc, should_run } in &steps { - if let Some(suite) = should_run.is_suite_path(path) { - desc.maybe_run(builder, vec![suite.clone()]); - return false; + { + let run_test_path = |path: &Path, is_for_rerunning_tests| { + for StepExtra { desc, should_run } in &steps { + if let Some(suite) = should_run.is_suite_path(path) { + desc.maybe_run(builder, vec![suite.clone()], is_for_rerunning_tests); + return false; + } } + true + }; + for path in previously_failed_test_paths { + run_test_path(&path, IsForRerunningTests::Yes); } - true - }); + paths.retain(|path| run_test_path(path, IsForRerunningTests::No)); + } if paths.is_empty() { return; @@ -229,7 +248,7 @@ pub(crate) fn match_paths_to_steps_and_run( // Handle all PathSets. for StepToRun { sort_index: _, desc, pathsets } in steps_to_run { if !pathsets.is_empty() { - desc.maybe_run(builder, pathsets); + desc.maybe_run(builder, pathsets, IsForRerunningTests::DontCare); } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index ae91b20406295..f2e91687f2d69 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -16,6 +16,7 @@ use tracing::instrument; pub use self::cargo::{Cargo, cargo_profile_var}; pub use crate::Compiler; use crate::core::build_steps::compile::{Std, StdLink}; +use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::build_steps::tool::RustcPrivateCompilers; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, @@ -62,7 +63,10 @@ pub struct Builder<'a> { /// The paths passed on the command line. Used by steps to figure out what /// to do. For example: with `./x check foo bar` we get `paths=["foo", /// "bar"]`. - pub paths: Vec, + paths: Vec, + + /// A list of tests that failed during a previous run, recorded with `--record`, + previously_failed_test_paths: Vec, /// Cached list of submodules from self.build.src. submodule_paths_cache: OnceLock>, @@ -243,6 +247,10 @@ pub struct RunConfig<'a> { pub builder: &'a Builder<'a>, pub target: TargetSelection, pub paths: Vec, + /// Set to true when invoking a step in "rerun" mode, `--rerun` was passed. + /// This makes it so rerunning happens in a separate step, distinct from the normal test step. + /// With this, all tests that are reran, run before other tests, and we fail-fast if any of the reran tests fail. + pub is_for_rerunning_tests: IsForRerunningTests, } impl RunConfig<'_> { @@ -454,7 +462,12 @@ impl StepDescription { } } - fn maybe_run(&self, builder: &Builder<'_>, mut pathsets: Vec) { + fn maybe_run( + &self, + builder: &Builder<'_>, + mut pathsets: Vec, + is_for_rerunning_tests: IsForRerunningTests, + ) { pathsets.retain(|set| !self.is_excluded(builder, set)); if pathsets.is_empty() { @@ -472,7 +485,12 @@ impl StepDescription { } for target in targets { - let run = RunConfig { builder, paths: pathsets.clone(), target: *target }; + let run = RunConfig { + builder, + paths: pathsets.clone(), + target: *target, + is_for_rerunning_tests, + }; (self.make_run)(run); } } @@ -1022,13 +1040,21 @@ impl<'a> Builder<'a> { } } + /// Returns all paths + pub fn paths(&self, is_for_rerunning_tests: IsForRerunningTests) -> &[PathBuf] { + match is_for_rerunning_tests { + IsForRerunningTests::DontCare | IsForRerunningTests::No => &self.paths, + IsForRerunningTests::Yes => &self.previously_failed_test_paths, + } + } + pub fn get_help(build: &Build, kind: Kind) -> Option { let step_descriptions = Builder::get_step_descriptions(kind); if step_descriptions.is_empty() { return None; } - let builder = Self::new_internal(build, kind, vec![]); + let builder = Self::new_internal(build, kind, vec![], vec![]); let builder = &builder; // The "build" kind here is just a placeholder, it will be replaced with something else in // the following statement. @@ -1056,7 +1082,12 @@ impl<'a> Builder<'a> { Some(help) } - fn new_internal(build: &Build, kind: Kind, paths: Vec) -> Builder<'_> { + fn new_internal( + build: &Build, + kind: Kind, + paths: Vec, + previously_failed_test_paths: Vec, + ) -> Builder<'_> { Builder { build, top_stage: build.config.stage, @@ -1065,6 +1096,7 @@ impl<'a> Builder<'a> { stack: RefCell::new(Vec::new()), time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), paths, + previously_failed_test_paths, submodule_paths_cache: Default::default(), log_cli_step_for_tests: None, } @@ -1072,6 +1104,7 @@ impl<'a> Builder<'a> { pub fn new(build: &Build) -> Builder<'_> { let paths = &build.config.paths; + let (kind, paths) = match build.config.cmd { Subcommand::Build { .. } => (Kind::Build, &paths[..]), Subcommand::Check { .. } => (Kind::Check, &paths[..]), @@ -1094,16 +1127,24 @@ impl<'a> Builder<'a> { Subcommand::Perf { .. } => (Kind::Perf, &paths[..]), }; - Self::new_internal(build, kind, paths.to_owned()) + // No need to special case these, since they weren't passed on the command line + // (and are only set int Subcommand::Test) + let previously_failed_test_paths = &build.config.previously_failed_test_paths; + + Self::new_internal(build, kind, paths.to_owned(), previously_failed_test_paths.to_owned()) } pub fn execute_cli(&self) { - self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths); + self.run_step_descriptions( + &Builder::get_step_descriptions(self.kind), + &self.paths, + &self.previously_failed_test_paths, + ); } /// Run all default documentation steps to build documentation. pub fn run_default_doc_steps(&self) { - self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); + self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[], &[]); } pub fn doc_rust_lang_org_channel(&self) -> String { @@ -1118,8 +1159,13 @@ impl<'a> Builder<'a> { format!("https://doc.rust-lang.org/{channel}") } - fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) { - cli_paths::match_paths_to_steps_and_run(self, v, paths); + fn run_step_descriptions( + &self, + v: &[StepDescription], + paths: &[PathBuf], + previously_failed_test_paths: &[PathBuf], + ) { + cli_paths::match_paths_to_steps_and_run(self, v, paths, previously_failed_test_paths); } /// Returns if `std` should be statically linked into `rustc_driver`. diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f34b284dbb9cc..7c961852a9727 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -35,7 +35,7 @@ fn run_build(paths: &[PathBuf], config: Config) -> Cache { let kind = config.cmd.kind(); let build = Build::new(config); let builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(kind), paths); + builder.run_step_descriptions(&Builder::get_step_descriptions(kind), paths, &[]); builder.cache } @@ -509,6 +509,7 @@ fn any_debug() { mod snapshot { use std::path::PathBuf; + use crate::core::build_steps::test::failed_tests::IsForRerunningTests; use crate::core::build_steps::{compile, dist, doc, test, tool}; use crate::core::builder::tests::{ RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, first, host_target, @@ -2378,7 +2379,7 @@ mod snapshot { let host = TargetSelection::from_user(&host_target()); steps.assert_contains(StepMetadata::test("compiletest-rustdoc-ui", host).stage(1)); - steps.assert_not_contains(test::Tidy); + steps.assert_not_contains(test::Tidy { is_for_rerunning_tests: IsForRerunningTests::No }); } #[test] @@ -3238,7 +3239,7 @@ impl ConfigBuilder { let kind = config.cmd.kind(); let build = Build::new(config); let builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(kind), &builder.paths); + builder.run_step_descriptions(&Builder::get_step_descriptions(kind), &builder.paths, &[]); builder.cache } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2c3b5251e12f6..aee5ab5ac9edd 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -30,6 +30,7 @@ use tracing::{instrument, span}; use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; +use crate::core::build_steps::test::failed_tests::collect_previously_failed_tests; pub use crate::core::config::flags::Subcommand; use crate::core::config::flags::{Color, Flags, Warnings}; use crate::core::config::target_selection::TargetSelectionList; @@ -124,6 +125,7 @@ pub struct Config { pub stage0_metadata: build_helper::stage0_parser::Stage0, pub android_ndk: Option, pub optimized_compiler_builtins: CompilerBuiltins, + pub record_failed_tests_path: PathBuf, pub stdout_is_tty: bool, pub stderr_is_tty: bool, @@ -306,6 +308,8 @@ pub struct Config { /// The paths to work with. For example: with `./x check foo bar` we get /// `paths=["foo", "bar"]`. pub paths: Vec, + /// A list of tests that failed during a previous run, recorded with `--record`, + pub previously_failed_test_paths: Vec, /// Command for visual diff display, e.g. `diff-tool --color=always`. pub compiletest_diff_tool: Option, @@ -504,6 +508,7 @@ impl Config { dist_stage: build_dist_stage, bench_stage: build_bench_stage, patch_binaries_for_nix: build_patch_binaries_for_nix, + record_failed_tests_path: build_record_failed_tests_path, // This field is only used by bootstrap.py metrics: _, android_ndk: build_android_ndk, @@ -1296,6 +1301,14 @@ impl Config { ); let verbose_tests = rust_verbose_tests.unwrap_or(exec_ctx.is_verbose()); + let record_failed_tests_path = + out.join(build_record_failed_tests_path.unwrap_or_else(|| "failed-tests".to_string())); + let previously_failed_test_paths = if flags_cmd.rerun() { + collect_previously_failed_tests(&record_failed_tests_path) + } else { + Vec::new() + }; + Config { // tidy-alphabetical-start android_ndk: build_android_ndk, @@ -1428,10 +1441,12 @@ impl Config { path_modification_cache, paths: flags_paths, prefix: install_prefix.map(PathBuf::from), + previously_failed_test_paths, print_step_rusage: build_print_step_rusage.unwrap_or(false), print_step_timings: build_print_step_timings.unwrap_or(false), profiler: build_profiler.unwrap_or(false), python: build_python.map(PathBuf::from), + record_failed_tests_path, reproducible_artifacts: flags_reproducible_artifact, reuse: build_reuse.map(PathBuf::from), rust_analyzer_info, diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 2f1e234d6ebc0..2ec4847655ccf 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -433,6 +433,15 @@ pub enum Subcommand { #[arg(long)] #[doc(hidden)] no_doc: bool, + + /// Record all the failed tests in a file in the build directory. + /// + /// On subsequent invocations, this set of tests can be rerun by passing `--rerun` + #[arg(long)] + record: bool, + /// Rerun tests that previously failed, and stored with `--record`. + #[arg(long)] + rerun: bool, }, /// Build and run some test suites *in Miri* Miri { @@ -707,6 +716,20 @@ impl Subcommand { _ => false, } } + + pub fn record(&self) -> bool { + match self { + Subcommand::Test { record, .. } => *record, + _ => false, + } + } + + pub fn rerun(&self) -> bool { + match self { + Subcommand::Test { rerun, .. } => *rerun, + _ => false, + } + } } /// Returns the shell completion for a given shell, if the result differs from the current diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 27bf753f6914d..ed20a2958e382 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -78,6 +78,7 @@ define_config! { tidy_extra_checks: Option = "tidy-extra-checks", ccache: Option = "ccache", exclude: Option> = "exclude", + record_failed_tests_path: Option = "record_failed_tests_path", } } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 3ae2373e1da21..27def14eef2d8 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -621,4 +621,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`x.py` stopped accepting partial argument names. Use full names to avoid errors.", }, + ChangeInfo { + change_id: 154586, + severity: ChangeSeverity::Info, + summary: "New option `build.record_failed_tests_path` to store failed tests when passing `--record`. These can be rerun with `--rerun`.", + }, ]; diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 1d133a9c9e2f3..e44fb3ef56aa9 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -6,12 +6,14 @@ //! and rustc) libtest doesn't include the rendered human-readable output as a JSON field. We had //! to reimplement all the rendering logic in this module because of that. +use std::fs::File; use std::io::{BufRead, BufReader, Read, Write}; use std::process::ChildStdout; use std::time::Duration; use termcolor::{Color, ColorSpec, WriteColor}; +use crate::core::build_steps::test::failed_tests::RecordFailedTests; use crate::core::builder::Builder; use crate::utils::exec::BootstrapCommand; @@ -20,21 +22,23 @@ const TERSE_TESTS_PER_LINE: usize = 88; pub(crate) fn add_flags_and_try_run_tests( builder: &Builder<'_>, cmd: &mut BootstrapCommand, + record_failed_tests: RecordFailedTests, ) -> bool { if !cmd.get_args().any(|arg| arg == "--") { cmd.arg("--"); } cmd.args(["-Z", "unstable-options", "--format", "json"]); - try_run_tests(builder, cmd, false) + try_run_tests(builder, cmd, false, record_failed_tests) } pub(crate) fn try_run_tests( builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool, + record_failed_tests: RecordFailedTests, ) -> bool { - if run_tests(builder, cmd, stream) { + if run_tests(builder, cmd, stream, record_failed_tests) { return true; } @@ -47,7 +51,12 @@ pub(crate) fn try_run_tests( false } -fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool { +fn run_tests( + builder: &Builder<'_>, + cmd: &mut BootstrapCommand, + stream: bool, + record_failed_tests: RecordFailedTests, +) -> bool { builder.do_if_verbose(|| println!("running: {cmd:?}")); let Some(mut streaming_command) = cmd.stream_capture_stdout(&builder.config.exec_ctx) else { @@ -56,7 +65,8 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> // This runs until the stdout of the child is closed, which means the child exited. We don't // run this on another thread since the builder is not Sync. - let renderer = Renderer::new(streaming_command.stdout.take().unwrap(), builder); + let renderer = + Renderer::new(streaming_command.stdout.take().unwrap(), builder, record_failed_tests); if stream { renderer.stream_all(); } else { @@ -87,10 +97,30 @@ struct Renderer<'a> { ignored_tests: usize, terse_tests_in_line: usize, ci_latest_logged_percentage: f64, + + failed_tests: Option, } impl<'a> Renderer<'a> { - fn new(stdout: ChildStdout, builder: &'a Builder<'a>) -> Self { + fn new( + stdout: ChildStdout, + builder: &'a Builder<'a>, + record_failed_tests: RecordFailedTests, + ) -> Self { + let failed_tests = record_failed_tests.path().and_then(|path| { + // create the file (overwriting any previous) to get ready to record new failed tests + match File::options().create(true).append(true).truncate(false).open(path) { + Ok(f) => Some(f), + Err(e) => { + println!( + "Couldn't open file {} to write test failutes to: {e}. (attempted because `--record` was passed). Test failures will not be recorded.", + path.display() + ); + None + } + } + }); + Self { stdout: BufReader::new(stdout), benches: Vec::new(), @@ -102,6 +132,7 @@ impl<'a> Renderer<'a> { ignored_tests: 0, terse_tests_in_line: 0, ci_latest_logged_percentage: 0.0, + failed_tests, } } @@ -360,6 +391,13 @@ impl<'a> Renderer<'a> { } Message::Test(TestMessage::Failed(outcome)) => { self.render_test_outcome(Outcome::Failed, &outcome); + if let Some(failed_tests) = &mut self.failed_tests + && let Err(e) = writeln!(failed_tests, "{}", outcome.name) + { + eprintln!( + "failed to write test failure to file: {e} (attempted because `--record` was passed)" + ); + } self.failures.push(outcome); } Message::Test(TestMessage::Timeout { name }) => { diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 92af4f04dcba9..c6067dc60c57b 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -454,6 +454,8 @@ complete -c x -n "__fish_x_using_subcommand test" -l rustfix-coverage -d 'enable complete -c x -n "__fish_x_using_subcommand test" -l no-capture -d 'don\'t capture stdout/stderr of tests' complete -c x -n "__fish_x_using_subcommand test" -l bypass-ignore-backends -d 'Ignore `//@ ignore-backends` directives' complete -c x -n "__fish_x_using_subcommand test" -l no-doc -d 'Deprecated. Use `--all-targets` or `--tests` instead' +complete -c x -n "__fish_x_using_subcommand test" -l record -d 'Record all the failed tests in a file in the build directory' +complete -c x -n "__fish_x_using_subcommand test" -l rerun -d 'Rerun tests that previously failed, and stored with `--record`' complete -c x -n "__fish_x_using_subcommand test" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x -n "__fish_x_using_subcommand test" -s i -l incremental -d 'use incremental compilation' complete -c x -n "__fish_x_using_subcommand test" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -507,6 +509,8 @@ complete -c x -n "__fish_x_using_subcommand t" -l rustfix-coverage -d 'enable th complete -c x -n "__fish_x_using_subcommand t" -l no-capture -d 'don\'t capture stdout/stderr of tests' complete -c x -n "__fish_x_using_subcommand t" -l bypass-ignore-backends -d 'Ignore `//@ ignore-backends` directives' complete -c x -n "__fish_x_using_subcommand t" -l no-doc -d 'Deprecated. Use `--all-targets` or `--tests` instead' +complete -c x -n "__fish_x_using_subcommand t" -l record -d 'Record all the failed tests in a file in the build directory' +complete -c x -n "__fish_x_using_subcommand t" -l rerun -d 'Rerun tests that previously failed, and stored with `--record`' complete -c x -n "__fish_x_using_subcommand t" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x -n "__fish_x_using_subcommand t" -s i -l incremental -d 'use incremental compilation' complete -c x -n "__fish_x_using_subcommand t" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index d4bf45f738365..4d541232e18ad 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -522,6 +522,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--no-capture', '--no-capture', [CompletionResultType]::ParameterName, 'don''t capture stdout/stderr of tests') [CompletionResult]::new('--bypass-ignore-backends', '--bypass-ignore-backends', [CompletionResultType]::ParameterName, 'Ignore `//@ ignore-backends` directives') [CompletionResult]::new('--no-doc', '--no-doc', [CompletionResultType]::ParameterName, 'Deprecated. Use `--all-targets` or `--tests` instead') + [CompletionResult]::new('--record', '--record', [CompletionResultType]::ParameterName, 'Record all the failed tests in a file in the build directory') + [CompletionResult]::new('--rerun', '--rerun', [CompletionResultType]::ParameterName, 'Rerun tests that previously failed, and stored with `--record`') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -582,6 +584,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--no-capture', '--no-capture', [CompletionResultType]::ParameterName, 'don''t capture stdout/stderr of tests') [CompletionResult]::new('--bypass-ignore-backends', '--bypass-ignore-backends', [CompletionResultType]::ParameterName, 'Ignore `//@ ignore-backends` directives') [CompletionResult]::new('--no-doc', '--no-doc', [CompletionResultType]::ParameterName, 'Deprecated. Use `--all-targets` or `--tests` instead') + [CompletionResult]::new('--record', '--record', [CompletionResultType]::ParameterName, 'Record all the failed tests in a file in the build directory') + [CompletionResult]::new('--rerun', '--rerun', [CompletionResultType]::ParameterName, 'Rerun tests that previously failed, and stored with `--record`') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 0dbbe1ea43efd..9b6d00d61f004 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -454,6 +454,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand test" -l rustfix-coverage -d ' complete -c x.py -n "__fish_x.py_using_subcommand test" -l no-capture -d 'don\'t capture stdout/stderr of tests' complete -c x.py -n "__fish_x.py_using_subcommand test" -l bypass-ignore-backends -d 'Ignore `//@ ignore-backends` directives' complete -c x.py -n "__fish_x.py_using_subcommand test" -l no-doc -d 'Deprecated. Use `--all-targets` or `--tests` instead' +complete -c x.py -n "__fish_x.py_using_subcommand test" -l record -d 'Record all the failed tests in a file in the build directory' +complete -c x.py -n "__fish_x.py_using_subcommand test" -l rerun -d 'Rerun tests that previously failed, and stored with `--record`' complete -c x.py -n "__fish_x.py_using_subcommand test" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_x.py_using_subcommand test" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_x.py_using_subcommand test" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -507,6 +509,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand t" -l rustfix-coverage -d 'ena complete -c x.py -n "__fish_x.py_using_subcommand t" -l no-capture -d 'don\'t capture stdout/stderr of tests' complete -c x.py -n "__fish_x.py_using_subcommand t" -l bypass-ignore-backends -d 'Ignore `//@ ignore-backends` directives' complete -c x.py -n "__fish_x.py_using_subcommand t" -l no-doc -d 'Deprecated. Use `--all-targets` or `--tests` instead' +complete -c x.py -n "__fish_x.py_using_subcommand t" -l record -d 'Record all the failed tests in a file in the build directory' +complete -c x.py -n "__fish_x.py_using_subcommand t" -l rerun -d 'Rerun tests that previously failed, and stored with `--record`' complete -c x.py -n "__fish_x.py_using_subcommand t" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_x.py_using_subcommand t" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_x.py_using_subcommand t" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index b4e3b8c580a49..8628d8d5751ae 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -522,6 +522,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--no-capture', '--no-capture', [CompletionResultType]::ParameterName, 'don''t capture stdout/stderr of tests') [CompletionResult]::new('--bypass-ignore-backends', '--bypass-ignore-backends', [CompletionResultType]::ParameterName, 'Ignore `//@ ignore-backends` directives') [CompletionResult]::new('--no-doc', '--no-doc', [CompletionResultType]::ParameterName, 'Deprecated. Use `--all-targets` or `--tests` instead') + [CompletionResult]::new('--record', '--record', [CompletionResultType]::ParameterName, 'Record all the failed tests in a file in the build directory') + [CompletionResult]::new('--rerun', '--rerun', [CompletionResultType]::ParameterName, 'Rerun tests that previously failed, and stored with `--record`') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -582,6 +584,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--no-capture', '--no-capture', [CompletionResultType]::ParameterName, 'don''t capture stdout/stderr of tests') [CompletionResult]::new('--bypass-ignore-backends', '--bypass-ignore-backends', [CompletionResultType]::ParameterName, 'Ignore `//@ ignore-backends` directives') [CompletionResult]::new('--no-doc', '--no-doc', [CompletionResultType]::ParameterName, 'Deprecated. Use `--all-targets` or `--tests` instead') + [CompletionResult]::new('--record', '--record', [CompletionResultType]::ParameterName, 'Record all the failed tests in a file in the build directory') + [CompletionResult]::new('--rerun', '--rerun', [CompletionResultType]::ParameterName, 'Rerun tests that previously failed, and stored with `--record`') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 8a7aee2a091c7..9ee50bb23f631 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -4638,7 +4638,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --record --rerun --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4852,7 +4852,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --record --rerun --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index e24da218a05d8..fc226ff8555b9 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -523,6 +523,8 @@ _arguments "${_arguments_options[@]}" : \ '--no-capture[don'\''t capture stdout/stderr of tests]' \ '--bypass-ignore-backends[Ignore \`//@ ignore-backends\` directives]' \ '--no-doc[Deprecated. Use \`--all-targets\` or \`--tests\` instead]' \ +'--record[Record all the failed tests in a file in the build directory]' \ +'--rerun[Rerun tests that previously failed, and stored with \`--record\`]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ @@ -585,6 +587,8 @@ _arguments "${_arguments_options[@]}" : \ '--no-capture[don'\''t capture stdout/stderr of tests]' \ '--bypass-ignore-backends[Ignore \`//@ ignore-backends\` directives]' \ '--no-doc[Deprecated. Use \`--all-targets\` or \`--tests\` instead]' \ +'--record[Record all the failed tests in a file in the build directory]' \ +'--rerun[Rerun tests that previously failed, and stored with \`--record\`]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index a4b44b73f47af..cfafa969128aa 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -4638,7 +4638,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --record --rerun --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4852,7 +4852,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --record --rerun --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index 7f43781684b4b..92554b4175446 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -523,6 +523,8 @@ _arguments "${_arguments_options[@]}" : \ '--no-capture[don'\''t capture stdout/stderr of tests]' \ '--bypass-ignore-backends[Ignore \`//@ ignore-backends\` directives]' \ '--no-doc[Deprecated. Use \`--all-targets\` or \`--tests\` instead]' \ +'--record[Record all the failed tests in a file in the build directory]' \ +'--rerun[Rerun tests that previously failed, and stored with \`--record\`]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ @@ -585,6 +587,8 @@ _arguments "${_arguments_options[@]}" : \ '--no-capture[don'\''t capture stdout/stderr of tests]' \ '--bypass-ignore-backends[Ignore \`//@ ignore-backends\` directives]' \ '--no-doc[Deprecated. Use \`--all-targets\` or \`--tests\` instead]' \ +'--record[Record all the failed tests in a file in the build directory]' \ +'--rerun[Rerun tests that previously failed, and stored with \`--record\`]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \