a73x

bb91afde

build: include muxg in release and install targets

a73x   2026-09-06 05:15

Commit message
build: include muxg in release and install targets

Makefile
Old New
@@ -45,9 +45,7 @@ deps:
45 build: mac-sdk 45 build: mac-sdk
46 $(ZIG) build 46 $(ZIG) build
47 47
48 # Only the ONE user-facing binary: everything else in zig-out/bin 48 # Install the two product binaries; the other build artifacts are test fixtures.
49 # (ptyclient, wsclient, rawmode, ...) is a test fixture that would shadow
50 # nothing useful on PATH.
51 BINDIR ?= $(HOME)/.local/bin 49 BINDIR ?= $(HOME)/.local/bin
52 # ReleaseSafe, like `release` — not the Debug tree `build` leaves in 50 # ReleaseSafe, like `release` — not the Debug tree `build` leaves in
53 # zig-out. ghostty gates its page-integrity check on ITS optimize mode, and 51 # zig-out. ghostty gates its page-integrity check on ITS optimize mode, and
@@ -70,19 +68,19 @@ INSTDIR ?= dist/install
70 # 92ms -> 114ms (musl memcpy on 15MB of paint bytes, paid once per 68 # 92ms -> 114ms (musl memcpy on 15MB of paint bytes, paid once per
71 # reattach); the solo and client legs moved inside their noise. 69 # reattach); the solo and client legs moved inside their noise.
72 # 70 #
73 # The target the installed and released binaries are built for. Static 71 # The installed mux target is static musl on Linux, for the reason above;
74 # musl on Linux, for the reason above; another OS names its triple here. 72 # muxg uses the native target so it can link the host's GUI libraries.
75 # The `?=` at the top of this file is what sets it, so a Mac gets its own. 73 # The `?=` at the top of this file is what sets it, so a Mac gets its own.
76 install: mac-sdk 74 install: mac-sdk
77 $(ZIG) build -Dtarget=$(MUX_TARGET) -Doptimize=ReleaseSafe -p $(INSTDIR) 75 $(ZIG) build -Dtarget=$(MUX_TARGET) -Doptimize=ReleaseSafe -p $(INSTDIR)
76 $(ZIG) build native -Doptimize=ReleaseSafe -p $(INSTDIR)
78 install -d $(BINDIR) 77 install -d $(BINDIR)
79 install -m755 $(INSTDIR)/bin/mux $(BINDIR)/ 78 install -m755 $(INSTDIR)/bin/mux $(INSTDIR)/bin/muxg $(BINDIR)/
80 rm -f $(BINDIR)/muxd $(BINDIR)/muxa $(BINDIR)/muxweb 79 rm -f $(BINDIR)/muxd $(BINDIR)/muxa $(BINDIR)/muxweb
81 80
82 # The artifact a release publishes, built the way every release since 81 # mux keeps its static musl build on Linux. muxg needs the host's dynamic GUI
83 # v0.0.1-4 has been: static musl so one binary runs on any x86_64 Linux, 82 # libraries, so build it separately with the native target in the same prefix.
84 # ReleaseSafe because the safety checks are worth more than the last few 83 # Both use ReleaseSafe and are stripped before packaging.
85 # percent and they survive stripping — only DWARF goes, 17MB to ~3MB each.
86 # 84 #
87 # TARRED, and that is the point of having a target at all. Publishing the 85 # TARRED, and that is the point of having a target at all. Publishing the
88 # binary bare loses the exec bit: it downloads 0644, and the first 86 # binary bare loses the exec bit: it downloads 0644, and the first
@@ -120,17 +118,20 @@ SHA256 ?= sha256sum
120 RELTAR = $(RELDIR)/mux-v$(VERSION)-$(RELEASE_TARGET).tar.gz 118 RELTAR = $(RELDIR)/mux-v$(VERSION)-$(RELEASE_TARGET).tar.gz
121 # The version guard below asks the STRIPPED artifact what it is, so a 119 # The version guard below asks the STRIPPED artifact what it is, so a
122 # stale stage directory cannot ship under a bumped number. 120 # stale stage directory cannot ship under a bumped number.
123 release: 121 release: mac-sdk
124 @test -n "$(VERSION)" || { echo "release: no version found in build.zig"; exit 1; } 122 @test -n "$(VERSION)" || { echo "release: no version found in build.zig"; exit 1; }
125 $(ZIG) build -Dtarget=$(RELEASE_BUILD_TARGET) -Doptimize=ReleaseSafe -p $(RELDIR)/stage 123 $(ZIG) build -Dtarget=$(RELEASE_BUILD_TARGET) -Doptimize=ReleaseSafe -p $(RELDIR)/stage
124 $(ZIG) build native -Doptimize=ReleaseSafe -p $(RELDIR)/stage
126 rm -rf $(RELBIN) $(RELTAR) 125 rm -rf $(RELBIN) $(RELTAR)
127 install -d $(RELBIN) 126 install -d $(RELBIN)
128 install -m755 $(RELDIR)/stage/bin/mux $(RELBIN)/ 127 install -m755 $(RELDIR)/stage/bin/mux $(RELDIR)/stage/bin/muxg $(RELBIN)/
129 strip $(RELBIN)/mux 128 strip $(RELBIN)/mux $(RELBIN)/muxg
130 @got=$$($(RELBIN)/mux --version | awk '{print $$2}'); \ 129 @for bin in mux muxg; do \
130 got=$$($(RELBIN)/$$bin --version | awk '{print $$2}'); \
131 [ "$$got" = "$(VERSION)" ] || { \ 131 [ "$$got" = "$(VERSION)" ] || { \
132 echo "release: mux reports $$got, expected $(VERSION)"; exit 1; } 132 echo "release: $$bin reports $$got, expected $(VERSION)"; exit 1; }; \
133 tar czf $(RELTAR) -C $(RELBIN) mux 133 done
134 tar czf $(RELTAR) -C $(RELBIN) mux muxg
134 @echo 135 @echo
135 @$(SHA256) $(RELTAR) 136 @$(SHA256) $(RELTAR)
136 @echo "install: tar xzf $(RELTAR) -C ~/.local/bin" 137 @echo "install: tar xzf $(RELTAR) -C ~/.local/bin"
README.md
Old New
@@ -16,13 +16,14 @@ dependency). The Makefile points at a pinned toolchain path; override with
16 ```sh 16 ```sh
17 make build # first build fetches + compiles the QUIC deps: ~30MB, a few minutes, once 17 make build # first build fetches + compiles the QUIC deps: ~30MB, a few minutes, once
18 make test && make e2e # verify 18 make test && make e2e # verify
19 make install # the one binary to ~/.local/bin (override BINDIR) 19 make install # mux + muxg in ReleaseSafe to ~/.local/bin (override BINDIR)
20 ``` 20 ```
21 21
22 The native client is an opt-in, dynamically linked second 22 The native client is a dynamically linked second
23 binary. It needs SDL3, freetype2, fontconfig, HarfBuzz and OpenGL development 23 binary. It needs SDL3, freetype2, fontconfig, HarfBuzz and OpenGL development
24 packages; OpenGL functions are loaded through SDL, with no direct libGL 24 packages; OpenGL functions are loaded through SDL, with no direct libGL
25 link. It is outside the default build and CI gates: 25 link. `make install` and `make release` build it alongside `mux`; it is outside
26 the default development build and CI gates:
26 27
27 ```sh 28 ```sh
28 make native # Debug development build and no-window unit tests 29 make native # Debug development build and no-window unit tests
@@ -122,9 +123,12 @@ that runs on any x86_64 Linux:
122 ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl 123 ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl
123 ``` 124 ```
124 125
125 `make release` does the whole release build instead — static musl on 126 `make release` builds both `mux` and `muxg` in `ReleaseSafe`, strips them,
126 Linux, `ReleaseSafe`, stripped, checked against `build.zig`'s version, and 127 checks both against `build.zig`'s version, and packages them in `dist/`.
127 tarred into `dist/`. The tarball is what to hand another machine, because it 128 On Linux, `mux` remains static musl; `muxg` uses the build host's native target
129 and needs compatible CPU, libc, SDL3, FreeType, fontconfig, and HarfBuzz libraries
130 on the destination, plus an OpenGL-capable display. These libraries are not
131 bundled. The tarball is what to hand another machine, because it
128 carries the exec bit that a bare downloaded binary does not: 132 carries the exec bit that a bare downloaded binary does not:
129 133
130 ```sh 134 ```sh
@@ -132,7 +136,7 @@ make release
132 ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < dist/mux-vVERSION-x86_64-linux-musl.tar.gz 136 ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < dist/mux-vVERSION-x86_64-linux-musl.tar.gz
133 ``` 137 ```
134 138
135 A release is one tarball per OS, named for the target it holds: 139 A release is one tarball per OS, retaining the daemon target's name:
136 `mux-vVERSION-x86_64-linux-musl.tar.gz` and 140 `mux-vVERSION-x86_64-linux-musl.tar.gz` and
137 `mux-vVERSION-aarch64-macos.tar.gz`. Each is cut on that OS — `make release` 141 `mux-vVERSION-aarch64-macos.tar.gz`. Each is cut on that OS — `make release`
138 builds for the host it runs on — and both are published under 142 builds for the host it runs on — and both are published under
@@ -145,8 +149,8 @@ curl -fsSL https://git.a73x.sh/mux/releases/vVERSION/mux-vVERSION-x86_64-linux-m
145 curl -fsSL https://git.a73x.sh/mux/releases/vVERSION/mux-vVERSION-aarch64-macos.tar.gz | tar xzf - -C ~/.local/bin 149 curl -fsSL https://git.a73x.sh/mux/releases/vVERSION/mux-vVERSION-aarch64-macos.tar.gz | tar xzf - -C ~/.local/bin
146 ``` 150 ```
147 151
148 On an Apple-silicon Mac the same build and install commands work, with two 152 On an Apple-silicon Mac the same build and install commands work, with the
149 things to install first and one binary you cannot cross-compile — see 153 toolchain and native dependencies installed first — see
150 [macOS](#macos). 154 [macOS](#macos).
151 155
152 ## Quick start, local 156 ## Quick start, local
@@ -850,7 +854,7 @@ system `zig` is neither used nor enough.
850 xcode-select --install 854 xcode-select --install
851 brew install cmake 855 brew install cmake
852 make build # first build fetches + compiles the QUIC deps, as on Linux 856 make build # first build fetches + compiles the QUIC deps, as on Linux
853 make install # an aarch64 binary in ~/.local/bin 857 make install # mux + muxg in ~/.local/bin; also needs the native libraries listed above
854 ``` 858 ```
855 859
856 The Mac tarball of a release is cut by `make release-mac`, which runs 860 The Mac tarball of a release is cut by `make release-mac`, which runs
build.zig
Old New
@@ -860,7 +860,7 @@ comptime {
860 } 860 }
861 861
862 pub fn build(b: *std.Build) void { 862 pub fn build(b: *std.Build) void {
863 // Single source for both binaries' --version. Bumped at tag time. 863 // Single source for the product binaries' --version. Bumped at tag time.
864 const version = "0.0.1-17"; 864 const version = "0.0.1-17";
865 const version_opts = b.addOptions(); 865 const version_opts = b.addOptions();
866 version_opts.addOption([]const u8, "version", version); 866 version_opts.addOption([]const u8, "version", version);
@@ -1145,6 +1145,7 @@ pub fn build(b: *std.Build) void {
1145 // there is deliberately no libGL link here. 1145 // there is deliberately no libGL link here.
1146 const native_mod = mods[comptime idxOf("native")]; 1146 const native_mod = mods[comptime idxOf("native")];
1147 const muxg_mod = mods[comptime idxOf("muxg")]; 1147 const muxg_mod = mods[comptime idxOf("muxg")];
1148 muxg_mod.addImport("build_options", build_opts_mod);
1148 const muxg_exe = b.addExecutable(.{ .name = "muxg", .root_module = muxg_mod }); 1149 const muxg_exe = b.addExecutable(.{ .name = "muxg", .root_module = muxg_mod });
1149 linkerFor(muxg_exe); 1150 linkerFor(muxg_exe);
1150 for ([_][]const u8{ "sdl3", "freetype2", "fontconfig", "harfbuzz" }) |lib| 1151 for ([_][]const u8{ "sdl3", "freetype2", "fontconfig", "harfbuzz" }) |lib|
src/cli/muxg.zig
Old New
@@ -54,7 +54,7 @@ pub fn main() !u8 {
54 if (try client.resolver.helper(alloc, args[1..])) |code| return code; 54 if (try client.resolver.helper(alloc, args[1..])) |code| return code;
55 55
56 var o: Arguments = .{}; 56 var o: Arguments = .{};
57 cliflags.parseStrict(Arguments, &o, args[1..]) catch |e| return cliflags.exitFor(e, usage, "muxg", std.fmt.comptimePrint("0.0.1 ({s})", .{@tagName(@import("builtin").mode)})); 57 cliflags.parseStrict(Arguments, &o, args[1..]) catch |e| return cliflags.exitFor(e, usage, "muxg", std.fmt.comptimePrint("{s} ({s})", .{ @import("build_options").version, @tagName(@import("builtin").mode) }));
58 const named: usize = @as(usize, @intFromBool(o.sock != null)) + @intFromBool(o.via != null) + o._targets; 58 const named: usize = @as(usize, @intFromBool(o.sock != null)) + @intFromBool(o.via != null) + o._targets;
59 if (named > 1) { 59 if (named > 1) {
60 std.debug.print("muxg: name one transport: HOST, --sock, --via or quic://\n{s}", .{usage}); 60 std.debug.print("muxg: name one transport: HOST, --sock, --via or quic://\n{s}", .{usage});