authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-10 10:48:52+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-20 00:14:58+00:00
logb8e568504e1dca38273a5845ac20d056cd7db5bb
tree82822f7b5d9be9aeec46a5cda41ea05a156137bc
parentb074fb7ddaefaa6ed9c5235d22b2f7378e3b80e8
signaturelock-open Commit is signed but in an unrecognized format.

std.Build: extend `test_runner` option to specify whether runner uses `std.zig.Server`

The previous logic here was trying to assume that custom test runners never used `std.zig.Server` to communicate with the build runner; however, it was flawed, because modifying the `test_runner` field on `Step.Compile` would not update this flag. That might have been intentional (allowing a way for the user to specify a custom test runner which *does* use the compiler server protocol), but if so, it was a flawed API, since it was too easy to update one field without updating the other. Instead, bundle these two pieces of state into a new type `std.Build.Step.Compile.TestRunner`. When passing a custom test runner, you are now *provided* to specify whether it is a "simple" runner, or whether it uses the compiler server protocol. This is a breaking change, but is unlikely to affect many people, since custom test runners are seldom used in the wild.

4 files changed, 29 insertions(+), 15 deletions(-)

lib/std/Build.zig+3-4
...@@ -979,7 +979,7 @@ pub const TestOptions = struct {...@@ -979,7 +979,7 @@ pub const TestOptions = struct {
979 /// Deprecated; use `.filters = &.{filter}` instead of `.filter = filter`.979 /// Deprecated; use `.filters = &.{filter}` instead of `.filter = filter`.
980 filter: ?[]const u8 = null,980 filter: ?[]const u8 = null,
981 filters: []const []const u8 = &.{},981 filters: []const []const u8 = &.{},
982 test_runner: ?LazyPath = null,982 test_runner: ?Step.Compile.TestRunner = null,
983 use_llvm: ?bool = null,983 use_llvm: ?bool = null,
984 use_lld: ?bool = null,984 use_lld: ?bool = null,
985 zig_lib_dir: ?LazyPath = null,985 zig_lib_dir: ?LazyPath = null,
...@@ -1136,9 +1136,8 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {...@@ -1136,9 +1136,8 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {
1136 run_step.addArtifactArg(exe);1136 run_step.addArtifactArg(exe);
1137 }1137 }
11381138
1139 if (exe.test_server_mode) {1139 const test_server_mode = if (exe.test_runner) |r| r.mode == .server else true;
1140 run_step.enableTestRunnerMode();1140 if (test_server_mode) run_step.enableTestRunnerMode();
1141 }
1142 } else {1141 } else {
1143 run_step.addArtifactArg(exe);1142 run_step.addArtifactArg(exe);
1144 }1143 }
lib/std/Build/Step/Compile.zig+18-9
...@@ -56,8 +56,7 @@ global_base: ?u64 = null,...@@ -56,8 +56,7 @@ global_base: ?u64 = null,
56zig_lib_dir: ?LazyPath,56zig_lib_dir: ?LazyPath,
57exec_cmd_args: ?[]const ?[]const u8,57exec_cmd_args: ?[]const ?[]const u8,
58filters: []const []const u8,58filters: []const []const u8,
59test_runner: ?LazyPath,59test_runner: ?TestRunner,
60test_server_mode: bool,
61wasi_exec_model: ?std.builtin.WasiExecModel = null,60wasi_exec_model: ?std.builtin.WasiExecModel = null,
6261
63installed_headers: ArrayList(HeaderInstallation),62installed_headers: ArrayList(HeaderInstallation),
...@@ -268,7 +267,7 @@ pub const Options = struct {...@@ -268,7 +267,7 @@ pub const Options = struct {
268 version: ?std.SemanticVersion = null,267 version: ?std.SemanticVersion = null,
269 max_rss: usize = 0,268 max_rss: usize = 0,
270 filters: []const []const u8 = &.{},269 filters: []const []const u8 = &.{},
271 test_runner: ?LazyPath = null,270 test_runner: ?TestRunner = null,
272 use_llvm: ?bool = null,271 use_llvm: ?bool = null,
273 use_lld: ?bool = null,272 use_lld: ?bool = null,
274 zig_lib_dir: ?LazyPath = null,273 zig_lib_dir: ?LazyPath = null,
...@@ -347,6 +346,14 @@ pub const HeaderInstallation = union(enum) {...@@ -347,6 +346,14 @@ pub const HeaderInstallation = union(enum) {
347 }346 }
348};347};
349348
349pub const TestRunner = struct {
350 path: LazyPath,
351 /// Test runners can either be "simple", running tests when spawned and terminating when the
352 /// tests are complete, or they can use `std.zig.Server` over stdio to interact more closely
353 /// with the build system.
354 mode: enum { simple, server },
355};
356
350pub fn create(owner: *std.Build, options: Options) *Compile {357pub fn create(owner: *std.Build, options: Options) *Compile {
351 const name = owner.dupe(options.name);358 const name = owner.dupe(options.name);
352 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {359 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
...@@ -411,8 +418,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -411,8 +418,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
411 .zig_lib_dir = null,418 .zig_lib_dir = null,
412 .exec_cmd_args = null,419 .exec_cmd_args = null,
413 .filters = options.filters,420 .filters = options.filters,
414 .test_runner = null,421 .test_runner = null, // set below
415 .test_server_mode = options.test_runner == null,
416 .rdynamic = false,422 .rdynamic = false,
417 .installed_path = null,423 .installed_path = null,
418 .force_undefined_symbols = StringHashMap(void).init(owner.allocator),424 .force_undefined_symbols = StringHashMap(void).init(owner.allocator),
...@@ -438,9 +444,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -438,9 +444,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
438 lp.addStepDependencies(&compile.step);444 lp.addStepDependencies(&compile.step);
439 }445 }
440446
441 if (options.test_runner) |lp| {447 if (options.test_runner) |runner| {
442 compile.test_runner = lp.dupe(compile.step.owner);448 compile.test_runner = .{
443 lp.addStepDependencies(&compile.step);449 .path = runner.path.dupe(compile.step.owner),
450 .mode = runner.mode,
451 };
452 runner.path.addStepDependencies(&compile.step);
444 }453 }
445454
446 // Only the PE/COFF format has a Resource Table which is where the manifest455 // Only the PE/COFF format has a Resource Table which is where the manifest
...@@ -1399,7 +1408,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1399,7 +1408,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
13991408
1400 if (compile.test_runner) |test_runner| {1409 if (compile.test_runner) |test_runner| {
1401 try zig_args.append("--test-runner");1410 try zig_args.append("--test-runner");
1402 try zig_args.append(test_runner.getPath2(b, step));1411 try zig_args.append(test_runner.path.getPath2(b, step));
1403 }1412 }
14041413
1405 for (b.debug_log_scopes) |log_scope| {1414 for (b.debug_log_scopes) |log_scope| {
test/standalone/test_runner_module_imports/build.zig+4-1
...@@ -13,7 +13,10 @@ pub fn build(b: *std.Build) void {...@@ -13,7 +13,10 @@ pub fn build(b: *std.Build) void {
1313
14 const t = b.addTest(.{14 const t = b.addTest(.{
15 .root_module = test_mod,15 .root_module = test_mod,
16 .test_runner = b.path("test_runner/main.zig"),16 .test_runner = .{
17 .path = b.path("test_runner/main.zig"),
18 .mode = .simple,
19 },
17 });20 });
1821
19 const test_step = b.step("test", "Run unit tests");22 const test_step = b.step("test", "Run unit tests");
test/standalone/test_runner_path/build.zig+4-1
...@@ -8,7 +8,10 @@ pub fn build(b: *std.Build) void {...@@ -8,7 +8,10 @@ pub fn build(b: *std.Build) void {
8 .target = b.graph.host,8 .target = b.graph.host,
9 .root_source_file = b.path("test.zig"),9 .root_source_file = b.path("test.zig"),
10 }) });10 }) });
11 test_exe.test_runner = b.path("test_runner.zig");11 test_exe.test_runner = .{
12 .path = b.path("test_runner.zig"),
13 .mode = .simple,
14 };
1215
13 const test_run = b.addRunArtifact(test_exe);16 const test_run = b.addRunArtifact(test_exe);
14 test_step.dependOn(&test_run.step);17 test_step.dependOn(&test_run.step);