Self-diagnosing toolchain setup for macOS 26.4+#468
Open
jeremybower wants to merge 4 commits into
Open
Conversation
make format ran the Xcode toolchain's built-in `swift format`, which is unpinned: a newer Xcode's formatter rewrites the whole tree (Swift call-site trailing commas), so contributors on different Xcodes disagree and produce spurious ~195-file churn. xcbeautify was piped via `mise exec` but absent from mise.toml, so with pipefail `make build-app`/`test` failed even when the build itself succeeded. swiftlint floated on `latest`. Pin all three in mise.toml (swift-format via the spm: backend, matching how zig/tuist/swiftlint are already managed; tag 602.0.0 ↔ Swift 6.2), point `make format` at the pinned `swift-format`, and make the xcbeautify pipe tolerant of its absence as defense in depth. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
On macOS 26.4+ the GhosttyKit build dies with a wall of `undefined symbol: _malloc, _free, _sigaction, ...` in build_zcu.o. The pinned Zig (0.15.2, required exactly by ghostty) can't link the macOS 26.4+ SDK: that SDK's libSystem.tbd dropped the plain arm64-macos target (keeping only arm64e-macos), and Zig 0.15.2's linker won't match — ziglang/zig#31658, fixed only in Zig 0.16+. The fix is to build against an Xcode <= 26.3 (ships the macOS 26.2 SDK, whose .tbd still has arm64-macos), without a global `sudo xcode-select -s` that would disrupt other projects needing a newer Xcode. Add scripts/select-developer-dir.sh: it scans candidate Xcodes (current selection, then versioned 26.3..26.0, then Xcode.app) and prints the first whose macOS SDK is Zig-linkable — probing with the `xcrun --sdk macosx` form Zig uses, not bare `xcrun --show-sdk-path` (which can resolve to the CommandLineTools SDK and mislead). build-ghostty.sh and build-zmx.sh self-resolve DEVELOPER_DIR (honoring an inherited value from the Makefile or Xcode's foreignBuild), and the Makefile exports it for build-app / test / run-app / archive so the app and its ghostty build share one toolchain. An explicit DEVELOPER_DIR still wins as an override. Document the macOS 26.4+ setup (Xcode 26.3, license/first-launch, Metal Toolchain, submodules, the verification quirk, and why no patches/ entry can fix a bug in Zig's own linker) in AGENTS.md and README.md. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
A first-time / returning contributor on macOS 26.4+ hits a chain of build failures and the worst (the Zig link failure) shows up as a 200-line undefined-symbol dump with no hint of the cause. Add scripts/doctor.sh: it checks every prerequisite in the order failures surface — mise on PATH, submodules initialized, a Zig-linkable Xcode (reusing select-developer-dir.sh), Xcode license/first-launch, the Metal Toolchain, and the pinned mise tools — and prints the exact fix command for each failure. `make doctor` runs it verbose; the build targets (build-app, test, build-ghostty-xcframework, build-zmx) gain a quiet `preflight` as an order-only prerequisite so a missing prerequisite fails fast with an actionable message instead of a linker dump. Set SUPACODE_SKIP_PREFLIGHT=1 to skip. Docs now point at `make doctor` as the front door. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The setup-macos action duplicated the "pick an Xcode <= 26.3" scan inline. Replace it with a call to scripts/select-developer-dir.sh so CI and local builds (and `make doctor`) share one source of truth for which Xcode can link the pinned Zig — and probe by libSystem.tbd rather than by version name. Still applies it via `sudo xcode-select -s`, which is fine on an ephemeral runner. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A first-time / returning contributor on macOS 26.4+ (Tahoe) hits a chain of build failures, each surfacing only after the previous is fixed — and the worst one (the GhosttyKit Zig link failure) shows up as a ~200-line
undefined symbol: _malloc, _free, _sigaction, …dump inbuild_zcu.owith no hint of the cause. The root cause: the pinned Zig (0.15.2, required exactly by ghostty) can't link the macOS 26.4+ SDK, which dropped the plainarm64-macosslice fromlibSystem.tbd(ziglang/zig#31658, fixed only in Zig 0.16+). The fix is to build against Xcode 26.3 (ships the macOS 26.2 SDK).This PR makes the prerequisites self-diagnosing and documents the setup, so contributors get a clear, actionable error the moment something is missing, and can keep multiple Xcodes side-by-side without a global
sudo xcode-select -s.What (4 self-contained, individually-buildable commits)
swift-format(spm:swiftlang/[email protected], reproducible across contributors' Xcodes instead of the drifting toolchainswift format),[email protected](was piped but unpinned → pipefail failures),[email protected].make formatuses the pinned formatter; xcbeautify pipe tolerates absence.DEVELOPER_DIR— newscripts/select-developer-dir.shscans candidate Xcodes and picks the first whose macOS SDK still hasarm64-macos(probing withxcrun --sdk macosx, the form Zig uses). The ghostty/zmx build scripts and the Makefile build targets export it per-build — no global switch;DEVELOPER_DIR=…overrides. Adds the "Building on macOS 26.4+" docs.make doctor— newscripts/doctor.shchecks mise-on-PATH, submodules, a Zig-linkable Xcode, Xcode license/first-launch, the Metal Toolchain, and the pinned tools, printing the exact fix for each failure. Build targets run it as a quiet order-onlypreflight(SUPACODE_SKIP_PREFLIGHT=1to skip).setup-macosaction reuses the shared selector instead of its inline scan.Why no
patches/entryThe link bug is in Zig's own self-hosted linker (
build_zcu.o), not ghostty source, so thepatches/*.patchmechanism (which only patches the ghostty submodule) can't fix it — and ghostty pins Zig to exactly0.15.2. The older-SDK + auto-DEVELOPER_DIRapproach is the long-term fix until ghostty supports Zig 0.16+.Verification
make build-app— full ghostty rebuild under the auto-pinnedDEVELOPER_DIR+ app → Build Succeededmake format→ zero.swiftchurn (pinned formatter agrees with the tree)make doctor→ all checks green;--quietsilent on success; failure path renders correct fixesmake lint→ clean🤖 Generated with Claude Code