authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-25 11:40:16+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-25 11:40:16+02:00
logbc081901dc73aa0f2dd64350d4693425e822ed89
treeb6141c31588b63046240f475d942a4ad321520fd
parent55c7a6d99d7897dec8562174803c1ad73f1d0b38

Step.Compile: differentiate between fuzzy and exact matches for compile errors


4 files changed, 83 insertions(+), 58 deletions(-)

lib/std/Build/Step.zig+1-1
...@@ -415,7 +415,7 @@ pub fn evalZigProcess(...@@ -415,7 +415,7 @@ pub fn evalZigProcess(
415 .Exited => {415 .Exited => {
416 // Note that the exit code may be 0 in this case due to the416 // Note that the exit code may be 0 in this case due to the
417 // compiler server protocol.417 // compiler server protocol.
418 if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) {418 if (compile.expect_errors != null and s.result_error_bundle.errorMessageCount() > 0) {
419 return error.NeedCompileErrorCheck;419 return error.NeedCompileErrorCheck;
420 }420 }
421 },421 },
lib/std/Build/Step/Compile.zig+67-44
...@@ -204,10 +204,10 @@ use_llvm: ?bool,...@@ -204,10 +204,10 @@ use_llvm: ?bool,
204use_lld: ?bool,204use_lld: ?bool,
205205
206/// This is an advanced setting that can change the intent of this Compile step.206/// This is an advanced setting that can change the intent of this Compile step.
207/// If this slice has nonzero length, it means that this Compile step exists to207/// If this value is non-null, it means that this Compile step exists to
208/// check for compile errors and return *success* if they match, and failure208/// check for compile errors and return *success* if they match, and failure
209/// otherwise.209/// otherwise.
210expect_errors: []const []const u8 = &.{},210expect_errors: ?ExpectedCompileErrors = null,
211211
212emit_directory: ?*GeneratedFile,212emit_directory: ?*GeneratedFile,
213213
...@@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile,...@@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile,
220generated_llvm_ir: ?*GeneratedFile,220generated_llvm_ir: ?*GeneratedFile,
221generated_h: ?*GeneratedFile,221generated_h: ?*GeneratedFile,
222222
223pub const ExpectedCompileErrors = union(enum) {
224 contains: []const u8,
225 exact: []const []const u8,
226};
227
223pub const CSourceFiles = struct {228pub const CSourceFiles = struct {
224 dependency: ?*std.Build.Dependency,229 dependency: ?*std.Build.Dependency,
225 /// If `dependency` is not null relative to it,230 /// If `dependency` is not null relative to it,
...@@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
21312136
2132 const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {2137 const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
2133 error.NeedCompileErrorCheck => {2138 error.NeedCompileErrorCheck => {
2134 assert(self.expect_errors.len != 0);2139 assert(self.expect_errors != null);
2135 try checkCompileErrors(self);2140 try checkCompileErrors(self);
2136 return;2141 return;
2137 },2142 },
...@@ -2390,52 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void {...@@ -2390,52 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void {
23902395
2391 // Render the expected lines into a string that we can compare verbatim.2396 // Render the expected lines into a string that we can compare verbatim.
2392 var expected_generated = std.ArrayList(u8).init(arena);2397 var expected_generated = std.ArrayList(u8).init(arena);
2398 const expect_errors = self.expect_errors.?;
23932399
2394 var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n');2400 var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n');
2395 for (self.expect_errors) |expect_line| {2401
2396 const actual_line = actual_line_it.next() orelse {2402 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
2397 try expected_generated.appendSlice(expect_line);2403 switch (expect_errors) {
2398 try expected_generated.append('\n');2404 .contains => |expect_line| {
2399 continue;2405 while (actual_line_it.next()) |actual_line| {
2400 };2406 if (!matchCompileError(actual_line, expect_line)) continue;
2401 if (mem.endsWith(u8, actual_line, expect_line)) {2407 return;
2402 try expected_generated.appendSlice(actual_line);
2403 try expected_generated.append('\n');
2404 continue;
2405 }
2406 if (mem.startsWith(u8, expect_line, ":?:?: ")) {
2407 if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {
2408 try expected_generated.appendSlice(actual_line);
2409 try expected_generated.append('\n');
2410 continue;
2411 }2408 }
2412 }2409
2413 // We scan for /?/ in expected line and if there is a match, we match everything2410 return self.step.fail(
2414 // up to and after /?/.2411 \\
2415 const expect_line_trim = mem.trim(u8, expect_line, " ");2412 \\========= should contain: ===============
2416 if (mem.indexOf(u8, expect_line_trim, "/?/")) |exp_index| {2413 \\{s}
2417 const actual_line_trim = mem.trim(u8, actual_line, " ");2414 \\========= but not found: ================
2418 const exp_lhs = expect_line_trim[0..exp_index];2415 \\{s}
2419 const exp_rhs = expect_line_trim[exp_index + "/?/".len ..];2416 \\=========================================
2420 if (mem.startsWith(u8, actual_line_trim, exp_lhs) and mem.endsWith(u8, actual_line_trim, exp_rhs)) {2417 , .{ expect_line, actual_stderr });
2421 try expected_generated.appendSlice(actual_line);2418 },
2419 .exact => |expect_lines| {
2420 for (expect_lines) |expect_line| {
2421 const actual_line = actual_line_it.next() orelse {
2422 try expected_generated.appendSlice(expect_line);
2423 try expected_generated.append('\n');
2424 continue;
2425 };
2426 if (matchCompileError(actual_line, expect_line)) {
2427 try expected_generated.appendSlice(actual_line);
2428 try expected_generated.append('\n');
2429 continue;
2430 }
2431 try expected_generated.appendSlice(expect_line);
2422 try expected_generated.append('\n');2432 try expected_generated.append('\n');
2423 continue;
2424 }2433 }
2425 }
2426 try expected_generated.appendSlice(expect_line);
2427 try expected_generated.append('\n');
2428 }
24292434
2430 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;2435 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
24312436
2432 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile2437 return self.step.fail(
2433 return self.step.fail(2438 \\
2434 \\2439 \\========= expected: =====================
2435 \\========= expected: =====================2440 \\{s}
2436 \\{s}2441 \\========= but found: ====================
2437 \\========= but found: ====================2442 \\{s}
2438 \\{s}2443 \\=========================================
2439 \\=========================================2444 , .{ expected_generated.items, actual_stderr });
2440 , .{ expected_generated.items, actual_stderr });2445 },
2446 }
2447}
2448
2449fn matchCompileError(actual: []const u8, expected: []const u8) bool {
2450 if (mem.endsWith(u8, actual, expected)) return true;
2451 if (mem.startsWith(u8, expected, ":?:?: ")) {
2452 if (mem.endsWith(u8, actual, expected[":?:?: ".len..])) return true;
2453 }
2454 // We scan for /?/ in expected line and if there is a match, we match everything
2455 // up to and after /?/.
2456 const expected_trim = mem.trim(u8, expected, " ");
2457 if (mem.indexOf(u8, expected_trim, "/?/")) |index| {
2458 const actual_trim = mem.trim(u8, actual, " ");
2459 const lhs = expected_trim[0..index];
2460 const rhs = expected_trim[index + "/?/".len ..];
2461 if (mem.startsWith(u8, actual_trim, lhs) and mem.endsWith(u8, actual_trim, rhs)) return true;
2462 }
2463 return false;
2441}2464}
test/link/elf.zig+14-12
...@@ -1617,11 +1617,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {...@@ -1617,11 +1617,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
1617 exe.addLibraryPath(scripts.getDirectory());1617 exe.addLibraryPath(scripts.getDirectory());
1618 exe.linkLibC();1618 exe.linkLibC();
16191619
1620 expectLinkErrors(exe, test_step, &.{1620 expectLinkErrors(
1621 "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",1621 exe,
1622 "note: tried libfoo.so",1622 test_step,
1623 "note: tried /?/libfoo.so",1623 .{
1624 });1624 .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",
1625 },
1626 );
16251627
1626 return test_step;1628 return test_step;
1627}1629}
...@@ -1645,10 +1647,10 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {...@@ -1645,10 +1647,10 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
1645 exe.addObject(obj);1647 exe.addObject(obj);
1646 exe.linkLibC();1648 exe.linkLibC();
16471649
1648 expectLinkErrors(exe, test_step, &.{1650 expectLinkErrors(exe, test_step, .{ .exact = &.{
1649 "invalid cpu architecture: expected 'x86_64', but found 'aarch64'",1651 "invalid cpu architecture: expected 'x86_64', but found 'aarch64'",
1650 "note: while parsing /?/a.o",1652 "note: while parsing /?/a.o",
1651 });1653 } });
16521654
1653 return test_step;1655 return test_step;
1654}1656}
...@@ -2853,12 +2855,12 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {...@@ -2853,12 +2855,12 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
2853 exe.linkLibrary(dylib);2855 exe.linkLibrary(dylib);
2854 exe.linkLibC();2856 exe.linkLibC();
28552857
2856 expectLinkErrors(exe, test_step, &.{2858 expectLinkErrors(exe, test_step, .{ .exact = &.{
2857 "unknown file type",2859 "unknown file type",
2858 "note: while parsing /?/liba.dylib",2860 "note: while parsing /?/liba.dylib",
2859 "undefined symbol: foo",2861 "undefined symbol: foo",
2860 "note: referenced by /?/a.o:.text",2862 "note: referenced by /?/a.o:.text",
2861 });2863 } });
28622864
2863 return test_step;2865 return test_step;
2864}2866}
...@@ -2892,11 +2894,11 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {...@@ -2892,11 +2894,11 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
2892 exe.addObject(obj2);2894 exe.addObject(obj2);
2893 exe.linkLibC();2895 exe.linkLibC();
28942896
2895 expectLinkErrors(exe, test_step, &.{2897 expectLinkErrors(exe, test_step, .{ .exact = &.{
2896 "error: undefined symbol: foo",2898 "error: undefined symbol: foo",
2897 "note: referenced by /?/a.o:.text.bar",2899 "note: referenced by /?/a.o:.text.bar",
2898 "note: referenced by /?/b.o:.text.main",2900 "note: referenced by /?/b.o:.text.main",
2899 });2901 } });
29002902
2901 return test_step;2903 return test_step;
2902}2904}
...@@ -3199,7 +3201,7 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {...@@ -3199,7 +3201,7 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
3199 comp.addAssemblyFile(file);3201 comp.addAssemblyFile(file);
3200}3202}
32013203
3202fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: []const []const u8) void {3204fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
3203 comp.expect_errors = expected_errors;3205 comp.expect_errors = expected_errors;
3204 const bin_file = comp.getEmittedBin();3206 const bin_file = comp.getEmittedBin();
3205 bin_file.addStepDependencies(test_step);3207 bin_file.addStepDependencies(test_step);
test/src/Cases.zig+1-1
...@@ -640,7 +640,7 @@ pub fn lowerToBuildSteps(...@@ -640,7 +640,7 @@ pub fn lowerToBuildSteps(
640 },640 },
641 .Error => |expected_msgs| {641 .Error => |expected_msgs| {
642 assert(expected_msgs.len != 0);642 assert(expected_msgs.len != 0);
643 artifact.expect_errors = expected_msgs;643 artifact.expect_errors = .{ .exact = expected_msgs };
644 parent_step.dependOn(&artifact.step);644 parent_step.dependOn(&artifact.step);
645 },645 },
646 .Execution => |expected_stdout| no_exec: {646 .Execution => |expected_stdout| no_exec: {