Session date: May 11, 2026
Branch: package
Agent scope: macOS .app bundle, runtime staging, asset/binary discovery, Linux GUI baseline.
This pass established the baseline macOS bundle and install-tree structure for spatialroot_gui. Prior to this work, the GUI had no install rules and all path resolution assumed a repo-root developer build. After this pass:
spatialroot_guiinstalls as a proper macOS.appbundle viacmake --install.- Speaker layout presets are staged into
Contents/Resources/on macOS. cult-transcoderis staged intoContents/Resources/bin/and is discoverable from the packaged bundle without env vars.- Layout and transcoder lookup is package-relative first, repo-root last.
- Linux GUI packaging is pinned to X11/XWayland.
Notarization, DMG creation, CPack polish, and Homebrew formulae were not attempted and remain future work.
process: stage app ad-hoc sign app create DMG with hdiutil verify codesign inside mounted DMG upload to GitHub release download and test
| File | Change |
|---|---|
| CMakeLists.txt (line 11) | Added include(GNUInstallDirs). Added SPATIALROOT_INSTALL_RESOURCE_ROOT and macOS bundle destination variables (SPATIALROOT_MACOS_APP_BUNDLE_NAME, SPATIALROOT_MACOS_APP_RESOURCES_DESTINATION). |
| source/gui/imgui/CMakeLists.txt (line 13) | Set MACOSX_BUNDLE TRUE + bundle metadata on spatialroot_gui. Added install(TARGETS … BUNDLE …) for macOS and RUNTIME for Linux. Added install(DIRECTORY … speaker_layouts …) for both platforms. Added Linux GLFW X11/Wayland pin. Added macOS app icon resource and set MACOSX_BUNDLE_ICON_FILE to the renamed icon. |
| source/gui/imgui/resources/SpatialRootApp.icns | Renamed macOS app icon to force Finder cache refresh (same artwork as previous SpatialRoot.icns). |
| source/gui/imgui/cmake/Info.plist.in | New file. Minimal macOS Info.plist template: bundle ID com.cultdsp.spatialroot, LSMinimumSystemVersion 11.0, NSHighResolutionCapable. CMake substitutes MACOSX_BUNDLE_* variables at configure time. |
| source/gui/imgui/src/App.cpp (line 124) | Added currentExecutablePath(), executableDirectory(), macBundleResourcesDirectory(), installPrefixFromExecutable(), layoutPackagedSubpath(). Updated layout search to: SPATIALROOT_ASSET_ROOT → bundle Contents/Resources → install-prefix share/spatialroot → executable-relative packaged path → repo-root fallback. Updated cult-transcoder search to: SPATIALROOT_CULT_TRANSCODER → executable-relative packaged locations → bundle Contents/Resources/bin → build-tree/dev fallbacks. |
| source/gui/imgui/src/main.cpp (line 13) | Updated usage comment to reflect package-relative lookup and repo-root fallback modes. |
| internal/cult_transcoder/CMakeLists.txt (line 258) | Added install(TARGETS cult-transcoder …) rules for both macOS (into Contents/Resources/bin) and Linux/other (into ${CMAKE_INSTALL_BINDIR}), under SpatialRootRuntime component. |
| internal/cult-allolib/external/rtaudio/CMakeLists.txt (line 343) | Suppressed or no-op'd unwanted rtaudio install rules that were polluting a full cmake --install run with vendored SDK/doc payloads. |
Install command:
cmake --install build --prefix <dir> --component SpatialRootRuntime
Resulting layout:
<prefix>/
└── Spatial Root.app/
└── Contents/
├── MacOS/
│ └── Spatial Root ← GUI executable
├── Info.plist ← generated from Info.plist.in
└── Resources/
├── SpatialRootApp.icns ← macOS Finder/Dock app icon
├── speaker_layouts/ ← all JSON layout presets
│ ├── stereo.json
│ ├── quad.json
│ └── ...
└── bin/
└── cult-transcoder ← transcoder binary
Linux install tree (<prefix>/bin/cult-transcoder, <prefix>/share/spatialroot/speaker_layouts/) is wired in but not validated on this pass.
SPATIALROOT_ASSET_ROOTenv var (directory containingsource/speaker_layouts/)- macOS bundle
Contents/Resources/speaker_layouts/ - Install-prefix
<prefix>/share/spatialroot/speaker_layouts/ - Executable-relative packaged fallback
- Repo-root
source/speaker_layouts/(dev fallback)
SPATIALROOT_CULT_TRANSCODERenv var- Executable-relative packaged locations
- macOS bundle
Contents/Resources/bin/cult-transcoder - Build-tree / dev fallbacks (
build/internal/cult_transcoder/cult-transcoder)
GLFW is forced to X11 on Linux (GLFW_BUILD_X11=ON, GLFW_BUILD_WAYLAND=OFF). The initial release target is X11/XWayland. Wayland package dependencies were not added to init.sh. This decision is intentional and should not be reversed without also validating a full Wayland release path and updating init.sh.
Packaging is ahead of signing. The current practical guidance is:
- Sigstore /
cosignis useful for artifact provenance and Linux-style verification, but it does not satisfy macOS Gatekeeper or Windows SmartScreen by itself. - macOS distribution that avoids Gatekeeper warnings still requires Apple-trusted signing and notarization, which in practice means an Apple Developer account.
- Windows OSS-friendly signing options may be worth evaluating separately, for example SignPath Foundation or comparable Authenticode sponsorship paths.
Treat signing/notarization as a release-engineering phase on top of the current bundle/install work, not as part of the core runtime-path audit.
| Command | Result |
|---|---|
cmake -S . -B build -DSPATIALROOT_BUILD_GUI=ON |
✅ passed |
cmake --build build --target spatialroot_gui cult-transcoder --parallel 8 |
✅ passed |
cmake --install build --prefix /private/tmp/spatialroot-stage.x8TkcZ --component SpatialRootRuntime |
✅ passed |
test -f '.../Spatial Root.app/Contents/Resources/speaker_layouts/stereo.json' |
✅ passed |
test -x '.../Spatial Root.app/Contents/Resources/bin/cult-transcoder' |
✅ passed |
test -f 'source/speaker_layouts/stereo.json' (source tree) |
✅ passed |
test -x 'build/internal/cult_transcoder/cult-transcoder' (build tree) |
✅ passed |
'<stage>/Spatial Root.app/Contents/MacOS/Spatial Root' --help from /private/tmp |
✅ passed (exited 1 before GLFW/App logging — expected for --help path without display) |
| Interactive GUI launch from staged bundle | open unavailable in this environment (kLSServerCommunicationErr -10822); staged files and lookup paths are correct |
| Item | Status |
|---|---|
Full cmake --install build --prefix <dir> (no component filter) stages vendored third-party SDK/doc payloads |
Known — use --component SpatialRootRuntime for now; a later pass should prune dependency install rules before CPack work |
| Interactive GUI launch outside repo root | Not validated in this environment — staged files are correct, human verification needed |
spatialroot_spatial_render packaging |
Out of scope for this pass; offline-render UI is hidden and not a current blocker |
| Notarization, DMG, CPack, Homebrew | Not attempted — future distribution pass |
| macOS Dock icon | Resolved in bundle metadata; Finder may cache old icons. If generic icon persists, restage to a new path or rename the app bundle. |
source/gui/imgui/cmake/Info.plist.in is a CMake-template plist (not a static file). CMake substitutes ${MACOSX_BUNDLE_EXECUTABLE_NAME}, ${MACOSX_BUNDLE_BUNDLE_NAME}, ${MACOSX_BUNDLE_SHORT_VERSION_STRING}, and ${MACOSX_BUNDLE_BUNDLE_VERSION} from the set_target_properties(spatialroot_gui … MACOSX_BUNDLE_*) values in the GUI CMakeLists.txt. Bundle version comes from PROJECT_VERSION (currently 0.1.0).
The bundle icon is staged via MACOSX_BUNDLE_ICON_FILE and the icon file is explicitly added as a bundle resource.
Current icon asset:
CMake wiring:
- source/gui/imgui/CMakeLists.txt sets
MACOSX_BUNDLE_ICON_FILEtoSpatialRootApp.icnsand adds the icon toMACOSX_PACKAGE_LOCATIONResources.
Finder caches app icons aggressively. If the staged .app still shows a generic icon:
- Restage to a new path and copy the app into a new folder.
- Rename the app bundle (e.g.,
Spatial Root 2.app) before checking Finder/Dock. - As a last resort, re-register LaunchServices for the bundle.
All install rules use COMPONENT SpatialRootRuntime. This isolates the runtime from vendored build-only payloads and makes the staged tree reproducible. Always use --component SpatialRootRuntime when staging for packaging.
include(GNUInstallDirs) was added to the root CMakeLists.txt. This provides CMAKE_INSTALL_BINDIR (bin), CMAKE_INSTALL_DATADIR (share), and CMAKE_INSTALL_LIBDIR (lib) — used by the install rules in the GUI and cult-transcoder CMakeLists files.