mirror of
https://github.com/ziglang/zig.git
synced 2025-12-08 07:13:08 +00:00
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.
302 lines
8.8 KiB
Zig
302 lines
8.8 KiB
Zig
in: std.fs.File,
|
|
out: std.fs.File,
|
|
receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
|
|
|
|
pub const Message = struct {
|
|
pub const Header = extern struct {
|
|
tag: Tag,
|
|
/// Size of the body only; does not include this Header.
|
|
bytes_len: u32,
|
|
};
|
|
|
|
pub const Tag = enum(u32) {
|
|
/// Body is a UTF-8 string.
|
|
zig_version,
|
|
/// Body is an ErrorBundle.
|
|
error_bundle,
|
|
/// Body is a UTF-8 string.
|
|
progress,
|
|
/// Body is a EmitBinPath.
|
|
emit_bin_path,
|
|
/// Body is a TestMetadata
|
|
test_metadata,
|
|
/// Body is a TestResults
|
|
test_results,
|
|
|
|
_,
|
|
};
|
|
|
|
/// Trailing:
|
|
/// * extra: [extra_len]u32,
|
|
/// * string_bytes: [string_bytes_len]u8,
|
|
/// See `std.zig.ErrorBundle`.
|
|
pub const ErrorBundle = extern struct {
|
|
extra_len: u32,
|
|
string_bytes_len: u32,
|
|
};
|
|
|
|
/// Trailing:
|
|
/// * name: [tests_len]u32
|
|
/// - null-terminated string_bytes index
|
|
/// * async_frame_len: [tests_len]u32,
|
|
/// - 0 means not async
|
|
/// * expected_panic_msg: [tests_len]u32,
|
|
/// - null-terminated string_bytes index
|
|
/// - 0 means does not expect pani
|
|
/// * string_bytes: [string_bytes_len]u8,
|
|
pub const TestMetadata = extern struct {
|
|
string_bytes_len: u32,
|
|
tests_len: u32,
|
|
};
|
|
|
|
pub const TestResults = extern struct {
|
|
index: u32,
|
|
flags: Flags,
|
|
|
|
pub const Flags = packed struct(u8) {
|
|
fail: bool,
|
|
skip: bool,
|
|
leak: bool,
|
|
|
|
reserved: u5 = 0,
|
|
};
|
|
};
|
|
|
|
/// Trailing:
|
|
/// * the file system path the emitted binary can be found
|
|
pub const EmitBinPath = extern struct {
|
|
flags: Flags,
|
|
|
|
pub const Flags = packed struct(u8) {
|
|
cache_hit: bool,
|
|
reserved: u7 = 0,
|
|
};
|
|
};
|
|
};
|
|
|
|
pub const Options = struct {
|
|
gpa: Allocator,
|
|
in: std.fs.File,
|
|
out: std.fs.File,
|
|
zig_version: []const u8,
|
|
};
|
|
|
|
pub fn init(options: Options) !Server {
|
|
var s: Server = .{
|
|
.in = options.in,
|
|
.out = options.out,
|
|
.receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
|
|
};
|
|
try s.serveStringMessage(.zig_version, options.zig_version);
|
|
return s;
|
|
}
|
|
|
|
pub fn deinit(s: *Server) void {
|
|
s.receive_fifo.deinit();
|
|
s.* = undefined;
|
|
}
|
|
|
|
pub fn receiveMessage(s: *Server) !InMessage.Header {
|
|
const Header = InMessage.Header;
|
|
const fifo = &s.receive_fifo;
|
|
|
|
while (true) {
|
|
const buf = fifo.readableSlice(0);
|
|
assert(fifo.readableLength() == buf.len);
|
|
if (buf.len >= @sizeOf(Header)) {
|
|
// workaround for https://github.com/ziglang/zig/issues/14904
|
|
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));
|
|
return .{
|
|
.tag = tag,
|
|
.bytes_len = bytes_len,
|
|
};
|
|
} else {
|
|
const needed = bytes_len - (buf.len - @sizeOf(Header));
|
|
const write_buffer = try fifo.writableWithSize(needed);
|
|
const amt = try s.in.read(write_buffer);
|
|
fifo.update(amt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const write_buffer = try fifo.writableWithSize(256);
|
|
const amt = try s.in.read(write_buffer);
|
|
fifo.update(amt);
|
|
}
|
|
}
|
|
|
|
pub fn receiveBody_u32(s: *Server) !u32 {
|
|
const fifo = &s.receive_fifo;
|
|
const buf = fifo.readableSlice(0);
|
|
const result = @ptrCast(*align(1) const u32, buf[0..4]).*;
|
|
fifo.discard(4);
|
|
return bswap(result);
|
|
}
|
|
|
|
pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
|
|
return s.serveMessage(.{
|
|
.tag = tag,
|
|
.bytes_len = @intCast(u32, msg.len),
|
|
}, &.{msg});
|
|
}
|
|
|
|
pub fn serveMessage(
|
|
s: *const Server,
|
|
header: OutMessage.Header,
|
|
bufs: []const []const u8,
|
|
) !void {
|
|
var iovecs: [10]std.os.iovec_const = undefined;
|
|
const header_le = bswap(header);
|
|
iovecs[0] = .{
|
|
.iov_base = @ptrCast([*]const u8, &header_le),
|
|
.iov_len = @sizeOf(OutMessage.Header),
|
|
};
|
|
for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
|
|
iovec.* = .{
|
|
.iov_base = buf.ptr,
|
|
.iov_len = buf.len,
|
|
};
|
|
}
|
|
try s.out.writevAll(iovecs[0 .. bufs.len + 1]);
|
|
}
|
|
|
|
pub fn serveEmitBinPath(
|
|
s: *Server,
|
|
fs_path: []const u8,
|
|
header: OutMessage.EmitBinPath,
|
|
) !void {
|
|
try s.serveMessage(.{
|
|
.tag = .emit_bin_path,
|
|
.bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)),
|
|
}, &.{
|
|
std.mem.asBytes(&header),
|
|
fs_path,
|
|
});
|
|
}
|
|
|
|
pub fn serveTestResults(
|
|
s: *Server,
|
|
msg: OutMessage.TestResults,
|
|
) !void {
|
|
const msg_le = bswap(msg);
|
|
try s.serveMessage(.{
|
|
.tag = .test_results,
|
|
.bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)),
|
|
}, &.{
|
|
std.mem.asBytes(&msg_le),
|
|
});
|
|
}
|
|
|
|
pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
|
|
const eb_hdr: OutMessage.ErrorBundle = .{
|
|
.extra_len = @intCast(u32, error_bundle.extra.len),
|
|
.string_bytes_len = @intCast(u32, error_bundle.string_bytes.len),
|
|
};
|
|
const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
|
|
4 * error_bundle.extra.len + error_bundle.string_bytes.len;
|
|
try s.serveMessage(.{
|
|
.tag = .error_bundle,
|
|
.bytes_len = @intCast(u32, bytes_len),
|
|
}, &.{
|
|
std.mem.asBytes(&eb_hdr),
|
|
// TODO: implement @ptrCast between slices changing the length
|
|
std.mem.sliceAsBytes(error_bundle.extra),
|
|
error_bundle.string_bytes,
|
|
});
|
|
}
|
|
|
|
pub const TestMetadata = struct {
|
|
names: []u32,
|
|
async_frame_sizes: []u32,
|
|
expected_panic_msgs: []u32,
|
|
string_bytes: []const u8,
|
|
};
|
|
|
|
pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
|
|
const header: OutMessage.TestMetadata = .{
|
|
.tests_len = bswap(@intCast(u32, test_metadata.names.len)),
|
|
.string_bytes_len = bswap(@intCast(u32, test_metadata.string_bytes.len)),
|
|
};
|
|
const bytes_len = @sizeOf(OutMessage.TestMetadata) +
|
|
3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len;
|
|
|
|
if (need_bswap) {
|
|
bswap_u32_array(test_metadata.names);
|
|
bswap_u32_array(test_metadata.async_frame_sizes);
|
|
bswap_u32_array(test_metadata.expected_panic_msgs);
|
|
}
|
|
defer if (need_bswap) {
|
|
bswap_u32_array(test_metadata.names);
|
|
bswap_u32_array(test_metadata.async_frame_sizes);
|
|
bswap_u32_array(test_metadata.expected_panic_msgs);
|
|
};
|
|
|
|
return s.serveMessage(.{
|
|
.tag = .test_metadata,
|
|
.bytes_len = @intCast(u32, bytes_len),
|
|
}, &.{
|
|
std.mem.asBytes(&header),
|
|
// TODO: implement @ptrCast between slices changing the length
|
|
std.mem.sliceAsBytes(test_metadata.names),
|
|
std.mem.sliceAsBytes(test_metadata.async_frame_sizes),
|
|
std.mem.sliceAsBytes(test_metadata.expected_panic_msgs),
|
|
test_metadata.string_bytes,
|
|
});
|
|
}
|
|
|
|
fn bswap(x: anytype) @TypeOf(x) {
|
|
if (!need_bswap) return x;
|
|
|
|
const T = @TypeOf(x);
|
|
switch (@typeInfo(T)) {
|
|
.Enum => return @intToEnum(T, @byteSwap(@enumToInt(x))),
|
|
.Int => return @byteSwap(x),
|
|
.Struct => |info| switch (info.layout) {
|
|
.Extern => {
|
|
var result: T = undefined;
|
|
inline for (info.fields) |field| {
|
|
@field(result, field.name) = bswap(@field(x, field.name));
|
|
}
|
|
return result;
|
|
},
|
|
.Packed => {
|
|
const I = info.backing_integer.?;
|
|
return @bitCast(T, @byteSwap(@bitCast(I, x)));
|
|
},
|
|
.Auto => @compileError("auto layout struct"),
|
|
},
|
|
else => @compileError("bswap on type " ++ @typeName(T)),
|
|
}
|
|
}
|
|
|
|
fn bswap_u32_array(slice: []u32) void {
|
|
comptime assert(need_bswap);
|
|
for (slice) |*elem| elem.* = @byteSwap(elem.*);
|
|
}
|
|
|
|
/// workaround for https://github.com/ziglang/zig/issues/14904
|
|
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(bytes_ptr: *const [4]u8) InMessage.Tag {
|
|
const int = std.mem.readIntLittle(u32, bytes_ptr);
|
|
return @intToEnum(InMessage.Tag, int);
|
|
}
|
|
|
|
const OutMessage = std.zig.Server.Message;
|
|
const InMessage = std.zig.Client.Message;
|
|
|
|
const Server = @This();
|
|
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
const native_endian = builtin.target.cpu.arch.endian();
|
|
const need_bswap = native_endian != .Little;
|