repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

Eric Bower  ·  2025-12-12

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    const run_step = b.step("run", "Run the app");
 20    const test_step = b.step("test", "Run unit tests");
 21
 22    const options = b.addOptions();
 23    options.addOption([]const u8, "version", version);
 24    options.addOption([]const u8, "ghostty_version", @import("build.zig.zon").dependencies.ghostty.hash);
 25
 26    const exe_mod = b.createModule(.{
 27        .root_source_file = b.path("src/main.zig"),
 28        .target = target,
 29        .optimize = optimize,
 30    });
 31    exe_mod.addOptions("build_options", options);
 32
 33    // You'll want to use a lazy dependency here so that ghostty is only
 34    // downloaded if you actually need it.
 35    if (b.lazyDependency("ghostty", .{
 36        .target = target,
 37        .optimize = optimize,
 38    })) |dep| {
 39        exe_mod.addImport(
 40            "ghostty-vt",
 41            dep.module("ghostty-vt"),
 42        );
 43    }
 44
 45    // Exe
 46    const exe = b.addExecutable(.{
 47        .name = "zmx",
 48        .root_module = exe_mod,
 49    });
 50    exe.linkLibC();
 51
 52    b.installArtifact(exe);
 53
 54    // Run
 55    const run_cmd = b.addRunArtifact(exe);
 56    run_cmd.step.dependOn(b.getInstallStep());
 57    if (b.args) |args| run_cmd.addArgs(args);
 58    run_step.dependOn(&run_cmd.step);
 59
 60    // Test
 61    const exe_unit_tests = b.addTest(.{
 62        .root_module = exe_mod,
 63    });
 64    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
 65    test_step.dependOn(&run_exe_unit_tests.step);
 66
 67    // This is where the interesting part begins.
 68    // As you can see we are re-defining the same executable but
 69    // we're binding it to a dedicated build step.
 70    const exe_check = b.addExecutable(.{
 71        .name = "zmx",
 72        .root_module = exe_mod,
 73    });
 74    exe_check.linkLibC();
 75    // There is no `b.installArtifact(exe_check);` here.
 76
 77    // Finally we add the "check" step which will be detected
 78    // by ZLS and automatically enable Build-On-Save.
 79    // If you copy this into your `build.zig`, make sure to rename 'foo'
 80    const check = b.step("check", "Check if foo compiles");
 81    check.dependOn(&exe_check.step);
 82
 83    // Release step - macOS can cross-compile to Linux, but Linux cannot cross-compile to macOS (needs SDK)
 84    const native_os = @import("builtin").os.tag;
 85    const release_targets = if (native_os == .macos) linux_targets ++ macos_targets else linux_targets;
 86    const release_step = b.step("release", "Build release binaries (macOS builds all, Linux builds Linux only)");
 87    for (release_targets) |release_target| {
 88        const resolved = b.resolveTargetQuery(release_target);
 89        const release_mod = b.createModule(.{
 90            .root_source_file = b.path("src/main.zig"),
 91            .target = resolved,
 92            .optimize = .ReleaseSafe,
 93        });
 94        release_mod.addOptions("build_options", options);
 95
 96        if (b.lazyDependency("ghostty", .{
 97            .target = resolved,
 98            .optimize = .ReleaseSafe,
 99        })) |dep| {
100            release_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
101        }
102
103        const release_exe = b.addExecutable(.{
104            .name = "zmx",
105            .root_module = release_mod,
106        });
107        release_exe.linkLibC();
108
109        const os_name = @tagName(release_target.os_tag orelse .linux);
110        const arch_name = @tagName(release_target.cpu_arch orelse .x86_64);
111        const tarball_name = b.fmt("zmx-{s}-{s}-{s}.tar.gz", .{ version, os_name, arch_name });
112
113        const tar = b.addSystemCommand(&.{ "tar", "--no-xattrs", "-czf" });
114
115        const tarball = tar.addOutputFileArg(tarball_name);
116        tar.addArg("-C");
117        tar.addDirectoryArg(release_exe.getEmittedBinDirectory());
118        tar.addArg("zmx");
119
120        const shasum = b.addSystemCommand(&.{ "shasum", "-a", "256" });
121        shasum.addFileArg(tarball);
122        const shasum_output = shasum.captureStdOut();
123
124        const install_tar = b.addInstallFile(tarball, b.fmt("dist/{s}", .{tarball_name}));
125        const install_sha = b.addInstallFile(shasum_output, b.fmt("dist/{s}.sha256", .{tarball_name}));
126        release_step.dependOn(&install_tar.step);
127        release_step.dependOn(&install_sha.step);
128    }
129
130    // Upload step - rsync docs and dist to pgs.sh
131    const upload_step = b.step("upload", "Upload docs and dist to pgs.sh:/zmx");
132
133    const rsync_docs = b.addSystemCommand(&.{ "rsync", "-rv", "docs/", "pgs.sh:/zmx" });
134    const rsync_dist = b.addSystemCommand(&.{ "rsync", "-rv", "zig-out/dist/", "pgs.sh:/zmx/a" });
135
136    upload_step.dependOn(&rsync_docs.step);
137    upload_step.dependOn(&rsync_dist.step);
138}