master
1b: *std.Build,
2step: *Step,
3test_filters: []const []const u8,
4targets: []const std.Build.ResolvedTarget,
5optimize_modes: []const OptimizeMode,
6convert_exe: *std.Build.Step.Compile,
7
8pub const Case = struct {
9 name: []const u8,
10 source: []const u8,
11 expect_error: []const u8,
12 expect_trace: []const u8,
13 /// On these arch/OS pairs we will not test the error trace on optimized LLVM builds because the
14 /// optimizations break the error trace. We will test the binary with error tracing disabled,
15 /// just to ensure that the expected error is still returned from `main`.
16 ///
17 /// LLVM ReleaseSmall builds always have the trace disabled regardless of this field, because it
18 /// seems that LLVM is particularly good at optimizing traces away in those.
19 disable_trace_optimized: []const DisableConfig = &.{},
20
21 pub const DisableConfig = struct { std.Target.Cpu.Arch, std.Target.Os.Tag };
22 pub const Backend = enum { llvm, selfhosted };
23};
24
25pub fn addCase(self: *ErrorTrace, case: Case) void {
26 for (self.targets) |*target| {
27 const triple: ?[]const u8 = if (target.query.isNative()) null else t: {
28 break :t target.query.zigTriple(self.b.graph.arena) catch @panic("OOM");
29 };
30 for (self.optimize_modes) |optimize| {
31 self.addCaseConfig(case, target, triple, optimize, .llvm);
32 }
33 if (shouldTestNonLlvm(&target.result)) {
34 for (self.optimize_modes) |optimize| {
35 self.addCaseConfig(case, target, triple, optimize, .selfhosted);
36 }
37 }
38 }
39}
40
41fn shouldTestNonLlvm(target: *const std.Target) bool {
42 if (comptime builtin.cpu.arch.endian() == .big) return false; // https://github.com/ziglang/zig/issues/25961
43 return switch (target.cpu.arch) {
44 .x86_64 => switch (target.ofmt) {
45 .elf => !target.os.tag.isBSD() and target.os.tag != .illumos,
46 else => false,
47 },
48 else => false,
49 };
50}
51
52fn addCaseConfig(
53 self: *ErrorTrace,
54 case: Case,
55 target: *const std.Build.ResolvedTarget,
56 triple: ?[]const u8,
57 optimize: OptimizeMode,
58 backend: Case.Backend,
59) void {
60 const b = self.b;
61
62 const error_tracing: bool = tracing: {
63 if (optimize == .Debug) break :tracing true;
64 if (backend != .llvm) break :tracing true;
65 if (optimize == .ReleaseSmall) break :tracing false;
66 for (case.disable_trace_optimized) |disable| {
67 const d_arch, const d_os = disable;
68 if (target.result.cpu.arch == d_arch and target.result.os.tag == d_os) {
69 // This particular configuration cannot do error tracing in optimized LLVM builds.
70 break :tracing false;
71 }
72 }
73 break :tracing true;
74 };
75
76 const annotated_case_name = b.fmt("check {s} ({s}{s}{s} {s})", .{
77 case.name,
78 triple orelse "",
79 if (triple != null) " " else "",
80 @tagName(optimize),
81 @tagName(backend),
82 });
83 if (self.test_filters.len > 0) {
84 for (self.test_filters) |test_filter| {
85 if (mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;
86 } else return;
87 }
88
89 const write_files = b.addWriteFiles();
90 const source_zig = write_files.add("source.zig", case.source);
91 const exe = b.addExecutable(.{
92 .name = "test",
93 .root_module = b.createModule(.{
94 .root_source_file = source_zig,
95 .optimize = optimize,
96 .target = target.*,
97 .error_tracing = error_tracing,
98 .strip = false,
99 }),
100 .use_llvm = switch (backend) {
101 .llvm => true,
102 .selfhosted => false,
103 },
104 });
105 exe.bundle_ubsan_rt = false;
106
107 const run = b.addRunArtifact(exe);
108 run.removeEnvironmentVariable("CLICOLOR_FORCE");
109 run.setEnvironmentVariable("NO_COLOR", "1");
110 run.expectExitCode(1);
111 run.expectStdOutEqual("");
112
113 const expected_stderr = switch (error_tracing) {
114 true => b.fmt("error: {s}\n{s}\n", .{ case.expect_error, case.expect_trace }),
115 false => b.fmt("error: {s}\n", .{case.expect_error}),
116 };
117
118 const check_run = b.addRunArtifact(self.convert_exe);
119 check_run.setName(annotated_case_name);
120 check_run.addFileArg(run.captureStdErr(.{}));
121 check_run.expectStdOutEqual(expected_stderr);
122
123 self.step.dependOn(&check_run.step);
124}
125
126const ErrorTrace = @This();
127const std = @import("std");
128const builtin = @import("builtin");
129const Step = std.Build.Step;
130const OptimizeMode = std.builtin.OptimizeMode;
131const mem = std.mem;