Skip to content

Add macOS (arm64) support: build fixes, shared ANGLE, and native launcher - #111

Open
jucardi wants to merge 8 commits into
PathOfBuildingCommunity:masterfrom
jucardi:feat/macos-build
Open

Add macOS (arm64) support: build fixes, shared ANGLE, and native launcher#111
jucardi wants to merge 8 commits into
PathOfBuildingCommunity:masterfrom
jucardi:feat/macos-build

Conversation

@jucardi

@jucardi jucardi commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Makes SimpleGraphic build and run on macOS (arm64) — the first working build of the __APPLE__ code paths that already exist in-tree — and adds the missing macOS launcher executable (counterpart of the Windows frontend).

Build fixes (all platform-guarded or provably Windows-neutral)

  • CMake: luasocket now compiles usocket.c on POSIX (wsocket.c/ws2_32 are WIN32-guarded); Lua modules get PREFIX "" SUFFIX ".so" on non-Windows so require finds them; sys_macos.mm is no longer clobbered out of the platform sources list (setlist(APPEND) bug); zstd link target selected by availability (libzstd_shared vs libzstd — static triplets only build the latter); install(TARGETS … LIBRARY DESTINATION) added for dylibs.
  • LuaJIT vcpkg port: the vendored configure shim now carries its executable bit (chmod +x, was 644 — never noticed because Windows CI takes the nmake path); the make-path install left bin/luajit as a self-referential symlink (the versioned binary it points at is never installed) — the port now copies the real binary from the build tree; the wide-CRT patch's POSIX fallback _lua_getenvcopy was strdup(getenv(name)) with no NULL check — os.getenv("UNSET") segfaulted; now NULL-guarded (the Windows path already was). This last one affects any future Linux build too.
  • C++ portability: missing includes (base64.c, r_texture.cpp, guarded); IndexUTF8ToUTF32 moved out of an #ifdef _WIN32 block it never belonged in (pure byte-decoding, three cross-platform call sites); std::min type mismatch (4ullsize_t(4), no-op on Windows); Sleep(1)sys->Sleep(1) in r_main.cppdisclosure: this is the one unguarded shared-path edit; it switches Windows from raw ::Sleep to the codebase's own sys abstraction (std::this_thread::sleep_for), the same pattern used at every other call site.

ANGLE as shared libraries on macOS

GLFW loads EGL at runtime via dlopen("libEGL.dylib") — the macOS analogue of the libEGL.dll/libGLESv2.dll pair shipped on Windows. A vcpkg overlay triplet (APPLE-only) keeps every dependency static except ANGLE; install aliases the ANGLE dylibs to the canonical names GLFW expects, with names derived from the build (configure fails loudly if the port's naming drifts).

macOS launcher (pob)

New macos/main.mm + CMake target: resolves the Lua entry script (CLI arg → POB_SCRIPT_PATH → compile-time default), sets exe-relative LUA_PATH/LUA_CPATH (LuaJIT's macOS defaults have no exe-relative entries, unlike Windows), captures a pob:// launch URL via Apple Events (best-effort at launch; delivery to a running instance not implemented), and calls the exported RunLuaFileAsWin. Consumed by PathOfBuildingCommunity/PathOfBuilding#10159.

Submodules: stock, no pointer changes

This PR builds with the unmodified upstream submodules — no pointer bumps, nothing to fetch from forks. Two upstream issues surfaced on macOS are handled with small, guarded build-level workarounds in this repo's CMake (both marked with removal notes):

  • luautf8 uses INT_MAX without including <limits.h> → force-include on non-Windows. Proper fix PR'd upstream: starwing/luautf8#62.
  • Lua-cURLv3's local Lua 5.2-compat luaL_setfuncs shim collides with static LuaJIT's export → the shim is renamed via a compile definition on non-Windows. Proper fix PR'd upstream: Lua-cURL/Lua-cURLv3#197.

If/when the upstream PRs merge, bump the submodules and drop the two workaround blocks.

Testing

  • Full build green: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 -DVCPKG_TARGET_TRIPLET=arm64-osx, then build + install.
  • Path of Building runs natively end-to-end on Apple Silicon: renderer init ~160 ms, full app startup ~2.2 s, passive tree + item databases loaded, ANGLE-on-Metal rendering.
  • Windows should be re-verified by CI on this branch (all changes are guarded; the two disclosed items above are behavior-equivalent).

🤖 Generated with Claude Code

jucardi and others added 7 commits August 5, 2026 19:35
…arget

First-ever compile of SimpleGraphic + native Lua modules for arm64-osx.
Bounded, platform-guarded fixes surfaced by the build, each upstreamable:

- CMakeLists.txt: build luasocket from usocket.c on non-Windows, guard
  the wsock32/ws2_32 link libs and LUA_BUILD_AS_DLL define to WIN32, name
  the four Lua modules "<name>.so" (not "lib<name>.dylib") so require()
  resolves them, select zstd::libzstd_shared vs zstd::libzstd depending on
  which target the triplet actually provides, add LIBRARY DESTINATION "."
  to every install(TARGETS ...) (Unix .dylib/.so are LIBRARY artifacts,
  not RUNTIME, so they were landing in dist/lib/ instead of dist/), and
  fix a bug where the WIN32/else block for SIMPLEGRAPHIC_PLATFORM_SOURCES
  overwrote rather than appended to the list, silently dropping
  sys_macos.mm on APPLE.
- cmake/FindLuaJIT.cmake: extend the vcpkg-branch search paths/names for
  where vcpkg installs LuaJIT on macOS (include/luajit-2.1,
  libluajit-5.1.a).
- vcpkg-ports/ports/luajit/2026-07-20_1/configure: add the executable bit
  (was committed 100644, so the port's ./configure failed with "Permission
  denied" on POSIX; Windows CI never runs this script).
- vcpkg-ports/ports/luajit/2026-07-20_1/portfile.cmake: on non-Windows,
  copy the real luajit binary from the buildtree instead of the
  self-referential symlink make install leaves behind, so
  vcpkg_copy_tools can find it.
- engine/common/base64.h, base64.c: add missing <stddef.h>/<string.h>.
- engine/render/r_texture.cpp: add missing <thread>/<algorithm>; fix
  std::min(4ull, ...) template deduction failures where size_t is
  `unsigned long` (macOS) rather than `unsigned long long`.
- engine/render/r_main.cpp: replace a bare Windows Sleep() call with the
  existing sys->Sleep() cross-platform abstraction already used
  elsewhere in this file.
- engine/common/common.cpp: move IndexUTF8ToUTF32 out of the #ifdef
  _WIN32 block it was accidentally nested in - it has no Windows
  dependencies and is called from cross-platform code, but was
  undefined (and unlinkable) on non-Windows platforms.
- libs/luautf8 (submodule commit): add missing <limits.h> for INT_MAX.
- libs/Lua-cURLv3 (submodule commit): skip the luaL_setfuncs compat
  shim when building against LuaJIT, which already provides it -
  avoids a duplicate-symbol link error under static linkage.

Verified: build/dist/ contains libSimpleGraphic.dylib, lcurl.so,
lua-utf8.so, socket.so, lzip.so, all Mach-O arm64.
Adds macos/main.mm (pob CLI launcher) and the pob CMake target. Resolves
the Lua entry script (CLI arg / POB_SCRIPT_PATH / compile-time default),
sets up LuaJIT search paths relative to the executable, captures a
pob:// launch URL via Apple Events, and hands off to
SimpleGraphic's RunLuaFileAsWin.

Discovery fix: kInternetEventClass/kAEGetURL are declared in
ApplicationServices/HIServices/InternetConfig.h, not pulled in
transitively by CoreServices.h on this SDK; added the explicit
ApplicationServices import.
GLFW's Cocoa EGL backend loads EGL/GLESv2 at runtime via
dlopen("libEGL.dylib")/dlopen("libGLESv2.dylib") (egl_context.c), mirroring
the libEGL.dll/libGLESv2.dll pair shipped alongside the Windows build. Under
the arm64-osx triplet's static linkage, ANGLE was compiled directly into
libSimpleGraphic.dylib with no standalone dylib for GLFW to find, so window
creation failed at runtime ("Could not create window, EGL: Library not
found") followed by a segfault.

Adds an overlay triplet (triplets/arm64-osx.cmake, same name so every other
port cache-hits) that forces dynamic linkage for the angle port only. Wires
the overlay into CMakeLists.txt via VCPKG_OVERLAY_TRIPLETS, sets
SimpleGraphic's install rpath to @loader_path, and installs the built ANGLE
dylibs into the same directory as libSimpleGraphic.dylib, aliased to the
canonical libEGL.dylib/libGLESv2.dylib names GLFW dlopens (the vcpkg build
names them liblibEGL_angle.dylib/liblibGLESv2_angle.dylib).

Windows/Linux unaffected: the overlay directory defines no triplet for those
platforms.
The non-Windows fallback in pob-wide-crt.patch defined
_lua_getenvcopy(name) as strdup(getenv(name)) unconditionally. getenv()
returns NULL for an unset variable, and strdup(NULL) is undefined
behavior — it segfaulted inside lj_cf_os_getenv (os.getenv from Lua) the
first time PoB's Lua code queried an environment variable that wasn't
set. The Windows path already guarded against this; the POSIX macro did
not. Changed to (getenv(name) ? strdup(getenv(name)) : NULL), matching
the Windows path's NULL-safety.

Upstreamable: yes, trivial NULL-safety fix; submodule/port-patch scope
only, no behavior change when the variable is actually set.
…install

- Wrap VCPKG_OVERLAY_TRIPLETS in APPLE guard to avoid spurious triplet overrides
- Extract EGL/GLESv2 dylib names dynamically from glob results instead of hardcoding
- Add validation that both required dylibs are present; fail early if missing
- Remove old symlinks before creating new ones for idempotent install
- Update comment to reflect @rpath-based install names

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revert the luautf8 and Lua-cURLv3 submodule pointers to their upstream
commits and replace the in-submodule fixes with equivalents in this
repo's build:
- lua-utf8: force-include <limits.h> on non-Windows (INT_MAX use)
- lcurl: rename its local Lua 5.2-compat luaL_setfuncs shim on
  non-Windows to avoid a duplicate symbol against static LuaJIT

The proper fixes are PR'd upstream (starwing/luautf8#62,
Lua-cURL/Lua-cURLv3#197); once merged, bump the submodules and drop
these workarounds.
…ildingCommunity#102

Two fixes from nelkonandparker's macOS port (PathOfBuildingCommunity#102) that this branch
lacked:
- Scale cursor coordinates by the framebuffer/window-size ratio so
  clicks and tooltips align on HiDPI (Retina) displays; the ratio is
  1.0 on Windows, leaving its behavior unchanged.
- Explicitly select ANGLE's Metal backend on macOS (GLFW init hint,
  APPLE-guarded) and request the port's metal feature for osx in the
  vcpkg manifest.

Co-authored-by: nelkonandparker <nelkonandparker@users.noreply.github.com>
@jucardi

jucardi commented Aug 7, 2026

Copy link
Copy Markdown
Author

Following up on the maintainer question about how this relates to the earlier macOS efforts (#101/#102) and #107 — I did a hunk-by-hunk comparison. Summary:

Independently identical fixes (good sign for both): the SIMPLEGRAPHIC_PLATFORM_SOURCES set()-clobber fix, the base64.c/common.cpp include/#ifdef fixes, the r_texture.cpp std::min type fix, and the LuaJIT getenv NULL-guard are byte-for-byte the same in #102 and this PR — arrived at independently.

What this PR has that #102 doesn't:

What #102 had that this PR lacked — now ported, with credit to @nelkonandparker (commit 8102dab):

  • Retina cursor-coordinate scaling (sys_video.cpp) — clicks/tooltips align on HiDPI displays; ratio is 1.0 on Windows so its behavior is unchanged.
  • Explicit ANGLE Metal backend selection on macOS (GLFW init hint + vcpkg metal feature for osx).

Not ported (judgment calls, happy to discuss): #102's Contents/Resources base-path redirect (this PR's launcher handles pathing differently — flat bundle mirroring the Windows runtime/ layout) and its LuaJIT unwind-mode patch (EXTERNALINTERNAL; possibly still worth evaluating separately).

Re #107: its Windows-CI-as-release-gate idea is complementary — this repo's existing Windows CI running on this PR serves the same purpose, and I'm happy to add a macOS build job using GitHub's free arm64 runners so both platforms are machine-checked without any maintainer needing a Mac.

🤖 Generated with Claude Code

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