Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ to `master` — that stays in sync with upstream via `git rebase upstream/master

## Current phase

**Phase 2runtime integration** (active). **Phase 1 #1–#5 complete** (2026-05-22).
**Phase 3.app bundle & distribution** (active). Phase 1 + Phase 2 complete (2026-05-25).

Check [open macOS issues](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues?q=label%3Amacos-port+is%3Aopen)
and **`MACOS_PORT.md` → “Next session — close #8”** for the handoff plan.
and **`MACOS_PORT.md` §Phase 3** for current status.

### Status snapshot (2026-05-25)

Expand Down
47 changes: 38 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -263,22 +263,34 @@ target_link_libraries(SimpleGraphic

if (APPLE)
set(PoB_HOST_TARGET pob-host)
# Bundle dir name is driven by OUTPUT_NAME, not BUNDLE_NAME. Keep in sync.
set(MACOS_APP_NAME "Path of Building-PoE2")
set(MACOS_FW_DEST "${MACOS_APP_NAME}.app/Contents/Frameworks")
add_executable(${PoB_HOST_TARGET} mac/host.cpp)
set_target_properties(${PoB_HOST_TARGET} PROPERTIES
OUTPUT_NAME "Path of Building-PoE2"
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME "${MACOS_APP_NAME}"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.braggpd.PathOfBuilding2"
MACOSX_BUNDLE_BUNDLE_VERSION "0.15.0"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.15.0"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist.in"
BUILD_RPATH "@executable_path;@loader_path"
INSTALL_RPATH "@executable_path/../Frameworks"
MACOSX_RPATH ON
)
target_link_libraries(${PoB_HOST_TARGET} PRIVATE SimpleGraphic)
set_target_properties(SimpleGraphic ${PoB_HOST_TARGET}
set_target_properties(SimpleGraphic
PROPERTIES
BUILD_RPATH "@executable_path;@loader_path"
INSTALL_RPATH "@executable_path;@loader_path"
INSTALL_RPATH "@loader_path"
MACOSX_RPATH ON
)
endif ()

if (APPLE)
install(TARGETS SimpleGraphic LIBRARY DESTINATION ".")
install(TARGETS ${PoB_HOST_TARGET} RUNTIME DESTINATION ".")
install(TARGETS ${PoB_HOST_TARGET} BUNDLE DESTINATION ".")
install(TARGETS SimpleGraphic LIBRARY DESTINATION "${MACOS_FW_DEST}")
else ()
install(TARGETS SimpleGraphic RUNTIME DESTINATION ".")
endif ()
Expand Down Expand Up @@ -336,7 +348,7 @@ if (APPLE)
set_target_properties(lcurl PROPERTIES PREFIX "" SUFFIX ".so")
endif ()
if (APPLE)
install(TARGETS lcurl LIBRARY DESTINATION ".")
install(TARGETS lcurl LIBRARY DESTINATION "${MACOS_FW_DEST}")
else ()
install(TARGETS lcurl RUNTIME DESTINATION ".")
endif ()
Expand All @@ -363,7 +375,7 @@ if (APPLE)
set_target_properties(lua-utf8 PROPERTIES PREFIX "" SUFFIX ".so")
endif ()
if (APPLE)
install(TARGETS lua-utf8 LIBRARY DESTINATION ".")
install(TARGETS lua-utf8 LIBRARY DESTINATION "${MACOS_FW_DEST}")
else ()
install(TARGETS lua-utf8 RUNTIME DESTINATION ".")
endif ()
Expand Down Expand Up @@ -411,7 +423,7 @@ if (APPLE)
set_target_properties(luasocket PROPERTIES PREFIX "" SUFFIX ".so")
endif ()
if (APPLE)
install(TARGETS luasocket LIBRARY DESTINATION ".")
install(TARGETS luasocket LIBRARY DESTINATION "${MACOS_FW_DEST}")
else ()
install(TARGETS luasocket RUNTIME DESTINATION ".")
endif ()
Expand All @@ -435,17 +447,18 @@ if (APPLE)
set_target_properties(lzip PROPERTIES PREFIX "" SUFFIX ".so")
endif ()
if (APPLE)
install(TARGETS lzip LIBRARY DESTINATION ".")
install(TARGETS lzip LIBRARY DESTINATION "${MACOS_FW_DEST}")
else ()
install(TARGETS lzip RUNTIME DESTINATION ".")
endif ()
install(FILES $<TARGET_RUNTIME_DLLS:lzip> DESTINATION ".")

if (APPLE)
# Lua modules sit in Contents/Frameworks/ next to SimpleGraphic; @loader_path finds siblings.
set_target_properties(lcurl luasocket lzip lua-utf8
PROPERTIES
BUILD_RPATH "@executable_path;@loader_path"
INSTALL_RPATH "@executable_path;@loader_path"
INSTALL_RPATH "@loader_path"
MACOSX_RPATH ON
)

Expand All @@ -456,11 +469,27 @@ if (APPLE)
set(MACOS_BUNDLE_SOCKET "${CMAKE_CURRENT_BINARY_DIR}/socket.so")
set(MACOS_BUNDLE_LZIP "${CMAKE_CURRENT_BINARY_DIR}/lzip.so")
set(MACOS_BUNDLE_UTF8 "${CMAKE_CURRENT_BINARY_DIR}/lua-utf8.so")
# MACOS_FW_DEST (the relative path) is substituted into the script at configure time;
# CMAKE_INSTALL_PREFIX is resolved at install time when the script runs.
set(_macos_bundle_script "${CMAKE_CURRENT_BINARY_DIR}/macos_bundle_runtime.cmake")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos_bundle_runtime.cmake"
"${_macos_bundle_script}"
@ONLY
)
install(SCRIPT "${_macos_bundle_script}")

# Ad-hoc sign the bundle so Gatekeeper allows it (#11).
# Users launching for the first time may need to right-click → Open.
# \${CMAKE_INSTALL_PREFIX} is escaped so it resolves at install time, not configure time.
install(CODE "
execute_process(
COMMAND codesign --deep --force --sign -
\"\${CMAKE_INSTALL_PREFIX}/${MACOS_APP_NAME}.app\"
RESULT_VARIABLE _codesign_result
)
if(NOT _codesign_result EQUAL 0)
message(WARNING \"codesign returned \${_codesign_result} — bundle may not be trusted\")
endif()
")
endif ()
11 changes: 9 additions & 2 deletions MACOS_PORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ First full Main load may take **1–3+ minutes** — do not kill early.

### Tasks

- [ ] **3.1** Add CPack/CMake `.app` bundle config in SimpleGraphic's `CMakeLists.txt` · [#10](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues/10)
- [x] **3.1** Add CPack/CMake `.app` bundle config in SimpleGraphic's `CMakeLists.txt` · [#10](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues/10)

```cmake
if (APPLE)
Expand All @@ -354,12 +354,13 @@ First full Main load may take **1–3+ minutes** — do not kill early.
endif()
```

- [ ] **3.2** Code signing strategy · [#11](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues/11)
- [x] **3.2** Code signing strategy · [#11](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues/11)

- Initial release: ad-hoc signing (`codesign --deep --force --sign -`)
Users see a first-launch warning; right-click → Open bypasses it.
- Long-term: shared Apple Developer ID ($99/yr) funded by donations.
Required for full notarization and no Gatekeeper warnings.
- Implemented as a CMake `install(CODE ...)` step after the bundle is assembled.

- [ ] **3.3** DMG packaging · [#12](https://github.com/braggpd/PathOfBuilding-SimpleGraphic/issues/12)

Expand Down Expand Up @@ -579,3 +580,9 @@ Target command: `brew install --cask path-of-building-2`
(5) Subscript `package.path` — added `runtime/lua/` so background threads can `require("xml")`.
Also: manifest.xml version parsing from C; `launch._isMacOS` platform flag; update check disabled.
**#8 success criteria met:** UI renders, passive tree loads, mouse tracks, text displays.
- **2026-05-25** — **Phase 3.1 + 3.2: `.app` bundle + ad-hoc signing.** `MACOSX_BUNDLE TRUE` on
`pob-host`; `mac/Info.plist.in` added; all dylibs install to `Contents/Frameworks/`; RPATHs
updated (`@executable_path/../Frameworks` for binary, `@loader_path` for dylibs);
`cmake/macos_bundle_runtime.cmake` copies vcpkg deps to `Frameworks/`; `codesign --deep
--force --sign -` added as final install step. `mac/host.cpp` auto-discovers `Launch.lua`
by walking up from the executable (handles both `.app` layout and flat dev layout).
17 changes: 10 additions & 7 deletions cmake/macos_bundle_runtime.cmake
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Installed by CMake install(SCRIPT) on APPLE — copies vcpkg runtime dylibs next to PoB binaries.
# CMAKE_INSTALL_PREFIX and paths below are substituted at configure time.
# Installed by CMake install(SCRIPT) on APPLE — copies vcpkg runtime dylibs into
# the .app bundle's Contents/Frameworks/. @...@ tokens are substituted at configure
# time; ${CMAKE_INSTALL_PREFIX} is resolved at install time.

set(_sg_lib_dir "@MACOS_BUNDLE_LIB_DIR@")
set(_install_prefix "${CMAKE_INSTALL_PREFIX}")
# @MACOS_FW_DEST@ = relative bundle path (configure-time constant).
# CMAKE_INSTALL_PREFIX = actual install root (install-time).
set(_fw_dir "${CMAKE_INSTALL_PREFIX}/@MACOS_FW_DEST@")
set(_built_libs
"@MACOS_BUNDLE_SG_DYLIB@"
"@MACOS_BUNDLE_LCURL@"
Expand Down Expand Up @@ -30,9 +33,9 @@ endif ()

foreach (_dep IN LISTS _resolved)
get_filename_component(_dep_name "${_dep}" NAME)
set(_dest "${_install_prefix}/${_dep_name}")
set(_dest "${_fw_dir}/${_dep_name}")
if (NOT EXISTS "${_dest}")
file(INSTALL "${_dep}" DESTINATION "${_install_prefix}" FOLLOW_SYMLINK_CHAIN)
file(INSTALL "${_dep}" DESTINATION "${_fw_dir}" FOLLOW_SYMLINK_CHAIN)
message(STATUS "Bundled runtime: ${_dep_name}")
endif ()
endforeach ()
Expand All @@ -44,8 +47,8 @@ foreach (_pair IN ITEMS
)
list(GET _pair 0 _egl_link)
list(GET _pair 1 _egl_target)
set(_egl_target_path "${_install_prefix}/${_egl_target}")
set(_egl_link_path "${_install_prefix}/${_egl_link}")
set(_egl_target_path "${_fw_dir}/${_egl_target}")
set(_egl_link_path "${_fw_dir}/${_egl_link}")
if (EXISTS "${_egl_target_path}" AND NOT EXISTS "${_egl_link_path}")
file(CREATE_LINK "${_egl_target}" "${_egl_link_path}" SYMBOLIC)
message(STATUS "EGL alias: ${_egl_link} -> ${_egl_target}")
Expand Down
25 changes: 25 additions & 0 deletions mac/Info.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Path of Building-PoE2</string>
<key>CFBundleIdentifier</key>
<string>com.braggpd.PathOfBuilding2</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>LSMinimumSystemVersion</key>
<string>13.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
39 changes: 38 additions & 1 deletion mac/host.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
// Path of Building 2 — macOS host launcher (dev / runtime-macos)
// Path of Building 2 — macOS host launcher
// Loads libSimpleGraphic.dylib by linking at build time; same role as
// Path of Building-PoE2.exe + SimpleGraphic.dll on Windows.

#include <cstdio>
#include <limits.h>
#include <mach-o/dyld.h>
#include <string>

extern "C" int RunLuaFileAsWin(int argc, char** argv);

// Walk up from the executable looking for src/Launch.lua.
// Works for both .app bundle layout (exe in Contents/MacOS/) and
// flat runtime-macos/ dev layout (exe alongside src/).
static std::string findDefaultScript()
{
char raw[PATH_MAX];
uint32_t size = sizeof(raw);
if (_NSGetExecutablePath(raw, &size) != 0) return {};
char resolved[PATH_MAX];
if (!realpath(raw, resolved)) return {};

std::string dir(resolved);
for (int i = 0; i < 6; ++i) {
auto slash = dir.rfind('/');
if (slash == std::string::npos) break;
dir = dir.substr(0, slash);
std::string candidate = dir + "/src/Launch.lua";
if (FILE* f = fopen(candidate.c_str(), "r")) {
fclose(f);
return candidate;
}
}
return {};
}

int main(int argc, char** argv)
{
if (argc < 2) {
std::string script = findDefaultScript();
if (!script.empty()) {
char* newArgv[] = { argv[0], script.data(), nullptr };
return RunLuaFileAsWin(2, newArgv);
}
}
return RunLuaFileAsWin(argc, argv);
}
4 changes: 4 additions & 0 deletions ui_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2601,7 +2601,11 @@ int ui_main_c::InitAPI(lua_State* L)
old_path += ";lua/?.lua;lua/?/init.lua";
#if __APPLE__ && __MACH__
// Dev layout: host in runtime-macos/, shared scripts in ../runtime/lua/ (see PoB #8).
// .app bundle layout: executable is in Contents/MacOS/; go 4 levels up to reach the
// directory containing the .app, then find runtime/lua/.
auto runtime_lua_dir = (ui->sys->basePath / ".." / "runtime" / "lua").lexically_normal();
if (!std::filesystem::exists(runtime_lua_dir))
runtime_lua_dir = (ui->sys->basePath / ".." / ".." / ".." / ".." / "runtime" / "lua").lexically_normal();
old_path += ";" + (runtime_lua_dir / "?.lua").generic_string();
old_path += ";" + (runtime_lua_dir / "?" / "init.lua").generic_string();
#endif
Expand Down
8 changes: 7 additions & 1 deletion ui_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,9 @@ void ui_main_c::ScriptInit()
lua_getfield(L, -1, "preload");
for (const auto& ext : kExts) {
auto soPath = (sys->basePath / ext.file).lexically_normal();
// .app bundle layout: .so files are in Contents/Frameworks/, not Contents/MacOS/.
if (!std::filesystem::exists(soPath))
soPath = (sys->basePath / ".." / "Frameworks" / ext.file).lexically_normal();
void* handle = dlopen(soPath.generic_u8string().c_str(), RTLD_NOW | RTLD_LOCAL);
if (handle) {
auto fn = reinterpret_cast<lua_CFunction>(dlsym(handle, ext.sym));
Expand Down Expand Up @@ -1631,8 +1634,11 @@ void ui_main_c::ScriptInit()

// sha1.common has no require(); safe before LIGHTFUNC install. (#8)
{
auto const commonLua =
auto commonLua =
(sys->basePath / ".." / "runtime" / "lua" / "sha1" / "common.lua").lexically_normal();
// .app bundle layout fallback: executable is in Contents/MacOS/, so go 4 levels up.
if (!std::filesystem::exists(commonLua))
commonLua = (sys->basePath / ".." / ".." / ".." / ".." / "runtime" / "lua" / "sha1" / "common.lua").lexically_normal();
if (!mac_preload_lua_file(L, sys, "sha1.common", commonLua)) {
sys->con->Printf("Warning: macOS sha1.common preload failed.\n");
}
Expand Down