Add macOS Apple Silicon native build support#536
Conversation
The macOS DIST bundle could not be launched on another Mac: after download/AirDrop (which quarantines the copy) Gatekeeper reported it as "damaged and can't be opened". Two independent defects caused this: 1. bundle_macosx_app.sh rewrites each Mach-O's dependent library paths with install_name_tool *after* the linker has signed them, which invalidates the signature, and never re-seals the bundle. On Apple Silicon a valid signature is mandatory to execute. Re-sign the dylibs, the executable, and then the whole bundle (ad-hoc) as the final step of bundling. 2. The Makefile packaged the .app with Info-ZIP `zip`, which drops the extended attributes that hold the code signatures of the nested resources under Contents/MacOS. Extraction then yielded an unsigned/"damaged" bundle. Package with `ditto` instead, which preserves signing metadata and is always available on macOS. The signature is ad-hoc (not Developer ID / notarized), so a quarantined copy still needs a one-time approval on first launch (right-click > Open, or `xattr -dr com.apple.quarantine <app>`), but it is no longer reported as damaged and runs normally thereafter. Also gitignore the generated macosx/libs/ third-party build tree.
The macOS build/release jobs were pinned to macos-15-intel with a hardcoded ARCH=x86_64, so only an Intel artifact was ever produced. Apple Silicon Macs had no native build. Turn both macOS jobs into a 2-arch matrix (macos-15-intel + macos-15), mirroring the existing Ubuntu x86_64/arm64 matrix, and derive ARCH from `uname -m` instead of hardcoding it. Also fix the release job's attest subject-path, which hardcoded build/darwin-x86_64, to use the per-arch build dir. To keep the two macOS artifacts from colliding (the zip was named poptracker_<ver>_macos.zip with no arch), include the arch in the name: poptracker_<ver>_macos_<arch>.zip — consistent with the Linux tarballs, which already embed the arch. Standard macos-15 runners are free for public repositories, so this adds no CI cost.
|
Hello, I think having separate Intel and Apple Silicon builds is not the best solution for the end-user, so fat binaries and a single download would be preferred. We briefly talked about this in Discord already. Afaict, Rosetta will cease to exist around Sep 2027 with macOS 28, so we have more than a year to "fix" this (if you have other info, please link it), but the "big" problem is that there will be a year of overlap where Intel macs are still supported with security updates on macOS 26 and macOS 28 can't run Intel apps anymore. I would like to have fat binaries from ~Sep 2027 at the latest until ~Sep 2028 at the earliest, when we can drop Intel support ourselves1. You can merge separate Intel and Apple Silicon binaries into one fat binary using the Re ditto: We should probably specify Footnotes
|
The macOS CI built two separate downloads (arm64 and x86_64); this combines them into one universal2 app that runs on any Mac. A single fat compile isn't practical here, since the bundled deps (SDL2, OpenSSL, freetype, ...) are built from source and don't emit universal binaries in one pass. So each arch still builds natively, then a merge job lipo-combines the two .app bundles: - build-macos / release-macos now upload their .app (tar'd to keep symlinks and perms) instead of a per-arch zip. - New *-universal jobs lipo-merge every Mach-O (macosx/make_universal.sh), re-sign ad-hoc (lipo breaks the signature), verify, then ditto-package a single poptracker_<version>_macos_universal.zip. Also pass --zlibCompressionLevel 9 to ditto, since the bundle data compresses well. A local `make CONF=DIST` still produces a per-arch zip.
|
Hi! Good call on the universal build, I am so used to seeing x86_64 and arm64 builds as a dev that I forgot universal builds existed. I kept the two build separate since the underlying dependencies are built from source and can't go universal in one go. I added a merge job that combines both archs with Let me know if that works for you, thanks! PS: As far as I know Rosetta will indeed be dropped starting on macOS 28, so next Fall. |
Ah, it seems like with 28 it'll be limited to specific apps and games. Somehow I missed that when researching earlier.
At first glance, the change looks good. I am gonna ask in Discord if we have someone to test on Intel and M1 (or newer). Builds should be those: https://github.com/MarcDufresne/PopTracker/actions/runs/27913528305 |
|
Wait, isn't macos 28 coming |
|
They release the versions like one year "ahead". 26 released at the end 2025. So we'll get 28 at the end of 2027. I wasn't clear with my "next Fall" comment. We do still have a year and a half pretty much, yes. |
black-sliver
left a comment
There was a problem hiding this comment.
This was tested now on 3 separate machines (1 intel, 1 m4 and another apple silicon) over on Discord and I looked over the code and almost everything looks good.
It's missing the attestation of the universal build for manually triggered build binaries - those builds are used for feature preview and bugfix testing.
I left a suggestion below. I hope I did not typo anything.
| # ditto preserves the signature's extended attributes (a plain zip strips them) | ||
| ditto -c -k --keepParent --sequesterRsrc --zlibCompressionLevel 9 poptracker.app "dist/poptracker_${VS}_macos_universal.zip" | ||
| ls -l dist | ||
| - name: Store universal ZIP |
There was a problem hiding this comment.
| - name: Store universal ZIP | |
| - name: Attest Build and AppBundle | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 | |
| with: | |
| subject-path: | | |
| poptracker.app/Contents/MacOS/poptracker | |
| dist/* | |
| - name: Store universal ZIP |
Hi! I noticed that PopTracker didn't have a native Apple Silicon build, which will be required starting from the next macOS release (Rosetta will be removed). Considering this, I wanted to add proper support to avoid any future issues.
I was able to build PopTracker, run it on my M1 Pro MacBook Pro, using a Majora's Mask pack, and connect to an AP server. As far as I could see, everything seemed to work perfectly fine. Build atrifacts available here: https://github.com/MarcDufresne/PopTracker/actions/runs/27890842995
I had to update the build process a bit, like proper signature and packaging, and some small tweaks to naming since macOS now has 2 architectures to build.
Cheers!