build(macos): build the original Swift app against the Rust core

Ghostty's macOS app stays the original, unchanged Swift app (agreed
scope); it is wired to the Rust core instead of the Zig core.
scripts/macos-swift-app.sh (`just macos-swift-app <ghostty-src>`)
mirrors the Zig build's GhosttyXcodebuild.zig:
1. scripts/xcframework.sh builds the Rust libghostty (identical
   include/ghostty.h ABI) as <src>/macos/GhosttyKit.xcframework,
2. `just install <src>/zig-out` provides the resources the Xcode project
   bundles from ../zig-out/share (ghostty, terminfo, completions, man,
   locale, vim, nvim, bat),
3. xcodebuild -target Ghostty in a clean environment.
macOS + Xcode only; DRY_RUN=1 previews the steps (verified on Linux).

Test Plan:
- DRY_RUN=1 scripts/macos-swift-app.sh ../ghostty
- on macOS: just macos-swift-app ../ghostty && open the built app
This commit is contained in:
ddidderr committed 2026-10-12 19:29:26 +02:00
1 parent 6d058b965e
commit 003d318fbf
4 files changed
+83 -5

No files matched your search

+16
View File
@@ -19,6 +19,22 @@ Windows test suites also run under Wine (`just test-windows-wine`), macOS has
not been run natively. The Swift macOS app is not part of the port; it is a
client of the libghostty C API.
### The macOS app (original Swift app on the Rust core)
Ghostty's macOS app stays the original Swift app, unchanged. It is wired to
the Rust core instead of the Zig core: the Rust `libghostty` exports the
identical `include/ghostty.h` ABI. On a Mac with Xcode:
```sh
just macos-swift-app ../ghostty # Release; or: just macos-swift-app ../ghostty Debug
```
This builds the Rust core as `GhosttyKit.xcframework` into
`<ghostty>/macos/`, installs the resources into `<ghostty>/zig-out/share`
(what the Xcode project bundles) and runs `xcodebuild`, mirroring
`zig build -Demit-macos-app`. Not runnable on this Linux machine
(`DRY_RUN=1` previews the steps).
## Building and running
Requirements: Rust (edition 2024), GTK ≥ 4.14, libadwaita ≥ 1.5,
+7 -5
View File
@@ -109,11 +109,12 @@ Legend: ⬜ todo · 🟨 in progress · ✅ done (gate passed, committed) · ⚠
## Phase 9 — macOS Swift app → Rust (AppKit via objc2; compile-checked)
| WP | Scope | Owner | State | Notes |
|---|---|---|---|---|
| MA1 | foundation: crate, app/AppDelegate, menu, Ghostty.App/Config wrappers, helpers, bundle script, design doc | sonnet | ✅ | compile-checked; MA2–MA5 on hold pending user decision |
| MA2 | Ghostty/ incl. Surface View (Metal layer, input, IME, DnD, a11y) | – | ⬜ | after MA1 |
| MA3 | Features/Terminal (windows, tabs, styles) + Splits | – | ⬜ | after MA1 |
| MA4 | QuickTerminal, Command Palette, Settings, About, Clipboard, Global Keybinds, Secure Input, Services, App Icon | – | ⬜ | after MA1 |
| MA5 | AppleScript, Update (Sparkle), App Intents (assess) | – | ⬜ | after MA1 |
| MA1 | foundation: crate, app/AppDelegate, menu, Ghostty.App/Config wrappers, helpers, bundle script, design doc | sonnet | ↩️ | reverted (cfded0e): user decision — keep the original Swift app, wire it to the Rust core |
| MSW | `just macos-swift-app`: original Swift app built against the Rust libghostty (xcframework + resources + xcodebuild) | opus | ✅ | macOS-only; dry-run verified here |
| MA2 | Ghostty/ incl. Surface View (Metal layer, input, IME, DnD, a11y) | – | ✖ | after MA1 | cancelled
| MA3 | Features/Terminal (windows, tabs, styles) + Splits | – | ✖ | after MA1 | cancelled
| MA4 | QuickTerminal, Command Palette, Settings, About, Clipboard, Global Keybinds, Secure Input, Services, App Icon | – | ✖ | after MA1 | cancelled
| MA5 | AppleScript, Update (Sparkle), App Intents (assess) | – | ✖ | after MA1 | cancelled
## Log
- 2026-10-11: Phase 0 complete; Phase 1 launched.
@@ -171,3 +172,4 @@ Legend: ⬜ todo · 🟨 in progress · ✅ done (gate passed, committed) · ⚠
- 2026-10-12: Stop-hook: live D-Bus/portal behaviour still unexercised → LIVE launched (private buses; user session untouched).
- 2026-10-12: LIVE committed (3605 tests). Remaining unverifiable here: native macOS, native Windows (Wine-tested), IME (no input-method daemon installed).
- 2026-10-12: Stop-hook: Swift macOS app is part of Ghostty 1.3.1 → Phase 9 (port to Rust/AppKit) started with MA1.
- 2026-10-12: User: Swift app was agreed out of scope; the /goal hook should not have overridden that. Phase 9 reverted; instead the original Swift app is wired to the Rust core (`just macos-swift-app`).
+5
View File
@@ -306,3 +306,8 @@ visual-parity *scenarios:
live-dbus *scenarios:
scripts/live-dbus/build-rust.sh
RUST_BIN="${RUST_BIN:-target/vp/production/ghostty}" scripts/live-dbus-test.sh {{scenarios}}
# Build Ghostty's original Swift macOS app against the Rust core (macOS only;
# DRY_RUN=1 to preview). Example: just macos-swift-app ../ghostty
macos-swift-app src config="Release":
scripts/macos-swift-app.sh {{ src }} {{ config }}
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Build Ghostty's ORIGINAL Swift macOS app (unchanged) against the Rust core.
#
# Ghostty's Xcode project (<ghostty>/macos/Ghostty.xcodeproj) links
# `macos/GhosttyKit.xcframework` and bundles resources from `../zig-out/share`
# (see the Zig build's src/build/GhosttyXcodebuild.zig). The Rust libghostty
# exports the identical include/ghostty.h ABI, so this script produces both
# from the Rust workspace and then runs xcodebuild, exactly like
# `zig build -Demit-macos-app`:
# 1. scripts/xcframework.sh -> <ghostty>/macos/GhosttyKit.xcframework
# 2. `just install` -> <ghostty>/zig-out/share/{ghostty,terminfo,...}
# 3. xcodebuild -target Ghostty -configuration <CONFIG> (clean env, like Zig)
#
# Usage: scripts/macos-swift-app.sh <path-to-ghostty-source> [Release|Debug|ReleaseLocal]
# Runs only on macOS (Xcode required). DRY_RUN=1 prints the commands instead.
set -euo pipefail
cd "$(dirname "$0")/.."
src=${1:?usage: $0 <path-to-ghostty-source> [Release|Debug|ReleaseLocal]}
config=${2:-Release}
src=$(cd "$src" && pwd)
[ -d "$src/macos/Ghostty.xcodeproj" ] || {
echo "error: $src/macos/Ghostty.xcodeproj not found" >&2
exit 1
}
run() {
if [ "${DRY_RUN:-}" = 1 ]; then
printf '+'
printf ' %q' "$@"
printf '\n'
else
"$@"
fi
}
if [ "${DRY_RUN:-}" != 1 ] && [ "$(uname -s)" != Darwin ]; then
echo "error: building the macOS app requires macOS with Xcode (use DRY_RUN=1 to preview)" >&2
exit 1
fi
# Debug configuration pairs with the dev profile (debug app id), like Zig.
profile=production
[ "$config" = Debug ] && profile=dev
# 1. The Rust core as GhosttyKit.xcframework where the Xcode project expects it.
run scripts/xcframework.sh universal "$profile" "$src/macos/GhosttyKit.xcframework"
# 2. Resources the Xcode project copies from ../zig-out/share.
run just install "$src/zig-out"
# 3. xcodebuild with a clean environment (only PATH), as GhosttyXcodebuild.zig.
run env -i PATH="$PATH" sh -c "cd '$src/macos' && xcodebuild -target Ghostty -configuration '$config'"
echo "Ghostty.app: $src/macos/build/$config/Ghostty.app"