| author | |
| committer | |
| log | 10ea7accf76d4ad59222ea5ff3585d6cc7cfe3b0 |
| tree | 4cac1b809102b963687c05c1505c489ba1c760f1 |
| parent | 5c5d1f93c4aa469cfba3f7838ae4a7db18e7152b |
| parent | bc081901dc73aa0f2dd64350d4693425e822ed89 |
| signature |
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,7 +415,7 @@ pub fn evalZigProcess( |
| 415 | .Exited => { | 415 | .Exited => { |
| 416 | // Note that the exit code may be 0 in this case due to the | 416 | // 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, |
| 204 | use_lld: ?bool, | 204 | use_lld: ?bool, |
| 205 | 205 | ||
| 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 to | 207 | /// 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 failure | 208 | /// check for compile errors and return *success* if they match, and failure |
| 209 | /// otherwise. | 209 | /// otherwise. |
| 210 | expect_errors: []const []const u8 = &.{}, | 210 | expect_errors: ?ExpectedCompileErrors = null, |
| 211 | 211 | ||
| 212 | emit_directory: ?*GeneratedFile, | 212 | emit_directory: ?*GeneratedFile, |
| 213 | 213 | ||
| ... | @@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile, | ... | @@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile, |
| 220 | generated_llvm_ir: ?*GeneratedFile, | 220 | generated_llvm_ir: ?*GeneratedFile, |
| 221 | generated_h: ?*GeneratedFile, | 221 | generated_h: ?*GeneratedFile, |
| 222 | 222 | ||
| 223 | pub const ExpectedCompileErrors = union(enum) { | ||
| 224 | contains: []const u8, | ||
| 225 | exact: []const []const u8, | ||
| 226 | }; | ||
| 227 | |||
| 223 | pub const CSourceFiles = struct { | 228 | pub 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 { |
| 2131 | 2136 | ||
| 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 { |
| 2390 | 2395 | ||
| 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.?; | ||
| 2393 | 2399 | ||
| 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 | } | ||
| 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 | 2437 | 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 | |||
| 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,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/17451 | 81 | // 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 | } |
| 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 | fn testLinkingC(b: *Build, opts: Options) *Step { | 1658 | fn testLinkingC(b: *Build, opts: Options) *Step { |
| 1605 | const test_step = addTestStep(b, "linking-c", opts); | 1659 | const test_step = addTestStep(b, "linking-c", opts); |
| 1606 | 1660 | ||
| ... | @@ -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 | } |
| 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 | fn testWeakExports(b: *Build, opts: Options) *Step { | 2906 | fn testWeakExports(b: *Build, opts: Options) *Step { |
| 2787 | const test_step = addTestStep(b, "weak-exports", opts); | 2907 | const test_step = addTestStep(b, "weak-exports", opts); |
| 2788 | 2908 | ||
| ... | @@ -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 | } |
| 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 | const std = @import("std"); | 3210 | const std = @import("std"); |
| 3085 | 3211 | ||
| 3086 | const Build = std.Build; | 3212 | const 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: { |