authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-04-14 10:26:35+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-16 12:37:08+02:00
log5492b1e3264af7151adb215cdf905a41f93d2713
treed42f9e512f3838fd66c98c93437153e8cf90eda7
parentbde87cfc71a0b9d3d2e35e4b2181d1f956ff5711
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

tests: move incremental target matrix out of manifests

Having the matrix of test targets for incremental compilation in the individual test manifests has turned out to be inconvenient for a few reasons: the tests are almost certain to accidentally get out of sync, disabling targets entirely is annoying to do, and incr-check needs to take care to print the target in all error messages (which currently does not always happen). If I recall correctly, I originally designed it this way because it allows targets to be disabled at the granularity of individual tests, but there's an easier approach to that: just let a test manifest that it should be *skipped* on a certain target! As skipping is the rare case, and also the case you want readers to notice, it makes sense for *it* to be explicitly specified, like how unit tests use `error.SkipZigTest`. So, `incr-check` no longer runs through a list of targets specified in the manifest. Instead, it accepts (and, in fact, requires) a single target on the command line, and runs the test for that specific target. If the file contains a `#skip_target` directive for that target, then `incr-check` exits immediately, so we can still disable targets at individual test granularity, but you can also disable a target for all tests by just commenting it out of the matrix in `test/tests.zig`. As a nice bonus, this also allows the build system to run different incremental test targets in parallel, because the targets are now different steps. This definitely seems like a better way to split the work between the build system and incr-check---sorry for getting this wrong initially!

39 files changed, 258 insertions(+), 425 deletions(-)

test/incremental/add_decl-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/add_decl_namespaced-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/add_remove_struct_fields-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const S = struct { x: u8 };3const S = struct { x: u8 };
test/incremental/add_remove_toplevel_fields-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const S = @This();3const S = @This();
test/incremental/analysis_error_and_syntax_error-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/bad_import-7
...@@ -1,10 +1,3 @@...@@ -1,10 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7
8#update=initial version1#update=initial version
9#file=main.zig2#file=main.zig
10pub fn main() !void {3pub fn main() !void {
test/incremental/change_embed_file-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/change_enum_tag_type-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const Tag = u2;3const Tag = u2;
test/incremental/change_exports-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9export fn foo() void {}3export fn foo() void {}
test/incremental/change_fn_type-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/change_generic_line_number-4
...@@ -1,7 +1,3 @@...@@ -1,7 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-llvm
4//#target=wasm32-wasi-selfhosted
5#update=initial version1#update=initial version
6#file=main.zig2#file=main.zig
7const std = @import("std");3const std = @import("std");
test/incremental/change_line_number-4
...@@ -1,7 +1,3 @@...@@ -1,7 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-llvm
4//#target=wasm32-wasi-selfhosted
5#update=initial version1#update=initial version
6#file=main.zig2#file=main.zig
7const std = @import("std");3const std = @import("std");
test/incremental/change_module-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#module=foo=foo.zig1#module=foo=foo.zig
82
9#update=initial version3#update=initial version
test/incremental/change_panic_handler-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !u8 {3pub fn main() !u8 {
test/incremental/change_panic_handler_explicit-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !u8 {3pub fn main() !u8 {
test/incremental/change_shift_op-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/change_struct_same_fields-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const S = extern struct { x: u8, y: u8 };3const S = extern struct { x: u8, y: u8 };
test/incremental/change_zon_file-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/change_zon_file_no_result_type-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/compile_error_then_log-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version with compile error1#update=initial version with compile error
8#file=main.zig2#file=main.zig
9pub fn main() void {}3pub fn main() void {}
test/incremental/compile_log-7
...@@ -1,10 +1,3 @@...@@ -1,10 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7
8#update=initial version with no compile log1#update=initial version with no compile log
9#file=main.zig2#file=main.zig
10const std = @import("std");3const std = @import("std");
test/incremental/delete_comptime_decls-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() void {}3pub fn main() void {}
test/incremental/dependency_on_type_of_inferred_global-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const foo = @as(u8, 123);3const foo = @as(u8, 123);
test/incremental/fix_astgen_failure-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version with error1#update=initial version with error
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/function_becomes_inline-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=non-inline version1#update=non-inline version
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/hello-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/make_decl_pub-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const foo = @import("foo.zig");3const foo = @import("foo.zig");
test/incremental/modify_inline_fn-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/move_src-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/no_change_preserves_tag_names-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/recursive_function_becomes_non_recursive-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub fn main() !void {3pub fn main() !void {
test/incremental/remove_enum_field-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const MyEnum = enum(u8) {3const MyEnum = enum(u8) {
test/incremental/remove_invalid_union_backing_enum-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const E = enum { a, b, c };3const E = enum { a, b, c };
test/incremental/temporary_parse_error-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/incremental/type_becomes_comptime_only-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const SomeType = u32;3const SomeType = u32;
test/incremental/type_dependency_loop-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9pub const A = struct { b: B };3pub const A = struct { b: B };
test/incremental/unreferenced_error-6
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1#target=x86_64-linux-selfhosted
2//#target=x86_64-windows-selfhosted
3#target=x86_64-linux-cbe
4#target=x86_64-windows-cbe
5#target=x86_64-linux-llvm
6//#target=wasm32-wasi-selfhosted
7#update=initial version1#update=initial version
8#file=main.zig2#file=main.zig
9const std = @import("std");3const std = @import("std");
test/tests.zig+37-11
...@@ -1989,6 +1989,26 @@ const c_abi_targets = blk: {...@@ -1989,6 +1989,26 @@ const c_abi_targets = blk: {
1989 };1989 };
1990};1990};
19911991
1992/// Unlike `test_targets` and `c_abi_targets`, these targets are just simple strings which we pass
1993/// directly to `incr-check`. They include the target triple and the compiler backend.
1994///
1995/// If only one specific test is failing on a target, instead of entirely disabling the target here,
1996/// you can skip the target for that specific test only by adding a line like this to the manifest:
1997/// #skip_target=x86_64-linux-selfhosted
1998const incremental_targets: []const []const u8 = &.{
1999 // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others
2000 // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang
2001 // or LLVM). We just have a couple here to make sure that it works.
2002 "x86_64-linux-cbe",
2003 "x86_64-linux-llvm",
2004
2005 "x86_64-linux-selfhosted",
2006 // https://codeberg.org/ziglang/zig/issues/31773
2007 //"x86_64-windows-selfhosted",
2008 // https://codeberg.org/ziglang/zig/issues/31810
2009 //"wasm32-wasi-selfhosted",
2010};
2011
1992fn compatible32bitArch(b: *std.Build) ?std.Target.Cpu.Arch {2012fn compatible32bitArch(b: *std.Build) ?std.Target.Cpu.Arch {
1993 const host = b.graph.host.result;2013 const host = b.graph.host.result;
1994 return switch (host.os.tag) {2014 return switch (host.os.tag) {
...@@ -2935,21 +2955,27 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons...@@ -2935,21 +2955,27 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons
2935 if (std.mem.indexOf(u8, entry.path, test_filter)) |_| break;2955 if (std.mem.indexOf(u8, entry.path, test_filter)) |_| break;
2936 } else if (test_filters.len > 0) continue;2956 } else if (test_filters.len > 0) continue;
29372957
2938 const run = b.addRunArtifact(incr_check);2958 for (incremental_targets) |target_str| {
2939 run.setName(b.fmt("incr-check '{s}'", .{entry.basename}));2959 const run = b.addRunArtifact(incr_check);
2960 run.setName(b.fmt("incr-check {s} '{s}'", .{ target_str, entry.basename }));
29402961
2941 run.addArg(b.graph.zig_exe);2962 run.addArg(b.graph.zig_exe);
2942 run.addFileArg(b.path("test/incremental/").path(b, entry.path));2963 run.addFileArg(b.path("test/incremental/").path(b, entry.path));
2943 run.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) });2964 run.addArgs(&.{
2965 "--zig-lib-dir", b.graph.zig_lib_directory.path orelse ".",
2966 "--target", target_str,
2967 });
29442968
2945 if (b.enable_qemu) run.addArg("-fqemu");2969 run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc
2946 if (b.enable_wine) run.addArg("-fwine");
2947 if (b.enable_wasmtime) run.addArg("-fwasmtime");
2948 if (b.enable_darling) run.addArg("-fdarling");
29492970
2950 run.addCheck(.{ .expect_term = .{ .exited = 0 } });2971 if (b.enable_qemu) run.addArg("-fqemu");
2972 if (b.enable_wine) run.addArg("-fwine");
2973 if (b.enable_wasmtime) run.addArg("-fwasmtime");
2974 if (b.enable_darling) run.addArg("-fdarling");
29512975
2952 test_step.dependOn(&run.step);2976 run.addCheck(.{ .expect_term = .{ .exited = 0 } });
2977 test_step.dependOn(&run.step);
2978 }
2953 }2979 }
2954}2980}
29552981
tools/incr-check.zig+221-194
...@@ -4,32 +4,44 @@ const Dir = std.Io.Dir;...@@ -4,32 +4,44 @@ const Dir = std.Io.Dir;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const Cache = std.Build.Cache;5const Cache = std.Build.Cache;
66
7const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-log foo] [--preserve-tmp] [--zig-cc-binary /path/to/zig]";7const usage =
8 \\Usage: incr-check <zig binary path> <input file> [options]
9 \\Options:
10 \\ --target triple-backend
11 \\ --quiet
12 \\ --zig-lib-dir /path/to/zig/lib
13 \\ --zig-cc-binary /path/to/zig
14 \\ -fqemu
15 \\ -fwine
16 \\ -fwasmtime
17 \\Debug Options:
18 \\ --preserve-tmp
19 \\ --debug-log foo
20;
821
9pub const std_options: std.Options = .{22pub const std_options: std.Options = .{
10 .logFn = logImpl,23 .logFn = logImpl,
11};24};
12var log_cur_update: ?struct { *const Case.Target, *const Case.Update } = null;25var log_cur_update: ?*const Case.Update = null;
13fn logImpl(26fn logImpl(
14 comptime level: std.log.Level,27 comptime level: std.log.Level,
15 comptime scope: @EnumLiteral(),28 comptime scope: @EnumLiteral(),
16 comptime format: []const u8,29 comptime format: []const u8,
17 args: anytype,30 args: anytype,
18) void {31) void {
19 const target, const update = log_cur_update orelse {32 const update = log_cur_update orelse {
20 return std.log.defaultLog(level, scope, format, args);33 return std.log.defaultLog(level, scope, format, args);
21 };34 };
22 std.log.defaultLog(35 std.log.defaultLog(
23 level,36 level,
24 scope,37 scope,
25 "[{s}-{t} '{s}'] " ++ format,38 "['{s}'] " ++ format,
26 .{ target.query, target.backend, update.name } ++ args,39 .{update.name} ++ args,
27 );40 );
28}41}
2942
30pub fn main(init: std.process.Init) !void {43pub fn main(init: std.process.Init) !void {
31 const gpa = init.gpa;44 const gpa = init.gpa;
32 const fatal = std.process.fatal;
33 const arena = init.arena.allocator();45 const arena = init.arena.allocator();
34 const io = init.io;46 const io = init.io;
35 const environ_map = init.environ_map;47 const environ_map = init.environ_map;
...@@ -39,11 +51,13 @@ pub fn main(init: std.process.Init) !void {...@@ -39,11 +51,13 @@ pub fn main(init: std.process.Init) !void {
39 var opt_input_file_name: ?[]const u8 = null;51 var opt_input_file_name: ?[]const u8 = null;
40 var opt_lib_dir: ?[]const u8 = null;52 var opt_lib_dir: ?[]const u8 = null;
41 var opt_cc_zig: ?[]const u8 = null;53 var opt_cc_zig: ?[]const u8 = null;
54 var opt_target: ?struct { std.Target.Query, Backend } = null;
42 var preserve_tmp = false;55 var preserve_tmp = false;
43 var enable_qemu: bool = false;56 var enable_qemu: bool = false;
44 var enable_wine: bool = false;57 var enable_wine: bool = false;
45 var enable_wasmtime: bool = false;58 var enable_wasmtime: bool = false;
46 var enable_darling: bool = false;59 var enable_darling: bool = false;
60 var quiet: bool = false;
4761
48 var debug_log_args: std.ArrayList([]const u8) = .empty;62 var debug_log_args: std.ArrayList([]const u8) = .empty;
4963
...@@ -52,11 +66,16 @@ pub fn main(init: std.process.Init) !void {...@@ -52,11 +66,16 @@ pub fn main(init: std.process.Init) !void {
52 while (arg_it.next()) |arg| {66 while (arg_it.next()) |arg| {
53 if (arg.len > 0 and arg[0] == '-') {67 if (arg.len > 0 and arg[0] == '-') {
54 if (std.mem.eql(u8, arg, "--zig-lib-dir")) {68 if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
55 opt_lib_dir = arg_it.next() orelse fatal("expected arg after --zig-lib-dir\n{s}", .{usage});69 opt_lib_dir = arg_it.next() orelse badUsage("expected arg after --zig-lib-dir", .{});
70 } else if (std.mem.eql(u8, arg, "--target")) {
71 const str = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{});
72 opt_target = parseTargetQueryAndBackend(str, "");
73 } else if (std.mem.eql(u8, arg, "--quiet")) {
74 quiet = true;
56 } else if (std.mem.eql(u8, arg, "--debug-log")) {75 } else if (std.mem.eql(u8, arg, "--debug-log")) {
57 try debug_log_args.append(76 try debug_log_args.append(
58 arena,77 arena,
59 arg_it.next() orelse fatal("expected arg after --debug-log\n{s}", .{usage}),78 arg_it.next() orelse badUsage("expected arg after --debug-log", .{}),
60 );79 );
61 } else if (std.mem.eql(u8, arg, "--preserve-tmp")) {80 } else if (std.mem.eql(u8, arg, "--preserve-tmp")) {
62 preserve_tmp = true;81 preserve_tmp = true;
...@@ -69,9 +88,9 @@ pub fn main(init: std.process.Init) !void {...@@ -69,9 +88,9 @@ pub fn main(init: std.process.Init) !void {
69 } else if (std.mem.eql(u8, arg, "-fdarling")) {88 } else if (std.mem.eql(u8, arg, "-fdarling")) {
70 enable_darling = true;89 enable_darling = true;
71 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {90 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
72 opt_cc_zig = arg_it.next() orelse fatal("expected arg after --zig-cc-binary\n{s}", .{usage});91 opt_cc_zig = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{});
73 } else {92 } else {
74 fatal("unknown option '{s}'\n{s}", .{ arg, usage });93 badUsage("unknown option '{s}'", .{arg});
75 }94 }
76 continue;95 continue;
77 }96 }
...@@ -80,24 +99,29 @@ pub fn main(init: std.process.Init) !void {...@@ -80,24 +99,29 @@ pub fn main(init: std.process.Init) !void {
80 } else if (opt_input_file_name == null) {99 } else if (opt_input_file_name == null) {
81 opt_input_file_name = arg;100 opt_input_file_name = arg;
82 } else {101 } else {
83 fatal("unknown argument '{s}'\n{s}", .{ arg, usage });102 badUsage("unknown argument '{s}'\n{s}", .{ arg, usage });
84 }103 }
85 }104 }
86 const zig_exe = opt_zig_exe orelse fatal("missing path to zig\n{s}", .{usage});105 const zig_exe = opt_zig_exe orelse badUsage("missing path to zig", .{});
87 const input_file_name = opt_input_file_name orelse fatal("missing input file\n{s}", .{usage});106 const input_file_name = opt_input_file_name orelse badUsage("missing input file", .{});
107 const target_query, const backend = opt_target orelse badUsage("missing required option '--target'", .{});
108
109 if (backend == .cbe and opt_lib_dir == null) {
110 std.process.fatal("'--zig-lib-dir' required when using backend 'cbe'", .{});
111 }
88112
89 const input_file_bytes = try Dir.cwd().readFileAlloc(io, input_file_name, arena, .limited(std.math.maxInt(u32)));113 const input_file_bytes = try Dir.cwd().readFileAlloc(io, input_file_name, arena, .limited(std.math.maxInt(u32)));
90 const case = try Case.parse(arena, io, input_file_bytes);114 const case: Case = try .parse(arena, input_file_bytes);
91115
92 // Check now: if there are any targets using the `cbe` backend, we need the lib dir.116 for (case.skip_targets) |skip| {
93 if (opt_lib_dir == null) {117 if (target_query.eql(skip.query) and backend == skip.backend) {
94 for (case.targets) |target| {118 if (!quiet) std.log.warn("skipping test because of a 'skip_target' match", .{});
95 if (target.backend == .cbe) {119 return;
96 fatal("'--zig-lib-dir' requried when using backend 'cbe'", .{});
97 }
98 }120 }
99 }121 }
100122
123 const target = try std.zig.system.resolveTargetQuery(io, target_query);
124
101 const prog_node = std.Progress.start(io, .{});125 const prog_node = std.Progress.start(io, .{});
102 defer prog_node.end();126 defer prog_node.end();
103127
...@@ -122,143 +146,137 @@ pub fn main(init: std.process.Init) !void {...@@ -122,143 +146,137 @@ pub fn main(init: std.process.Init) !void {
122146
123 const host = try std.zig.system.resolveTargetQuery(io, .{});147 const host = try std.zig.system.resolveTargetQuery(io, .{});
124148
125 const debug_log_verbose = debug_log_args.items.len != 0;149 var child_args: std.ArrayList([]const u8) = .empty;
126150 try child_args.appendSlice(arena, &.{
127 for (case.targets) |target| {151 resolved_zig_exe,
128 const target_prog_node = node: {152 "build-exe",
129 var name_buf: [std.Progress.Node.max_name_len]u8 = undefined;153 "-fincremental",
130 const name = std.fmt.bufPrint(&name_buf, "{s}-{t}", .{ target.query, target.backend }) catch &name_buf;154 "-fno-ubsan-rt",
131 break :node prog_node.start(name, case.updates.len);155 "-target",
132 };156 try target_query.zigTriple(arena),
133 defer target_prog_node.end();157 "--cache-dir",
134158 ".local-cache",
135 if (debug_log_verbose) {159 "--global-cache-dir",
136 std.log.scoped(.status).info("target: '{s}-{t}'", .{ target.query, target.backend });160 ".global-cache",
137 }161 });
138 var child_args: std.ArrayList([]const u8) = .empty;162 try child_args.append(arena, "--listen=-");
139 try child_args.appendSlice(arena, &.{163
140 resolved_zig_exe,164 if (opt_resolved_lib_dir) |resolved_lib_dir| {
141 "build-exe",165 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
142 "-fincremental",166 }
143 "-fno-ubsan-rt",167 switch (backend) {
144 "-target",168 .sema => try child_args.append(arena, "-fno-emit-bin"),
145 target.query,169 .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
146 "--cache-dir",170 .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }),
147 ".local-cache",171 .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
148 "--global-cache-dir",172 }
149 ".global-cache",173 for (debug_log_args.items) |arg| {
150 });174 try child_args.appendSlice(arena, &.{ "--debug-log", arg });
151 try child_args.append(arena, "--listen=-");175 }
152176 for (case.modules) |mod| {
153 if (opt_resolved_lib_dir) |resolved_lib_dir| {177 try child_args.appendSlice(arena, &.{ "--dep", mod.name });
154 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });178 }
155 }179 try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file}));
156 switch (target.backend) {180 for (case.modules) |mod| {
157 .sema => try child_args.append(arena, "-fno-emit-bin"),181 try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file }));
158 .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),182 }
159 .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }),
160 .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
161 }
162 for (debug_log_args.items) |arg| {
163 try child_args.appendSlice(arena, &.{ "--debug-log", arg });
164 }
165 for (case.modules) |mod| {
166 try child_args.appendSlice(arena, &.{ "--dep", mod.name });
167 }
168 try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file}));
169 for (case.modules) |mod| {
170 try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file }));
171 }
172183
173 const zig_prog_node = target_prog_node.start("zig build-exe", 0);184 const zig_prog_node = prog_node.start("zig", 0);
174 defer zig_prog_node.end();185 defer zig_prog_node.end();
175
176 var cc_child_args: std.ArrayList([]const u8) = .empty;
177 if (target.backend == .cbe) {
178 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
179 try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe)
180 else
181 resolved_zig_exe;
182
183 try cc_child_args.appendSlice(arena, &.{
184 resolved_cc_zig_exe,
185 "cc",
186 "-target",
187 target.query,
188 "-I",
189 opt_resolved_lib_dir.?, // verified earlier
190 });
191186
192 try cc_child_args.append(arena, "-o");187 var cc_child_args: std.ArrayList([]const u8) = .empty;
193 }188 if (backend == .cbe) {
189 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
190 try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe)
191 else
192 resolved_zig_exe;
194193
195 var child = try std.process.spawn(io, .{194 try cc_child_args.appendSlice(arena, &.{
196 .argv = child_args.items,195 resolved_cc_zig_exe,
197 .stdin = .pipe,196 "cc",
198 .stdout = .pipe,197 "-target",
199 .stderr = .pipe,198 try target_query.zigTriple(arena),
200 .progress_node = zig_prog_node,199 "-I",
201 .cwd = .{ .path = tmp_dir_path },200 opt_resolved_lib_dir.?, // verified earlier
202 });201 });
203 defer child.kill(io);
204
205 var eval: Eval = .{
206 .arena = arena,
207 .io = io,
208 .case = case,
209 .host = host,
210 .target = target,
211 .tmp_dir = tmp_dir,
212 .tmp_dir_path = tmp_dir_path,
213 .child = &child,
214 .allow_stderr = debug_log_verbose,
215 .preserve_tmp_on_fatal = preserve_tmp,
216 .cc_child_args = &cc_child_args,
217 .enable_qemu = enable_qemu,
218 .enable_wine = enable_wine,
219 .enable_wasmtime = enable_wasmtime,
220 .enable_darling = enable_darling,
221 };
222202
223 var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;203 try cc_child_args.append(arena, "-o");
224 var multi_reader: Io.File.MultiReader = undefined;204 }
225 multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
226 defer multi_reader.deinit();
227205
228 for (case.updates) |update| {206 var child = try std.process.spawn(io, .{
229 var update_node = target_prog_node.start(update.name, 0);207 .argv = child_args.items,
230 defer update_node.end();208 .stdin = .pipe,
209 .stdout = .pipe,
210 .stderr = .pipe,
211 .progress_node = zig_prog_node,
212 .cwd = .{ .path = tmp_dir_path },
213 });
214 defer child.kill(io);
215
216 const updates_prog_node = prog_node.start("updates", case.updates.len);
217 defer updates_prog_node.end();
218
219 var eval: Eval = .{
220 .arena = arena,
221 .io = io,
222 .case = case,
223 .host = host,
224 .target = target,
225 .backend = backend,
226 .tmp_dir = tmp_dir,
227 .tmp_dir_path = tmp_dir_path,
228 .child = &child,
229 .allow_compiler_stderr = debug_log_args.items.len != 0,
230 .quiet = quiet,
231 .preserve_tmp_on_fatal = preserve_tmp,
232 .cc_child_args = &cc_child_args,
233 .enable_qemu = enable_qemu,
234 .enable_wine = enable_wine,
235 .enable_wasmtime = enable_wasmtime,
236 .enable_darling = enable_darling,
237 };
231238
232 if (debug_log_verbose) {239 var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
233 std.log.scoped(.status).info("update: '{s}'", .{update.name});240 var multi_reader: Io.File.MultiReader = undefined;
234 }241 multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
242 defer multi_reader.deinit();
235243
236 log_cur_update = .{ &target, &update };244 for (case.updates) |update| {
237 defer log_cur_update = null;245 var update_prog_node = updates_prog_node.start(update.name, 0);
246 defer update_prog_node.end();
238247
239 eval.write(update);248 if (debug_log_args.items.len != 0) {
240 try eval.requestUpdate();249 // Print a line separating the debug logs from the compiler in the stderr output.
241 try eval.check(&multi_reader, update, update_node);250 std.log.scoped(.status).info("update: '{s}'", .{update.name});
242 }251 }
243252
244 try eval.end(&multi_reader);253 log_cur_update = &update;
254 defer log_cur_update = null;
245255
246 waitChild(&child, &eval);256 eval.write(update);
257 try eval.requestUpdate();
258 try eval.check(&multi_reader, update, update_prog_node);
247 }259 }
260
261 try eval.end(&multi_reader);
262
263 waitChild(&child, &eval);
248}264}
249265
250const Eval = struct {266const Eval = struct {
251 arena: Allocator,267 arena: Allocator,
252 io: Io,268 io: Io,
253 host: std.Target,
254 case: Case,269 case: Case,
255 target: Case.Target,270 host: std.Target,
271 target: std.Target,
272 backend: Backend,
256 tmp_dir: Dir,273 tmp_dir: Dir,
257 tmp_dir_path: []const u8,274 tmp_dir_path: []const u8,
258 child: *std.process.Child,275 child: *std.process.Child,
259 allow_stderr: bool,276 allow_compiler_stderr: bool,
277 quiet: bool,
260 preserve_tmp_on_fatal: bool,278 preserve_tmp_on_fatal: bool,
261 /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.279 /// When `backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.
262 /// The arguments `out.c in.c` must be appended before spawning the subprocess.280 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
263 cc_child_args: *std.ArrayList([]const u8),281 cc_child_args: *std.ArrayList([]const u8),
264282
...@@ -307,7 +325,7 @@ const Eval = struct {...@@ -307,7 +325,7 @@ const Eval = struct {
307 .error_bundle => {325 .error_bundle => {
308 const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body);326 const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body);
309 if (stderr.bufferedLen() > 0) {327 if (stderr.bufferedLen() > 0) {
310 if (eval.allow_stderr) {328 if (eval.allow_compiler_stderr) {
311 std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()});329 std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()});
312 } else {330 } else {
313 eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()});331 eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()});
...@@ -325,14 +343,14 @@ const Eval = struct {...@@ -325,14 +343,14 @@ const Eval = struct {
325 _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable;343 _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable;
326344
327 if (stderr.bufferedLen() > 0) {345 if (stderr.bufferedLen() > 0) {
328 if (eval.allow_stderr) {346 if (eval.allow_compiler_stderr) {
329 std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()});347 std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()});
330 } else {348 } else {
331 eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()});349 eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()});
332 }350 }
333 stderr.tossBuffered();351 stderr.tossBuffered();
334 }352 }
335 if (eval.target.backend == .sema) {353 if (eval.backend == .sema) {
336 try eval.checkSuccessOutcome(update, null, prog_node);354 try eval.checkSuccessOutcome(update, null, prog_node);
337 continue;355 continue;
338 }356 }
...@@ -342,7 +360,7 @@ const Eval = struct {...@@ -342,7 +360,7 @@ const Eval = struct {
342360
343 const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{361 const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{
344 .root_name = "root", // corresponds to the module name "root"362 .root_name = "root", // corresponds to the module name "root"
345 .target = &eval.target.resolved,363 .target = &eval.target,
346 .output_mode = .Exe,364 .output_mode = .Exe,
347 });365 });
348 const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name });366 const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name });
...@@ -357,7 +375,7 @@ const Eval = struct {...@@ -357,7 +375,7 @@ const Eval = struct {
357375
358 const buffered_stderr = stderr.buffered();376 const buffered_stderr = stderr.buffered();
359 if (buffered_stderr.len > 0) {377 if (buffered_stderr.len > 0) {
360 if (eval.allow_stderr) {378 if (eval.allow_compiler_stderr) {
361 std.log.info("stderr:\n{s}", .{buffered_stderr});379 std.log.info("stderr:\n{s}", .{buffered_stderr});
362 } else {380 } else {
363 eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr});381 eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr});
...@@ -450,12 +468,12 @@ const Eval = struct {...@@ -450,12 +468,12 @@ const Eval = struct {
450 .stdout, .exit_code => {},468 .stdout, .exit_code => {},
451 }469 }
452 const emitted_path = opt_emitted_path orelse {470 const emitted_path = opt_emitted_path orelse {
453 std.debug.assert(eval.target.backend == .sema);471 std.debug.assert(eval.backend == .sema);
454 return;472 return;
455 };473 };
456 const io = eval.io;474 const io = eval.io;
457475
458 const binary_path = switch (eval.target.backend) {476 const binary_path = switch (eval.backend) {
459 .sema => unreachable,477 .sema => unreachable,
460 .selfhosted, .llvm => emitted_path,478 .selfhosted, .llvm => emitted_path,
461 .cbe => bin: {479 .cbe => bin: {
...@@ -470,15 +488,15 @@ const Eval = struct {...@@ -470,15 +488,15 @@ const Eval = struct {
470 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(488 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(
471 io,489 io,
472 &eval.host,490 &eval.host,
473 &eval.target.resolved,491 &eval.target,
474 .{ .link_libc = eval.target.backend == .cbe },492 .{ .link_libc = eval.backend == .cbe },
475 )) {493 )) {
476 .bad_dl, .bad_os_or_cpu => {494 .bad_dl, .bad_os_or_cpu => {
477 // This binary cannot be executed on this host.495 // This binary cannot be executed on this host.
478 if (eval.allow_stderr) {496 if (!eval.quiet) {
479 std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{497 std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{
480 try eval.host.zigTriple(eval.arena),498 try eval.host.zigTriple(eval.arena),
481 try eval.target.resolved.zigTriple(eval.arena),499 try eval.target.zigTriple(eval.arena),
482 });500 });
483 }501 }
484 return;502 return;
...@@ -534,10 +552,10 @@ const Eval = struct {...@@ -534,10 +552,10 @@ const Eval = struct {
534 }) catch |err| {552 }) catch |err| {
535 if (is_foreign) {553 if (is_foreign) {
536 // Chances are the foreign executor isn't available. Skip this evaluation.554 // Chances are the foreign executor isn't available. Skip this evaluation.
537 if (eval.allow_stderr) {555 if (!eval.quiet) {
538 std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{556 std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{
539 binary_path,557 binary_path,
540 try eval.target.resolved.zigTriple(eval.arena),558 try eval.target.zigTriple(eval.arena),
541 err,559 err,
542 });560 });
543 }561 }
...@@ -658,30 +676,30 @@ const Eval = struct {...@@ -658,30 +676,30 @@ const Eval = struct {
658 }676 }
659};677};
660678
679const Backend = enum {
680 /// Run semantic analysis only. Runtime output will not be tested, but we still verify
681 /// that compilation succeeds. Corresponds to `-fno-emit-bin`.
682 sema,
683 /// Use the self-hosted code generation backend for this target.
684 /// Corresponds to `-fno-llvm -fno-lld`.
685 selfhosted,
686 /// Use the LLVM backend.
687 /// Corresponds to `-fllvm -flld`.
688 llvm,
689 /// Use the C backend. The output is compiled with `zig cc`.
690 /// Corresponds to `-ofmt=c`.
691 cbe,
692};
693
661const Case = struct {694const Case = struct {
662 updates: []Update,695 updates: []Update,
663 root_source_file: []const u8,696 root_source_file: []const u8,
664 targets: []const Target,697 skip_targets: []const SkipTarget,
665 modules: []const Module,698 modules: []const Module,
666699
667 const Target = struct {700 const SkipTarget = struct {
668 query: []const u8,701 query: std.Target.Query,
669 resolved: std.Target,
670 backend: Backend,702 backend: Backend,
671 const Backend = enum {
672 /// Run semantic analysis only. Runtime output will not be tested, but we still verify
673 /// that compilation succeeds. Corresponds to `-fno-emit-bin`.
674 sema,
675 /// Use the self-hosted code generation backend for this target.
676 /// Corresponds to `-fno-llvm -fno-lld`.
677 selfhosted,
678 /// Use the LLVM backend.
679 /// Corresponds to `-fllvm -flld`.
680 llvm,
681 /// Use the C backend. The output is compiled with `zig cc`.
682 /// Corresponds to `-ofmt=c`.
683 cbe,
684 };
685 };703 };
686704
687 const Module = struct {705 const Module = struct {
...@@ -721,10 +739,10 @@ const Case = struct {...@@ -721,10 +739,10 @@ const Case = struct {
721 },739 },
722 };740 };
723741
724 fn parse(arena: Allocator, io: Io, bytes: []const u8) !Case {742 fn parse(arena: Allocator, bytes: []const u8) !Case {
725 const fatal = std.process.fatal;743 const fatal = std.process.fatal;
726744
727 var targets: std.ArrayList(Target) = .empty;745 var skip_targets: std.ArrayList(SkipTarget) = .empty;
728 var modules: std.ArrayList(Module) = .empty;746 var modules: std.ArrayList(Module) = .empty;
729 var updates: std.ArrayList(Update) = .empty;747 var updates: std.ArrayList(Update) = .empty;
730 var changes: std.ArrayList(FullContents) = .empty;748 var changes: std.ArrayList(FullContents) = .empty;
...@@ -739,29 +757,13 @@ const Case = struct {...@@ -739,29 +757,13 @@ const Case = struct {
739 const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment757 const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment
740 if (val.len == 0) {758 if (val.len == 0) {
741 fatal("line {d}: missing value", .{line_n});759 fatal("line {d}: missing value", .{line_n});
742 } else if (std.mem.eql(u8, key, "target")) {760 } else if (std.mem.eql(u8, key, "skip_target")) {
743 const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse761 const query, const backend = parseTargetQueryAndBackend(
744 fatal("line {d}: target does not include backend", .{line_n});762 val,
745763 try std.fmt.allocPrint(arena, "line {d}: ", .{line_n}),
746 const query = val[0..split_idx];764 );
747765 try skip_targets.append(arena, .{
748 const backend_str = val[split_idx + 1 ..];
749 const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse
750 fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str });
751
752 const parsed_query = std.Build.parseTargetQuery(.{
753 .arch_os_abi = query,
754 .object_format = switch (backend) {
755 .sema, .selfhosted, .llvm => null,
756 .cbe => "c",
757 },
758 }) catch fatal("line {d}: invalid target query '{s}'", .{ line_n, query });
759
760 const resolved = try std.zig.system.resolveTargetQuery(io, parsed_query);
761
762 try targets.append(arena, .{
763 .query = query,766 .query = query,
764 .resolved = resolved,
765 .backend = backend,767 .backend = backend,
766 });768 });
767 } else if (std.mem.eql(u8, key, "module")) {769 } else if (std.mem.eql(u8, key, "module")) {
...@@ -872,10 +874,6 @@ const Case = struct {...@@ -872,10 +874,6 @@ const Case = struct {
872 }874 }
873 }875 }
874876
875 if (targets.items.len == 0) {
876 fatal("missing target", .{});
877 }
878
879 if (changes.items.len > 0) {877 if (changes.items.len > 0) {
880 const last_update = &updates.items[updates.items.len - 1];878 const last_update = &updates.items[updates.items.len - 1];
881 last_update.changes = changes.items; // arena so no need for toOwnedSlice879 last_update.changes = changes.items; // arena so no need for toOwnedSlice
...@@ -885,7 +883,7 @@ const Case = struct {...@@ -885,7 +883,7 @@ const Case = struct {
885 return .{883 return .{
886 .updates = updates.items,884 .updates = updates.items,
887 .root_source_file = root_source_file orelse fatal("missing root source file", .{}),885 .root_source_file = root_source_file orelse fatal("missing root source file", .{}),
888 .targets = targets.items, // arena so no need for toOwnedSlice886 .skip_targets = skip_targets.items, // arena so no need for toOwnedSlice
889 .modules = modules.items,887 .modules = modules.items,
890 };888 };
891 }889 }
...@@ -970,3 +968,32 @@ fn rand64(io: Io) u64 {...@@ -970,3 +968,32 @@ fn rand64(io: Io) u64 {
970 io.random(@ptrCast(&x));968 io.random(@ptrCast(&x));
971 return x;969 return x;
972}970}
971
972/// Calls `std.process.fatal` on error. The error messages are prefixed with `err_prefix`.
973fn parseTargetQueryAndBackend(input_str: []const u8, err_prefix: []const u8) struct { std.Target.Query, Backend } {
974 const fatal = std.process.fatal;
975
976 const split_idx = std.mem.lastIndexOfScalar(u8, input_str, '-') orelse
977 fatal("{s}target does not include backend", .{err_prefix});
978
979 const query = input_str[0..split_idx];
980
981 const backend_str = input_str[split_idx + 1 ..];
982 const backend: Backend = std.meta.stringToEnum(Backend, backend_str) orelse
983 fatal("{s}invalid backend '{s}'", .{ err_prefix, backend_str });
984
985 const parsed_query = std.Build.parseTargetQuery(.{
986 .arch_os_abi = query,
987 .object_format = switch (backend) {
988 .sema, .selfhosted, .llvm => null,
989 .cbe => "c",
990 },
991 }) catch fatal("{s}invalid target query '{s}'", .{ err_prefix, query });
992
993 return .{ parsed_query, backend };
994}
995
996fn badUsage(comptime fmt: []const u8, args: anytype) noreturn {
997 std.log.err(fmt ++ "\n{s}", args ++ .{usage});
998 std.process.exit(1);
999}