const std = @import("std"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); // libghostty-vt provides the terminal emulator core: escape sequence // parsing, screen/scrollback state, and input encoding. Everything above // it (PTY, rendering, windowing) is ours. const ghostty = b.dependency("ghostty", .{ .target = target, .optimize = optimize, }); // GTK4/libadwaita bindings. This is the same generated binding set // Ghostty uses for its Linux apprt, so it matches the GObject // introspection data of the GTK we link against. const gobject = b.dependency("gobject", .{ .target = target, .optimize = optimize, }); const exe = b.addExecutable(.{ .name = "playpen", .root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .link_libc = true, }), }); exe.root_module.addImport("ghostty-vt", ghostty.module("ghostty-vt")); const gobject_imports = .{ .{ "adw", "adw1" }, .{ "cairo", "cairo1" }, .{ "gdk", "gdk4" }, .{ "gio", "gio2" }, .{ "glib", "glib2" }, .{ "glibunix", "glibunix2" }, .{ "gobject", "gobject2" }, .{ "gtk", "gtk4" }, .{ "pango", "pango1" }, .{ "pangocairo", "pangocairo1" }, }; inline for (gobject_imports) |import| { const name, const module = import; exe.root_module.addImport(name, gobject.module(module)); } exe.root_module.linkSystemLibrary("gtk4", .{}); exe.root_module.linkSystemLibrary("libadwaita-1", .{}); // WebKitGTK backs the web panes. There are no generated bindings for it in // the zig-gobject set we use, so `src/webkit.zig` declares the handful of // C entry points we need by hand and this links them. exe.root_module.linkSystemLibrary("webkitgtk-6.0", .{}); b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| run_cmd.addArgs(args); b.step("run", "Run the app").dependOn(&run_cmd.step); // Tests are rooted at Layouts.zig rather than main.zig: the parts of this // app worth testing in isolation are the ones that are pure data — layout // parsing, parameter substitution, the `$(...)` expansion and the script // runner underneath it. Everything else is a widget tree, which wants a // display and a person looking at it. // // Rooting there also keeps the test binary off GTK entirely; Layouts.zig // and script.zig reach for GLib and nothing above it. const tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/Layouts.zig"), .target = target, .optimize = optimize, .link_libc = true, }), }); tests.root_module.addImport("glib", gobject.module("glib2")); const test_step = b.step("test", "Run the tests"); test_step.dependOn(&b.addRunArtifact(tests).step); // A second root for the same reason, one step further out: `emoji.zig` is a // table and a search over it, and it imports nothing at all. It cannot hang // off the root above because a test binary has exactly one root, and // Layouts.zig has no reason to reach for the emoji table. const emoji_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/emoji.zig"), .target = target, .optimize = optimize, }), }); test_step.dependOn(&b.addRunArtifact(emoji_tests).step); }