Skip to content

Commit f89bd6d

Browse files
chrisbbreuerclaude
andcommitted
fix: handle Zig 0.16-dev.2736 API removals
- std.Thread.Mutex removed: implement spinlock Mutex using atomics - std.posix.clock_gettime removed: use std.c.clock_gettime directly - std.posix.close removed: use std.c.close via closeFd wrapper - Add link_libc=true to all build targets (std.c.* needs explicit libc) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1a85e6f commit f89bd6d

2 files changed

Lines changed: 78 additions & 21 deletions

File tree

build.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub fn build(b: *std.Build) void {
88
const lib_module = b.addModule("zig-test-framework", .{
99
.root_source_file = b.path("src/lib.zig"),
1010
.target = target,
11+
.link_libc = true,
1112
});
1213

1314
// Create the test runner executable
@@ -17,6 +18,7 @@ pub fn build(b: *std.Build) void {
1718
.root_source_file = b.path("src/main.zig"),
1819
.target = target,
1920
.optimize = optimize,
21+
.link_libc = true,
2022
}),
2123
});
2224

@@ -45,6 +47,7 @@ pub fn build(b: *std.Build) void {
4547
.root_source_file = b.path("tests/test_runner_test.zig"),
4648
.target = target,
4749
.optimize = optimize,
50+
.link_libc = true,
4851
}),
4952
});
5053
const run_test_runner_tests = b.addRunArtifact(test_runner_tests);
@@ -55,6 +58,7 @@ pub fn build(b: *std.Build) void {
5558
.root_source_file = b.path("tests/assertions_test.zig"),
5659
.target = target,
5760
.optimize = optimize,
61+
.link_libc = true,
5862
}),
5963
});
6064
const run_assertions_tests = b.addRunArtifact(assertions_tests);
@@ -65,6 +69,7 @@ pub fn build(b: *std.Build) void {
6569
.root_source_file = b.path("tests/suite_test.zig"),
6670
.target = target,
6771
.optimize = optimize,
72+
.link_libc = true,
6873
}),
6974
});
7075
const run_suite_tests = b.addRunArtifact(suite_tests);
@@ -75,6 +80,7 @@ pub fn build(b: *std.Build) void {
7580
.root_source_file = b.path("tests/matchers_test.zig"),
7681
.target = target,
7782
.optimize = optimize,
83+
.link_libc = true,
7884
}),
7985
});
8086
const run_matchers_tests = b.addRunArtifact(matchers_tests);
@@ -86,6 +92,7 @@ pub fn build(b: *std.Build) void {
8692
.root_source_file = b.path("tests/hooks_test.zig"),
8793
.target = target,
8894
.optimize = optimize,
95+
.link_libc = true,
8996
.imports = &.{
9097
.{ .name = "zig-test-framework", .module = lib_module },
9198
},
@@ -102,6 +109,7 @@ pub fn build(b: *std.Build) void {
102109
.root_source_file = b.path("tests/reporter_test.zig"),
103110
.target = target,
104111
.optimize = optimize,
112+
.link_libc = true,
105113
}),
106114
});
107115
const run_reporter_tests = b.addRunArtifact(reporter_tests);
@@ -112,6 +120,7 @@ pub fn build(b: *std.Build) void {
112120
.root_source_file = b.path("tests/cli_test.zig"),
113121
.target = target,
114122
.optimize = optimize,
123+
.link_libc = true,
115124
}),
116125
});
117126
const run_cli_tests = b.addRunArtifact(cli_tests);
@@ -122,6 +131,7 @@ pub fn build(b: *std.Build) void {
122131
.root_source_file = b.path("tests/filter_test.zig"),
123132
.target = target,
124133
.optimize = optimize,
134+
.link_libc = true,
125135
}),
126136
});
127137
const run_filter_tests = b.addRunArtifact(filter_tests);
@@ -132,6 +142,7 @@ pub fn build(b: *std.Build) void {
132142
.root_source_file = b.path("tests/mock_test.zig"),
133143
.target = target,
134144
.optimize = optimize,
145+
.link_libc = true,
135146
}),
136147
});
137148
const run_mock_tests = b.addRunArtifact(mock_tests);
@@ -143,6 +154,7 @@ pub fn build(b: *std.Build) void {
143154
.root_source_file = b.path("tests/comprehensive_mock_test.zig"),
144155
.target = target,
145156
.optimize = optimize,
157+
.link_libc = true,
146158
.imports = &.{
147159
.{ .name = "zig-test-framework", .module = lib_module },
148160
},
@@ -160,6 +172,7 @@ pub fn build(b: *std.Build) void {
160172
.root_source_file = b.path("tests/snapshot_usage_test.zig"),
161173
.target = target,
162174
.optimize = optimize,
175+
.link_libc = true,
163176
.imports = &.{
164177
.{ .name = "zig-test-framework", .module = lib_module },
165178
},
@@ -177,6 +190,7 @@ pub fn build(b: *std.Build) void {
177190
.root_source_file = b.path("tests/time_test.zig"),
178191
.target = target,
179192
.optimize = optimize,
193+
.link_libc = true,
180194
.imports = &.{
181195
.{ .name = "zig-test-framework", .module = lib_module },
182196
},
@@ -207,6 +221,7 @@ pub fn build(b: *std.Build) void {
207221
.root_source_file = b.path("examples/basic_test.zig"),
208222
.target = target,
209223
.optimize = optimize,
224+
.link_libc = true,
210225
.imports = &.{
211226
.{ .name = "zig-test-framework", .module = lib_module },
212227
},
@@ -219,6 +234,7 @@ pub fn build(b: *std.Build) void {
219234
.root_source_file = b.path("examples/advanced_test.zig"),
220235
.target = target,
221236
.optimize = optimize,
237+
.link_libc = true,
222238
.imports = &.{
223239
.{ .name = "zig-test-framework", .module = lib_module },
224240
},

src/compat.zig

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ const builtin = @import("builtin");
77
// Time utilities (std.time.milliTimestamp removed in 0.16)
88
// ============================================================
99

10+
/// Get clock_gettime result as seconds and nanoseconds.
11+
fn getRealtimeClock() struct { sec: i64, nsec: i64 } {
12+
if (comptime builtin.os.tag == .linux or builtin.os.tag == .macos or
13+
builtin.os.tag == .ios or builtin.os.tag == .tvos or
14+
builtin.os.tag == .watchos or builtin.os.tag == .visionos or
15+
builtin.os.tag == .freebsd or builtin.os.tag == .netbsd or
16+
builtin.os.tag == .openbsd or builtin.os.tag == .dragonfly)
17+
{
18+
var ts: std.c.timespec = .{ .sec = 0, .nsec = 0 };
19+
const rc = std.c.clock_gettime(std.c.CLOCK.REALTIME, &ts);
20+
if (rc == 0) {
21+
return .{ .sec = ts.sec, .nsec = ts.nsec };
22+
}
23+
}
24+
return .{ .sec = 0, .nsec = 0 };
25+
}
26+
1027
/// Get current wall-clock time in nanoseconds since Unix epoch.
1128
/// Replaces std.time.nanoTimestamp() which was removed in Zig 0.16.
1229
pub fn nanoTimestamp() i128 {
@@ -16,26 +33,22 @@ pub fn nanoTimestamp() i128 {
1633
const intervals: i128 = @as(i128, @as(u64, ft.dwHighDateTime) << 32 | @as(u64, ft.dwLowDateTime));
1734
return intervals * 100 - EPOCH_DIFF;
1835
} else {
19-
const ts = std.posix.clock_gettime(.REALTIME) catch return 0;
20-
return @as(i128, ts.sec) * 1_000_000_000 + @as(i128, ts.nsec);
36+
const clock = getRealtimeClock();
37+
return @as(i128, clock.sec) * 1_000_000_000 + @as(i128, clock.nsec);
2138
}
2239
}
2340

2441
/// Get current wall-clock time in milliseconds since Unix epoch.
2542
/// Replaces std.time.milliTimestamp() which was removed in Zig 0.16.
2643
pub fn milliTimestamp() i64 {
2744
if (comptime builtin.os.tag == .windows) {
28-
// On Windows, use the Win32 API
2945
const ft = std.os.windows.GetSystemTimeAsFileTime();
30-
// Convert from Windows FILETIME (100ns intervals since 1601-01-01)
31-
// to Unix timestamp in milliseconds
32-
const EPOCH_DIFF: i64 = 11644473600000; // ms between 1601 and 1970
46+
const EPOCH_DIFF: i64 = 11644473600000;
3347
const intervals: i64 = @bitCast(@as(u64, ft.dwHighDateTime) << 32 | @as(u64, ft.dwLowDateTime));
3448
return @divFloor(intervals, 10000) - EPOCH_DIFF;
3549
} else {
36-
// POSIX: use clock_gettime with REALTIME clock
37-
const ts = std.posix.clock_gettime(.REALTIME) catch return 0;
38-
return @as(i64, ts.sec) * 1000 + @divFloor(@as(i64, ts.nsec), 1_000_000);
50+
const clock = getRealtimeClock();
51+
return @as(i64, clock.sec) * 1000 + @divFloor(@as(i64, clock.nsec), 1_000_000);
3952
}
4053
}
4154

@@ -48,22 +61,50 @@ pub fn milliTimestamp() i64 {
4861
pub fn sleep(ns: u64) void {
4962
const s: isize = @intCast(ns / std.time.ns_per_s);
5063
const remaining_ns: isize = @intCast(ns % std.time.ns_per_s);
51-
var ts = std.posix.timespec{ .sec = s, .nsec = remaining_ns };
64+
var ts: std.c.timespec = .{ .sec = s, .nsec = remaining_ns };
5265
while (true) {
5366
const rc = std.c.nanosleep(&ts, &ts);
54-
switch (std.c.errno(rc)) {
55-
.SUCCESS => break,
56-
.INTR => continue,
57-
else => break,
58-
}
67+
if (rc == 0) break;
68+
// On EINTR, retry with remaining time
69+
continue;
5970
}
6071
}
6172

6273
// ============================================================
63-
// Mutex (std.Thread.Mutex still exists in 0.16 but may move)
74+
// Mutex (std.Thread.Mutex removed in 0.16-dev.2736+)
75+
// Uses simple spinlock via atomics since std.Io.Mutex needs Io.
6476
// ============================================================
6577

66-
pub const Mutex = std.Thread.Mutex;
78+
/// Simple spinlock mutex for use without Io.
79+
/// Replaces std.Thread.Mutex which was removed in Zig 0.16.
80+
pub const Mutex = struct {
81+
state: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
82+
83+
pub fn lock(self: *Mutex) void {
84+
while (self.state.cmpxchgWeak(0, 1, .acquire, .monotonic) != null) {
85+
// Spin
86+
std.atomic.spinLoopHint();
87+
}
88+
}
89+
90+
pub fn unlock(self: *Mutex) void {
91+
self.state.store(0, .release);
92+
}
93+
94+
pub fn tryLock(self: *Mutex) bool {
95+
return self.state.cmpxchgStrong(0, 1, .acquire, .monotonic) == null;
96+
}
97+
};
98+
99+
// ============================================================
100+
// File descriptor close wrapper
101+
// ============================================================
102+
103+
/// Close a file descriptor using libc.
104+
/// Replaces std.posix.close() which was removed in Zig 0.16.
105+
fn closeFd(fd: std.posix.fd_t) void {
106+
_ = std.c.close(fd);
107+
}
67108

68109
// ============================================================
69110
// File I/O helpers (std.fs.cwd() removed, needs std.Io now)
@@ -79,7 +120,7 @@ pub fn readFileAlloc(allocator: std.mem.Allocator, path: []const u8) ![]const u8
79120
if (err == error.FileNotFound) return error.FileNotFound;
80121
return err;
81122
};
82-
defer std.posix.close(fd);
123+
defer closeFd(fd);
83124

84125
// Read file in chunks
85126
var result = std.ArrayList(u8).empty;
@@ -106,7 +147,7 @@ pub fn writeFile(allocator: std.mem.Allocator, path: []const u8, content: []cons
106147
.CREAT = true,
107148
.TRUNC = true,
108149
}, 0o644) catch |err| return err;
109-
defer std.posix.close(fd);
150+
defer closeFd(fd);
110151

111152
var remaining = content;
112153
while (remaining.len > 0) {
@@ -274,7 +315,7 @@ pub fn spawnAndWait(
274315
const dev_null: [*:0]const u8 = "/dev/null";
275316
const null_fd = std.posix.openatZ(std.posix.AT.FDCWD, dev_null, .{ .ACCMODE = .WRONLY }, 0) catch std.process.exit(127);
276317
if (std.c.dup2(null_fd, 1) < 0) std.process.exit(127);
277-
std.posix.close(null_fd);
318+
closeFd(null_fd);
278319
},
279320
else => {},
280321
}
@@ -285,7 +326,7 @@ pub fn spawnAndWait(
285326
const dev_null: [*:0]const u8 = "/dev/null";
286327
const null_fd = std.posix.openatZ(std.posix.AT.FDCWD, dev_null, .{ .ACCMODE = .WRONLY }, 0) catch std.process.exit(127);
287328
if (std.c.dup2(null_fd, 2) < 0) std.process.exit(127);
288-
std.posix.close(null_fd);
329+
closeFd(null_fd);
289330
},
290331
else => {},
291332
}

0 commit comments

Comments
 (0)