mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
make the build runner and test runner talk to each other
std.Build.addTest creates a CompileStep as before, however, this kind of step no longer actually runs the unit tests. Instead it only compiles it, and one must additionally create a RunStep from the CompileStep in order to actually run the tests. RunStep gains integration with the default test runner, which now supports the standard --listen=- argument in order to communicate over stdin and stdout. It also reports test statistics; how many passed, failed, and leaked, as well as directly associating the relevant stderr with the particular test name that failed. This separation of CompileStep and RunStep means that `CompileStep.Kind.test_exe` is no longer needed, and therefore has been removed in this commit. * build runner: show unit test statistics in build summary * added Step.writeManifest since many steps want to treat it as a warning and emit the same message if it fails. * RunStep: fixed error message that prints the failed command printing the original argv and not the adjusted argv in case an interpreter was used. * RunStep: fixed not passing the command line arguments to the interpreter. * move src/Server.zig to std.zig.Server so that the default test runner can use it. * the simpler test runner function which is used by work-in-progress backends now no longer prints to stderr, which is necessary in order for the build runner to not print the stderr as a warning message.
This commit is contained in:
parent
ef5f8bd7c6
commit
ede5dcffea
@ -518,6 +518,7 @@ set(ZIG_STAGE2_SOURCES
|
|||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
|
||||||
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
|
"${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
|
||||||
@ -623,7 +624,6 @@ set(ZIG_STAGE2_SOURCES
|
|||||||
"${CMAKE_SOURCE_DIR}/src/print_targets.zig"
|
"${CMAKE_SOURCE_DIR}/src/print_targets.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/src/print_zir.zig"
|
"${CMAKE_SOURCE_DIR}/src/print_zir.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/src/register_manager.zig"
|
"${CMAKE_SOURCE_DIR}/src/register_manager.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/src/Server.zig"
|
|
||||||
"${CMAKE_SOURCE_DIR}/src/target.zig"
|
"${CMAKE_SOURCE_DIR}/src/target.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/src/tracy.zig"
|
"${CMAKE_SOURCE_DIR}/src/tracy.zig"
|
||||||
"${CMAKE_SOURCE_DIR}/src/translate_c.zig"
|
"${CMAKE_SOURCE_DIR}/src/translate_c.zig"
|
||||||
|
|||||||
@ -416,6 +416,12 @@ fn runStepNames(
|
|||||||
}
|
}
|
||||||
assert(run.memory_blocked_steps.items.len == 0);
|
assert(run.memory_blocked_steps.items.len == 0);
|
||||||
|
|
||||||
|
var test_skip_count: usize = 0;
|
||||||
|
var test_fail_count: usize = 0;
|
||||||
|
var test_pass_count: usize = 0;
|
||||||
|
var test_leak_count: usize = 0;
|
||||||
|
var test_count: usize = 0;
|
||||||
|
|
||||||
var success_count: usize = 0;
|
var success_count: usize = 0;
|
||||||
var skipped_count: usize = 0;
|
var skipped_count: usize = 0;
|
||||||
var failure_count: usize = 0;
|
var failure_count: usize = 0;
|
||||||
@ -425,6 +431,12 @@ fn runStepNames(
|
|||||||
defer compile_error_steps.deinit(gpa);
|
defer compile_error_steps.deinit(gpa);
|
||||||
|
|
||||||
for (step_stack.keys()) |s| {
|
for (step_stack.keys()) |s| {
|
||||||
|
test_fail_count += s.test_results.fail_count;
|
||||||
|
test_skip_count += s.test_results.skip_count;
|
||||||
|
test_leak_count += s.test_results.leak_count;
|
||||||
|
test_pass_count += s.test_results.passCount();
|
||||||
|
test_count += s.test_results.test_count;
|
||||||
|
|
||||||
switch (s.state) {
|
switch (s.state) {
|
||||||
.precheck_unstarted => unreachable,
|
.precheck_unstarted => unreachable,
|
||||||
.precheck_started => unreachable,
|
.precheck_started => unreachable,
|
||||||
@ -468,6 +480,11 @@ fn runStepNames(
|
|||||||
if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {};
|
if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {};
|
||||||
if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {};
|
if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {};
|
||||||
|
|
||||||
|
if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
|
||||||
|
if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {};
|
||||||
|
if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {};
|
||||||
|
if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {};
|
||||||
|
|
||||||
if (run.enable_summary == null) {
|
if (run.enable_summary == null) {
|
||||||
ttyconf.setColor(stderr, .Dim) catch {};
|
ttyconf.setColor(stderr, .Dim) catch {};
|
||||||
stderr.writeAll(" (disable with -fno-summary)") catch {};
|
stderr.writeAll(" (disable with -fno-summary)") catch {};
|
||||||
@ -566,6 +583,13 @@ fn printTreeStep(
|
|||||||
try ttyconf.setColor(stderr, .Green);
|
try ttyconf.setColor(stderr, .Green);
|
||||||
if (s.result_cached) {
|
if (s.result_cached) {
|
||||||
try stderr.writeAll(" cached");
|
try stderr.writeAll(" cached");
|
||||||
|
} else if (s.test_results.test_count > 0) {
|
||||||
|
const pass_count = s.test_results.passCount();
|
||||||
|
try stderr.writer().print(" {d} passed", .{pass_count});
|
||||||
|
if (s.test_results.skip_count > 0) {
|
||||||
|
try ttyconf.setColor(stderr, .Yellow);
|
||||||
|
try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
try stderr.writeAll(" success");
|
try stderr.writeAll(" success");
|
||||||
}
|
}
|
||||||
@ -609,15 +633,46 @@ fn printTreeStep(
|
|||||||
},
|
},
|
||||||
|
|
||||||
.failure => {
|
.failure => {
|
||||||
try ttyconf.setColor(stderr, .Red);
|
|
||||||
if (s.result_error_bundle.errorMessageCount() > 0) {
|
if (s.result_error_bundle.errorMessageCount() > 0) {
|
||||||
|
try ttyconf.setColor(stderr, .Red);
|
||||||
try stderr.writer().print(" {d} errors\n", .{
|
try stderr.writer().print(" {d} errors\n", .{
|
||||||
s.result_error_bundle.errorMessageCount(),
|
s.result_error_bundle.errorMessageCount(),
|
||||||
});
|
});
|
||||||
|
try ttyconf.setColor(stderr, .Reset);
|
||||||
|
} else if (!s.test_results.isSuccess()) {
|
||||||
|
try stderr.writer().print(" {d}/{d} passed", .{
|
||||||
|
s.test_results.passCount(), s.test_results.test_count,
|
||||||
|
});
|
||||||
|
if (s.test_results.fail_count > 0) {
|
||||||
|
try stderr.writeAll(", ");
|
||||||
|
try ttyconf.setColor(stderr, .Red);
|
||||||
|
try stderr.writer().print("{d} failed", .{
|
||||||
|
s.test_results.fail_count,
|
||||||
|
});
|
||||||
|
try ttyconf.setColor(stderr, .Reset);
|
||||||
|
}
|
||||||
|
if (s.test_results.skip_count > 0) {
|
||||||
|
try stderr.writeAll(", ");
|
||||||
|
try ttyconf.setColor(stderr, .Yellow);
|
||||||
|
try stderr.writer().print("{d} skipped", .{
|
||||||
|
s.test_results.skip_count,
|
||||||
|
});
|
||||||
|
try ttyconf.setColor(stderr, .Reset);
|
||||||
|
}
|
||||||
|
if (s.test_results.leak_count > 0) {
|
||||||
|
try stderr.writeAll(", ");
|
||||||
|
try ttyconf.setColor(stderr, .Red);
|
||||||
|
try stderr.writer().print("{d} leaked", .{
|
||||||
|
s.test_results.leak_count,
|
||||||
|
});
|
||||||
|
try ttyconf.setColor(stderr, .Reset);
|
||||||
|
}
|
||||||
|
try stderr.writeAll("\n");
|
||||||
} else {
|
} else {
|
||||||
|
try ttyconf.setColor(stderr, .Red);
|
||||||
try stderr.writeAll(" failure\n");
|
try stderr.writeAll(" failure\n");
|
||||||
|
try ttyconf.setColor(stderr, .Reset);
|
||||||
}
|
}
|
||||||
try ttyconf.setColor(stderr, .Reset);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -531,7 +531,6 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
|
|||||||
|
|
||||||
pub const TestOptions = struct {
|
pub const TestOptions = struct {
|
||||||
name: []const u8 = "test",
|
name: []const u8 = "test",
|
||||||
kind: CompileStep.Kind = .@"test",
|
|
||||||
root_source_file: FileSource,
|
root_source_file: FileSource,
|
||||||
target: CrossTarget = .{},
|
target: CrossTarget = .{},
|
||||||
optimize: std.builtin.Mode = .Debug,
|
optimize: std.builtin.Mode = .Debug,
|
||||||
@ -542,7 +541,7 @@ pub const TestOptions = struct {
|
|||||||
pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
|
pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
|
||||||
return CompileStep.create(b, .{
|
return CompileStep.create(b, .{
|
||||||
.name = options.name,
|
.name = options.name,
|
||||||
.kind = options.kind,
|
.kind = .@"test",
|
||||||
.root_source_file = options.root_source_file,
|
.root_source_file = options.root_source_file,
|
||||||
.target = options.target,
|
.target = options.target,
|
||||||
.optimize = options.optimize,
|
.optimize = options.optimize,
|
||||||
@ -626,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
|
|||||||
/// Creates a `RunStep` with an executable built with `addExecutable`.
|
/// Creates a `RunStep` with an executable built with `addExecutable`.
|
||||||
/// Add command line arguments with methods of `RunStep`.
|
/// Add command line arguments with methods of `RunStep`.
|
||||||
pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
|
pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
|
||||||
assert(exe.kind == .exe or exe.kind == .test_exe);
|
|
||||||
|
|
||||||
// It doesn't have to be native. We catch that if you actually try to run it.
|
// It doesn't have to be native. We catch that if you actually try to run it.
|
||||||
// Consider that this is declarative; the run step may not be run unless a user
|
// Consider that this is declarative; the run step may not be run unless a user
|
||||||
// option is supplied.
|
// option is supplied.
|
||||||
const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name}));
|
const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name}));
|
||||||
run_step.addArtifactArg(exe);
|
run_step.addArtifactArg(exe);
|
||||||
|
|
||||||
if (exe.kind == .test_exe) {
|
if (exe.kind == .@"test") {
|
||||||
run_step.addArg(b.zig_exe);
|
run_step.stdio = .zig_test;
|
||||||
|
run_step.addArgs(&.{"--listen=-"});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exe.vcpkg_bin_path) |path| {
|
if (exe.vcpkg_bin_path) |path| {
|
||||||
|
|||||||
@ -289,7 +289,6 @@ pub const Kind = enum {
|
|||||||
lib,
|
lib,
|
||||||
obj,
|
obj,
|
||||||
@"test",
|
@"test",
|
||||||
test_exe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Linkage = enum { dynamic, static };
|
pub const Linkage = enum { dynamic, static };
|
||||||
@ -328,7 +327,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {
|
|||||||
.exe => "zig build-exe",
|
.exe => "zig build-exe",
|
||||||
.lib => "zig build-lib",
|
.lib => "zig build-lib",
|
||||||
.obj => "zig build-obj",
|
.obj => "zig build-obj",
|
||||||
.test_exe, .@"test" => "zig test",
|
.@"test" => "zig test",
|
||||||
},
|
},
|
||||||
name_adjusted,
|
name_adjusted,
|
||||||
@tagName(options.optimize),
|
@tagName(options.optimize),
|
||||||
@ -410,7 +409,7 @@ fn computeOutFileNames(self: *CompileStep) void {
|
|||||||
.output_mode = switch (self.kind) {
|
.output_mode = switch (self.kind) {
|
||||||
.lib => .Lib,
|
.lib => .Lib,
|
||||||
.obj => .Obj,
|
.obj => .Obj,
|
||||||
.exe, .@"test", .test_exe => .Exe,
|
.exe, .@"test" => .Exe,
|
||||||
},
|
},
|
||||||
.link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
|
.link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
|
||||||
.dynamic => .Dynamic,
|
.dynamic => .Dynamic,
|
||||||
@ -621,7 +620,7 @@ pub fn producesPdbFile(self: *CompileStep) bool {
|
|||||||
if (!self.target.isWindows() and !self.target.isUefi()) return false;
|
if (!self.target.isWindows() and !self.target.isUefi()) return false;
|
||||||
if (self.target.getObjectFormat() == .c) return false;
|
if (self.target.getObjectFormat() == .c) return false;
|
||||||
if (self.strip == true) return false;
|
if (self.strip == true) return false;
|
||||||
return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
|
return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn linkLibC(self: *CompileStep) void {
|
pub fn linkLibC(self: *CompileStep) void {
|
||||||
@ -850,19 +849,19 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
|
|||||||
|
|
||||||
pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
|
pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
|
||||||
const b = self.step.owner;
|
const b = self.step.owner;
|
||||||
assert(self.kind == .@"test" or self.kind == .test_exe);
|
assert(self.kind == .@"test");
|
||||||
self.name_prefix = b.dupe(text);
|
self.name_prefix = b.dupe(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
|
pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
|
||||||
const b = self.step.owner;
|
const b = self.step.owner;
|
||||||
assert(self.kind == .@"test" or self.kind == .test_exe);
|
assert(self.kind == .@"test");
|
||||||
self.filter = if (text) |t| b.dupe(t) else null;
|
self.filter = if (text) |t| b.dupe(t) else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
|
pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
|
||||||
const b = self.step.owner;
|
const b = self.step.owner;
|
||||||
assert(self.kind == .@"test" or self.kind == .test_exe);
|
assert(self.kind == .@"test");
|
||||||
self.test_runner = if (path) |p| b.dupePath(p) else null;
|
self.test_runner = if (path) |p| b.dupePath(p) else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,7 +937,7 @@ pub fn getOutputLibSource(self: *CompileStep) FileSource {
|
|||||||
/// Returns the generated header file.
|
/// Returns the generated header file.
|
||||||
/// This function can only be called for libraries or object files which have `emit_h` set.
|
/// This function can only be called for libraries or object files which have `emit_h` set.
|
||||||
pub fn getOutputHSource(self: *CompileStep) FileSource {
|
pub fn getOutputHSource(self: *CompileStep) FileSource {
|
||||||
assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
|
assert(self.kind != .exe and self.kind != .@"test");
|
||||||
assert(self.emit_h);
|
assert(self.emit_h);
|
||||||
return .{ .generated = &self.output_h_path_source };
|
return .{ .generated = &self.output_h_path_source };
|
||||||
}
|
}
|
||||||
@ -1243,7 +1242,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
.exe => "build-exe",
|
.exe => "build-exe",
|
||||||
.obj => "build-obj",
|
.obj => "build-obj",
|
||||||
.@"test" => "test",
|
.@"test" => "test",
|
||||||
.test_exe => "test",
|
|
||||||
};
|
};
|
||||||
try zig_args.append(cmd);
|
try zig_args.append(cmd);
|
||||||
|
|
||||||
@ -1293,7 +1291,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
|
|
||||||
.other_step => |other| switch (other.kind) {
|
.other_step => |other| switch (other.kind) {
|
||||||
.exe => @panic("Cannot link with an executable build artifact"),
|
.exe => @panic("Cannot link with an executable build artifact"),
|
||||||
.test_exe => @panic("Cannot link with an executable build artifact"),
|
|
||||||
.@"test" => @panic("Cannot link with a test"),
|
.@"test" => @panic("Cannot link with a test"),
|
||||||
.obj => {
|
.obj => {
|
||||||
try zig_args.append(other.getOutputSource().getPath(b));
|
try zig_args.append(other.getOutputSource().getPath(b));
|
||||||
@ -1661,83 +1658,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
try zig_args.append("--test-cmd-bin");
|
try zig_args.append("--test-cmd-bin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
|
|
||||||
|
|
||||||
switch (b.host.getExternalExecutor(self.target_info, .{
|
|
||||||
.qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
|
|
||||||
.link_libc = transitive_deps.is_linking_libc,
|
|
||||||
})) {
|
|
||||||
.native => {},
|
|
||||||
.bad_dl, .bad_os_or_cpu => {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
.rosetta => if (b.enable_rosetta) {
|
|
||||||
try zig_args.append("--test-cmd-bin");
|
|
||||||
} else {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
.qemu => |bin_name| ok: {
|
|
||||||
if (b.enable_qemu) qemu: {
|
|
||||||
const glibc_dir_arg = if (need_cross_glibc)
|
|
||||||
b.glibc_runtimes_dir orelse break :qemu
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append(bin_name);
|
|
||||||
if (glibc_dir_arg) |dir| {
|
|
||||||
// TODO look into making this a call to `linuxTriple`. This
|
|
||||||
// needs the directory to be called "i686" rather than
|
|
||||||
// "x86" which is why we do it manually here.
|
|
||||||
const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
|
|
||||||
const cpu_arch = self.target.getCpuArch();
|
|
||||||
const os_tag = self.target.getOsTag();
|
|
||||||
const abi = self.target.getAbi();
|
|
||||||
const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
|
|
||||||
"i686"
|
|
||||||
else
|
|
||||||
@tagName(cpu_arch);
|
|
||||||
const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{
|
|
||||||
dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
|
|
||||||
});
|
|
||||||
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append("-L");
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append(full_dir);
|
|
||||||
}
|
|
||||||
try zig_args.append("--test-cmd-bin");
|
|
||||||
break :ok;
|
|
||||||
}
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
.wine => |bin_name| if (b.enable_wine) {
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append(bin_name);
|
|
||||||
try zig_args.append("--test-cmd-bin");
|
|
||||||
} else {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
.wasmtime => |bin_name| if (b.enable_wasmtime) {
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append(bin_name);
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append("--dir=.");
|
|
||||||
try zig_args.append("--test-cmd-bin");
|
|
||||||
} else {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
.darling => |bin_name| if (b.enable_darling) {
|
|
||||||
try zig_args.append("--test-cmd");
|
|
||||||
try zig_args.append(bin_name);
|
|
||||||
try zig_args.append("--test-cmd-bin");
|
|
||||||
} else {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (self.kind == .test_exe) {
|
|
||||||
try zig_args.append("--test-no-exec");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.appendModuleArgs(&zig_args);
|
try self.appendModuleArgs(&zig_args);
|
||||||
|
|||||||
@ -32,12 +32,11 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
|
|||||||
.artifact = artifact,
|
.artifact = artifact,
|
||||||
.dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
|
.dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
|
||||||
.obj => @panic("Cannot install a .obj build artifact."),
|
.obj => @panic("Cannot install a .obj build artifact."),
|
||||||
.@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),
|
.exe, .@"test" => InstallDir{ .bin = {} },
|
||||||
.exe, .test_exe => InstallDir{ .bin = {} },
|
|
||||||
.lib => InstallDir{ .lib = {} },
|
.lib => InstallDir{ .lib = {} },
|
||||||
},
|
},
|
||||||
.pdb_dir = if (artifact.producesPdbFile()) blk: {
|
.pdb_dir = if (artifact.producesPdbFile()) blk: {
|
||||||
if (artifact.kind == .exe or artifact.kind == .test_exe) {
|
if (artifact.kind == .exe or artifact.kind == .@"test") {
|
||||||
break :blk InstallDir{ .bin = {} };
|
break :blk InstallDir{ .bin = {} };
|
||||||
} else {
|
} else {
|
||||||
break :blk InstallDir{ .lib = {} };
|
break :blk InstallDir{ .lib = {} };
|
||||||
|
|||||||
@ -92,6 +92,9 @@ pub const StdIo = union(enum) {
|
|||||||
/// Note that an explicit check for exit code 0 needs to be added to this
|
/// Note that an explicit check for exit code 0 needs to be added to this
|
||||||
/// list if such a check is desireable.
|
/// list if such a check is desireable.
|
||||||
check: std.ArrayList(Check),
|
check: std.ArrayList(Check),
|
||||||
|
/// This RunStep is running a zig unit test binary and will communicate
|
||||||
|
/// extra metadata over the IPC protocol.
|
||||||
|
zig_test,
|
||||||
|
|
||||||
pub const Check = union(enum) {
|
pub const Check = union(enum) {
|
||||||
expect_stderr_exact: []const u8,
|
expect_stderr_exact: []const u8,
|
||||||
@ -324,6 +327,7 @@ fn hasSideEffects(self: RunStep) bool {
|
|||||||
.infer_from_args => !self.hasAnyOutputArgs(),
|
.infer_from_args => !self.hasAnyOutputArgs(),
|
||||||
.inherit => true,
|
.inherit => true,
|
||||||
.check => false,
|
.check => false,
|
||||||
|
.zig_test => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,11 +370,6 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||||
// Unfortunately we have no way to collect progress from arbitrary programs.
|
|
||||||
// Perhaps in the future Zig could offer some kind of opt-in IPC mechanism that
|
|
||||||
// processes could use to supply progress updates.
|
|
||||||
_ = prog_node;
|
|
||||||
|
|
||||||
const b = step.owner;
|
const b = step.owner;
|
||||||
const arena = b.allocator;
|
const arena = b.allocator;
|
||||||
const self = @fieldParentPtr(RunStep, "step", step);
|
const self = @fieldParentPtr(RunStep, "step", step);
|
||||||
@ -439,7 +438,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
hashStdIo(&man.hash, self.stdio);
|
hashStdIo(&man.hash, self.stdio);
|
||||||
|
|
||||||
if (has_side_effects) {
|
if (has_side_effects) {
|
||||||
try runCommand(self, argv_list.items, has_side_effects, null);
|
try runCommand(self, argv_list.items, has_side_effects, null, prog_node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,8 +491,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
argv_list.items[placeholder.index] = cli_arg;
|
argv_list.items[placeholder.index] = cli_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
try runCommand(self, argv_list.items, has_side_effects, &digest);
|
try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node);
|
||||||
try man.writeManifest();
|
|
||||||
|
try step.writeManifest(&man);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn formatTerm(
|
fn formatTerm(
|
||||||
@ -546,6 +546,7 @@ fn runCommand(
|
|||||||
argv: []const []const u8,
|
argv: []const []const u8,
|
||||||
has_side_effects: bool,
|
has_side_effects: bool,
|
||||||
digest: ?*const [std.Build.Cache.hex_digest_len]u8,
|
digest: ?*const [std.Build.Cache.hex_digest_len]u8,
|
||||||
|
prog_node: *std.Progress.Node,
|
||||||
) !void {
|
) !void {
|
||||||
const step = &self.step;
|
const step = &self.step;
|
||||||
const b = step.owner;
|
const b = step.owner;
|
||||||
@ -554,7 +555,15 @@ fn runCommand(
|
|||||||
try step.handleChildProcUnsupported(self.cwd, argv);
|
try step.handleChildProcUnsupported(self.cwd, argv);
|
||||||
try Step.handleVerbose(step.owner, self.cwd, argv);
|
try Step.handleVerbose(step.owner, self.cwd, argv);
|
||||||
|
|
||||||
const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: {
|
const allow_skip = switch (self.stdio) {
|
||||||
|
.check, .zig_test => self.skip_foreign_checks,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var interp_argv = std.ArrayList([]const u8).init(b.allocator);
|
||||||
|
defer interp_argv.deinit();
|
||||||
|
|
||||||
|
const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: {
|
||||||
// InvalidExe: cpu arch mismatch
|
// InvalidExe: cpu arch mismatch
|
||||||
// FileNotFound: can happen with a wrong dynamic linker path
|
// FileNotFound: can happen with a wrong dynamic linker path
|
||||||
if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
|
if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
|
||||||
@ -566,10 +575,10 @@ fn runCommand(
|
|||||||
.artifact => |exe| exe,
|
.artifact => |exe| exe,
|
||||||
else => break :interpret,
|
else => break :interpret,
|
||||||
};
|
};
|
||||||
if (exe.kind != .exe) break :interpret;
|
switch (exe.kind) {
|
||||||
|
.exe, .@"test" => {},
|
||||||
var interp_argv = std.ArrayList([]const u8).init(b.allocator);
|
else => break :interpret,
|
||||||
defer interp_argv.deinit();
|
}
|
||||||
|
|
||||||
const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc;
|
const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc;
|
||||||
switch (b.host.getExternalExecutor(exe.target_info, .{
|
switch (b.host.getExternalExecutor(exe.target_info, .{
|
||||||
@ -577,14 +586,13 @@ fn runCommand(
|
|||||||
.link_libc = exe.is_linking_libc,
|
.link_libc = exe.is_linking_libc,
|
||||||
})) {
|
})) {
|
||||||
.native, .rosetta => {
|
.native, .rosetta => {
|
||||||
if (self.stdio == .check and self.skip_foreign_checks)
|
if (allow_skip) return error.MakeSkipped;
|
||||||
return error.MakeSkipped;
|
|
||||||
|
|
||||||
break :interpret;
|
break :interpret;
|
||||||
},
|
},
|
||||||
.wine => |bin_name| {
|
.wine => |bin_name| {
|
||||||
if (b.enable_wine) {
|
if (b.enable_wine) {
|
||||||
try interp_argv.append(bin_name);
|
try interp_argv.append(bin_name);
|
||||||
|
try interp_argv.appendSlice(argv);
|
||||||
} else {
|
} else {
|
||||||
return failForeign(self, "-fwine", argv[0], exe);
|
return failForeign(self, "-fwine", argv[0], exe);
|
||||||
}
|
}
|
||||||
@ -617,6 +625,8 @@ fn runCommand(
|
|||||||
try interp_argv.append("-L");
|
try interp_argv.append("-L");
|
||||||
try interp_argv.append(full_dir);
|
try interp_argv.append(full_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try interp_argv.appendSlice(argv);
|
||||||
} else {
|
} else {
|
||||||
return failForeign(self, "-fqemu", argv[0], exe);
|
return failForeign(self, "-fqemu", argv[0], exe);
|
||||||
}
|
}
|
||||||
@ -624,6 +634,7 @@ fn runCommand(
|
|||||||
.darling => |bin_name| {
|
.darling => |bin_name| {
|
||||||
if (b.enable_darling) {
|
if (b.enable_darling) {
|
||||||
try interp_argv.append(bin_name);
|
try interp_argv.append(bin_name);
|
||||||
|
try interp_argv.appendSlice(argv);
|
||||||
} else {
|
} else {
|
||||||
return failForeign(self, "-fdarling", argv[0], exe);
|
return failForeign(self, "-fdarling", argv[0], exe);
|
||||||
}
|
}
|
||||||
@ -632,13 +643,15 @@ fn runCommand(
|
|||||||
if (b.enable_wasmtime) {
|
if (b.enable_wasmtime) {
|
||||||
try interp_argv.append(bin_name);
|
try interp_argv.append(bin_name);
|
||||||
try interp_argv.append("--dir=.");
|
try interp_argv.append("--dir=.");
|
||||||
|
try interp_argv.append(argv[0]);
|
||||||
|
try interp_argv.append("--");
|
||||||
|
try interp_argv.appendSlice(argv[1..]);
|
||||||
} else {
|
} else {
|
||||||
return failForeign(self, "-fwasmtime", argv[0], exe);
|
return failForeign(self, "-fwasmtime", argv[0], exe);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.bad_dl => |foreign_dl| {
|
.bad_dl => |foreign_dl| {
|
||||||
if (self.stdio == .check and self.skip_foreign_checks)
|
if (allow_skip) return error.MakeSkipped;
|
||||||
return error.MakeSkipped;
|
|
||||||
|
|
||||||
const host_dl = b.host.dynamic_linker.get() orelse "(none)";
|
const host_dl = b.host.dynamic_linker.get() orelse "(none)";
|
||||||
|
|
||||||
@ -650,8 +663,7 @@ fn runCommand(
|
|||||||
, .{ host_dl, foreign_dl });
|
, .{ host_dl, foreign_dl });
|
||||||
},
|
},
|
||||||
.bad_os_or_cpu => {
|
.bad_os_or_cpu => {
|
||||||
if (self.stdio == .check and self.skip_foreign_checks)
|
if (allow_skip) return error.MakeSkipped;
|
||||||
return error.MakeSkipped;
|
|
||||||
|
|
||||||
const host_name = try b.host.target.zigTriple(b.allocator);
|
const host_name = try b.host.target.zigTriple(b.allocator);
|
||||||
const foreign_name = try exe.target.zigTriple(b.allocator);
|
const foreign_name = try exe.target.zigTriple(b.allocator);
|
||||||
@ -667,11 +679,9 @@ fn runCommand(
|
|||||||
RunStep.addPathForDynLibsInternal(&self.step, b, exe);
|
RunStep.addPathForDynLibsInternal(&self.step, b, exe);
|
||||||
}
|
}
|
||||||
|
|
||||||
try interp_argv.append(argv[0]);
|
|
||||||
|
|
||||||
try Step.handleVerbose(step.owner, self.cwd, interp_argv.items);
|
try Step.handleVerbose(step.owner, self.cwd, interp_argv.items);
|
||||||
|
|
||||||
break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects) catch |e| {
|
break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| {
|
||||||
return step.fail("unable to spawn {s}: {s}", .{
|
return step.fail("unable to spawn {s}: {s}", .{
|
||||||
interp_argv.items[0], @errorName(e),
|
interp_argv.items[0], @errorName(e),
|
||||||
});
|
});
|
||||||
@ -683,6 +693,7 @@ fn runCommand(
|
|||||||
|
|
||||||
step.result_duration_ns = result.elapsed_ns;
|
step.result_duration_ns = result.elapsed_ns;
|
||||||
step.result_peak_rss = result.peak_rss;
|
step.result_peak_rss = result.peak_rss;
|
||||||
|
step.test_results = result.stdio.test_results;
|
||||||
|
|
||||||
// Capture stdout and stderr to GeneratedFile objects.
|
// Capture stdout and stderr to GeneratedFile objects.
|
||||||
const Stream = struct {
|
const Stream = struct {
|
||||||
@ -693,13 +704,13 @@ fn runCommand(
|
|||||||
for ([_]Stream{
|
for ([_]Stream{
|
||||||
.{
|
.{
|
||||||
.captured = self.captured_stdout,
|
.captured = self.captured_stdout,
|
||||||
.is_null = result.stdout_null,
|
.is_null = result.stdio.stdout_null,
|
||||||
.bytes = result.stdout,
|
.bytes = result.stdio.stdout,
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.captured = self.captured_stderr,
|
.captured = self.captured_stderr,
|
||||||
.is_null = result.stderr_null,
|
.is_null = result.stdio.stderr_null,
|
||||||
.bytes = result.stderr,
|
.bytes = result.stdio.stderr,
|
||||||
},
|
},
|
||||||
}) |stream| {
|
}) |stream| {
|
||||||
if (stream.captured) |output| {
|
if (stream.captured) |output| {
|
||||||
@ -724,11 +735,13 @@ fn runCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
|
||||||
|
|
||||||
switch (self.stdio) {
|
switch (self.stdio) {
|
||||||
.check => |checks| for (checks.items) |check| switch (check) {
|
.check => |checks| for (checks.items) |check| switch (check) {
|
||||||
.expect_stderr_exact => |expected_bytes| {
|
.expect_stderr_exact => |expected_bytes| {
|
||||||
assert(!result.stderr_null);
|
assert(!result.stdio.stderr_null);
|
||||||
if (!mem.eql(u8, expected_bytes, result.stderr)) {
|
if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) {
|
||||||
return step.fail(
|
return step.fail(
|
||||||
\\
|
\\
|
||||||
\\========= expected this stderr: =========
|
\\========= expected this stderr: =========
|
||||||
@ -739,14 +752,14 @@ fn runCommand(
|
|||||||
\\{s}
|
\\{s}
|
||||||
, .{
|
, .{
|
||||||
expected_bytes,
|
expected_bytes,
|
||||||
result.stderr,
|
result.stdio.stderr,
|
||||||
try Step.allocPrintCmd(arena, self.cwd, argv),
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.expect_stderr_match => |match| {
|
.expect_stderr_match => |match| {
|
||||||
assert(!result.stderr_null);
|
assert(!result.stdio.stderr_null);
|
||||||
if (mem.indexOf(u8, result.stderr, match) == null) {
|
if (mem.indexOf(u8, result.stdio.stderr, match) == null) {
|
||||||
return step.fail(
|
return step.fail(
|
||||||
\\
|
\\
|
||||||
\\========= expected to find in stderr: =========
|
\\========= expected to find in stderr: =========
|
||||||
@ -757,14 +770,14 @@ fn runCommand(
|
|||||||
\\{s}
|
\\{s}
|
||||||
, .{
|
, .{
|
||||||
match,
|
match,
|
||||||
result.stderr,
|
result.stdio.stderr,
|
||||||
try Step.allocPrintCmd(arena, self.cwd, argv),
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.expect_stdout_exact => |expected_bytes| {
|
.expect_stdout_exact => |expected_bytes| {
|
||||||
assert(!result.stdout_null);
|
assert(!result.stdio.stdout_null);
|
||||||
if (!mem.eql(u8, expected_bytes, result.stdout)) {
|
if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) {
|
||||||
return step.fail(
|
return step.fail(
|
||||||
\\
|
\\
|
||||||
\\========= expected this stdout: =========
|
\\========= expected this stdout: =========
|
||||||
@ -775,14 +788,14 @@ fn runCommand(
|
|||||||
\\{s}
|
\\{s}
|
||||||
, .{
|
, .{
|
||||||
expected_bytes,
|
expected_bytes,
|
||||||
result.stdout,
|
result.stdio.stdout,
|
||||||
try Step.allocPrintCmd(arena, self.cwd, argv),
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.expect_stdout_match => |match| {
|
.expect_stdout_match => |match| {
|
||||||
assert(!result.stdout_null);
|
assert(!result.stdio.stdout_null);
|
||||||
if (mem.indexOf(u8, result.stdout, match) == null) {
|
if (mem.indexOf(u8, result.stdio.stdout, match) == null) {
|
||||||
return step.fail(
|
return step.fail(
|
||||||
\\
|
\\
|
||||||
\\========= expected to find in stdout: =========
|
\\========= expected to find in stdout: =========
|
||||||
@ -793,8 +806,8 @@ fn runCommand(
|
|||||||
\\{s}
|
\\{s}
|
||||||
, .{
|
, .{
|
||||||
match,
|
match,
|
||||||
result.stdout,
|
result.stdio.stdout,
|
||||||
try Step.allocPrintCmd(arena, self.cwd, argv),
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -803,33 +816,46 @@ fn runCommand(
|
|||||||
return step.fail("the following command {} (expected {}):\n{s}", .{
|
return step.fail("the following command {} (expected {}):\n{s}", .{
|
||||||
fmtTerm(result.term),
|
fmtTerm(result.term),
|
||||||
fmtTerm(expected_term),
|
fmtTerm(expected_term),
|
||||||
try Step.allocPrintCmd(arena, self.cwd, argv),
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.zig_test => {
|
||||||
|
const expected_term: std.process.Child.Term = .{ .Exited = 0 };
|
||||||
|
if (!termMatches(expected_term, result.term)) {
|
||||||
|
return step.fail("the following command {} (expected {}):\n{s}", .{
|
||||||
|
fmtTerm(result.term),
|
||||||
|
fmtTerm(expected_term),
|
||||||
|
try Step.allocPrintCmd(arena, self.cwd, final_argv),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!result.stdio.test_results.isSuccess()) {
|
||||||
|
return step.fail(
|
||||||
|
"the following test command failed:\n{s}",
|
||||||
|
.{try Step.allocPrintCmd(arena, self.cwd, final_argv)},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
try step.handleChildProcessTerm(result.term, self.cwd, argv);
|
try step.handleChildProcessTerm(result.term, self.cwd, final_argv);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChildProcResult = struct {
|
const ChildProcResult = struct {
|
||||||
// These use boolean flags instead of optionals as a workaround for
|
|
||||||
// https://github.com/ziglang/zig/issues/14783
|
|
||||||
stdout: []const u8,
|
|
||||||
stderr: []const u8,
|
|
||||||
stdout_null: bool,
|
|
||||||
stderr_null: bool,
|
|
||||||
term: std.process.Child.Term,
|
term: std.process.Child.Term,
|
||||||
elapsed_ns: u64,
|
elapsed_ns: u64,
|
||||||
peak_rss: usize,
|
peak_rss: usize,
|
||||||
|
|
||||||
|
stdio: StdIoResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn spawnChildAndCollect(
|
fn spawnChildAndCollect(
|
||||||
self: *RunStep,
|
self: *RunStep,
|
||||||
argv: []const []const u8,
|
argv: []const []const u8,
|
||||||
has_side_effects: bool,
|
has_side_effects: bool,
|
||||||
|
prog_node: *std.Progress.Node,
|
||||||
) !ChildProcResult {
|
) !ChildProcResult {
|
||||||
const b = self.step.owner;
|
const b = self.step.owner;
|
||||||
const arena = b.allocator;
|
const arena = b.allocator;
|
||||||
@ -848,16 +874,19 @@ fn spawnChildAndCollect(
|
|||||||
.infer_from_args => if (has_side_effects) .Inherit else .Close,
|
.infer_from_args => if (has_side_effects) .Inherit else .Close,
|
||||||
.inherit => .Inherit,
|
.inherit => .Inherit,
|
||||||
.check => .Close,
|
.check => .Close,
|
||||||
|
.zig_test => .Pipe,
|
||||||
};
|
};
|
||||||
child.stdout_behavior = switch (self.stdio) {
|
child.stdout_behavior = switch (self.stdio) {
|
||||||
.infer_from_args => if (has_side_effects) .Inherit else .Ignore,
|
.infer_from_args => if (has_side_effects) .Inherit else .Ignore,
|
||||||
.inherit => .Inherit,
|
.inherit => .Inherit,
|
||||||
.check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore,
|
.check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore,
|
||||||
|
.zig_test => .Pipe,
|
||||||
};
|
};
|
||||||
child.stderr_behavior = switch (self.stdio) {
|
child.stderr_behavior = switch (self.stdio) {
|
||||||
.infer_from_args => if (has_side_effects) .Inherit else .Pipe,
|
.infer_from_args => if (has_side_effects) .Inherit else .Pipe,
|
||||||
.inherit => .Inherit,
|
.inherit => .Inherit,
|
||||||
.check => .Pipe,
|
.check => .Pipe,
|
||||||
|
.zig_test => .Pipe,
|
||||||
};
|
};
|
||||||
if (self.captured_stdout != null) child.stdout_behavior = .Pipe;
|
if (self.captured_stdout != null) child.stdout_behavior = .Pipe;
|
||||||
if (self.captured_stderr != null) child.stderr_behavior = .Pipe;
|
if (self.captured_stderr != null) child.stderr_behavior = .Pipe;
|
||||||
@ -871,6 +900,219 @@ fn spawnChildAndCollect(
|
|||||||
});
|
});
|
||||||
var timer = try std.time.Timer.start();
|
var timer = try std.time.Timer.start();
|
||||||
|
|
||||||
|
const result = if (self.stdio == .zig_test)
|
||||||
|
evalZigTest(self, &child, prog_node)
|
||||||
|
else
|
||||||
|
evalGeneric(self, &child);
|
||||||
|
|
||||||
|
const term = try child.wait();
|
||||||
|
const elapsed_ns = timer.read();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.stdio = try result,
|
||||||
|
.term = term,
|
||||||
|
.elapsed_ns = elapsed_ns,
|
||||||
|
.peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const StdIoResult = struct {
|
||||||
|
// These use boolean flags instead of optionals as a workaround for
|
||||||
|
// https://github.com/ziglang/zig/issues/14783
|
||||||
|
stdout: []const u8,
|
||||||
|
stderr: []const u8,
|
||||||
|
stdout_null: bool,
|
||||||
|
stderr_null: bool,
|
||||||
|
test_results: Step.TestResults,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn evalZigTest(
|
||||||
|
self: *RunStep,
|
||||||
|
child: *std.process.Child,
|
||||||
|
prog_node: *std.Progress.Node,
|
||||||
|
) !StdIoResult {
|
||||||
|
const gpa = self.step.owner.allocator;
|
||||||
|
const arena = self.step.owner.allocator;
|
||||||
|
|
||||||
|
var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
|
||||||
|
.stdout = child.stdout.?,
|
||||||
|
.stderr = child.stderr.?,
|
||||||
|
});
|
||||||
|
defer poller.deinit();
|
||||||
|
|
||||||
|
try sendMessage(child.stdin.?, .query_test_metadata);
|
||||||
|
|
||||||
|
const Header = std.zig.Server.Message.Header;
|
||||||
|
|
||||||
|
const stdout = poller.fifo(.stdout);
|
||||||
|
const stderr = poller.fifo(.stderr);
|
||||||
|
|
||||||
|
var fail_count: u32 = 0;
|
||||||
|
var skip_count: u32 = 0;
|
||||||
|
var leak_count: u32 = 0;
|
||||||
|
var test_count: u32 = 0;
|
||||||
|
|
||||||
|
var metadata: ?TestMetadata = null;
|
||||||
|
|
||||||
|
var sub_prog_node: ?std.Progress.Node = null;
|
||||||
|
defer if (sub_prog_node) |*n| n.end();
|
||||||
|
|
||||||
|
poll: while (try poller.poll()) {
|
||||||
|
while (true) {
|
||||||
|
const buf = stdout.readableSlice(0);
|
||||||
|
assert(stdout.readableLength() == buf.len);
|
||||||
|
if (buf.len < @sizeOf(Header)) continue :poll;
|
||||||
|
const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
|
||||||
|
const header_and_msg_len = header.bytes_len + @sizeOf(Header);
|
||||||
|
if (buf.len < header_and_msg_len) continue :poll;
|
||||||
|
const body = buf[@sizeOf(Header)..][0..header.bytes_len];
|
||||||
|
switch (header.tag) {
|
||||||
|
.zig_version => {
|
||||||
|
if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
|
||||||
|
return self.step.fail(
|
||||||
|
"zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
|
||||||
|
.{ builtin.zig_version_string, body },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.test_metadata => {
|
||||||
|
const TmHdr = std.zig.Server.Message.TestMetadata;
|
||||||
|
const tm_hdr = @ptrCast(*align(1) const TmHdr, body);
|
||||||
|
test_count = tm_hdr.tests_len;
|
||||||
|
|
||||||
|
const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)];
|
||||||
|
const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)];
|
||||||
|
const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)];
|
||||||
|
const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len];
|
||||||
|
|
||||||
|
const names = std.mem.bytesAsSlice(u32, names_bytes);
|
||||||
|
const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes);
|
||||||
|
const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes);
|
||||||
|
const names_aligned = try arena.alloc(u32, names.len);
|
||||||
|
for (names_aligned, names) |*dest, src| dest.* = src;
|
||||||
|
|
||||||
|
const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len);
|
||||||
|
for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src;
|
||||||
|
|
||||||
|
const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len);
|
||||||
|
for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src;
|
||||||
|
|
||||||
|
prog_node.setEstimatedTotalItems(names.len);
|
||||||
|
metadata = .{
|
||||||
|
.string_bytes = try arena.dupe(u8, string_bytes),
|
||||||
|
.names = names_aligned,
|
||||||
|
.async_frame_lens = async_frame_lens_aligned,
|
||||||
|
.expected_panic_msgs = expected_panic_msgs_aligned,
|
||||||
|
.next_index = 0,
|
||||||
|
.prog_node = prog_node,
|
||||||
|
};
|
||||||
|
|
||||||
|
try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);
|
||||||
|
},
|
||||||
|
.test_results => {
|
||||||
|
const md = metadata.?;
|
||||||
|
|
||||||
|
const TrHdr = std.zig.Server.Message.TestResults;
|
||||||
|
const tr_hdr = @ptrCast(*align(1) const TrHdr, body);
|
||||||
|
fail_count += @boolToInt(tr_hdr.flags.fail);
|
||||||
|
skip_count += @boolToInt(tr_hdr.flags.skip);
|
||||||
|
leak_count += @boolToInt(tr_hdr.flags.leak);
|
||||||
|
|
||||||
|
if (tr_hdr.flags.fail or tr_hdr.flags.leak) {
|
||||||
|
const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0);
|
||||||
|
const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n");
|
||||||
|
const label = if (tr_hdr.flags.fail) "failed" else "leaked";
|
||||||
|
if (msg.len > 0) {
|
||||||
|
try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg });
|
||||||
|
} else {
|
||||||
|
try self.step.addError("'{s}' {s}", .{ name, label });
|
||||||
|
}
|
||||||
|
stderr.discard(msg.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);
|
||||||
|
},
|
||||||
|
else => {}, // ignore other messages
|
||||||
|
}
|
||||||
|
stdout.discard(header_and_msg_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr.readableLength() > 0) {
|
||||||
|
const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n");
|
||||||
|
if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send EOF to stdin.
|
||||||
|
child.stdin.?.close();
|
||||||
|
child.stdin = null;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.stdout = &.{},
|
||||||
|
.stderr = &.{},
|
||||||
|
.stdout_null = true,
|
||||||
|
.stderr_null = true,
|
||||||
|
.test_results = .{
|
||||||
|
.test_count = test_count,
|
||||||
|
.fail_count = fail_count,
|
||||||
|
.skip_count = skip_count,
|
||||||
|
.leak_count = leak_count,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const TestMetadata = struct {
|
||||||
|
names: []const u32,
|
||||||
|
async_frame_lens: []const u32,
|
||||||
|
expected_panic_msgs: []const u32,
|
||||||
|
string_bytes: []const u8,
|
||||||
|
next_index: u32,
|
||||||
|
prog_node: *std.Progress.Node,
|
||||||
|
|
||||||
|
fn testName(tm: TestMetadata, index: u32) []const u8 {
|
||||||
|
return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void {
|
||||||
|
while (metadata.next_index < metadata.names.len) {
|
||||||
|
const i = metadata.next_index;
|
||||||
|
metadata.next_index += 1;
|
||||||
|
|
||||||
|
if (metadata.async_frame_lens[i] != 0) continue;
|
||||||
|
if (metadata.expected_panic_msgs[i] != 0) continue;
|
||||||
|
|
||||||
|
const name = metadata.testName(i);
|
||||||
|
if (sub_prog_node.*) |*n| n.end();
|
||||||
|
sub_prog_node.* = metadata.prog_node.start(name, 0);
|
||||||
|
|
||||||
|
try sendRunTestMessage(in, i);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
try sendMessage(in, .exit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
|
||||||
|
const header: std.zig.Client.Message.Header = .{
|
||||||
|
.tag = tag,
|
||||||
|
.bytes_len = 0,
|
||||||
|
};
|
||||||
|
try file.writeAll(std.mem.asBytes(&header));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sendRunTestMessage(file: std.fs.File, index: u32) !void {
|
||||||
|
const header: std.zig.Client.Message.Header = .{
|
||||||
|
.tag = .run_test,
|
||||||
|
.bytes_len = 4,
|
||||||
|
};
|
||||||
|
const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index);
|
||||||
|
try file.writeAll(full_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult {
|
||||||
|
const arena = self.step.owner.allocator;
|
||||||
|
|
||||||
if (self.stdin) |stdin| {
|
if (self.stdin) |stdin| {
|
||||||
child.stdin.?.writeAll(stdin) catch |err| {
|
child.stdin.?.writeAll(stdin) catch |err| {
|
||||||
return self.step.fail("unable to write stdin: {s}", .{@errorName(err)});
|
return self.step.fail("unable to write stdin: {s}", .{@errorName(err)});
|
||||||
@ -925,17 +1167,12 @@ fn spawnChildAndCollect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const term = try child.wait();
|
|
||||||
const elapsed_ns = timer.read();
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.stdout = stdout_bytes,
|
.stdout = stdout_bytes,
|
||||||
.stderr = stderr_bytes,
|
.stderr = stderr_bytes,
|
||||||
.stdout_null = stdout_null,
|
.stdout_null = stdout_null,
|
||||||
.stderr_null = stderr_null,
|
.stderr_null = stderr_null,
|
||||||
.term = term,
|
.test_results = .{},
|
||||||
.elapsed_ns = elapsed_ns,
|
|
||||||
.peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,7 +1203,7 @@ fn failForeign(
|
|||||||
exe: *CompileStep,
|
exe: *CompileStep,
|
||||||
) error{ MakeFailed, MakeSkipped, OutOfMemory } {
|
) error{ MakeFailed, MakeSkipped, OutOfMemory } {
|
||||||
switch (self.stdio) {
|
switch (self.stdio) {
|
||||||
.check => {
|
.check, .zig_test => {
|
||||||
if (self.skip_foreign_checks)
|
if (self.skip_foreign_checks)
|
||||||
return error.MakeSkipped;
|
return error.MakeSkipped;
|
||||||
|
|
||||||
@ -987,7 +1224,7 @@ fn failForeign(
|
|||||||
|
|
||||||
fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
|
fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
|
||||||
switch (stdio) {
|
switch (stdio) {
|
||||||
.infer_from_args, .inherit => {},
|
.infer_from_args, .inherit, .zig_test => {},
|
||||||
.check => |checks| for (checks.items) |check| {
|
.check => |checks| for (checks.items) |check| {
|
||||||
hh.add(@as(std.meta.Tag(StdIo.Check), check));
|
hh.add(@as(std.meta.Tag(StdIo.Check), check));
|
||||||
switch (check) {
|
switch (check) {
|
||||||
|
|||||||
@ -35,11 +35,27 @@ result_cached: bool,
|
|||||||
result_duration_ns: ?u64,
|
result_duration_ns: ?u64,
|
||||||
/// 0 means unavailable or not reported.
|
/// 0 means unavailable or not reported.
|
||||||
result_peak_rss: usize,
|
result_peak_rss: usize,
|
||||||
|
test_results: TestResults,
|
||||||
|
|
||||||
/// The return addresss associated with creation of this step that can be useful
|
/// The return addresss associated with creation of this step that can be useful
|
||||||
/// to print along with debugging messages.
|
/// to print along with debugging messages.
|
||||||
debug_stack_trace: [n_debug_stack_frames]usize,
|
debug_stack_trace: [n_debug_stack_frames]usize,
|
||||||
|
|
||||||
|
pub const TestResults = struct {
|
||||||
|
fail_count: u32 = 0,
|
||||||
|
skip_count: u32 = 0,
|
||||||
|
leak_count: u32 = 0,
|
||||||
|
test_count: u32 = 0,
|
||||||
|
|
||||||
|
pub fn isSuccess(tr: TestResults) bool {
|
||||||
|
return tr.fail_count == 0 and tr.leak_count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn passCount(tr: TestResults) u32 {
|
||||||
|
return tr.test_count - tr.fail_count - tr.skip_count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void;
|
pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void;
|
||||||
|
|
||||||
const n_debug_stack_frames = 4;
|
const n_debug_stack_frames = 4;
|
||||||
@ -134,6 +150,7 @@ pub fn init(options: Options) Step {
|
|||||||
.result_cached = false,
|
.result_cached = false,
|
||||||
.result_duration_ns = null,
|
.result_duration_ns = null,
|
||||||
.result_peak_rss = 0,
|
.result_peak_rss = 0,
|
||||||
|
.test_results = .{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +169,10 @@ pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkip
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!s.test_results.isSuccess()) {
|
||||||
|
return error.MakeFailed;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {
|
if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {
|
||||||
const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{
|
const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{
|
||||||
s.result_peak_rss, s.max_rss,
|
s.result_peak_rss, s.max_rss,
|
||||||
@ -346,9 +367,7 @@ pub fn evalZigProcess(
|
|||||||
s.result_cached = ebp_hdr.flags.cache_hit;
|
s.result_cached = ebp_hdr.flags.cache_hit;
|
||||||
result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);
|
result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);
|
||||||
},
|
},
|
||||||
_ => {
|
else => {}, // ignore other messages
|
||||||
// Unrecognized message.
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
stdout.discard(header_and_msg_len);
|
stdout.discard(header_and_msg_len);
|
||||||
}
|
}
|
||||||
@ -475,3 +494,11 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer
|
|||||||
const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
|
const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
|
||||||
return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path });
|
return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void {
|
||||||
|
if (s.test_results.isSuccess()) {
|
||||||
|
man.writeManifest() catch |err| {
|
||||||
|
try s.addError("unable to write cache manifest: {s}", .{@errorName(err)});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -282,7 +282,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try man.writeManifest();
|
try step.writeManifest(&man);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std = @import("../std.zig");
|
const std = @import("../std.zig");
|
||||||
|
|||||||
@ -26,6 +26,13 @@ pub const Message = struct {
|
|||||||
/// swap.
|
/// swap.
|
||||||
/// No body.
|
/// No body.
|
||||||
hot_update,
|
hot_update,
|
||||||
|
/// Ask the test runner for metadata about all the unit tests that can
|
||||||
|
/// be run. Server will respond with a `test_metadata` message.
|
||||||
|
/// No body.
|
||||||
|
query_test_metadata,
|
||||||
|
/// Ask the test runner to run a particular test.
|
||||||
|
/// The message body is a u32 test index.
|
||||||
|
run_test,
|
||||||
|
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
in: std.fs.File,
|
||||||
|
out: std.fs.File,
|
||||||
|
receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
|
||||||
|
|
||||||
pub const Message = struct {
|
pub const Message = struct {
|
||||||
pub const Header = extern struct {
|
pub const Header = extern struct {
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
@ -14,6 +18,11 @@ pub const Message = struct {
|
|||||||
progress,
|
progress,
|
||||||
/// Body is a EmitBinPath.
|
/// Body is a EmitBinPath.
|
||||||
emit_bin_path,
|
emit_bin_path,
|
||||||
|
/// Body is a TestMetadata
|
||||||
|
test_metadata,
|
||||||
|
/// Body is a TestResults
|
||||||
|
test_results,
|
||||||
|
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,6 +35,33 @@ pub const Message = struct {
|
|||||||
string_bytes_len: u32,
|
string_bytes_len: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Trailing:
|
||||||
|
/// * name: [tests_len]u32
|
||||||
|
/// - null-terminated string_bytes index
|
||||||
|
/// * async_frame_len: [tests_len]u32,
|
||||||
|
/// - 0 means not async
|
||||||
|
/// * expected_panic_msg: [tests_len]u32,
|
||||||
|
/// - null-terminated string_bytes index
|
||||||
|
/// - 0 means does not expect pani
|
||||||
|
/// * string_bytes: [string_bytes_len]u8,
|
||||||
|
pub const TestMetadata = extern struct {
|
||||||
|
string_bytes_len: u32,
|
||||||
|
tests_len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TestResults = extern struct {
|
||||||
|
index: u32,
|
||||||
|
flags: Flags,
|
||||||
|
|
||||||
|
pub const Flags = packed struct(u8) {
|
||||||
|
fail: bool,
|
||||||
|
skip: bool,
|
||||||
|
leak: bool,
|
||||||
|
|
||||||
|
reserved: u5 = 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/// Trailing:
|
/// Trailing:
|
||||||
/// * the file system path the emitted binary can be found
|
/// * the file system path the emitted binary can be found
|
||||||
pub const EmitBinPath = extern struct {
|
pub const EmitBinPath = extern struct {
|
||||||
@ -37,3 +73,167 @@ pub const Message = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
gpa: Allocator,
|
||||||
|
in: std.fs.File,
|
||||||
|
out: std.fs.File,
|
||||||
|
zig_version: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(options: Options) !Server {
|
||||||
|
var s: Server = .{
|
||||||
|
.in = options.in,
|
||||||
|
.out = options.out,
|
||||||
|
.receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
|
||||||
|
};
|
||||||
|
try s.serveStringMessage(.zig_version, options.zig_version);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(s: *Server) void {
|
||||||
|
s.receive_fifo.deinit();
|
||||||
|
s.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiveMessage(s: *Server) !InMessage.Header {
|
||||||
|
const Header = InMessage.Header;
|
||||||
|
const fifo = &s.receive_fifo;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const buf = fifo.readableSlice(0);
|
||||||
|
assert(fifo.readableLength() == buf.len);
|
||||||
|
if (buf.len >= @sizeOf(Header)) {
|
||||||
|
const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
|
||||||
|
|
||||||
|
if (buf.len - @sizeOf(Header) >= header.bytes_len) {
|
||||||
|
const result = header.*;
|
||||||
|
fifo.discard(@sizeOf(Header));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
const needed = header.bytes_len - (buf.len - @sizeOf(Header));
|
||||||
|
const write_buffer = try fifo.writableWithSize(needed);
|
||||||
|
const amt = try s.in.read(write_buffer);
|
||||||
|
fifo.update(amt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const write_buffer = try fifo.writableWithSize(256);
|
||||||
|
const amt = try s.in.read(write_buffer);
|
||||||
|
fifo.update(amt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiveBody_u32(s: *Server) !u32 {
|
||||||
|
const fifo = &s.receive_fifo;
|
||||||
|
const buf = fifo.readableSlice(0);
|
||||||
|
const result = @ptrCast(*align(1) const u32, buf[0..4]).*;
|
||||||
|
fifo.discard(4);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
|
||||||
|
return s.serveMessage(.{
|
||||||
|
.tag = tag,
|
||||||
|
.bytes_len = @intCast(u32, msg.len),
|
||||||
|
}, &.{msg});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serveMessage(
|
||||||
|
s: *const Server,
|
||||||
|
header: OutMessage.Header,
|
||||||
|
bufs: []const []const u8,
|
||||||
|
) !void {
|
||||||
|
var iovecs: [10]std.os.iovec_const = undefined;
|
||||||
|
iovecs[0] = .{
|
||||||
|
.iov_base = @ptrCast([*]const u8, &header),
|
||||||
|
.iov_len = @sizeOf(OutMessage.Header),
|
||||||
|
};
|
||||||
|
for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
|
||||||
|
iovec.* = .{
|
||||||
|
.iov_base = buf.ptr,
|
||||||
|
.iov_len = buf.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try s.out.writevAll(iovecs[0 .. bufs.len + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serveEmitBinPath(
|
||||||
|
s: *Server,
|
||||||
|
fs_path: []const u8,
|
||||||
|
header: OutMessage.EmitBinPath,
|
||||||
|
) !void {
|
||||||
|
try s.serveMessage(.{
|
||||||
|
.tag = .emit_bin_path,
|
||||||
|
.bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)),
|
||||||
|
}, &.{
|
||||||
|
std.mem.asBytes(&header),
|
||||||
|
fs_path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serveTestResults(
|
||||||
|
s: *Server,
|
||||||
|
msg: OutMessage.TestResults,
|
||||||
|
) !void {
|
||||||
|
try s.serveMessage(.{
|
||||||
|
.tag = .test_results,
|
||||||
|
.bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)),
|
||||||
|
}, &.{
|
||||||
|
std.mem.asBytes(&msg),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
|
||||||
|
const eb_hdr: OutMessage.ErrorBundle = .{
|
||||||
|
.extra_len = @intCast(u32, error_bundle.extra.len),
|
||||||
|
.string_bytes_len = @intCast(u32, error_bundle.string_bytes.len),
|
||||||
|
};
|
||||||
|
const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
|
||||||
|
4 * error_bundle.extra.len + error_bundle.string_bytes.len;
|
||||||
|
try s.serveMessage(.{
|
||||||
|
.tag = .error_bundle,
|
||||||
|
.bytes_len = @intCast(u32, bytes_len),
|
||||||
|
}, &.{
|
||||||
|
std.mem.asBytes(&eb_hdr),
|
||||||
|
// TODO: implement @ptrCast between slices changing the length
|
||||||
|
std.mem.sliceAsBytes(error_bundle.extra),
|
||||||
|
error_bundle.string_bytes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TestMetadata = struct {
|
||||||
|
names: []const u32,
|
||||||
|
async_frame_sizes: []const u32,
|
||||||
|
expected_panic_msgs: []const u32,
|
||||||
|
string_bytes: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
|
||||||
|
const header: OutMessage.TestMetadata = .{
|
||||||
|
.tests_len = @intCast(u32, test_metadata.names.len),
|
||||||
|
.string_bytes_len = @intCast(u32, test_metadata.string_bytes.len),
|
||||||
|
};
|
||||||
|
const bytes_len = @sizeOf(OutMessage.TestMetadata) +
|
||||||
|
3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len;
|
||||||
|
return s.serveMessage(.{
|
||||||
|
.tag = .test_metadata,
|
||||||
|
.bytes_len = @intCast(u32, bytes_len),
|
||||||
|
}, &.{
|
||||||
|
std.mem.asBytes(&header),
|
||||||
|
// TODO: implement @ptrCast between slices changing the length
|
||||||
|
std.mem.sliceAsBytes(test_metadata.names),
|
||||||
|
std.mem.sliceAsBytes(test_metadata.async_frame_sizes),
|
||||||
|
std.mem.sliceAsBytes(test_metadata.expected_panic_msgs),
|
||||||
|
test_metadata.string_bytes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutMessage = std.zig.Server.Message;
|
||||||
|
const InMessage = std.zig.Client.Message;
|
||||||
|
|
||||||
|
const Server = @This();
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|||||||
@ -8,14 +8,130 @@ pub const std_options = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var log_err_count: usize = 0;
|
var log_err_count: usize = 0;
|
||||||
|
var cmdline_buffer: [4096]u8 = undefined;
|
||||||
|
var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
if (builtin.zig_backend != .stage1 and
|
if (builtin.zig_backend == .stage2_wasm or
|
||||||
builtin.zig_backend != .stage2_llvm and
|
builtin.zig_backend == .stage2_x86_64 or
|
||||||
builtin.zig_backend != .stage2_c)
|
builtin.zig_backend == .stage2_aarch64)
|
||||||
{
|
{
|
||||||
return main2() catch @panic("test failure");
|
return mainSimple() catch @panic("test failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const args = std.process.argsAlloc(fba.allocator()) catch
|
||||||
|
@panic("unable to parse command line args");
|
||||||
|
|
||||||
|
var listen = false;
|
||||||
|
|
||||||
|
for (args[1..]) |arg| {
|
||||||
|
if (std.mem.eql(u8, arg, "--listen=-")) {
|
||||||
|
listen = true;
|
||||||
|
} else {
|
||||||
|
@panic("unrecognized command line argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen) {
|
||||||
|
return mainServer();
|
||||||
|
} else {
|
||||||
|
return mainTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mainServer() void {
|
||||||
|
return mainServerFallible() catch @panic("internal test runner failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mainServerFallible() !void {
|
||||||
|
var server = try std.zig.Server.init(.{
|
||||||
|
.gpa = fba.allocator(),
|
||||||
|
.in = std.io.getStdIn(),
|
||||||
|
.out = std.io.getStdOut(),
|
||||||
|
.zig_version = builtin.zig_version_string,
|
||||||
|
});
|
||||||
|
defer server.deinit();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const hdr = try server.receiveMessage();
|
||||||
|
switch (hdr.tag) {
|
||||||
|
.exit => {
|
||||||
|
return std.process.exit(0);
|
||||||
|
},
|
||||||
|
.query_test_metadata => {
|
||||||
|
std.testing.allocator_instance = .{};
|
||||||
|
defer if (std.testing.allocator_instance.deinit()) {
|
||||||
|
@panic("internal test runner memory leak");
|
||||||
|
};
|
||||||
|
|
||||||
|
var string_bytes: std.ArrayListUnmanaged(u8) = .{};
|
||||||
|
defer string_bytes.deinit(std.testing.allocator);
|
||||||
|
try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
|
||||||
|
|
||||||
|
const test_fns = builtin.test_functions;
|
||||||
|
const names = try std.testing.allocator.alloc(u32, test_fns.len);
|
||||||
|
defer std.testing.allocator.free(names);
|
||||||
|
const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len);
|
||||||
|
defer std.testing.allocator.free(async_frame_sizes);
|
||||||
|
const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
|
||||||
|
defer std.testing.allocator.free(expected_panic_msgs);
|
||||||
|
|
||||||
|
for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| {
|
||||||
|
name.* = @intCast(u32, string_bytes.items.len);
|
||||||
|
try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1);
|
||||||
|
string_bytes.appendSliceAssumeCapacity(test_fn.name);
|
||||||
|
string_bytes.appendAssumeCapacity(0);
|
||||||
|
|
||||||
|
async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0);
|
||||||
|
expected_panic_msg.* = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try server.serveTestMetadata(.{
|
||||||
|
.names = names,
|
||||||
|
.async_frame_sizes = async_frame_sizes,
|
||||||
|
.expected_panic_msgs = expected_panic_msgs,
|
||||||
|
.string_bytes = string_bytes.items,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.run_test => {
|
||||||
|
std.testing.allocator_instance = .{};
|
||||||
|
const index = try server.receiveBody_u32();
|
||||||
|
const test_fn = builtin.test_functions[index];
|
||||||
|
if (test_fn.async_frame_size != null)
|
||||||
|
@panic("TODO test runner implement async tests");
|
||||||
|
var fail = false;
|
||||||
|
var skip = false;
|
||||||
|
var leak = false;
|
||||||
|
test_fn.func() catch |err| switch (err) {
|
||||||
|
error.SkipZigTest => skip = true,
|
||||||
|
else => {
|
||||||
|
fail = true;
|
||||||
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
leak = std.testing.allocator_instance.deinit();
|
||||||
|
try server.serveTestResults(.{
|
||||||
|
.index = index,
|
||||||
|
.flags = .{
|
||||||
|
.fail = fail,
|
||||||
|
.skip = skip,
|
||||||
|
.leak = leak,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)});
|
||||||
|
std.process.exit(1);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mainTerminal() void {
|
||||||
const test_fn_list = builtin.test_functions;
|
const test_fn_list = builtin.test_functions;
|
||||||
var ok_count: usize = 0;
|
var ok_count: usize = 0;
|
||||||
var skip_count: usize = 0;
|
var skip_count: usize = 0;
|
||||||
@ -118,51 +234,17 @@ pub fn log(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main2() anyerror!void {
|
/// Simpler main(), exercising fewer language features, so that
|
||||||
var skipped: usize = 0;
|
/// work-in-progress backends can handle it.
|
||||||
var failed: usize = 0;
|
pub fn mainSimple() anyerror!void {
|
||||||
// Simpler main(), exercising fewer language features, so that stage2 can handle it.
|
//const stderr = std.io.getStdErr();
|
||||||
for (builtin.test_functions) |test_fn| {
|
for (builtin.test_functions) |test_fn| {
|
||||||
test_fn.func() catch |err| {
|
test_fn.func() catch |err| {
|
||||||
if (err != error.SkipZigTest) {
|
if (err != error.SkipZigTest) {
|
||||||
failed += 1;
|
//stderr.writeAll(test_fn.name) catch {};
|
||||||
} else {
|
//stderr.writeAll("\n") catch {};
|
||||||
skipped += 1;
|
return err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (builtin.zig_backend == .stage2_wasm or
|
|
||||||
builtin.zig_backend == .stage2_x86_64 or
|
|
||||||
builtin.zig_backend == .stage2_aarch64 or
|
|
||||||
builtin.zig_backend == .stage2_llvm or
|
|
||||||
builtin.zig_backend == .stage2_c)
|
|
||||||
{
|
|
||||||
const passed = builtin.test_functions.len - skipped - failed;
|
|
||||||
const stderr = std.io.getStdErr();
|
|
||||||
writeInt(stderr, passed) catch {};
|
|
||||||
stderr.writeAll(" passed; ") catch {};
|
|
||||||
writeInt(stderr, skipped) catch {};
|
|
||||||
stderr.writeAll(" skipped; ") catch {};
|
|
||||||
writeInt(stderr, failed) catch {};
|
|
||||||
stderr.writeAll(" failed.\n") catch {};
|
|
||||||
}
|
|
||||||
if (failed != 0) {
|
|
||||||
return error.TestsFailed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeInt(stderr: std.fs.File, int: usize) anyerror!void {
|
|
||||||
const base = 10;
|
|
||||||
var buf: [100]u8 = undefined;
|
|
||||||
var a: usize = int;
|
|
||||||
var index: usize = buf.len;
|
|
||||||
while (true) {
|
|
||||||
const digit = a % base;
|
|
||||||
index -= 1;
|
|
||||||
buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower);
|
|
||||||
a /= base;
|
|
||||||
if (a == 0) break;
|
|
||||||
}
|
|
||||||
const slice = buf[index..];
|
|
||||||
try stderr.writeAll(slice);
|
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/Server.zig
113
src/Server.zig
@ -1,113 +0,0 @@
|
|||||||
in: std.fs.File,
|
|
||||||
out: std.fs.File,
|
|
||||||
receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
|
|
||||||
|
|
||||||
pub const Options = struct {
|
|
||||||
gpa: Allocator,
|
|
||||||
in: std.fs.File,
|
|
||||||
out: std.fs.File,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(options: Options) !Server {
|
|
||||||
var s: Server = .{
|
|
||||||
.in = options.in,
|
|
||||||
.out = options.out,
|
|
||||||
.receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
|
|
||||||
};
|
|
||||||
try s.serveStringMessage(.zig_version, build_options.version);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(s: *Server) void {
|
|
||||||
s.receive_fifo.deinit();
|
|
||||||
s.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn receiveMessage(s: *Server) !InMessage.Header {
|
|
||||||
const Header = InMessage.Header;
|
|
||||||
const fifo = &s.receive_fifo;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const buf = fifo.readableSlice(0);
|
|
||||||
assert(fifo.readableLength() == buf.len);
|
|
||||||
if (buf.len >= @sizeOf(Header)) {
|
|
||||||
const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
|
|
||||||
if (header.bytes_len != 0)
|
|
||||||
return error.InvalidClientMessage;
|
|
||||||
const result = header.*;
|
|
||||||
fifo.discard(@sizeOf(Header));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const write_buffer = try fifo.writableWithSize(256);
|
|
||||||
const amt = try s.in.read(write_buffer);
|
|
||||||
fifo.update(amt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
|
|
||||||
return s.serveMessage(.{
|
|
||||||
.tag = tag,
|
|
||||||
.bytes_len = @intCast(u32, msg.len),
|
|
||||||
}, &.{msg});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serveMessage(
|
|
||||||
s: *const Server,
|
|
||||||
header: OutMessage.Header,
|
|
||||||
bufs: []const []const u8,
|
|
||||||
) !void {
|
|
||||||
var iovecs: [10]std.os.iovec_const = undefined;
|
|
||||||
iovecs[0] = .{
|
|
||||||
.iov_base = @ptrCast([*]const u8, &header),
|
|
||||||
.iov_len = @sizeOf(OutMessage.Header),
|
|
||||||
};
|
|
||||||
for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
|
|
||||||
iovec.* = .{
|
|
||||||
.iov_base = buf.ptr,
|
|
||||||
.iov_len = buf.len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
try s.out.writevAll(iovecs[0 .. bufs.len + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serveEmitBinPath(
|
|
||||||
s: *Server,
|
|
||||||
fs_path: []const u8,
|
|
||||||
header: std.zig.Server.Message.EmitBinPath,
|
|
||||||
) !void {
|
|
||||||
try s.serveMessage(.{
|
|
||||||
.tag = .emit_bin_path,
|
|
||||||
.bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)),
|
|
||||||
}, &.{
|
|
||||||
std.mem.asBytes(&header),
|
|
||||||
fs_path,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
|
|
||||||
const eb_hdr: std.zig.Server.Message.ErrorBundle = .{
|
|
||||||
.extra_len = @intCast(u32, error_bundle.extra.len),
|
|
||||||
.string_bytes_len = @intCast(u32, error_bundle.string_bytes.len),
|
|
||||||
};
|
|
||||||
const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) +
|
|
||||||
4 * error_bundle.extra.len + error_bundle.string_bytes.len;
|
|
||||||
try s.serveMessage(.{
|
|
||||||
.tag = .error_bundle,
|
|
||||||
.bytes_len = @intCast(u32, bytes_len),
|
|
||||||
}, &.{
|
|
||||||
std.mem.asBytes(&eb_hdr),
|
|
||||||
// TODO: implement @ptrCast between slices changing the length
|
|
||||||
std.mem.sliceAsBytes(error_bundle.extra),
|
|
||||||
error_bundle.string_bytes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const OutMessage = std.zig.Server.Message;
|
|
||||||
const InMessage = std.zig.Client.Message;
|
|
||||||
|
|
||||||
const Server = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
const build_options = @import("build_options");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const assert = std.debug.assert;
|
|
||||||
21
src/main.zig
21
src/main.zig
@ -10,6 +10,7 @@ const ArrayList = std.ArrayList;
|
|||||||
const Ast = std.zig.Ast;
|
const Ast = std.zig.Ast;
|
||||||
const warn = std.log.warn;
|
const warn = std.log.warn;
|
||||||
const ThreadPool = std.Thread.Pool;
|
const ThreadPool = std.Thread.Pool;
|
||||||
|
const cleanExit = std.process.cleanExit;
|
||||||
|
|
||||||
const tracy = @import("tracy.zig");
|
const tracy = @import("tracy.zig");
|
||||||
const Compilation = @import("Compilation.zig");
|
const Compilation = @import("Compilation.zig");
|
||||||
@ -26,7 +27,7 @@ const target_util = @import("target.zig");
|
|||||||
const crash_report = @import("crash_report.zig");
|
const crash_report = @import("crash_report.zig");
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("Module.zig");
|
||||||
const AstGen = @import("AstGen.zig");
|
const AstGen = @import("AstGen.zig");
|
||||||
const Server = @import("Server.zig");
|
const Server = std.zig.Server;
|
||||||
|
|
||||||
pub const std_options = struct {
|
pub const std_options = struct {
|
||||||
pub const wasiCwd = wasi_cwd;
|
pub const wasiCwd = wasi_cwd;
|
||||||
@ -3545,6 +3546,7 @@ fn serve(
|
|||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.in = in,
|
.in = in,
|
||||||
.out = out,
|
.out = out,
|
||||||
|
.zig_version = build_options.version,
|
||||||
});
|
});
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
@ -3656,8 +3658,8 @@ fn serve(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
else => {
|
||||||
@panic("TODO unrecognized message from client");
|
fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5624,19 +5626,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat
|
|||||||
return std.zig.system.NativeTargetInfo.detect(cross_target);
|
return std.zig.system.NativeTargetInfo.detect(cross_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate that we are now terminating with a successful exit code.
|
|
||||||
/// In debug builds, this is a no-op, so that the calling code's
|
|
||||||
/// cleanup mechanisms are tested and so that external tools that
|
|
||||||
/// check for resource leaks can be accurate. In release builds, this
|
|
||||||
/// calls exit(0), and does not return.
|
|
||||||
pub fn cleanExit() void {
|
|
||||||
if (builtin.mode == .Debug) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const usage_ast_check =
|
const usage_ast_check =
|
||||||
\\Usage: zig ast-check [file]
|
\\Usage: zig ast-check [file]
|
||||||
\\
|
\\
|
||||||
|
|||||||
@ -8,8 +8,8 @@ const assert = std.debug.assert;
|
|||||||
|
|
||||||
const main = @import("main.zig");
|
const main = @import("main.zig");
|
||||||
const fatal = main.fatal;
|
const fatal = main.fatal;
|
||||||
const cleanExit = main.cleanExit;
|
const Server = std.zig.Server;
|
||||||
const Server = @import("Server.zig");
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
pub fn cmdObjCopy(
|
pub fn cmdObjCopy(
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
@ -116,6 +116,7 @@ pub fn cmdObjCopy(
|
|||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.in = std.io.getStdIn(),
|
.in = std.io.getStdIn(),
|
||||||
.out = std.io.getStdOut(),
|
.out = std.io.getStdOut(),
|
||||||
|
.zig_version = build_options.version,
|
||||||
});
|
});
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ pub fn cmdObjCopy(
|
|||||||
const hdr = try server.receiveMessage();
|
const hdr = try server.receiveMessage();
|
||||||
switch (hdr.tag) {
|
switch (hdr.tag) {
|
||||||
.exit => {
|
.exit => {
|
||||||
return cleanExit();
|
return std.process.cleanExit();
|
||||||
},
|
},
|
||||||
.update => {
|
.update => {
|
||||||
if (seen_update) {
|
if (seen_update) {
|
||||||
@ -144,7 +145,7 @@ pub fn cmdObjCopy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cleanExit();
|
return std.process.cleanExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const usage =
|
const usage =
|
||||||
|
|||||||
@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
|
|||||||
});
|
});
|
||||||
test_exe.linkLibrary(lib_a);
|
test_exe.linkLibrary(lib_a);
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
test_step.dependOn(&test_exe.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
|
|||||||
});
|
});
|
||||||
test_exe.linkLibrary(lib_a);
|
test_exe.linkLibrary(lib_a);
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
test_step.dependOn(&test_exe.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,5 +35,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
|
|||||||
test_exe.linkLibrary(lib_b);
|
test_exe.linkLibrary(lib_b);
|
||||||
test_exe.addIncludePath(".");
|
test_exe.addIncludePath(".");
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
test_step.dependOn(&test_exe.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,5 +32,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
|
|||||||
test_exe.linkLibrary(lib);
|
test_exe.linkLibrary(lib);
|
||||||
test_exe.linkLibC();
|
test_exe.linkLibC();
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
const run = test_exe.run();
|
||||||
|
run.skip_foreign_checks = true;
|
||||||
|
|
||||||
|
test_step.dependOn(&run.step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -547,15 +547,12 @@ pub fn lowerToBuildSteps(
|
|||||||
parent_step.dependOn(&artifact.step);
|
parent_step.dependOn(&artifact.step);
|
||||||
},
|
},
|
||||||
.Execution => |expected_stdout| {
|
.Execution => |expected_stdout| {
|
||||||
if (case.is_test) {
|
const run = b.addRunArtifact(artifact);
|
||||||
parent_step.dependOn(&artifact.step);
|
run.skip_foreign_checks = true;
|
||||||
} else {
|
if (!case.is_test) {
|
||||||
const run = b.addRunArtifact(artifact);
|
|
||||||
run.skip_foreign_checks = true;
|
|
||||||
run.expectStdOutEqual(expected_stdout);
|
run.expectStdOutEqual(expected_stdout);
|
||||||
|
|
||||||
parent_step.dependOn(&run.step);
|
|
||||||
}
|
}
|
||||||
|
parent_step.dependOn(&run.step);
|
||||||
},
|
},
|
||||||
.Header => @panic("TODO"),
|
.Header => @panic("TODO"),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,5 +11,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
|
main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
|
||||||
main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
|
main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
|
||||||
|
|
||||||
test_step.dependOn(&main.step);
|
test_step.dependOn(&main.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,5 +28,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
main.linkLibrary(obj1);
|
main.linkLibrary(obj1);
|
||||||
main.linkLibrary(obj2);
|
main.linkLibrary(obj2);
|
||||||
|
|
||||||
test_step.dependOn(&main.step);
|
test_step.dependOn(&main.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
test2.setTestRunner("src/main.zig");
|
test2.setTestRunner("src/main.zig");
|
||||||
test3.setTestRunner("src/main.zig");
|
test3.setTestRunner("src/main.zig");
|
||||||
|
|
||||||
test_step.dependOn(&test1.step);
|
test_step.dependOn(&test1.run().step);
|
||||||
test_step.dependOn(&test2.step);
|
test_step.dependOn(&test2.run().step);
|
||||||
test_step.dependOn(&test3.step);
|
test_step.dependOn(&test3.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,5 +9,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
});
|
});
|
||||||
test_exe.setMainPkgPath(".");
|
test_exe.setMainPkgPath(".");
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
test_step.dependOn(&test_exe.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?);
|
options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?);
|
||||||
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&main.step);
|
test_step.dependOn(&main.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,5 +17,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
});
|
});
|
||||||
main.pie = true;
|
main.pie = true;
|
||||||
|
|
||||||
test_step.dependOn(&main.step);
|
test_step.dependOn(&main.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,5 +21,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
test_exe.linkLibrary(foo);
|
test_exe.linkLibrary(foo);
|
||||||
test_exe.addIncludePath(".");
|
test_exe.addIncludePath(".");
|
||||||
|
|
||||||
test_step.dependOn(&test_exe.step);
|
test_step.dependOn(&test_exe.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
t.addModule("module2", module2);
|
t.addModule("module2", module2);
|
||||||
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&t.step);
|
test_step.dependOn(&t.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.root_source_file = .{ .path = "test.zig" },
|
.root_source_file = .{ .path = "test.zig" },
|
||||||
.kind = .test_exe,
|
|
||||||
});
|
});
|
||||||
test_exe.test_runner = "test_runner.zig";
|
test_exe.test_runner = "test_runner.zig";
|
||||||
|
|
||||||
|
|||||||
@ -12,5 +12,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
});
|
});
|
||||||
main.addIncludePath(".");
|
main.addIncludePath(".");
|
||||||
|
|
||||||
test_step.dependOn(&main.step);
|
test_step.dependOn(&main.run().step);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -596,7 +596,7 @@ pub fn addStandaloneTests(
|
|||||||
});
|
});
|
||||||
if (case.link_libc) exe.linkLibC();
|
if (case.link_libc) exe.linkLibC();
|
||||||
|
|
||||||
step.dependOn(&exe.step);
|
step.dependOn(&exe.run().step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -981,14 +981,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
|
|||||||
});
|
});
|
||||||
const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
|
const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
|
||||||
const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
|
const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
|
||||||
these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{
|
|
||||||
options.name,
|
|
||||||
triple_prefix,
|
|
||||||
@tagName(test_target.optimize_mode),
|
|
||||||
libc_prefix,
|
|
||||||
single_threaded_txt,
|
|
||||||
backend_txt,
|
|
||||||
}));
|
|
||||||
these_tests.single_threaded = test_target.single_threaded;
|
these_tests.single_threaded = test_target.single_threaded;
|
||||||
these_tests.setFilter(options.test_filter);
|
these_tests.setFilter(options.test_filter);
|
||||||
if (test_target.link_libc) {
|
if (test_target.link_libc) {
|
||||||
@ -1014,7 +1006,18 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
step.dependOn(&these_tests.step);
|
const run = these_tests.run();
|
||||||
|
run.skip_foreign_checks = true;
|
||||||
|
run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{
|
||||||
|
options.name,
|
||||||
|
triple_prefix,
|
||||||
|
@tagName(test_target.optimize_mode),
|
||||||
|
libc_prefix,
|
||||||
|
single_threaded_txt,
|
||||||
|
backend_txt,
|
||||||
|
}));
|
||||||
|
|
||||||
|
step.dependOn(&run.step);
|
||||||
}
|
}
|
||||||
return step;
|
return step;
|
||||||
}
|
}
|
||||||
@ -1053,7 +1056,9 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S
|
|||||||
@tagName(optimize_mode),
|
@tagName(optimize_mode),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
step.dependOn(&test_step.step);
|
const run = test_step.run();
|
||||||
|
run.skip_foreign_checks = true;
|
||||||
|
step.dependOn(&run.step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return step;
|
return step;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user