Skip to content

Build a native Apple Silicon (arm64) macOS app - #531

Open
jayvee6 wants to merge 1 commit into
agateau:masterfrom
jayvee6:apple-silicon-native
Open

Build a native Apple Silicon (arm64) macOS app#531
jayvee6 wants to merge 1 commit into
agateau:masterfrom
jayvee6:apple-silicon-native

Conversation

@jayvee6

@jayvee6 jayvee6 commented Jun 13, 2026

Copy link
Copy Markdown

Summary

Pixel Wheels does not run natively on Apple Silicon Macs — it requires Rosetta 2, and fails outright on a Mac without it. This PR makes the macOS build native arm64.

Two independent things kept the game on x86_64:

  1. The engine was pinned to libGDX 1.9.14, whose LWJGL 3.2.x backend ships only x86_64 macOS natives. A native arm64 JVM cannot load them.
  2. The macOS .app is cross-built on a Linux runner with packr, which only emits Intel binaries (libgdx/packr#198) and bundles an x86_64 JRE 8.

Changes

  • Engine: bump libGDX 1.9.141.13.0 (LWJGL 3.3.x, which bundles native macOS arm64 libraries) and gdx-controllers 2.2.22.2.4 (newer Jamepad with arm64 macOS natives).
  • Launch flag: pass -XstartOnFirstThread on macOS in every launch path — the run/install Makefile targets and the gradle :desktop:run task. LWJGL3/GLFW hangs at startup on macOS without it.
  • Packaging: build the macOS app with jpackage instead of packr. jpackage produces a real native .app bundling a runtime image of the running JDK, so it must run on macOS. Adds:
    • tools/packaging/create-macos-archive (the jpackage build)
    • tools/packaging/smoke-test-macos (asserts native libraries load on the host arch)
    • macos-archive / macos-dist / smoke-tests-macos Makefile targets
    • a build-macos CI job on macos-14 that reuses the desktop jar from the Linux job, so the asset toolchain only runs once
    • removal of the (now dead) packr macOS path from tools/packaging/create-archives
  • Signing / notarization: wired as optional, secret-gated steps. With no secrets the build still produces a working unsigned .app; with a Developer ID it signs and notarizes. Fully documented in tools/packaging/macos/SIGNING.md.

Linux and Windows packaging are unchanged (still packr).

Verification

Built and run on an Apple Silicon Mac (M-series, macOS 26.5, Temurin/Homebrew JDK 17):

  • A native aarch64 JVM drives LWJGL3's GLFW window and OpenGL context all the way into PwGame.create(). On 1.9.14 this path threw UnsatisfiedLinkError at new Lwjgl3Application() and never reached create().
  • The jpackage .app launcher is a native Mach-O 64-bit executable arm64, with the correct bundle id and version.
  • make macos-archive + make smoke-tests-macosPASS (native macOS app started on arm64).
  • :desktop:dist builds cleanly under JDK 17.

⚠️ Two things only the maintainer can do

  1. Android regression test. libGDX was pinned to 1.9.14 with the note "Do not update to 1.10.0, it causes random freeze on my test tablet." That was an Android concern; the desktop fix needs the engine bump. Please re-verify the freeze does not occur on a device against 1.13.0 before releasing. (I could not test Android — no device/SDK.)
  2. Signing secrets. Native + notarized releases need your Apple Developer ID. The CI hooks and the exact secrets/steps are in tools/packaging/macos/SIGNING.md. Without them the build is simply unsigned (users run xattr -cr), same as today.

Open questions for you

  • OK to jump straight to libGDX 1.13.0, or would you prefer the smallest viable bump (1.11.0, the first with arm64 macOS natives)?
  • Should the release pipeline also ship an x86_64 / Universal2 build, or is arm64-only acceptable now? (Easy follow-up: add a macos-13 matrix leg.)

Pixel Wheels could not run natively on Apple Silicon Macs. Two independent
things kept it on x86_64:

1. The engine was pinned to libGDX 1.9.14, whose LWJGL 3.2.x backend ships
   only x86_64 macOS natives. A native arm64 JVM cannot load them, so the
   game required Rosetta 2 (and failed outright without it).

2. The macOS .app was cross-built on a Linux runner with packr, which only
   emits Intel binaries (libgdx/packr#198), bundling an x86_64 JRE 8.

Changes:

- Bump libGDX 1.9.14 -> 1.13.0 (LWJGL 3.3.x, arm64 macOS natives) and
  gdx-controllers 2.2.2 -> 2.2.4 (newer Jamepad with arm64 macOS natives).
- Pass -XstartOnFirstThread on macOS in every launch path (Makefile run/
  install targets and the gradle :desktop:run task); LWJGL3 hangs without it.
- Package the macOS app with jpackage instead of packr. jpackage builds a
  real native .app bundling a runtime image of the running JDK, so it must
  run on macOS. Added tools/packaging/create-macos-archive, a
  smoke-test-macos check, Makefile targets, and a build-macos CI job on
  macos-14 that reuses the desktop jar from the Linux job.
- Code signing + notarization are wired as optional, secret-gated steps
  (unsigned build still works); see tools/packaging/macos/SIGNING.md.

Verified on an Apple Silicon Mac: a native aarch64 JVM drives LWJGL3's GLFW
window and OpenGL context into PwGame.create(), and the jpackage .app
launcher is a native arm64 Mach-O. The asset pipeline is unchanged.

NOTE FOR MAINTAINER: the libGDX bump was originally avoided because 1.10.0
"causes random freeze on my test tablet". That Android regression must be
re-verified on-device against 1.13.0 before release. Signing/notarization
needs your Apple Developer ID secrets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jayvee6

jayvee6 commented Jun 13, 2026

Copy link
Copy Markdown
Author

Verified on Apple Silicon

Built and run on an M-series Mac (macOS 26.5, aarch64) with a Homebrew Temurin JDK 17. Three levels of proof, native (no Rosetta):

1. Engine bring-up — a native aarch64 JVM drives LWJGL3's GLFW window + OpenGL context all the way into PwGame.create():

[PwGame.logStartup] Pixel Wheels: version='1.0.1'
[PwGame.logStartup] Java: vendor='Homebrew' version='17.0.19'
[PwGame.logStartup] OS: name='Mac OS X' version='26.5.1' arch='aarch64'

On 1.9.14 this path throws UnsatisfiedLinkError at new Lwjgl3Application() and never reaches create(), because only x86_64 macOS natives are present.

2. jpackage bundlemake macos-archive produces a .app whose launcher is native arm64:

$ file "Pixel Wheels.app/Contents/MacOS/Pixel Wheels"
Mach-O 64-bit executable arm64

and make smoke-tests-macosPASS (native macOS app started on arm64, no native-library load errors).

3. Full render — with the asset pipeline built, the game runs and renders its main menu natively on arm64, no exceptions. (Screenshot to follow.)

Not covered here (needs the maintainer)

  • Android regression: the libGDX pin to 1.9.14 was due to a 1.10.0 "random freeze on my test tablet". That must be re-checked on a device against 1.13.0 before release — I have no Android device/SDK to test it.
  • Signing/notarization: needs your Apple Developer ID. Hooks + exact secrets/steps are in tools/packaging/macos/SIGNING.md; without them the build is simply unsigned (same as today).

Linux/Windows packaging is unchanged (still packr).

@jayvee6

jayvee6 commented Jun 13, 2026

Copy link
Copy Markdown
Author
Screenshot 2026-06-13 at 3 57 45 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant