dependencies
managing external code - when to vendor, when to use system libs, how to keep things reproducible.
lazy dependencies
dependencies marked .lazy = true in build.zig.zon are only fetched when actually used:
// build.zig.zon
.dependencies = .{
.harfbuzz = .{ .path = "./pkg/harfbuzz", .lazy = true },
.macos = .{ .path = "./pkg/macos", .lazy = true },
},
// build.zig
if (b.lazyDependency("harfbuzz", .{ .target = target })) |dep| {
step.linkLibrary(dep.artifact("harfbuzz"));
}
why: platform-specific deps (like macos) don't slow down linux builds. optional features don't fetch deps when disabled.
vendored vs system libraries
ghostty supports both - vendored for reproducibility, system for distro integration:
if (b.systemIntegrationOption("freetype", .{})) {
step.linkSystemLibrary2("freetype2", .{});
} else {
step.linkLibrary(freetype_dep.artifact("freetype"));
}
with smart defaults:
_ = b.systemIntegrationOption("freetype", .{
.default = if (target.result.os.tag.isDarwin()) false else null,
});
macos defaults to vendored (for universal binaries). linux defaults to "ask pkg-config".
users override with -Dsystem-freetype=true or -Dsystem-freetype=false.
c library wrappers
each c dependency gets its own package with a build.zig:
pkg/
├── freetype/
│ ├── build.zig # builds the C library
│ ├── build.zig.zon # declares the package
│ └── main.zig # zig bindings
├── harfbuzz/
└── libpng/
the wrapper handles:
- platform-specific source file selection
- compiler flags for c code
- exposing a zig module with bindings
pinning for reproducibility
bun pins every dependency to exact git commits:
register_repository(
NAME boringssl
REPOSITORY oven-sh/boringssl
COMMIT f1ffd9e83d4f5c28a9c70d73f9a4e6fcf310062f
)
and forks dependencies to their own org (oven-sh/boringssl). no surprise breakage from upstream.
in zig terms, this means using .hash in build.zig.zon and avoiding .url pointing to master branches.
detecting what's installed
before defaulting to system libs, check what's available:
pub fn gtkTargets(b: *std.Build) struct { x11: bool, wayland: bool } {
var code: u8 = undefined;
const output = b.runAllowFail(
&.{ "pkg-config", "--variable=targets", "gtk4" },
&code,
.Ignore,
) catch return .{ .x11 = false, .wayland = false };
return .{
.x11 = std.mem.indexOf(u8, output, "x11") != null,
.wayland = std.mem.indexOf(u8, output, "wayland") != null,
};
}
fails gracefully when pkg-config isn't available.
tangled packages
tangled.sh hosts zig packages. fetch without .tar.gz extension:
zig fetch --save https://tangled.sh/zzstoatzz.io/zat/archive/main
zig fetch checks Content-Type headers to determine archive format - tangled handles this server-side.
sources: