bootstrap: Make ./x test compiler actually run the compiler unit tests by Zalathar · Pull Request #134919 · rust-lang/rust (original) (raw)

FTR, in my investigation, the root problem is potentially the concept of the PathSet itself. It admits several issues (some of these are over-simplifications and may be inaccurate or imprecise, but is sufficient to demonstrate the complexity and the issue at hand):

Both CodegenGCC and CodegenCranelift test Steps have should_run conditions:

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_cranelift"])
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_gcc"])
}

These test Steps are registered here via describe!:

test::CodegenCranelift,
test::CodegenGCC,

When the cli receives the filter string compiler, seemingly both test Steps would be eligible. However, there is a problem. These two test Step's should_runs don't use suite_path, they use run.paths (not saying that they should use suite_path, either). This means that they are not eligible for suite_path special handling that will try to exercise all the test suites:

// 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 (desc, should_run) in v.iter().zip(&should_runs) {
if let Some(suite) = should_run.is_suite_path(path) {
desc.maybe_run(builder, vec![suite.clone()]);
return false;
}
}
true
});

Instead, the Codegen{Cranelift,Gcc} test Steps go through the non-suite-path handling, which has "remove-PathSet-once-matched" semantics, so the first one to match (first Step to be registered in the test describe! that is eligible to match the PathSet will "eat" the PathSet) prevents any other non-suite-path test Steps that are registered later from being matched.

for (desc, should_run) in v.iter().zip(&should_runs) {
let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind);

Indeed, if you inject some printf logging, you can see how the PathSet is consumed:

[07:49] Joe:rust | ./x test compiler --dry-run
Building bootstrap
   Compiling bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap)
    Finished `dev` profile [unoptimized] target(s) in 1.76s
StepDescription::run: paths=["compiler"]
StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: true, only_hosts: true, should_run: 0x55e3fe0ece60, make_run: 0x55e3fe0ece80, name: "bootstrap::core::build_steps::test::CodegenCranelift", kind: Test }, pathsets=[Set({test::compiler/rustc_codegen_cranelift})]
StepDescription::maybe_run: pathsets=[Set({test::compiler/rustc_codegen_cranelift})]
CodegenCranelift
Building stage0 library artifacts (x86_64-unknown-linux-gnu)
Building compiler artifacts (stage0 -> stage1, x86_64-unknown-linux-gnu)
Creating a sysroot for stage1 compiler (use `rustup toolchain link 'name' build/host/stage1`)
cranelift not in rust.codegen-backends. skipping
StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: false, only_hosts: false, should_run: 0x55e3fe4b8690, make_run: 0x55e3fe4b86b0, name: "bootstrap::core::build_steps::toolstate::ToolStateCheck", kind: Test }, pathsets=[]
StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: true, only_hosts: true, should_run: 0x55e3fe0dad90, make_run: 0x55e3fe0daed0, name: "bootstrap::core::build_steps::test::Tidy", kind: Test }, pathsets=[]
[... some entries trimmed ...]
"bootstrap::core::build_steps::test::CodegenGCC", kind: Test }, pathsets=[]

Why does moving test::CodegenGCC and test::CodegenCranelift seemingly unblock ./x test compiler? Well, it's because test::CrateLibrustc is now the non-suite-path test Step that first gets to eat the PathSet:

StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: true, only_hosts: true, should_run: 0x5610308a9ef0, make_run: 0x5610308a9f40, name: "bootstrap::core::build_steps::test::CrateLibrustc", kind: Test }, pathsets=[Set({test::compiler})]
[... some entries trimmed ...]
StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: true, only_hosts: true, should_run: 0x5610308afe60, make_run: 0x5610308afe80, name: "bootstrap::core::build_steps::test::CodegenCranelift", kind: Test }, pathsets=[]
StepDescription::run: handle all `PathSet`s: desc=StepDescription { default: true, only_hosts: true, should_run: 0x5610308b0d20, make_run: 0x5610308b0d40, name: "bootstrap::core::build_steps::test::CodegenGCC", kind: Test }, pathsets=[]

So now CodegenCranelift no longer tries to run because the PathSet got eaten by an earlier-registered test Step.

Ok. Now you may ask: how does #133492 regress ./x test compiler? Because before #133492, PathSet-based eligibility check only matches suffixes, but now #133492 also make PathSet-based eligibility also matches prefixes, making #133492 a necessary condition to break ./x test compiler but is not the "root cause", in my honest opinion. The sufficient condition is arguably the PathSet-related handling itself.

I'm also only describing why this is a problem, I don't quite have a good solution in mind just yet.

(Also I'm not sure why there's a specific distinction of suite_path, I suspect that's only used for compiletest-managed tests/.)