authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-25 23:20:12+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-25 23:20:12+02:00
log10ea7accf76d4ad59222ea5ff3585d6cc7cfe3b0
tree4cac1b809102b963687c05c1505c489ba1c760f1
parent5c5d1f93c4aa469cfba3f7838ae4a7db18e7152b
parentbc081901dc73aa0f2dd64350d4693425e822ed89
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17706 from ziglang/elf-error-tests

elf: test error generation

4 files changed, 196 insertions(+), 34 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+68-32
...@@ -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,39 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void {...@@ -2390,39 +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);2408 }
2403 try expected_generated.append('\n');2409
2404 continue;2410 return self.step.fail(
2405 }2411 \\
2406 if (mem.startsWith(u8, expect_line, ":?:?: ")) {2412 \\========= should contain: ===============
2407 if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {2413 \\{s}
2408 try expected_generated.appendSlice(actual_line);2414 \\========= but not found: ================
2415 \\{s}
2416 \\=========================================
2417 , .{ expect_line, actual_stderr });
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);
2409 try expected_generated.append('\n');2432 try expected_generated.append('\n');
2410 continue;
2411 }2433 }
2412 }
2413 try expected_generated.appendSlice(expect_line);
2414 try expected_generated.append('\n');
2415 }
24162434
2417 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;2435 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
24182436
2419 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile2437 return self.step.fail(
2420 return self.step.fail(2438 \\
2421 \\2439 \\========= expected: =====================
2422 \\========= expected: =====================2440 \\{s}
2423 \\{s}2441 \\========= but found: ====================
2424 \\========= but found: ====================2442 \\{s}
2425 \\{s}2443 \\=========================================
2426 \\=========================================2444 , .{ expected_generated.items, actual_stderr });
2427 , .{ 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;
2428}2464}
test/link/elf.zig+126
...@@ -76,6 +76,8 @@ pub fn build(b: *Build) void {...@@ -76,6 +76,8 @@ pub fn build(b: *Build) void {
76 elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target }));76 elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target }));
77 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));77 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
78 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));78 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));
79 elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));
80 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));
79 // https://github.com/ziglang/zig/issues/1745181 // https://github.com/ziglang/zig/issues/17451
80 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));82 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
81 elf_step.dependOn(testPie(b, .{ .target = glibc_target }));83 elf_step.dependOn(testPie(b, .{ .target = glibc_target }));
...@@ -99,6 +101,8 @@ pub fn build(b: *Build) void {...@@ -99,6 +101,8 @@ pub fn build(b: *Build) void {
99 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));101 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));
100 elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));102 elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));
101 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));103 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));
104 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = glibc_target }));
105 elf_step.dependOn(testUnresolvedError(b, .{ .target = glibc_target }));
102 elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));106 elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));
103 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));107 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));
104 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));108 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
...@@ -1601,6 +1605,56 @@ fn testLdScript(b: *Build, opts: Options) *Step {...@@ -1601,6 +1605,56 @@ fn testLdScript(b: *Build, opts: Options) *Step {
1601 return test_step;1605 return test_step;
1602}1606}
16031607
1608fn testLdScriptPathError(b: *Build, opts: Options) *Step {
1609 const test_step = addTestStep(b, "ld-script-path-error", opts);
1610
1611 const scripts = WriteFile.create(b);
1612 _ = scripts.add("liba.so", "INPUT(libfoo.so)");
1613
1614 const exe = addExecutable(b, "main", opts);
1615 addCSourceBytes(exe, "int main() { return 0; }", &.{});
1616 exe.linkSystemLibrary2("a", .{});
1617 exe.addLibraryPath(scripts.getDirectory());
1618 exe.linkLibC();
1619
1620 expectLinkErrors(
1621 exe,
1622 test_step,
1623 .{
1624 .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",
1625 },
1626 );
1627
1628 return test_step;
1629}
1630
1631fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
1632 const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
1633
1634 const obj = addObject(b, "a", .{
1635 .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu },
1636 });
1637 addCSourceBytes(obj, "int foo;", &.{});
1638 obj.strip = true;
1639
1640 const exe = addExecutable(b, "main", opts);
1641 addCSourceBytes(exe,
1642 \\extern int foo;
1643 \\int main() {
1644 \\ return foo;
1645 \\}
1646 , &.{});
1647 exe.addObject(obj);
1648 exe.linkLibC();
1649
1650 expectLinkErrors(exe, test_step, .{ .exact = &.{
1651 "invalid cpu architecture: expected 'x86_64', but found 'aarch64'",
1652 "note: while parsing /?/a.o",
1653 } });
1654
1655 return test_step;
1656}
1657
1604fn testLinkingC(b: *Build, opts: Options) *Step {1658fn testLinkingC(b: *Build, opts: Options) *Step {
1605 const test_step = addTestStep(b, "linking-c", opts);1659 const test_step = addTestStep(b, "linking-c", opts);
16061660
...@@ -2783,6 +2837,72 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {...@@ -2783,6 +2837,72 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {
2783 return test_step;2837 return test_step;
2784}2838}
27852839
2840fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
2841 const test_step = addTestStep(b, "unknown-file-type-error", opts);
2842
2843 const dylib = addSharedLibrary(b, "a", .{
2844 .target = .{ .cpu_arch = .x86_64, .os_tag = .macos },
2845 });
2846 addZigSourceBytes(dylib, "export var foo: i32 = 0;");
2847
2848 const exe = addExecutable(b, "main", opts);
2849 addCSourceBytes(exe,
2850 \\extern int foo;
2851 \\int main() {
2852 \\ return foo;
2853 \\}
2854 , &.{});
2855 exe.linkLibrary(dylib);
2856 exe.linkLibC();
2857
2858 expectLinkErrors(exe, test_step, .{ .exact = &.{
2859 "unknown file type",
2860 "note: while parsing /?/liba.dylib",
2861 "undefined symbol: foo",
2862 "note: referenced by /?/a.o:.text",
2863 } });
2864
2865 return test_step;
2866}
2867
2868fn testUnresolvedError(b: *Build, opts: Options) *Step {
2869 const test_step = addTestStep(b, "unresolved-error", opts);
2870
2871 const obj1 = addObject(b, "a", opts);
2872 addCSourceBytes(obj1,
2873 \\#include <stdio.h>
2874 \\int foo();
2875 \\int bar() {
2876 \\ return foo() + 1;
2877 \\}
2878 , &.{"-ffunction-sections"});
2879 obj1.linkLibC();
2880
2881 const obj2 = addObject(b, "b", opts);
2882 addCSourceBytes(obj2,
2883 \\#include <stdio.h>
2884 \\int foo();
2885 \\int bar();
2886 \\int main() {
2887 \\ return foo() + bar();
2888 \\}
2889 , &.{"-ffunction-sections"});
2890 obj2.linkLibC();
2891
2892 const exe = addExecutable(b, "main", opts);
2893 exe.addObject(obj1);
2894 exe.addObject(obj2);
2895 exe.linkLibC();
2896
2897 expectLinkErrors(exe, test_step, .{ .exact = &.{
2898 "error: undefined symbol: foo",
2899 "note: referenced by /?/a.o:.text.bar",
2900 "note: referenced by /?/b.o:.text.main",
2901 } });
2902
2903 return test_step;
2904}
2905
2786fn testWeakExports(b: *Build, opts: Options) *Step {2906fn testWeakExports(b: *Build, opts: Options) *Step {
2787 const test_step = addTestStep(b, "weak-exports", opts);2907 const test_step = addTestStep(b, "weak-exports", opts);
27882908
...@@ -3081,6 +3201,12 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {...@@ -3081,6 +3201,12 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
3081 comp.addAssemblyFile(file);3201 comp.addAssemblyFile(file);
3082}3202}
30833203
3204fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
3205 comp.expect_errors = expected_errors;
3206 const bin_file = comp.getEmittedBin();
3207 bin_file.addStepDependencies(test_step);
3208}
3209
3084const std = @import("std");3210const std = @import("std");
30853211
3086const Build = std.Build;3212const Build = std.Build;
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: {