main
build.zig
Eric Bower
·
2026-10-23
1const std = @import("std");
2const Translator = @import("translate_c").Translator;
3const build_zig_zon = @import("build.zig.zon");
4
5const linux_targets: []const std.Target.Query = &.{
6 .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
7 .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl },
8};
9
10const macos_targets: []const std.Target.Query = &.{
11 .{ .cpu_arch = .x86_64, .os_tag = .macos },
12 .{ .cpu_arch = .aarch64, .os_tag = .macos },
13};
14
15pub fn build(b: *std.Build) void {
16 const target = b.standardTargetOptions(.{});
17 // const is_macos = target.result.os.tag == .macos;
18 const optimize = b.standardOptimizeOption(.{});
19 const version = b.option([]const u8, "version", "Version string for release") orelse
20 @as([]const u8, build_zig_zon.version);
21
22 const enable_daemon = b.option(bool, "daemon", "Include daemon support and Ghostty VT in zmx pkg") orelse true;
23
24 const options = b.addOptions();
25 options.addOption([]const u8, "version", version);
26 const ghostty_ver = build_zig_zon.dependencies.ghostty.hash;
27 options.addOption([]const u8, "ghostty_version", ghostty_ver);
28 options.addOption(bool, "has_daemon", enable_daemon);
29
30 const translate_c = b.dependency("translate_c", .{});
31 const translator: Translator = .init(translate_c, .{
32 .c_source_file = b.path("src/c.h"),
33 .target = target,
34 .optimize = optimize,
35 });
36
37 const exe_mod = b.createModule(.{
38 .root_source_file = b.path("src/main.zig"),
39 .target = target,
40 .optimize = optimize,
41 .link_libc = true,
42 .imports = &.{
43 .{
44 .name = "cross",
45 .module = translator.mod,
46 },
47 },
48 });
49 exe_mod.addOptions("build_options", options);
50
51 const dep = if (enable_daemon) b.lazyDependency("ghostty", .{
52 .target = target,
53 .optimize = optimize,
54 .@"emit-lib-vt" = true,
55 // Not redundant: in lib-vt mode emit-xcframework defaults to "xcodebuild
56 // on PATH" (true even via the CLT stub), which pulls in the iOS SDK at
57 // configure time and breaks builds without full Xcode.
58 .@"emit-xcframework" = false,
59 .@"emit-macos-app" = false,
60 }) else null;
61 if (dep) |d| {
62 const vt_mod = d.module("ghostty-vt");
63 if (target.result.os.tag == .openbsd) vt_mod.sanitize_c = .off;
64 exe_mod.addImport("ghostty-vt", vt_mod);
65 }
66
67 // zmx module: programmatic interface for embedding or external tools
68 const zmx_pkg_mod = b.addModule("zmx", .{
69 .root_source_file = b.path("src/lib.zig"),
70 .target = target,
71 .optimize = optimize,
72 .link_libc = true,
73 .imports = &.{
74 .{
75 .name = "cross",
76 .module = translator.mod,
77 },
78 },
79 });
80
81 zmx_pkg_mod.addOptions("build_options", options);
82 if (dep) |d| {
83 const vt_mod = d.module("ghostty-vt");
84 if (target.result.os.tag == .openbsd) vt_mod.sanitize_c = .off;
85 zmx_pkg_mod.addImport("ghostty-vt", vt_mod);
86 }
87
88 // Examples: build the usage examples in examples/ against the zmx module
89 {
90 const examples_step = b.step("examples", "Build usage examples");
91 const Example = struct { name: []const u8, file: []const u8 };
92 const example_sources: []const Example = &.{
93 .{ .name = "example-run-and-wait", .file = "examples/run_and_wait.zig" },
94 .{ .name = "example-list-sessions", .file = "examples/list_sessions.zig" },
95 };
96 for (example_sources) |ex| {
97 const ex_mod = b.createModule(.{
98 .root_source_file = b.path(ex.file),
99 .target = target,
100 .optimize = optimize,
101 .link_libc = true,
102 .imports = &.{
103 .{
104 .name = "cross",
105 .module = translator.mod,
106 },
107 },
108 });
109 ex_mod.addImport("zmx", zmx_pkg_mod);
110 const ex_exe = b.addExecutable(.{ .name = ex.name, .root_module = ex_mod });
111 const install = b.addInstallArtifact(ex_exe, .{});
112 examples_step.dependOn(&install.step);
113 }
114 }
115
116 // Run
117 {
118 const run_step = b.step("run", "Run the app");
119 const exe = b.addExecutable(.{
120 .name = "zmx",
121 // .use_llvm = true,
122 // .use_lld = !is_macos,
123 .root_module = exe_mod,
124 });
125
126 if (enable_daemon) {
127 b.installArtifact(exe);
128 const run_cmd = b.addRunArtifact(exe);
129 run_cmd.step.dependOn(b.getInstallStep());
130 run_cmd.addPassthruArgs();
131 run_step.dependOn(&run_cmd.step);
132 }
133 }
134
135 // Test
136 {
137 const test_step = b.step("test", "Run unit tests");
138 const test_module = b.addModule("test", .{
139 .root_source_file = b.path("src/test.zig"),
140 .target = target,
141 .optimize = optimize,
142 .link_libc = true,
143 .imports = &.{
144 .{
145 .name = "cross",
146 .module = translator.mod,
147 },
148 },
149 });
150 test_module.addOptions("build_options", options);
151 const test_dep = b.dependency("ghostty", .{
152 .target = target,
153 .optimize = optimize,
154 .@"emit-lib-vt" = true,
155 .@"emit-xcframework" = false,
156 .@"emit-macos-app" = false,
157 });
158 test_module.addImport(
159 "ghostty-vt",
160 test_dep.module("ghostty-vt"),
161 );
162 const exe_unit_tests = b.addTest(.{
163 .root_module = test_module,
164 // .use_llvm = true,
165 // .use_lld = !is_macos,
166 });
167
168 const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
169 test_step.dependOn(&run_exe_unit_tests.step);
170 }
171
172 // Check for LSP integration
173 {
174 const check = b.step("check", "Check if zmx compiles");
175 if (enable_daemon) {
176 const exe_check = b.addExecutable(.{
177 .name = "zmx",
178 .root_module = exe_mod,
179 });
180 check.dependOn(&exe_check.step);
181 } else {
182 const lib_check = b.addLibrary(.{
183 .name = "zmx",
184 .root_module = zmx_pkg_mod,
185 .linkage = .static,
186 });
187 check.dependOn(&lib_check.step);
188 }
189 }
190
191 // Release step - cross-compile to all targets from any host
192 {
193 const release_step = b.step(
194 "release",
195 "Build release binaries for all platforms",
196 );
197 const release_targets = linux_targets ++ macos_targets;
198 for (release_targets) |release_target| {
199 const resolved = b.resolveTargetQuery(release_target);
200 const release_mod = b.createModule(.{
201 .root_source_file = b.path("src/main.zig"),
202 .target = resolved,
203 .optimize = .ReleaseSafe,
204 .link_libc = true,
205 .imports = &.{
206 .{
207 .name = "cross",
208 .module = translator.mod,
209 },
210 },
211 });
212 release_mod.addOptions("build_options", options);
213
214 if (b.lazyDependency("ghostty", .{
215 .target = resolved,
216 .optimize = .ReleaseSafe,
217 .@"emit-lib-vt" = true,
218 .@"emit-xcframework" = false,
219 .@"emit-macos-app" = false,
220 })) |release_dep| {
221 release_mod.addImport("ghostty-vt", release_dep.module("ghostty-vt"));
222 }
223
224 // const is_local_macos = resolved.result.os.tag == .macos;
225 const release_exe = b.addExecutable(.{
226 .name = "zmx",
227 // .use_llvm = true,
228 // .use_lld = !is_local_macos,
229 .root_module = release_mod,
230 });
231
232 const os_name = @tagName(release_target.os_tag orelse .linux);
233 const arch_name = @tagName(release_target.cpu_arch orelse .x86_64);
234 const tarball_name = b.fmt("zmx-{s}-{s}-{s}.tar.gz", .{ version, os_name, arch_name });
235
236 const tar = b.addSystemCommand(&.{ "tar", "-czf" });
237
238 const tarball = tar.addOutputFileArg(tarball_name);
239 tar.addArg("-C");
240 tar.addDirectoryArg(release_exe.getEmittedBinDirectory());
241 tar.addArg("zmx");
242
243 const shasum = b.addSystemCommand(&.{"sha256sum"});
244 shasum.addFileArg(tarball);
245 const shasum_output = shasum.captureStdOut(.{});
246
247 const install_tar = b.addInstallFile(tarball, b.fmt("dist/{s}", .{tarball_name}));
248 const install_sha = b.addInstallFile(
249 shasum_output,
250 b.fmt("dist/{s}.sha256", .{tarball_name}),
251 );
252 release_step.dependOn(&install_tar.step);
253 release_step.dependOn(&install_sha.step);
254 }
255 }
256}