a73x

3362909e

build: the shell gate reaches deps/, and a release names its own target

a73x   2026-09-03 17:04

Commit message
build: the shell gate reaches deps/, and a release names its own target

Five review findings from task 1.

The native-Darwin comment in the dep script named the wrong package: ngtcp2's
cmake calls find_package(wolfssl) and never looks for ngtcp2, so a brew
wolfSSL is what the root-path fence keeps out. Someone debugging a bad
Mach-O link would have uninstalled the wrong formula. The comment now also
records that these exact flags built the QUIC stack natively on squirtle in
about a minute, so the fence standing without CMAKE_SYSTEM_NAME is measured.

build.zig's shellGate globbed test, tools and deps/quic but not deps itself,
so deps/mac-sdk.sh -- the one script whose Linux runtime path is a `uname`
gate that exits before any syntax error downstream is reached -- was the one
script no gate parsed. A planted `if` with no `fi` now fails both `sh -n` and
shellcheck under `make check`.

The script's header claimed the marker check runs first when two probes
precede it. Reworded rather than reordered: the marker compares against the
real SDK's path, which only the xcrun fork knows, and an Xcode update in
place leaves MacOSX.sdk at the same path, so a marker consulted before the
tbd grep would match its own stale shadow and keep shadowing an SDK that had
been fixed. The comment now says that.

Hoisting MUX_TARGET so a Mac installs a Mac binary also handed `make release`
a Darwin meaning it was never designed for: a Mach-O binary inside
mux-vN-aarch64-macos.tar.gz, which the publish flow would take for the Linux
artifact. RELEASE_TARGET is its own pinned x86_64-linux-musl, so what a
release IS no longer follows the host that cuts it.

And e2e, soak, bench, agent, coverage and xversion-build gained the mac-sdk
prerequisite, so a standalone `make e2e` on a fresh Mac clone cannot link
against the unshadowed SDK. Inside ci this never bit, because check builds
the shim first.

Every Linux target still expands to what it did before the port: identical
for release, vm, deps, xversion and ci, and one added `sh ./deps/mac-sdk.sh`
line for the rest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SakwJEwD9dXBoRP5kWbemW

Makefile
Old New
@@ -77,12 +77,20 @@ install: mac-sdk
77 VERSION := $(shell sed -n 's/^[[:space:]]*const version = "\(.*\)";/\1/p' build.zig | head -1) 77 VERSION := $(shell sed -n 's/^[[:space:]]*const version = "\(.*\)";/\1/p' build.zig | head -1)
78 RELDIR ?= dist 78 RELDIR ?= dist
79 RELBIN = $(RELDIR)/v$(VERSION) 79 RELBIN = $(RELDIR)/v$(VERSION)
80 RELTAR = $(RELDIR)/mux-v$(VERSION)-$(MUX_TARGET).tar.gz 80 # Its OWN target, not MUX_TARGET: what a release IS must not follow the host
81 # that cuts it. MUX_TARGET follows the host so a Mac installs a Mac binary,
82 # and hoisting it to the top of this file put `make release` on a Mac one
83 # `?=` away from writing a Mach-O binary into mux-vN-aarch64-macos.tar.gz --
84 # an artifact the publish flow would take for the Linux one. Cutting a
85 # release stays a Linux job for a second reason anyway: the checksum line
86 # below spells sha256sum, which macOS does not ship.
87 RELEASE_TARGET ?= x86_64-linux-musl
88 RELTAR = $(RELDIR)/mux-v$(VERSION)-$(RELEASE_TARGET).tar.gz
81 # The version guard below asks the STRIPPED artifact what it is, so a 89 # The version guard below asks the STRIPPED artifact what it is, so a
82 # stale stage directory cannot ship under a bumped number. 90 # stale stage directory cannot ship under a bumped number.
83 release: 91 release:
84 @test -n "$(VERSION)" || { echo "release: no version found in build.zig"; exit 1; } 92 @test -n "$(VERSION)" || { echo "release: no version found in build.zig"; exit 1; }
85 $(ZIG) build -Dtarget=$(MUX_TARGET) -Doptimize=ReleaseSafe -p $(RELDIR)/stage 93 $(ZIG) build -Dtarget=$(RELEASE_TARGET) -Doptimize=ReleaseSafe -p $(RELDIR)/stage
86 rm -rf $(RELBIN) $(RELTAR) 94 rm -rf $(RELBIN) $(RELTAR)
87 install -d $(RELBIN) 95 install -d $(RELBIN)
88 install -m755 $(RELDIR)/stage/bin/mux $(RELBIN)/ 96 install -m755 $(RELDIR)/stage/bin/mux $(RELBIN)/
@@ -100,13 +108,13 @@ release:
100 test: mac-sdk 108 test: mac-sdk
101 $(ZIG) build test 109 $(ZIG) build test
102 110
103 e2e: 111 e2e: mac-sdk
104 $(ZIG) build e2e 112 $(ZIG) build e2e
105 113
106 soak: 114 soak: mac-sdk
107 $(ZIG) build soak 115 $(ZIG) build soak
108 116
109 bench: 117 bench: mac-sdk
110 $(ZIG) build bench 118 $(ZIG) build bench
111 119
112 # ReleaseSafe into its own prefix, for the reason throughput.sh opens with: 120 # ReleaseSafe into its own prefix, for the reason throughput.sh opens with:
@@ -131,7 +139,7 @@ bench:
131 # Outside `ci` deliberately — it needs an optional system package and is 139 # Outside `ci` deliberately — it needs an optional system package and is
132 # still several times slower than the suite it wraps. 140 # still several times slower than the suite it wraps.
133 COVDIR ?= dist/coverage 141 COVDIR ?= dist/coverage
134 coverage: 142 coverage: mac-sdk
135 $(ZIG) build -Doptimize=ReleaseSafe -p $(COVDIR) 143 $(ZIG) build -Doptimize=ReleaseSafe -p $(COVDIR)
136 ./test/coverage.sh $(COVDIR)/bin/mux \ 144 ./test/coverage.sh $(COVDIR)/bin/mux \
137 $(COVDIR)/bin/rawmode $(COVDIR)/bin/delaypipe $(COVDIR)/bin/render \ 145 $(COVDIR)/bin/rawmode $(COVDIR)/bin/delaypipe $(COVDIR)/bin/render \
@@ -179,7 +187,7 @@ ci:
179 $(MAKE) agent 187 $(MAKE) agent
180 $(MAKE) throughput 188 $(MAKE) throughput
181 189
182 agent: 190 agent: mac-sdk
183 $(ZIG) build agent 191 $(ZIG) build agent
184 192
185 # Cross-version gate (test/xversion.sh): this tree's client against a 193 # Cross-version gate (test/xversion.sh): this tree's client against a
@@ -195,7 +203,7 @@ agent:
195 # purpose: a bare `-Dtarget=` build overwrites zig-out/bin with binaries 203 # purpose: a bare `-Dtarget=` build overwrites zig-out/bin with binaries
196 # for the wrong libc, and the next `make e2e` would silently run those. 204 # for the wrong libc, and the next `make e2e` would silently run those.
197 XVER_OLD_WORKTREE ?= .. 205 XVER_OLD_WORKTREE ?= ..
198 xversion-build: 206 xversion-build: mac-sdk
199 $(ZIG) build -Dtarget=x86_64-linux-musl -p $(CURDIR)/.xversion/new 207 $(ZIG) build -Dtarget=x86_64-linux-musl -p $(CURDIR)/.xversion/new
200 cd $(XVER_OLD_WORKTREE) && $(ZIG) build -Dtarget=x86_64-linux-musl -p $(CURDIR)/.xversion/old 208 cd $(XVER_OLD_WORKTREE) && $(ZIG) build -Dtarget=x86_64-linux-musl -p $(CURDIR)/.xversion/old
201 209
build.zig
Old New
@@ -649,7 +649,7 @@ fn checkOneRootReaches(b: *std.Build, root: []const u8, subdir: []const u8, pref
649 fn shellGate(b: *std.Build, step: *std.Build.Step) void { 649 fn shellGate(b: *std.Build, step: *std.Build.Step) void {
650 var paths: [64][]const u8 = undefined; 650 var paths: [64][]const u8 = undefined;
651 var n: usize = 0; 651 var n: usize = 0;
652 for ([_][]const u8{ "test", "tools", "deps/quic" }) |sub| { 652 for ([_][]const u8{ "test", "tools", "deps", "deps/quic" }) |sub| {
653 var dir = b.build_root.handle.openDir(sub, .{ .iterate = true }) catch |err| 653 var dir = b.build_root.handle.openDir(sub, .{ .iterate = true }) catch |err|
654 fatal("shell gate: cannot open {s}/ ({s})", .{ sub, @errorName(err) }); 654 fatal("shell gate: cannot open {s}/ ({s})", .{ sub, @errorName(err) });
655 defer dir.close(); 655 defer dir.close();
deps/mac-sdk.sh
Old New
@@ -15,7 +15,17 @@
15 # 15 #
16 # Self-retiring: when the real SDK's stub lists arm64-macos again, no shadow 16 # Self-retiring: when the real SDK's stub lists arm64-macos again, no shadow
17 # is built and the shim, if a stale one is on PATH, passes straight through. 17 # is built and the shim, if a stale one is on PATH, passes straight through.
18 # Idempotent and cheap: the marker check is the first thing it does. 18 # Idempotent: a run that finds its own shadow already standing over the
19 # current SDK exits without touching it.
20 #
21 # That marker check is deliberately LAST of the three, after the xcrun fork
22 # and the tbd grep, even though `mac-sdk` is a phony target every make on a
23 # Mac runs. The fork cannot move: the marker compares against the real SDK's
24 # path and only xcrun knows it. The grep cannot move either, because an
25 # Xcode update in place leaves MacOSX.sdk at the same path -- so a marker
26 # consulted first would match its own stale shadow and go on shadowing an
27 # SDK that had been fixed. One fork and one local grep is what the retire
28 # check costs, and it is the cheaper half of the two.
19 # 29 #
20 # ./deps/mac-sdk.sh builds deps/mac-sdk/{sdk,bin/xcrun} 30 # ./deps/mac-sdk.sh builds deps/mac-sdk/{sdk,bin/xcrun}
21 set -eu 31 set -eu
deps/quic/build-deps.sh
Old New
@@ -123,11 +123,22 @@ case "$T" in
123 XTRA="-DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_SYSTEM_PROCESSOR=arm64 -DCMAKE_FIND_ROOT_PATH=$OUT -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY" 123 XTRA="-DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_SYSTEM_PROCESSOR=arm64 -DCMAKE_FIND_ROOT_PATH=$OUT -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY"
124 WOLF_XTRA="-DWOLFSSL_SYS_CA_CERTS=no" ;; 124 WOLF_XTRA="-DWOLFSSL_SYS_CA_CERTS=no" ;;
125 native) 125 native)
126 # A native build ON a Mac wants the same two fences for the same 126 # A native build ON a Mac wants the same two fences for the same two
127 # two reasons, minus the cross-compile words: brew carries a 127 # reasons, minus the cross-compile words. ngtcp2's cmake calls
128 # libngtcp2 that ngtcp2's own cmake would otherwise find, and the 128 # find_package(wolfssl), so a brew-installed wolfSSL is what it finds
129 # CA path still wants Security.framework linked into a PSK-only 129 # ahead of the one built here -- the same hazard the cross branch
130 # binary that never verifies a certificate. 130 # above measured, and the reason to name wolfSSL rather than ngtcp2
131 # when someone goes looking for the offending package. And the CA
132 # path still wants Security.framework linked into a PSK-only binary
133 # that never verifies a certificate.
134 #
135 # Measured on squirtle 2026-09-03: with exactly these flags the QUIC
136 # stack built natively in about a minute, so the root-path fence
137 # standing without CMAKE_SYSTEM_NAME (which the cross branch needs
138 # and a native build has no business setting) is a measurement and
139 # not a guess. The log is in
140 # docs/superpowers/specs/2026-09-03-macos-port-design.md, "Hardware
141 # findings, 2026-09-03".
131 if [ "$(uname)" = Darwin ]; then 142 if [ "$(uname)" = Darwin ]; then
132 XTRA="-DCMAKE_FIND_ROOT_PATH=$OUT -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY" 143 XTRA="-DCMAKE_FIND_ROOT_PATH=$OUT -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY"
133 WOLF_XTRA="-DWOLFSSL_SYS_CA_CERTS=no" 144 WOLF_XTRA="-DWOLFSSL_SYS_CA_CERTS=no"