build: package haul as a macOS .app bundle

Double-clicking the bare binary in Finder opens a Terminal window alongside
the app: Finder has no better idea what to do with a Mach-O executable than
hand it to Terminal.app, and the terminal stays up as long as the process
does. macOS has no counterpart to -H=windowsgui, so the only fix is to ship
an application bundle, which Finder launches directly.

Add `make package-darwin`, which runs a pinned `fyne package --target
darwin` to produce ./haul.app. Its metadata comes from cmd/haul/FyneApp.toml
— the app ID there must stay in step with the one app.NewWithID uses, or
macOS treats the bundle and the running app as separate applications. The
icon is a placeholder; `fyne package` will not run without one.

Note that `fyne package` rewrites FyneApp.toml in place on every run, so the
file is left in that tool's canonical formatting rather than a commented
version it would only strip again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 22:30:22 +10:00
parent b533d35f95
commit a8a60f5860
5 changed files with 87 additions and 4 deletions
+50 -2
View File
@@ -205,6 +205,51 @@ Unlike Linux, Windows needs no graphics development *packages* to build: OpenGL
ships with the operating system. Getting a usable OpenGL at **run** time is a
separate matter — see below.
### Packaging on macOS
`make build` produces a plain Unix executable, and that is all you need if you
launch haul from a shell. **Double-click it in Finder, though, and macOS opens a
Terminal window alongside the app.** That is not a bug in haul and no build flag
fixes it — it is what Finder does with any bare executable: having no better
idea what a Mach-O file is, it hands it to Terminal.app to run, and the terminal
stays on screen for as long as the process does. macOS has no equivalent of
Windows' `-H=windowsgui`.
The fix is to ship an application *bundle* — a `haul.app` directory carrying an
`Info.plist` — which Finder launches directly, with a Dock icon and no terminal:
```sh
make package-darwin # -> ./haul.app
```
Then drag `haul.app` into `/Applications`. Run it on a Mac: it compiles with the
host cgo toolchain, and it builds for the host architecture, so an Intel Mac
gives you an `amd64` bundle and Apple silicon an `arm64` one.
Under the hood that target runs [`fyne
package`](https://docs.fyne.io/started/packaging), which reads its metadata from
`cmd/haul/FyneApp.toml`, converts `cmd/haul/Icon.png` into the bundle's
`.icns` icon, and writes the `Info.plist`. Without `make`:
```sh
go run fyne.io/tools/cmd/fyne@v1.7.2 package --target darwin --src ./cmd/haul --release
```
Note that `fyne package` **rewrites `cmd/haul/FyneApp.toml` in place**, bumping
`Build` on every run and dropping any comments you add to it. That is by design;
commit the bump or discard it as you prefer.
The bundle is unsigned, so Gatekeeper blocks the first launch on any Mac that
did not build it. Right-click the app and choose **Open** (once — after that it
starts normally), or clear the quarantine flag:
```sh
xattr -dr com.apple.quarantine /Applications/haul.app
```
Signing and notarising properly needs a paid Apple Developer ID, after which
`fyne release --target darwin --certificate <name>` handles it.
### Running over Remote Desktop (or in a VM)
A build that compiles perfectly will still fail to start in an RDP session with:
@@ -269,7 +314,8 @@ Software rendering is slower than a GPU, but haul draws two tables and some text
| `make run` | Build, then run (needs a `DISPLAY`) |
| `make run-xvfb` | Build, then run against a virtual X display |
| `make dist` | Release binary into `./dist` |
| `make clean` | Remove `bin/` and `dist/` |
| `make package-darwin` | macOS `.app` bundle into `./haul.app` (run on a Mac) |
| `make clean` | Remove `bin/`, `dist/` and `haul.app/` |
The Makefile picks up a Go toolchain at `~/.local/go/bin/go` if one is there,
falling back to whatever `go` is on `PATH` — so it works on machines where Go
@@ -297,7 +343,9 @@ toolchain:
| `windows/amd64` | `gcc-mingw-w64` |
| `darwin/*` | a Mac, or [osxcross](https://github.com/tpoechtrager/osxcross) |
For that reason `make dist` builds only the native binary. The practical route
For that reason `make dist` builds only the native binary — and on macOS the
binary alone is not the artefact you want to hand out, see [Packaging on
macOS](#packaging-on-macos). The practical route
to the others is [`fyne-cross`](https://github.com/fyne-io/fyne-cross), which
does each build inside a Docker image carrying the right toolchain: