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