Commit 4a233d1871

Andrew Kelley <andrew@ziglang.org>
2023-04-14 02:22:53
CI: more C backend test coverage
The CI now runs C backend tests in addition to compiling them. It uses -std=c99 -pedantic -Werror in order to catch non-conformant C code. This necessitated disabling a test case that caused a C compile error, in addition to disabling a handful of warnings that are already being triggered by Zig's C backend output for the behavior tests. The upshot is that I was able to, very cleanly, integrate the C backend tests into the build system, so that it communicates via the test runner protocol along with all the other behavior tests.
1 parent 3c93c16
Changed files (5)
lib/std/Build/RunStep.zig
@@ -140,6 +140,11 @@ pub fn setName(self: *RunStep, name: []const u8) void {
     self.rename_step_with_output_arg = false;
 }
 
+pub fn enableTestRunnerMode(rs: *RunStep) void {
+    rs.stdio = .zig_test;
+    rs.addArgs(&.{"--listen=-"});
+}
+
 pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
     self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
     self.step.dependOn(&artifact.step);
lib/std/zig/Server.zig
@@ -104,11 +104,9 @@ pub fn receiveMessage(s: *Server) !InMessage.Header {
         const buf = fifo.readableSlice(0);
         assert(fifo.readableLength() == buf.len);
         if (buf.len >= @sizeOf(Header)) {
-            const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
             // workaround for https://github.com/ziglang/zig/issues/14904
-            const bytes_len = bswap_and_workaround_u32(&header.bytes_len);
-            // workaround for https://github.com/ziglang/zig/issues/14904
-            const tag = bswap_and_workaround_tag(&header.tag);
+            const bytes_len = bswap_and_workaround_u32(buf[4..][0..4]);
+            const tag = bswap_and_workaround_tag(buf[0..][0..4]);
 
             if (buf.len - @sizeOf(Header) >= bytes_len) {
                 fifo.discard(@sizeOf(Header));
@@ -281,14 +279,12 @@ fn bswap_u32_array(slice: []u32) void {
 }
 
 /// workaround for https://github.com/ziglang/zig/issues/14904
-fn bswap_and_workaround_u32(x: *align(1) const u32) u32 {
-    const bytes_ptr = @ptrCast(*const [4]u8, x);
+fn bswap_and_workaround_u32(bytes_ptr: *const [4]u8) u32 {
     return std.mem.readIntLittle(u32, bytes_ptr);
 }
 
 /// workaround for https://github.com/ziglang/zig/issues/14904
-fn bswap_and_workaround_tag(x: *align(1) const InMessage.Tag) InMessage.Tag {
-    const bytes_ptr = @ptrCast(*const [4]u8, x);
+fn bswap_and_workaround_tag(bytes_ptr: *const [4]u8) InMessage.Tag {
     const int = std.mem.readIntLittle(u32, bytes_ptr);
     return @intToEnum(InMessage.Tag, int);
 }
lib/std/Build.zig
@@ -679,8 +679,7 @@ pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
     run_step.addArtifactArg(exe);
 
     if (exe.kind == .@"test") {
-        run_step.stdio = .zig_test;
-        run_step.addArgs(&.{"--listen=-"});
+        run_step.enableTestRunnerMode();
     }
 
     if (exe.vcpkg_bin_path) |path| {
test/behavior/atomics.zig
@@ -209,6 +209,12 @@ test "atomicrmw with floats" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 
+    if (builtin.zig_backend == .stage2_c) {
+        // TODO: test.c:34929:7: error: address argument to atomic operation must be a pointer to integer or pointer ('zig_f32 *' (aka 'float *') invalid
+        // when compiling with -std=c99 -pedantic
+        return error.SkipZigTest;
+    }
+
     if ((builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_c) and
         builtin.cpu.arch == .aarch64)
     {
test/tests.zig
@@ -988,18 +988,59 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
         these_tests.overrideZigLibDir("lib");
         these_tests.addIncludePath("test");
 
-        const run = b.addRunArtifact(these_tests);
-        run.skip_foreign_checks = true;
-        run.setName(b.fmt("run test {s}-{s}-{s}{s}{s}{s}", .{
+        const qualified_name = b.fmt("{s}-{s}-{s}{s}{s}{s}", .{
             options.name,
             triple_txt,
             @tagName(test_target.optimize_mode),
             libc_suffix,
             single_threaded_suffix,
             backend_suffix,
-        }));
+        });
+
+        if (test_target.target.ofmt == std.Target.ObjectFormat.c) {
+            var altered_target = test_target.target;
+            altered_target.ofmt = null;
 
-        step.dependOn(&run.step);
+            const compile_c = b.addExecutable(.{
+                .name = qualified_name,
+                .link_libc = test_target.link_libc,
+                .target = altered_target,
+            });
+            compile_c.overrideZigLibDir("lib");
+            compile_c.addCSourceFileSource(.{
+                .source = these_tests.getOutputSource(),
+                .args = &.{
+                    // TODO output -std=c89 compatible C code
+                    "-std=c99",
+                    "-pedantic",
+                    "-Werror",
+                    // TODO stop violating these pedantic errors
+                    "-Wno-address-of-packed-member",
+                    "-Wno-gnu-folding-constant",
+                    "-Wno-incompatible-pointer-types",
+                    "-Wno-overlength-strings",
+                },
+            });
+            compile_c.addIncludePath("lib"); // for zig.h
+            if (test_target.link_libc == false and test_target.target.getOsTag() == .windows) {
+                compile_c.subsystem = .Console;
+                compile_c.linkSystemLibrary("kernel32");
+                compile_c.linkSystemLibrary("ntdll");
+            }
+
+            const run = b.addRunArtifact(compile_c);
+            run.skip_foreign_checks = true;
+            run.enableTestRunnerMode();
+            run.setName(b.fmt("run test {s}", .{qualified_name}));
+
+            step.dependOn(&run.step);
+        } else {
+            const run = b.addRunArtifact(these_tests);
+            run.skip_foreign_checks = true;
+            run.setName(b.fmt("run test {s}", .{qualified_name}));
+
+            step.dependOn(&run.step);
+        }
     }
     return step;
 }