From f294799b22fe73ef8ef8ee30f110bca987cd5de6 Mon Sep 17 00:00:00 2001 From: nacimoualla Date: Sat, 29 Aug 2026 12:10:13 +0100 Subject: [PATCH] macOS: add KytyPS5.app bundle packaging Add a proper macOS application bundle when the launcher is built. launcher becomes KytyPS5.app (CFBundleIdentifier org.kytyps5.kytyps5) with KytyPS5 as the executable, and kyty_emulator is installed as a helper inside Contents/MacOS so QCoreApplication::applicationDirPath() finds it. MoltenVK is auto-discovered via SDL_GetBasePath() in that directory, so no SDL_VULKAN_LIBRARY is required. - src/launcher/Info.plist.in: minimal bundle plist (version from PROJECT_VERSION, LSMinimumSystemVersion 13.0). - src/launcher/CMakeLists.txt: set MACOSX_BUNDLE, BUNDLE DESTINATION, install helper, re-sign helper with JIT entitlements, and invoke macdeployqt when available. Keep flat kyty_emulator for CLI. - .github/workflows/build.yml: bundle MoltenVK into KytyPS5.app/Contents/MacOS when present, verify bundle artifacts (file/lipo/codesign), and handle bundle paths in the release zip. - README.md: document bundle double-click and MoltenVK locations. --- .github/workflows/build.yml | 37 ++++++++++++++++++++++++--- README.md | 15 ++++++++--- src/launcher/CMakeLists.txt | 50 +++++++++++++++++++++++++++++++++++-- src/launcher/Info.plist.in | 17 +++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 src/launcher/Info.plist.in diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76a2b3137..7ea61c216 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -218,18 +218,39 @@ jobs: _Build/macos/install/libMoltenVK.dylib install -m 644 "$package/LICENSE" _Build/macos/install/LICENSE.MoltenVK codesign --force --sign - --timestamp=none _Build/macos/install/libMoltenVK.dylib + # Also bundle inside KytyPS5.app when the launcher bundle is produced. + if [[ -d _Build/macos/install/KytyPS5.app ]]; then + install -m 755 \ + "$package/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" \ + _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib + install -m 644 "$package/LICENSE" _Build/macos/install/KytyPS5.app/Contents/Resources/LICENSE.MoltenVK 2>/dev/null || \ + install -m 644 "$package/LICENSE" _Build/macos/install/KytyPS5.app/Contents/MacOS/LICENSE.MoltenVK + codesign --force --sign - --timestamp=none _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib + fi - name: Verify artifacts shell: bash run: | - file _Build/macos/install/launcher file _Build/macos/install/kyty_emulator file _Build/macos/install/libMoltenVK.dylib - lipo _Build/macos/install/launcher -verify_arch x86_64 lipo _Build/macos/install/kyty_emulator -verify_arch x86_64 lipo _Build/macos/install/libMoltenVK.dylib -verify_arch x86_64 codesign --verify --strict _Build/macos/install/kyty_emulator codesign --verify --strict _Build/macos/install/libMoltenVK.dylib + if [[ -d _Build/macos/install/KytyPS5.app ]]; then + file _Build/macos/install/KytyPS5.app/Contents/MacOS/KytyPS5 + file _Build/macos/install/KytyPS5.app/Contents/MacOS/kyty_emulator + lipo _Build/macos/install/KytyPS5.app/Contents/MacOS/KytyPS5 -verify_arch x86_64 + lipo _Build/macos/install/KytyPS5.app/Contents/MacOS/kyty_emulator -verify_arch x86_64 + codesign --verify --strict _Build/macos/install/KytyPS5.app/Contents/MacOS/kyty_emulator + if [[ -f _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib ]]; then + lipo _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib -verify_arch x86_64 + codesign --verify --strict _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib + fi + else + file _Build/macos/install/launcher + lipo _Build/macos/install/launcher -verify_arch x86_64 + fi - name: Upload macOS artifacts uses: actions/upload-artifact@v4 @@ -426,8 +447,18 @@ jobs: run: | macos_dir="artifacts/KytyPS5-macOS-x86_64" test -d "$macos_dir" + if [[ -d "$macos_dir/KytyPS5.app" ]]; then + chmod a+x \ + "$macos_dir/KytyPS5.app/Contents/MacOS/KytyPS5" \ + "$macos_dir/KytyPS5.app/Contents/MacOS/kyty_emulator" || true + if [[ -f "$macos_dir/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib" ]]; then + chmod a+x "$macos_dir/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib" + fi + fi + if [[ -f "$macos_dir/launcher" ]]; then + chmod a+x "$macos_dir/launcher" + fi chmod a+x \ - "$macos_dir/launcher" \ "$macos_dir/kyty_emulator" \ "$macos_dir/libMoltenVK.dylib" ( diff --git a/README.md b/README.md index 3adabc2d4..5cb26d990 100644 --- a/README.md +++ b/README.md @@ -220,17 +220,22 @@ cmake --install _Build/macos --prefix _Build/macos/install ``` The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated -guest code; no manual signing step is required. +guest code; no manual signing step is required. When the launcher is built, the install +also produces `_Build/macos/install/KytyPS5.app` — double-click to launch the GUI. +A flat `kyty_emulator` is kept for CLI usage. Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the [MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy -`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` and ad-hoc sign it: +`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` (and, for +the bundle, into `KytyPS5.app/Contents/MacOS/`) and ad-hoc sign it: ```bash codesign --force --sign - _Build/macos/install/libMoltenVK.dylib +# For the bundle (if present): +codesign --force --sign - _Build/macos/install/KytyPS5.app/Contents/MacOS/libMoltenVK.dylib ``` -Release archives already include a signed `libMoltenVK.dylib`. +Release archives already include a signed `libMoltenVK.dylib` (both flat and inside the bundle). ### Regression tests @@ -274,6 +279,10 @@ To use the graphical launcher: ./_Build/linux/install/launcher ``` +```bash +open _Build/macos/install/KytyPS5.app # or double-click in Finder +``` + On first launch, add one or more game folders in the global settings. The launcher searches those folders recursively for game directories containing `eboot.bin`. Select a detected game and run it from the game list. diff --git a/src/launcher/CMakeLists.txt b/src/launcher/CMakeLists.txt index 45b2bbbf6..9a18959a3 100644 --- a/src/launcher/CMakeLists.txt +++ b/src/launcher/CMakeLists.txt @@ -65,11 +65,57 @@ if(WIN32) ) endif() -set(launcher_name "launcher") +if(APPLE) + set(launcher_name "KytyPS5") +else() + set(launcher_name "launcher") +endif() set_target_properties(launcher PROPERTIES OUTPUT_NAME ${launcher_name}) -install(TARGETS launcher DESTINATION .) +if(APPLE) + # macOS bundle: KytyPS5.app with launcher as main executable and + # kyty_emulator as helper inside Contents/MacOS. MoltenVK is found + # via SDL_GetBasePath() + libMoltenVK.dylib, so placing it next to + # the executables (Contents/MacOS) is sufficient; no rpath fixup needed + # for the non-Qt case. Qt deployment (Frameworks + plugins) is handled + # by macdeployqt when a Qt installation is present. + set_target_properties(launcher PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in" + MACOSX_BUNDLE_BUNDLE_NAME "KytyPS5" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.kytyps5.kytyps5" + MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}" + ) + install(TARGETS launcher BUNDLE DESTINATION .) + install(TARGETS kyty_emulator DESTINATION "KytyPS5.app/Contents/MacOS") + # Ad-hoc sign the helper with JIT entitlements inside the bundle (mirrors + # the top-level kyty_emulator signing). The bundle itself will be + # re-signed after Qt/MoltenVK deployment. + install(CODE " + execute_process(COMMAND codesign -s - --force --options runtime + --entitlements \"${KYTY_SOURCE_DIR}/macos_jit.entitlements\" + \"\${CMAKE_INSTALL_PREFIX}/KytyPS5.app/Contents/MacOS/kyty_emulator\" + RESULT_VARIABLE _kyty_sign_result) + if(NOT _kyty_sign_result EQUAL 0) + message(WARNING \"Failed to codesign bundled kyty_emulator\") + endif() + ") + # Deploy Qt frameworks/plugins into the bundle when macdeployqt is available. + find_program(KYTY_MACDEPLOYQT NAMES macdeployqt PATHS "${Qt6_DIR}/../../../bin" NO_DEFAULT_PATH) + if(KYTY_MACDEPLOYQT) + install(CODE " + execute_process(COMMAND \"${KYTY_MACDEPLOYQT}\" \"\${CMAKE_INSTALL_PREFIX}/KytyPS5.app\" + RESULT_VARIABLE _kyty_deploy_result) + if(NOT _kyty_deploy_result EQUAL 0) + message(WARNING \"macdeployqt failed with \${_kyty_deploy_result}\") + endif() + ") + endif() +else() + install(TARGETS launcher DESTINATION .) +endif() if(WIN32) install(CODE " diff --git a/src/launcher/Info.plist.in b/src/launcher/Info.plist.in new file mode 100644 index 000000000..74f2e05a3 --- /dev/null +++ b/src/launcher/Info.plist.in @@ -0,0 +1,17 @@ + + + + + CFBundleNameKytyPS5 + CFBundleDisplayNameKytyPS5 + CFBundleIdentifierorg.kytyps5.kytyps5 + CFBundleVersion@PROJECT_VERSION@ + CFBundleShortVersionString@PROJECT_VERSION@ + CFBundlePackageTypeAPPL + CFBundleSignature???? + CFBundleExecutableKytyPS5 + LSMinimumSystemVersion13.0 + NSHighResolutionCapable + NSRequiresAquaSystemAppearance + +