dependencies

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: