| author | |
| committer | |
| log | 10ea7accf76d4ad59222ea5ff3585d6cc7cfe3b0 |
| tree | 4cac1b809102b963687c05c1505c489ba1c760f1 |
| parent | 5c5d1f93c4aa469cfba3f7838ae4a7db18e7152b |
| parent | bc081901dc73aa0f2dd64350d4693425e822ed89 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
elf: test error generation4 files changed, 196 insertions(+), 34 deletions(-)
lib/std/Build/Step.zig+1-1| ... | ... | @@ -415,7 +415,7 @@ pub fn evalZigProcess( |
| 415 | 415 | .Exited => { |
| 416 | 416 | // Note that the exit code may be 0 in this case due to the |
| 417 | 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 | 419 | return error.NeedCompileErrorCheck; |
| 420 | 420 | } |
| 421 | 421 | }, |
lib/std/Build/Step/Compile.zig+68-32| ... | ... | @@ -204,10 +204,10 @@ use_llvm: ?bool, |
| 204 | 204 | use_lld: ?bool, |
| 205 | 205 | |
| 206 | 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 to | |
| 207 | /// If this value is non-null, it means that this Compile step exists to | |
| 208 | 208 | /// check for compile errors and return *success* if they match, and failure |
| 209 | 209 | /// otherwise. |
| 210 | expect_errors: []const []const u8 = &.{}, | |
| 210 | expect_errors: ?ExpectedCompileErrors = null, | |
| 211 | 211 | |
| 212 | 212 | emit_directory: ?*GeneratedFile, |
| 213 | 213 | |
| ... | ... | @@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile, |
| 220 | 220 | generated_llvm_ir: ?*GeneratedFile, |
| 221 | 221 | generated_h: ?*GeneratedFile, |
| 222 | 222 | |
| 223 | pub const ExpectedCompileErrors = union(enum) { | |
| 224 | contains: []const u8, | |
| 225 | exact: []const []const u8, | |
| 226 | }; | |
| 227 | ||
| 223 | 228 | pub const CSourceFiles = struct { |
| 224 | 229 | dependency: ?*std.Build.Dependency, |
| 225 | 230 | /// If `dependency` is not null relative to it, |
| ... | ... | @@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 2131 | 2136 | |
| 2132 | 2137 | const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { |
| 2133 | 2138 | error.NeedCompileErrorCheck => { |
| 2134 | assert(self.expect_errors.len != 0); | |
| 2139 | assert(self.expect_errors != null); | |
| 2135 | 2140 | try checkCompileErrors(self); |
| 2136 | 2141 | return; |
| 2137 | 2142 | }, |
| ... | ... | @@ -2390,39 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void { |
| 2390 | 2395 | |
| 2391 | 2396 | // Render the expected lines into a string that we can compare verbatim. |
| 2392 | 2397 | var expected_generated = std.ArrayList(u8).init(arena); |
| 2398 | const expect_errors = self.expect_errors.?; | |
| 2393 | 2399 | |
| 2394 | 2400 | var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n'); |
| 2395 | for (self.expect_errors) |expect_line| { | |
| 2396 | const actual_line = actual_line_it.next() orelse { | |
| 2397 | try expected_generated.appendSlice(expect_line); | |
| 2398 | try expected_generated.append('\n'); | |
| 2399 | continue; | |
| 2400 | }; | |
| 2401 | if (mem.endsWith(u8, actual_line, expect_line)) { | |
| 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); | |
| 2401 | ||
| 2402 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile | |
| 2403 | switch (expect_errors) { | |
| 2404 | .contains => |expect_line| { | |
| 2405 | while (actual_line_it.next()) |actual_line| { | |
| 2406 | if (!matchCompileError(actual_line, expect_line)) continue; | |
| 2407 | return; | |
| 2408 | } | |
| 2409 | ||
| 2410 | return self.step.fail( | |
| 2411 | \\ | |
| 2412 | \\========= should contain: =============== | |
| 2413 | \\{s} | |
| 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 | 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 | } | |
| 2416 | 2434 | |
| 2417 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | |
| 2435 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | |
| 2418 | 2436 | |
| 2419 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile | |
| 2420 | return self.step.fail( | |
| 2421 | \\ | |
| 2422 | \\========= expected: ===================== | |
| 2423 | \\{s} | |
| 2424 | \\========= but found: ==================== | |
| 2425 | \\{s} | |
| 2426 | \\========================================= | |
| 2427 | , .{ expected_generated.items, actual_stderr }); | |
| 2437 | return self.step.fail( | |
| 2438 | \\ | |
| 2439 | \\========= expected: ===================== | |
| 2440 | \\{s} | |
| 2441 | \\========= but found: ==================== | |
| 2442 | \\{s} | |
| 2443 | \\========================================= | |
| 2444 | , .{ expected_generated.items, actual_stderr }); | |
| 2445 | }, | |
| 2446 | } | |
| 2447 | } | |
| 2448 | ||
| 2449 | fn 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 | 76 | elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target })); |
| 77 | 77 | elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target })); |
| 78 | 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 | 81 | // https://github.com/ziglang/zig/issues/17451 |
| 80 | 82 | // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target })); |
| 81 | 83 | elf_step.dependOn(testPie(b, .{ .target = glibc_target })); |
| ... | ... | @@ -99,6 +101,8 @@ pub fn build(b: *Build) void { |
| 99 | 101 | elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target })); |
| 100 | 102 | elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target })); |
| 101 | 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 | 106 | elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target })); |
| 103 | 107 | elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target })); |
| 104 | 108 | elf_step.dependOn(testZNow(b, .{ .target = glibc_target })); |
| ... | ... | @@ -1601,6 +1605,56 @@ fn testLdScript(b: *Build, opts: Options) *Step { |
| 1601 | 1605 | return test_step; |
| 1602 | 1606 | } |
| 1603 | 1607 | |
| 1608 | fn 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 | ||
| 1631 | fn 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 | ||
| 1604 | 1658 | fn testLinkingC(b: *Build, opts: Options) *Step { |
| 1605 | 1659 | const test_step = addTestStep(b, "linking-c", opts); |
| 1606 | 1660 | |
| ... | ... | @@ -2783,6 +2837,72 @@ fn testTlsStatic(b: *Build, opts: Options) *Step { |
| 2783 | 2837 | return test_step; |
| 2784 | 2838 | } |
| 2785 | 2839 | |
| 2840 | fn 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 | ||
| 2868 | fn 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 | ||
| 2786 | 2906 | fn testWeakExports(b: *Build, opts: Options) *Step { |
| 2787 | 2907 | const test_step = addTestStep(b, "weak-exports", opts); |
| 2788 | 2908 | |
| ... | ... | @@ -3081,6 +3201,12 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void { |
| 3081 | 3201 | comp.addAssemblyFile(file); |
| 3082 | 3202 | } |
| 3083 | 3203 | |
| 3204 | fn 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 | ||
| 3084 | 3210 | const std = @import("std"); |
| 3085 | 3211 | |
| 3086 | 3212 | const Build = std.Build; |
test/src/Cases.zig+1-1| ... | ... | @@ -640,7 +640,7 @@ pub fn lowerToBuildSteps( |
| 640 | 640 | }, |
| 641 | 641 | .Error => |expected_msgs| { |
| 642 | 642 | assert(expected_msgs.len != 0); |
| 643 | artifact.expect_errors = expected_msgs; | |
| 643 | artifact.expect_errors = .{ .exact = expected_msgs }; | |
| 644 | 644 | parent_step.dependOn(&artifact.step); |
| 645 | 645 | }, |
| 646 | 646 | .Execution => |expected_stdout| no_exec: { |