pkg/wuffs: build without libc (#13942)

This modifies our wuffs dependency so that it no longer requires libc.

This unblocks using wuffs from libghostty-vt on freestanding targets,
which we'll eventually want for some Kitty graphics stuff.
This commit is contained in:
Mitchell Hashimoto
2026-08-21 05:51:42 -07:00
committed by GitHub
5 changed files with 79 additions and 8 deletions

View File

@@ -41,7 +41,6 @@ pub fn build(b: *std.Build) !void {
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
const unit_tests = b.addTest(.{
@@ -49,6 +48,9 @@ pub fn build(b: *std.Build) !void {
.root_module = module,
});
// Windows always has a libc available.
const windows = target.result.os.tag == .windows;
translate: {
const translate_c = b.lazyImport(@This(), "translate_c") orelse break :translate;
const translate_c_dep = b.lazyDependency("translate_c", .{}) orelse break :translate;
@@ -56,13 +58,16 @@ pub fn build(b: *std.Build) !void {
.c_source_file = b.addWriteFiles().add("wuffs_c.h", &wuffs_c_source),
.target = target,
.optimize = optimize,
.libc_file = if (target.result.os.tag.isDarwin()) libc_file: {
switch (try @import("apple_sdk").pathsForTarget(b, target.result)) {
inline else => |paths| break :libc_file paths.libc,
}
} else null,
.link_libc = windows,
});
// Wuffs only needs stdlib.h and string.h from libc, and only for
// a handful of declarations. We provide minimal versions of these
// headers so that wuffs can be translated and compiled without
// libc, notably for freestanding targets (wasm32) but this also
// avoids requiring an Apple SDK for translate-c on macOS.
if (!windows) wuffs_c.addIncludePath(b.path("include"));
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.append(b.allocator, "-DWUFFS_IMPLEMENTATION");

View File

@@ -22,12 +22,11 @@
.hash = "N-V-__8AADYiAAB_80AWnH1AxXC0tql9thT-R-DYO1gBqTLc",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
.paths = .{
"build.zig",
"build.zig.zon",
"include",
"src",
},
}

View File

@@ -0,0 +1,13 @@
// Minimal stdlib.h so that wuffs can be translated and compiled without
// requiring libc headers. See string.h in this directory for details.
#ifndef GHOSTTY_WUFFS_STDLIB_H
#define GHOSTTY_WUFFS_STDLIB_H
#include <stddef.h>
void *malloc(size_t size);
void *calloc(size_t count, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#endif

View File

@@ -0,0 +1,22 @@
// Minimal string.h so that wuffs can be translated and compiled without
// requiring libc headers (e.g. for wasm32-freestanding targets, or to
// avoid needing an Apple SDK for translate-c on macOS).
//
// Wuffs only calls the mem* family, whose symbols are provided by Zig's
// compiler-rt on targets without libc and by libc everywhere else. The
// declarations here match the standard C ABI, so this header is safe to
// use on every target, including ones that do link libc.
#ifndef GHOSTTY_WUFFS_STRING_H
#define GHOSTTY_WUFFS_STRING_H
#include <stddef.h>
void *memcpy(void *dst, const void *src, size_t n);
void *memmove(void *dst, const void *src, size_t n);
void *memset(void *b, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
#endif

View File

@@ -16,6 +16,38 @@ pub const ImageData = struct {
data: []u8,
};
// Wuffs' generated `wuffs_foo__bar__alloc()` convenience functions are the
// only code that references libc's calloc/free. We never call them
// and linker garbage collection strips them, but that isn't guaranteed.
// When libc isn't linked there would be nothing to provide calloc/free if
// they survive, so export stubs to satisfy the link. Weak so that any real
// definition wins. Hidden keeps them out of the export table where the
// format honors it (e.g. wasm).
comptime {
if (!builtin.link_libc) {
@export(&callocStub, .{
.name = "calloc",
.linkage = .weak,
.visibility = .hidden,
});
@export(&freeStub, .{
.name = "free",
.linkage = .weak,
.visibility = .hidden,
});
}
}
fn callocStub(count: usize, size: usize) callconv(.c) ?*anyopaque {
_ = count;
_ = size;
return null;
}
fn freeStub(ptr: ?*anyopaque) callconv(.c) void {
_ = ptr;
}
test {
refAllDeclsRecursive(@This());
}