Files
KytyPS5/src/launcher/CMakeLists.txt
T
nacimoualla 4e8e335b7b macOS: full support — MoltenVK auto-discovery, portability fix, and KytyPS5.app bundle (#394)
* macOS: auto-discover bundled MoltenVK on startup

On macOS the Vulkan loader comes from MoltenVK. SDL's default
probe only searches system paths, so a bundled libMoltenVK.dylib
next to the executable is invisible under the hardened runtime.

Probe it explicitly via SDL_GetBasePath() before falling back to
SDL's default search. Respect SDL_VULKAN_LIBRARY overrides and
log which loader is used. Update README to reflect that no
environment variable is required for bundled builds.

Co-Authored-By: internal-model

* window: fix Vulkan portability enumeration on macOS

MoltenVK requires VK_KHR_portability_enumeration and
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR to surface
portability devices via enumeratePhysicalDevices. Without the flag
the adapter is hidden on some driver versions, causing
"Could not find suitable device" failures.

Add the instance extension and flag when available, and add
diagnostic logs for all MoltenVK fallbacks (depthClipEnable,
colorWriteEnable, depthBounds, fragmentShaderBarycentric)
so black-screen diagnostics are actionable.

* 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.

* launcher: fix bundle rpath for Qt frameworks

The bundle executable had @rpath references to Qt frameworks but no
LC_RPATH, causing dyld to abort at launch:

  Library not loaded: @rpath/QtConcurrent.framework
  Reason: no LC_RPATH's found

Set INSTALL_RPATH to @executable_path/../Frameworks and
BUILD_WITH_INSTALL_RPATH so the built and installed bundles
resolve Qt from Contents/Frameworks. Verified via otool -l
(LC_RPATH now present) and macdeployqt now succeeds without
"Cannot resolve rpath" errors.

* launcher: add Import game button

Add a prominent Import button to ConfigurationListWidget that lets
users select a game folder (containing eboot.bin) or a folder
containing multiple games via QFileDialog. Handles:

- Single game folder selected: adds its parent to game_dirs (Scan
  searches subdirectories, so parent is needed)
- Folder with multiple games: adds the selected folder
- Deduplication via PathKey/NormalizeGameDirectories, writes
  settings, rescans, and selects the imported game if found
- Shows info/warning dialogs when no games are found

UI: QToolButton with SP_DirOpenIcon and text Import beside the
existing toolbar, styled with a blue background for visibility.
Logic in ConfigurationListWidget::import_game().

* macOS: fix bundled loader and artifact packaging

* Revert "launcher: add Import game button"

This reverts commit 2f670ef95c.

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-29 17:08:54 +04:00

200 lines
6.5 KiB
CMake

find_package(Qt6 COMPONENTS Concurrent Network Widgets REQUIRED)
set(CMAKE_AUTOUIC_SEARCH_PATHS forms)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
file(GLOB launcher_src
"include/*.h"
"src/*.cpp"
)
file(GLOB launcher_forms
"forms/*.ui"
)
add_executable(launcher WIN32
${launcher_src}
${launcher_forms}
resources.qrc
)
target_link_libraries(launcher Qt6::Concurrent Qt6::Network Qt6::Widgets)
if(WIN32)
target_link_libraries(launcher setupapi)
endif()
if (CLANG AND NOT KYTY_CLANG_CL)
target_compile_options(launcher PRIVATE -frtti)
target_link_libraries(launcher pthread)
endif()
target_include_directories(launcher PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/src")
set_target_properties(launcher PROPERTIES AUTOUIC ON AUTOMOC ON AUTORCC ON)
if(WIN32 AND NOT KYTY_CLANG_CL)
set_target_properties(launcher PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS}")
elseif(LINUX)
# Use the same linker as kyty_emulator.
set_target_properties(launcher PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS}")
endif()
add_dependencies(launcher kyty_emulator)
if(KYTY_CLANG_CL)
add_custom_command(TARGET launcher POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_PDB_FILE:kyty_emulator> $<TARGET_FILE_DIR:launcher>/$<TARGET_PDB_FILE_NAME:kyty_emulator>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE_DIR:kyty_emulator>/${KYTY_EMULATOR_MAP_NAME} $<TARGET_FILE_DIR:launcher>/${KYTY_EMULATOR_MAP_NAME}
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${KYTY_THIRD_PARTY_DIR}/winpthread/bin/libwinpthread-1.dll" $<TARGET_FILE_DIR:launcher>/libwinpthread-1.dll
)
endif()
if(WIN32)
find_program(QT_WINDEPLOYQT NAMES windeployqt PATHS "${Qt6_DIR}/../../../bin")
if(NOT QT_WINDEPLOYQT)
message(FATAL_ERROR "Could not find windeployqt")
endif()
add_custom_command(TARGET launcher POST_BUILD
COMMAND ${QT_WINDEPLOYQT} $<TARGET_FILE:launcher>
--no-compiler-runtime
--no-system-d3d-compiler
--no-system-dxc-compiler
--no-opengl-sw
--no-translations
COMMENT "Deploying Qt runtime and TLS plugins"
VERBATIM
)
endif()
set(launcher_name "launcher")
if(APPLE)
set(launcher_name "KytyPS5")
endif()
set_target_properties(launcher PROPERTIES OUTPUT_NAME ${launcher_name})
if(APPLE)
# macOS bundle: KytyPS5.app with launcher as main executable and
# kyty_emulator as helper inside Contents/MacOS. MoltenVK is found
# next to a flat executable or in the bundle's Contents/Frameworks;
# no rpath fixup is needed because SDL loads it by absolute path.
# Qt deployment (Frameworks + plugins) is handled by macdeployqt.
set_target_properties(launcher PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
INSTALL_RPATH "@executable_path/../Frameworks"
BUILD_WITH_INSTALL_RPATH TRUE
)
endif()
install(TARGETS launcher
BUNDLE DESTINATION .
RUNTIME DESTINATION .)
if(APPLE)
install(TARGETS kyty_emulator DESTINATION "KytyPS5.app/Contents/MacOS")
# Deploy and ad-hoc sign the Qt frameworks and plugins in the bundle.
find_program(KYTY_MACDEPLOYQT NAMES macdeployqt PATHS "${Qt6_DIR}/../../../bin" NO_DEFAULT_PATH)
if(NOT KYTY_MACDEPLOYQT)
message(FATAL_ERROR "Could not find macdeployqt")
endif()
install(CODE "
execute_process(COMMAND \"${KYTY_MACDEPLOYQT}\" \"\${CMAKE_INSTALL_PREFIX}/KytyPS5.app\"
-codesign=-
RESULT_VARIABLE _kyty_deploy_result)
if(NOT _kyty_deploy_result EQUAL 0)
message(FATAL_ERROR \"macdeployqt failed with \${_kyty_deploy_result}\")
endif()
")
endif()
if(WIN32)
install(CODE "
execute_process(
COMMAND ${QT_WINDEPLOYQT} \"\${CMAKE_INSTALL_PREFIX}/${launcher_name}.exe\"
--no-compiler-runtime
--no-system-d3d-compiler
--no-system-dxc-compiler
--no-opengl-sw
--no-translations
)
")
elseif(LINUX)
# Bundle the Qt runtime and plugins.
set_target_properties(launcher PROPERTIES
INSTALL_RPATH "$ORIGIN/lib"
BUILD_WITH_INSTALL_RPATH FALSE
LINK_FLAGS "${KYTY_LD_OPTIONS} -Wl,--disable-new-dtags")
# Support distribution and official Qt layouts.
get_target_property(kyty_qt_core_lib Qt6::Core IMPORTED_LOCATION_RELEASE)
if(NOT kyty_qt_core_lib)
get_target_property(kyty_qt_core_lib Qt6::Core IMPORTED_LOCATION)
endif()
get_filename_component(kyty_qt_libdir "${kyty_qt_core_lib}" DIRECTORY)
set(kyty_qt_plugins_dir "")
foreach(kyty_candidate
"${kyty_qt_libdir}/qt6/plugins"
"${kyty_qt_libdir}/../plugins"
"${Qt6_DIR}/../../../plugins")
if(IS_DIRECTORY "${kyty_candidate}/platforms")
get_filename_component(kyty_qt_plugins_dir "${kyty_candidate}" ABSOLUTE)
break()
endif()
endforeach()
if(kyty_qt_plugins_dir)
message(STATUS "Qt plugins for launcher deployment: ${kyty_qt_plugins_dir}")
set(kyty_qt_plugin_files "")
foreach(kyty_qt_plugin platforms tls imageformats xcbglintegrations wayland-shell-integration)
if(IS_DIRECTORY "${kyty_qt_plugins_dir}/${kyty_qt_plugin}")
file(GLOB kyty_qt_plugin_group
"${kyty_qt_plugins_dir}/${kyty_qt_plugin}/*.so"
"${kyty_qt_plugins_dir}/${kyty_qt_plugin}/*.so.*")
list(APPEND kyty_qt_plugin_files ${kyty_qt_plugin_group})
install(DIRECTORY "${kyty_qt_plugins_dir}/${kyty_qt_plugin}" DESTINATION "plugins")
endif()
endforeach()
install(CODE "
set(kyty_plugin_files \"${kyty_qt_plugin_files}\")
file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES \"${CMAKE_CURRENT_BINARY_DIR}/${launcher_name}\"
LIBRARIES \${kyty_plugin_files}
RESOLVED_DEPENDENCIES_VAR kyty_resolved
POST_INCLUDE_REGEXES \"libQt6\" \"libicu\"
POST_EXCLUDE_REGEXES \".*\")
foreach(kyty_dep \${kyty_resolved})
file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/lib\"
TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES \"\${kyty_dep}\")
endforeach()
")
install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/qt.conf\" \"[Paths]\\nPlugins = plugins\\n\")
")
else()
message(WARNING "Qt plugin directory not found; the installed launcher will need a system Qt")
endif()
endif()
set(tidy_dirs "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(iwyu_maps "${CMAKE_CURRENT_SOURCE_DIR}/utils/qt6_16.imp")
get_property(inc_headers TARGET launcher PROPERTY INCLUDE_DIRECTORIES)
list(APPEND inc_headers
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}/launcher_autogen/include
${Qt6Widgets_INCLUDE_DIRS}
${Qt6Core_INCLUDE_DIRS}
${Qt6Gui_INCLUDE_DIRS}
#${Qt6_DIR}/../../../include
#${Qt6_DIR}/../../../include/QtWidgets
#${Qt6_DIR}/../../../include/QtCore
#${Qt6_DIR}/../../../include/QtGui
)
include_what_you_use_with_mappings(launcher "${inc_headers}" "${iwyu_maps}")
clang_tidy_check(launcher "" "${tidy_dirs}" "${inc_headers}")