Eric Bower
·
2026-04-26
build.zig
1const std = @import("std");
2
3const linux_targets: []const std.Target.Query = &.{
4 .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
5 .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl },
6};
7
8const macos_targets: []const std.Target.Query = &.{
9 .{ .cpu_arch = .x86_64, .os_tag = .macos },
10 .{ .cpu_arch = .aarch64, .os_tag = .macos },
11};
12
13pub fn build(b: *std.Build) void {
14 const target = b.standardTargetOptions(.{});
15 const optimize = b.standardOptimizeOption(.{});
16 const version = b.option([]const u8, "version", "Version string for release") orelse
17 @as([]const u8, @import("build.zig.zon").version);
18
19 var code: u8 = 0;
20 const git_sha = std.mem.trim(u8, b.runAllowFail(
21 &.{ "git", "rev-parse", "--short", "HEAD" },
22 &code,
23 .Inherit,
24 ) catch "unknown", "\n");
25
26 const options = b.addOptions();
27 options.addOption([]const u8, "version", version);
28 options.addOption([]const u8, "git_sha", git_sha);
29 const ghostty_ver = @import("build.zig.zon").dependencies.ghostty.hash;
30 options.addOption([]const u8, "ghostty_version", ghostty_ver);
31
32 const exe_mod = b.createModule(.{
33 .root_source_file = b.path("src/main.zig"),
34 .target = target,
35 .optimize = optimize,
36 });
37 exe_mod.addOptions("build_options", options);
38
39 const dep = b.dependency("ghostty", .{
40 .target = target,
41 .optimize = optimize,
42 });
43 exe_mod.addImport(
44 "ghostty-vt",
45 dep.module("ghostty-vt"),
46 );
47
48 // Run
49 {
50 const run_step = b.step("run", "Run the app");
51 const exe = b.addExecutable(.{
52 .name = "zmx",
53 .root_module = exe_mod,
54 });
55 exe.linkLibC();
56 b.installArtifact(exe);
57 const run_cmd = b.addRunArtifact(exe);
58 run_cmd.step.dependOn(b.getInstallStep());
59 if (b.args) |args| run_cmd.addArgs(args);
60 run_step.dependOn(&run_cmd.step);
61 }
62
63 // Test
64 {
65 const test_step = b.step("test", "Run unit tests");
66 const test_module = b.addModule("test", .{
67 .root_source_file = b.path("src/test.zig"),
68 .target = target,
69 .optimize = optimize,
70 });
71 const test_dep = b.dependency("ghostty", .{
72 .target = target,
73 .optimize = optimize,
74 });
75 test_module.addImport(
76 "ghostty-vt",
77 test_dep.module("ghostty-vt"),
78 );
79 const exe_unit_tests = b.addTest(.{
80 .root_module = test_module,
81 });
82 const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
83 test_step.dependOn(&run_exe_unit_tests.step);
84 }
85
86 // Integration tests (bats)
87 {
88 const integration_step = b.step("test-integration", "Run bats integration tests");
89 const bats = b.addSystemCommand(&.{ "bats", "test/session.bats" });
90 bats.step.dependOn(b.getInstallStep());
91 integration_step.dependOn(&bats.step);
92 }
93
94 // Check for LSP integration
95 {
96 const check = b.step("check", "Check if zmx compiles");
97 const exe_check = b.addExecutable(.{
98 .name = "zmx",
99 .root_module = exe_mod,
100 });
101 exe_check.linkLibC();
102
103 // Finally we add the "check" step which will be detected
104 // by ZLS and automatically enable Build-On-Save.
105 // If you copy this into your `build.zig`, make sure to rename 'foo'
106 check.dependOn(&exe_check.step);
107 }
108
109 // Release step - cross-compile to all targets from any host
110 {
111 const release_step = b.step(
112 "release",
113 "Build release binaries for all platforms",
114 );
115 const release_targets = linux_targets ++ macos_targets;
116 for (release_targets) |release_target| {
117 const resolved = b.resolveTargetQuery(release_target);
118 const release_mod = b.createModule(.{
119 .root_source_file = b.path("src/main.zig"),
120 .target = resolved,
121 .optimize = .ReleaseSafe,
122 });
123 release_mod.addOptions("build_options", options);
124
125 if (b.lazyDependency("ghostty", .{
126 .target = resolved,
127 .optimize = .ReleaseSafe,
128 })) |release_dep| {
129 release_mod.addImport("ghostty-vt", release_dep.module("ghostty-vt"));
130 }
131
132 const release_exe = b.addExecutable(.{
133 .name = "zmx",
134 .root_module = release_mod,
135 });
136 release_exe.linkLibC();
137
138 const os_name = @tagName(release_target.os_tag orelse .linux);
139 const arch_name = @tagName(release_target.cpu_arch orelse .x86_64);
140 const tarball_name = b.fmt("zmx-{s}-{s}-{s}.tar.gz", .{ version, os_name, arch_name });
141
142 const tar = b.addSystemCommand(&.{ "tar", "--no-xattrs", "-czf" });
143
144 const tarball = tar.addOutputFileArg(tarball_name);
145 tar.addArg("-C");
146 tar.addDirectoryArg(release_exe.getEmittedBinDirectory());
147 tar.addArg("zmx");
148
149 const shasum = b.addSystemCommand(&.{ "shasum", "-a", "256" });
150 shasum.addFileArg(tarball);
151 const shasum_output = shasum.captureStdOut();
152
153 const install_tar = b.addInstallFile(tarball, b.fmt("dist/{s}", .{tarball_name}));
154 const install_sha = b.addInstallFile(
155 shasum_output,
156 b.fmt("dist/{s}.sha256", .{tarball_name}),
157 );
158 release_step.dependOn(&install_tar.step);
159 release_step.dependOn(&install_sha.step);
160 }
161 }
162
163 // Upload artifacts to pgs
164 {
165 const upload_step = b.step("upload", "Upload docs and dist to pgs.sh:/zmx");
166 const gen_doc = b.addSystemCommand(&.{ "sh", "-c", "cat README.md | pdocs -tmpl index.tmpl -toc | ssh pgs.sh /zmx/index.html" });
167 const rsync_docs = b.addSystemCommand(&.{ "rsync", "-v", "./logo.png", "pgs.sh:/zmx/" });
168 const rsync_dist = b.addSystemCommand(&.{ "rsync", "-rv", "zig-out/dist/", "pgs.sh:/zmx/a" });
169
170 upload_step.dependOn(&gen_doc.step);
171 upload_step.dependOn(&rsync_docs.step);
172 upload_step.dependOn(&rsync_dist.step);
173 }
174}