Skip to content

Commit 85e7846

Browse files
chore: wip
1 parent 7ef20b1 commit 85e7846

2 files changed

Lines changed: 82 additions & 17 deletions

File tree

packages/zig-dtsx/build.zig

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,6 @@ pub fn build(b: *std.Build) void {
3939
if (optimize != .Debug) {
4040
exe.root_module.strip = true;
4141
}
42-
// Zig 0.17 removed @cImport as a language builtin. On non-Windows, expose
43-
// POSIX stdio/dirent/fcntl/unistd via a translate-c module imported as "c".
44-
// Windows builds use manually-declared externs in main.zig.
45-
if (target.result.os.tag != .windows) {
46-
const translate_c = b.addTranslateC(.{
47-
.root_source_file = b.path("src/c_imports.h"),
48-
.target = target,
49-
.optimize = optimize,
50-
});
51-
exe.root_module.addImport("c", translate_c.createModule());
52-
}
5342
b.installArtifact(exe);
5443

5544
// CLI-only step (for cross-compilation without shared library)

packages/zig-dtsx/src/main.zig

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ const Scanner = @import("scanner.zig").Scanner;
77
const emitter = @import("emitter.zig");
88

99
// Platform-aware C stdio bindings.
10-
// On Windows, translate-c can't lower stdin/stdout (they're runtime function
11-
// calls Zig can't fold at comptime), so we declare the extern functions
12-
// manually. On POSIX targets we use a translate-c module wired up in
13-
// build.zig and imported as "c". (Zig 0.17 removed @cImport as a language
14-
// builtin, so this can no longer be done inline.)
10+
//
11+
// Zig 0.17 removed `@cImport` as a language builtin, so we declare every C
12+
// symbol we need manually here instead of pulling them in from system
13+
// headers. This also keeps cross-compilation working on CI runners where
14+
// `addTranslateC` blows up with `CacheCheckFailed` for cross targets.
15+
//
16+
// On Windows we use UCRT's `_findfirst`/`__acrt_iob_func` family. On POSIX
17+
// (Linux + the BSD-derived Apple platforms) we use stdio + dirent + open(2)
18+
// directly. Stdio FILE* globals have different external symbol names
19+
// across libcs (`stdin`/`stdout`/`stderr` on glibc/musl, `__stdinp` etc.
20+
// on Apple), so we expose them as functions that resolve via `@extern`.
1521
const c = if (builtin.os.tag == .windows) struct {
1622
pub const FILE = opaque {};
1723
pub extern "c" fn __acrt_iob_func(index: c_int) *FILE;
@@ -37,7 +43,77 @@ const c = if (builtin.os.tag == .windows) struct {
3743
pub extern "c" fn _findnext(handle: isize, fileinfo: *_finddata_t) c_int;
3844
pub extern "c" fn _findclose(handle: isize) c_int;
3945
pub extern "c" fn _mkdir(path: [*:0]const u8) c_int;
40-
} else @import("c");
46+
} else struct {
47+
pub const FILE = opaque {};
48+
pub const DIR = opaque {};
49+
50+
/// True when targeting an Apple libc (macOS / iOS / tvOS / watchOS).
51+
/// Used to pick the correct external symbol names + struct layouts.
52+
const apple_libc = builtin.os.tag.isDarwin();
53+
54+
/// `struct dirent` layout differs between glibc/musl and Apple libc.
55+
/// We only ever read `d_name`, but its offset matters, so the struct
56+
/// has to be accurate up to that field.
57+
pub const dirent = if (apple_libc) extern struct {
58+
d_ino: u64,
59+
d_seekoff: u64,
60+
d_reclen: u16,
61+
d_namlen: u16,
62+
d_type: u8,
63+
d_name: [1024]u8,
64+
} else extern struct {
65+
d_ino: c_ulong,
66+
d_off: c_long,
67+
d_reclen: c_ushort,
68+
d_type: u8,
69+
d_name: [256]u8,
70+
};
71+
72+
pub extern "c" fn fopen(path: [*:0]const u8, mode: [*:0]const u8) ?*FILE;
73+
pub extern "c" fn fclose(stream: *FILE) c_int;
74+
pub extern "c" fn fread(ptr: [*]u8, size: usize, nmemb: usize, stream: *FILE) usize;
75+
pub extern "c" fn fwrite(ptr: [*]const u8, size: usize, nmemb: usize, stream: *FILE) usize;
76+
pub extern "c" fn fseek(stream: *FILE, offset: c_long, whence: c_int) c_int;
77+
pub extern "c" fn ftell(stream: *FILE) c_long;
78+
pub const SEEK_SET: c_int = 0;
79+
pub const SEEK_END: c_int = 2;
80+
81+
pub extern "c" fn opendir(name: [*:0]const u8) ?*DIR;
82+
pub extern "c" fn readdir(dirp: *DIR) ?*dirent;
83+
pub extern "c" fn closedir(dirp: *DIR) c_int;
84+
85+
pub extern "c" fn open(path: [*:0]const u8, flags: c_int, ...) c_int;
86+
pub extern "c" fn openat(dirfd: c_int, path: [*:0]const u8, flags: c_int, ...) c_int;
87+
pub extern "c" fn close(fd: c_int) c_int;
88+
pub extern "c" fn read(fd: c_int, buf: [*]u8, count: usize) isize;
89+
pub extern "c" fn write(fd: c_int, buf: [*]const u8, count: usize) isize;
90+
pub extern "c" fn lseek(fd: c_int, offset: c_long, whence: c_int) c_long;
91+
pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
92+
93+
// fcntl.h flag values (these *do* differ across platforms — Apple uses
94+
// hex bits, Linux uses octal — so we pick per OS rather than guessing).
95+
pub const O_RDONLY: c_int = 0;
96+
pub const O_WRONLY: c_int = 1;
97+
pub const O_CREAT: c_int = if (apple_libc) 0x0200 else 0o100;
98+
pub const O_TRUNC: c_int = if (apple_libc) 0x0400 else 0o1000;
99+
100+
// Stdio FILE* globals. The on-disk symbol names differ between Apple
101+
// libc (`__stdinp` etc.) and glibc/musl (`stdin` etc.), so resolve them
102+
// via @extern. Exposed as functions so getStdioPtr's "function-like"
103+
// branch picks them up correctly.
104+
pub fn stdin() *FILE {
105+
const ptr = @extern(**FILE, .{ .name = if (apple_libc) "__stdinp" else "stdin" });
106+
return ptr.*;
107+
}
108+
pub fn stdout() *FILE {
109+
const ptr = @extern(**FILE, .{ .name = if (apple_libc) "__stdoutp" else "stdout" });
110+
return ptr.*;
111+
}
112+
pub fn stderr() *FILE {
113+
const ptr = @extern(**FILE, .{ .name = if (apple_libc) "__stderrp" else "stderr" });
114+
return ptr.*;
115+
}
116+
};
41117

42118
fn getStdout() *c.FILE {
43119
if (builtin.os.tag == .windows) return c.__acrt_iob_func(1);

0 commit comments

Comments
 (0)