From ede438c0e11b341890b007b8bad2c829547e1b47 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 6 Jun 2026 20:18:06 -0400 Subject: [PATCH 1/4] chore: migrate to qt tray for all platforms --- .github/workflows/ci-freebsd.yml | 1 - .github/workflows/ci-macos.yml | 4 +++- .github/workflows/ci-windows.yml | 2 ++ cmake/packaging/linux.cmake | 11 ----------- docker/clion-toolchain.dockerfile | 1 - docs/building.md | 7 ++++++- packaging/linux/Arch/PKGBUILD | 1 - packaging/linux/copr/Sunshine.spec | 2 -- packaging/sunshine.rb | 5 ++--- scripts/linux_build.sh | 3 --- scripts/macos_build.sh | 2 ++ third-party/tray | 2 +- 12 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-freebsd.yml b/.github/workflows/ci-freebsd.yml index f0bff9ed165..65af4adb940 100644 --- a/.github/workflows/ci-freebsd.yml +++ b/.github/workflows/ci-freebsd.yml @@ -107,7 +107,6 @@ jobs: devel/evdev-proto \ devel/git \ devel/libevdev \ - devel/libnotify \ devel/llvm${{ env.FREEBSD_CLANG_VERSION }} \ devel/ninja \ devel/pkgconf \ diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 7ec3b8c5c8a..4c2f04ee664 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -86,7 +86,9 @@ jobs: icu4c@78 \ miniupnpc \ openssl@3 \ - opus + opus \ + qtbase \ + qtsvg - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 47271c0b020..80a030bb6bb 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -108,6 +108,8 @@ jobs: "mingw-w64-${TOOLCHAIN}-openssl" "mingw-w64-${TOOLCHAIN}-opus" "mingw-w64-${TOOLCHAIN}-toolchain" + "mingw-w64-${TOOLCHAIN}-qt6-base" + "mingw-w64-${TOOLCHAIN}-qt6-svg" ) if [[ "${MATRIX_MSYSTEM}" == "ucrt64" ]]; then diff --git a/cmake/packaging/linux.cmake b/cmake/packaging/linux.cmake index 8f493b0757c..b3f675d295a 100644 --- a/cmake/packaging/linux.cmake +++ b/cmake/packaging/linux.cmake @@ -154,17 +154,6 @@ if(${SUNSHINE_TRAY} STREQUAL 1) # Icons used by the Qt tray backend are no longer installed to the hicolor icon theme, # because Qt6 will not allow icons not part of the theme... so we will use icons from our web directory instead - set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ - ${CPACK_DEBIAN_PACKAGE_DEPENDS}, \ - libnotify4" - ) - set(CPACK_RPM_PACKAGE_REQUIRES "\ - ${CPACK_RPM_PACKAGE_REQUIRES}, \ - libnotify >= 0.8.0" - ) - list(APPEND CPACK_FREEBSD_PACKAGE_DEPS - devel/libnotify - ) if(TRAY_QT_VERSION EQUAL 6) set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ ${CPACK_DEBIAN_PACKAGE_DEPENDS}, \ diff --git a/docker/clion-toolchain.dockerfile b/docker/clion-toolchain.dockerfile index e08a2db5025..c2a468736a5 100644 --- a/docker/clion-toolchain.dockerfile +++ b/docker/clion-toolchain.dockerfile @@ -39,7 +39,6 @@ apt-get install -y --no-install-recommends \ libevdev-dev \ libgbm-dev \ libminiupnpc-dev \ - libnotify-dev \ libnuma-dev \ libopus-dev \ libpulse-dev \ diff --git a/docs/building.md b/docs/building.md index 8b80ce2efad..9dc2a7804b0 100644 --- a/docs/building.md +++ b/docs/building.md @@ -27,7 +27,6 @@ pkg install -y \ devel/evdev-proto \ devel/git \ devel/libevdev \ - devel/libnotify \ devel/ninja \ devel/pkgconf \ devel/qt6-base \ @@ -94,6 +93,8 @@ dependencies=( "openssl@3" "opus" "pkg-config" + "qtbase" + "qtsvg" ) brew install "${dependencies[@]}" ``` @@ -122,6 +123,8 @@ dependencies=( "ninja" "npm9" "pkgconfig" + "qt6-qtbase" + "qt6-qtsvg" ) sudo port install "${dependencies[@]}" ``` @@ -166,6 +169,8 @@ dependencies=( "mingw-w64-${TOOLCHAIN}-openssl" "mingw-w64-${TOOLCHAIN}-opus" "mingw-w64-${TOOLCHAIN}-toolchain" + "mingw-w64-${TOOLCHAIN}-qt6-base" + "mingw-w64-${TOOLCHAIN}-qt6-svg" ) if [[ "${MSYSTEM}" == "UCRT64" ]]; then dependencies+=( diff --git a/packaging/linux/Arch/PKGBUILD b/packaging/linux/Arch/PKGBUILD index 00a934cd2ca..7e2c06682cd 100644 --- a/packaging/linux/Arch/PKGBUILD +++ b/packaging/linux/Arch/PKGBUILD @@ -35,7 +35,6 @@ depends=( 'libdrm' 'libevdev' 'libmfx' - 'libnotify' 'libpipewire' 'libpulse' 'libva' diff --git a/packaging/linux/copr/Sunshine.spec b/packaging/linux/copr/Sunshine.spec index f79c35d2b4c..85ee83c9f03 100644 --- a/packaging/linux/copr/Sunshine.spec +++ b/packaging/linux/copr/Sunshine.spec @@ -31,7 +31,6 @@ BuildRequires: libcap-devel BuildRequires: libcurl-devel BuildRequires: libdrm-devel BuildRequires: libevdev-devel -BuildRequires: libnotify-devel >= 0.8.0 BuildRequires: libva-devel BuildRequires: libX11-devel BuildRequires: libxcb-devel @@ -153,7 +152,6 @@ BuildRequires: libqt6-qtsvg-devel %global cuda_dir %{_builddir}/cuda # Common runtime requirements -Requires: libnotify >= 0.8.0 Requires: miniupnpc >= 2.2.4 Requires: which >= 2.21 diff --git a/packaging/sunshine.rb b/packaging/sunshine.rb index 3a88114fd34..cc35fb18301 100644 --- a/packaging/sunshine.rb +++ b/packaging/sunshine.rb @@ -55,6 +55,8 @@ class Sunshine < Formula depends_on "miniupnpc" depends_on "openssl@3" depends_on "opus" + depends_on "qtbase" + depends_on "qtsvg" on_macos do depends_on "llvm" => [:build, :test] @@ -74,7 +76,6 @@ class Sunshine < Formula depends_on "libcap" depends_on "libdrm" depends_on "libice" - depends_on "libnotify" depends_on "libsm" depends_on "libva" depends_on "libx11" @@ -91,8 +92,6 @@ class Sunshine < Formula depends_on "pango" depends_on "pipewire" depends_on "pulseaudio" - depends_on "qtbase" - depends_on "qtsvg" depends_on "shaderc" depends_on "systemd" depends_on "vulkan-loader" diff --git a/scripts/linux_build.sh b/scripts/linux_build.sh index 2c1011436b0..8afc1d343f3 100755 --- a/scripts/linux_build.sh +++ b/scripts/linux_build.sh @@ -283,7 +283,6 @@ function add_arch_deps() { 'libdrm' 'libevdev' 'libmfx' - 'libnotify' 'libpulse' 'libva' 'libx11' @@ -344,7 +343,6 @@ function add_debian_based_deps() { "libevdev-dev" "libgbm-dev" "libminiupnpc-dev" - "libnotify-dev" "libnuma-dev" "libopus-dev" "libpipewire-0.3-dev" @@ -450,7 +448,6 @@ function add_fedora_deps() { "libcurl-devel" "libdrm-devel" "libevdev-devel" - "libnotify-devel" "libX11-devel" # X11 "libxcb-devel" # X11 "libXcursor-devel" # X11 diff --git a/scripts/macos_build.sh b/scripts/macos_build.sh index 7c24209836d..5a27f2b78d6 100755 --- a/scripts/macos_build.sh +++ b/scripts/macos_build.sh @@ -37,6 +37,8 @@ required_formulas=( "openssl@3" "opus" "llvm" + "qtbase" + "qtsvg" ) function _usage() { diff --git a/third-party/tray b/third-party/tray index df9af119085..c7bb9e515bb 160000 --- a/third-party/tray +++ b/third-party/tray @@ -1 +1 @@ -Subproject commit df9af119085e2cd3f1a9e88e4cc865963879d468 +Subproject commit c7bb9e515bb2de3bb56448b3b070f3e72138868f From edeeb44ca82a8e918da37ca3ac1142a9ec8d460a Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 6 Jun 2026 21:50:48 -0400 Subject: [PATCH 2/4] Add Qt packaging helpers & deploy runtimes Introduce cmake/packaging/qt.cmake with sunshine_find_qt_tool and sunshine_require_qt_tool to locate Qt deployment tools. Wire macdeployqt into macos packaging (run during install to fix up .app bundles) when the tray backend is enabled, and integrate windeployqt into Windows packaging: add post-build deploy commands for binaries and run windeployqt at install time with curated options. These changes ensure Qt runtimes are bundled for the tray::tray backend on macOS and Windows. --- cmake/packaging/macos.cmake | 22 ++++++++- cmake/packaging/qt.cmake | 85 +++++++++++++++++++++++++++++++++++ cmake/packaging/windows.cmake | 31 +++++++++++++ tests/CMakeLists.txt | 14 ++++++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 cmake/packaging/qt.cmake diff --git a/cmake/packaging/macos.cmake b/cmake/packaging/macos.cmake index 623541043d1..dc772d343e2 100644 --- a/cmake/packaging/macos.cmake +++ b/cmake/packaging/macos.cmake @@ -37,14 +37,34 @@ else() PATTERN ".DS_Store" EXCLUDE PATTERN "._*" EXCLUDE) + set(SUNSHINE_BUNDLE_FIXUP_DIRS "") + set(SUNSHINE_MACDEPLOYQT_CODE "") + if(SUNSHINE_ENABLE_TRAY) + include("${CMAKE_MODULE_PATH}/packaging/qt.cmake") + sunshine_require_qt_tool(macdeployqt SUNSHINE_MACDEPLOYQT_EXECUTABLE) + sunshine_collect_qt_library_dirs(SUNSHINE_BUNDLE_FIXUP_DIRS) + set(SUNSHINE_MACDEPLOYQT_CODE " + message(STATUS \"Running macdeployqt for: \${_app}\") + execute_process( + COMMAND \"${SUNSHINE_MACDEPLOYQT_EXECUTABLE}\" \"\${_app}\" -no-strip + RESULT_VARIABLE _macdeployqt_result + ) + if(NOT _macdeployqt_result EQUAL 0) + message(FATAL_ERROR \"macdeployqt failed: \${_macdeployqt_result}\") + endif() +") + endif() + # Pull in non-system dylibs for a self-contained .app install(CODE " set(_app \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_NAME}.app\") + set(_bundle_fixup_dirs \"${SUNSHINE_BUNDLE_FIXUP_DIRS}\") +${SUNSHINE_MACDEPLOYQT_CODE} message(STATUS \"Running fixup_bundle for: \${_app}\") include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS TRUE) - fixup_bundle(\"\${_app}\" \"\" \"\") + fixup_bundle(\"\${_app}\" \"\" \"\${_bundle_fixup_dirs}\") # Remove Finder/resource-fork metadata that breaks codesign. execute_process(COMMAND /usr/bin/xattr -rc \"\${_app}\") diff --git a/cmake/packaging/qt.cmake b/cmake/packaging/qt.cmake new file mode 100644 index 00000000000..d149b09d8c1 --- /dev/null +++ b/cmake/packaging/qt.cmake @@ -0,0 +1,85 @@ +# Qt packaging helpers + +function(sunshine_find_qt_tool tool_name out_var) + foreach(qt_major IN ITEMS 6 5) + if(TARGET Qt${qt_major}::${tool_name}) + get_target_property(_qt_tool Qt${qt_major}::${tool_name} IMPORTED_LOCATION) + if(_qt_tool) + set(${out_var} "${_qt_tool}" PARENT_SCOPE) + return() + endif() + endif() + endforeach() + + set(_qt_tool_names "${tool_name}" "${tool_name}6" "${tool_name}5") + string(TOUPPER "${tool_name}" _qt_tool_name) + set(_qt_tool_var "SUNSHINE_${_qt_tool_name}_EXECUTABLE") + find_program(${_qt_tool_var} NAMES ${_qt_tool_names}) + set(${out_var} "${${_qt_tool_var}}" PARENT_SCOPE) +endfunction() + +function(sunshine_require_qt_tool tool_name out_var) + sunshine_find_qt_tool("${tool_name}" _qt_tool) + if(NOT _qt_tool) + message(FATAL_ERROR + "${tool_name} is required to package Sunshine with the Qt tray backend") + endif() + + set(${out_var} "${_qt_tool}" PARENT_SCOPE) +endfunction() + +function(sunshine_deploy_qt_runtime target_name deploy_tool) + if(NOT TARGET "${target_name}") + return() + endif() + + add_custom_command(TARGET "${target_name}" POST_BUILD + COMMAND "${deploy_tool}" + ${ARGN} + --dir "$" + "$" + COMMENT "Deploying Qt runtime for ${target_name}" + VERBATIM) +endfunction() + +function(sunshine_collect_qt_library_dirs out_var) + set(_qt_dirs "") + foreach(qt_major IN ITEMS 6 5) + foreach(qt_component IN ITEMS Core Gui Widgets Svg DBus) + if(TARGET Qt${qt_major}::${qt_component}) + set(_qt_location "") + foreach(_qt_property IN ITEMS + IMPORTED_LOCATION_RELEASE + IMPORTED_LOCATION_RELWITHDEBINFO + IMPORTED_LOCATION) + get_target_property(_qt_property_location Qt${qt_major}::${qt_component} ${_qt_property}) + if(_qt_property_location) + set(_qt_location "${_qt_property_location}") + break() + endif() + endforeach() + + if(_qt_location) + get_filename_component(_qt_dir "${_qt_location}" DIRECTORY) + foreach(_qt_depth RANGE 1 8) + if(NOT _qt_dir) + break() + endif() + + list(APPEND _qt_dirs "${_qt_dir}") + get_filename_component(_qt_parent "${_qt_dir}" DIRECTORY) + if(_qt_parent STREQUAL _qt_dir) + break() + endif() + set(_qt_dir "${_qt_parent}") + endforeach() + endif() + endif() + endforeach() + endforeach() + + if(_qt_dirs) + list(REMOVE_DUPLICATES _qt_dirs) + endif() + set(${out_var} "${_qt_dirs}" PARENT_SCOPE) +endfunction() diff --git a/cmake/packaging/windows.cmake b/cmake/packaging/windows.cmake index 69830da6b9a..a85daceb2f5 100644 --- a/cmake/packaging/windows.cmake +++ b/cmake/packaging/windows.cmake @@ -9,6 +9,37 @@ if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND DEFINED _MINHOOK_DLL) install(FILES "${_MINHOOK_DLL}" DESTINATION "." COMPONENT application) endif() +if(SUNSHINE_ENABLE_TRAY) + include("${CMAKE_MODULE_PATH}/packaging/qt.cmake") + sunshine_require_qt_tool(windeployqt SUNSHINE_WINDEPLOYQT_EXECUTABLE) + + set(SUNSHINE_WINDEPLOYQT_OPTIONS + --no-opengl-sw + --no-quick-import + --no-system-d3d-compiler + --no-translations) + + sunshine_deploy_qt_runtime( + sunshine + "${SUNSHINE_WINDEPLOYQT_EXECUTABLE}" + ${SUNSHINE_WINDEPLOYQT_OPTIONS}) + + install(CODE " + set(_qt_deploy_dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\") + set(_qt_deploy_options ${SUNSHINE_WINDEPLOYQT_OPTIONS}) + execute_process( + COMMAND \"${SUNSHINE_WINDEPLOYQT_EXECUTABLE}\" + \${_qt_deploy_options} + --dir \"\${_qt_deploy_dir}\" + \"\${_qt_deploy_dir}/sunshine.exe\" + RESULT_VARIABLE _qt_deploy_result + ) + if(NOT _qt_deploy_result EQUAL 0) + message(FATAL_ERROR \"windeployqt failed: \${_qt_deploy_result}\") + endif() + " COMPONENT application) +endif() + # ViGEmBus installer set(SUNSHINE_THIRD_PARTY_DIR "third-party") set(VIGEMBUS_INSTALLER "${CMAKE_BINARY_DIR}/${SUNSHINE_THIRD_PARTY_DIR}/vigembus_installer.exe") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 984481f6010..b69d6f9d16d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -190,4 +190,18 @@ if (WIN32) VERBATIM ) endif() + + if(SUNSHINE_ENABLE_TRAY) + include("${CMAKE_MODULE_PATH}/packaging/qt.cmake") + sunshine_require_qt_tool(windeployqt SUNSHINE_WINDEPLOYQT_EXECUTABLE) + set(SUNSHINE_WINDEPLOYQT_OPTIONS + --no-opengl-sw + --no-quick-import + --no-system-d3d-compiler + --no-translations) + sunshine_deploy_qt_runtime( + ${PROJECT_NAME} + "${SUNSHINE_WINDEPLOYQT_EXECUTABLE}" + ${SUNSHINE_WINDEPLOYQT_OPTIONS}) + endif() endif () From cb8984a77b8e489e96f5515c3c1572d89bb28ba2 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 7 Jun 2026 08:45:13 -0400 Subject: [PATCH 3/4] Ignore bundled DLLs when checking linkage Update Windows CI linkage check to avoid false positives by computing binary_dir and passing it into awk. When a linked DLL originates from a toolchain bin path, awk now checks for the DLL next to the binary (binary_dir/$1) and suppresses the warning if the file is present. Also ensure 'not found' cases are printed and skipped. This prevents reporting DLLs that are intentionally bundled with the binary. --- .github/workflows/ci-windows.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 80a030bb6bb..dd6b118643e 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -272,14 +272,23 @@ jobs: echo "${linkage}" echo "::endgroup::" - unexpected_for_binary="$(awk ' + binary_dir="$(dirname "${binary}")" + unexpected_for_binary="$(awk -v binary_dir="${binary_dir}" ' BEGIN { IGNORECASE = 1 } $1 == "zlib1.dll" { next } $1 ~ /^minhook-detours\..*\.dll$/ { next } $1 ~ /^api-ms-win-/ { next } $1 ~ /^ext-ms-win-/ { next } - $0 ~ /=>[[:space:]]+not found/ { print } - $0 ~ /=>[[:space:]]+\/(clangarm64|clang64|mingw32|mingw64|ucrt64)\/bin\// { print } + $0 ~ /=>[[:space:]]+not found/ { print; next } + $0 ~ /=>[[:space:]]+\/(clangarm64|clang64|mingw32|mingw64|ucrt64)\/bin\// { + deployed_dll = binary_dir "/" $1 + if ((getline _ < deployed_dll) >= 0) { + close(deployed_dll) + next + } + close(deployed_dll) + print + } ' <<< "${linkage}")" if [[ -n "${unexpected_for_binary}" ]]; then From 4fe76cc39e164366dfed2df97c4830f6dc4c4601 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 7 Jun 2026 09:52:24 -0400 Subject: [PATCH 4/4] Add MSYS2 runtime dependency deployment Introduce a CMake helper to deploy MSYS2/MinGW runtime DLLs discovered via ldd and cygpath. Added cmake/packaging/windows_runtime_deps.cmake which requires SUNSHINE_RUNTIME_TARGET and SUNSHINE_RUNTIME_OUTPUT_DIR, uses ldd to enumerate dependencies, converts MSYS paths with cygpath, copies needed DLLs into the output dir, and recursively processes newly copied DLLs. Hooked this script into existing packaging: qt.cmake's deploy function now invokes the script for the given target, and windows.cmake runs it after windeployqt and fails the build on errors. This ensures MSYS2 runtime DLLs reported by ldd are bundled with the application. --- cmake/packaging/qt.cmake | 4 + cmake/packaging/windows.cmake | 11 +++ cmake/packaging/windows_runtime_deps.cmake | 98 ++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 cmake/packaging/windows_runtime_deps.cmake diff --git a/cmake/packaging/qt.cmake b/cmake/packaging/qt.cmake index d149b09d8c1..b0485769bf4 100644 --- a/cmake/packaging/qt.cmake +++ b/cmake/packaging/qt.cmake @@ -38,6 +38,10 @@ function(sunshine_deploy_qt_runtime target_name deploy_tool) ${ARGN} --dir "$" "$" + COMMAND "${CMAKE_COMMAND}" + "-DSUNSHINE_RUNTIME_TARGET=$" + "-DSUNSHINE_RUNTIME_OUTPUT_DIR=$" + -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/windows_runtime_deps.cmake" COMMENT "Deploying Qt runtime for ${target_name}" VERBATIM) endfunction() diff --git a/cmake/packaging/windows.cmake b/cmake/packaging/windows.cmake index a85daceb2f5..4ec461cce0e 100644 --- a/cmake/packaging/windows.cmake +++ b/cmake/packaging/windows.cmake @@ -37,6 +37,17 @@ if(SUNSHINE_ENABLE_TRAY) if(NOT _qt_deploy_result EQUAL 0) message(FATAL_ERROR \"windeployqt failed: \${_qt_deploy_result}\") endif() + + execute_process( + COMMAND \"${CMAKE_COMMAND}\" + \"-DSUNSHINE_RUNTIME_TARGET=\${_qt_deploy_dir}/sunshine.exe\" + \"-DSUNSHINE_RUNTIME_OUTPUT_DIR=\${_qt_deploy_dir}\" + -P \"${CMAKE_MODULE_PATH}/packaging/windows_runtime_deps.cmake\" + RESULT_VARIABLE _runtime_deploy_result + ) + if(NOT _runtime_deploy_result EQUAL 0) + message(FATAL_ERROR \"Runtime dependency deployment failed: \${_runtime_deploy_result}\") + endif() " COMPONENT application) endif() diff --git a/cmake/packaging/windows_runtime_deps.cmake b/cmake/packaging/windows_runtime_deps.cmake new file mode 100644 index 00000000000..6f8f2c09f90 --- /dev/null +++ b/cmake/packaging/windows_runtime_deps.cmake @@ -0,0 +1,98 @@ +# Deploy MSYS2 runtime DLLs that are reported by ldd for a Windows binary. + +if(NOT DEFINED SUNSHINE_RUNTIME_TARGET) + message(FATAL_ERROR "SUNSHINE_RUNTIME_TARGET is required") +endif() + +if(NOT DEFINED SUNSHINE_RUNTIME_OUTPUT_DIR) + message(FATAL_ERROR "SUNSHINE_RUNTIME_OUTPUT_DIR is required") +endif() + +string(REGEX REPLACE "^\"|\"$" "" SUNSHINE_RUNTIME_TARGET "${SUNSHINE_RUNTIME_TARGET}") +string(REGEX REPLACE "^\"|\"$" "" SUNSHINE_RUNTIME_OUTPUT_DIR "${SUNSHINE_RUNTIME_OUTPUT_DIR}") + +find_program(SUNSHINE_LDD_EXECUTABLE ldd) +if(NOT SUNSHINE_LDD_EXECUTABLE) + message(FATAL_ERROR "ldd is required to deploy MSYS2 runtime dependencies") +endif() + +find_program(SUNSHINE_CYGPATH_EXECUTABLE cygpath) +if(NOT SUNSHINE_CYGPATH_EXECUTABLE) + message(FATAL_ERROR "cygpath is required to deploy MSYS2 runtime dependencies") +endif() + +function(sunshine_msys_path_to_cmake msys_path out_var) + execute_process( + COMMAND "${SUNSHINE_CYGPATH_EXECUTABLE}" -m "${msys_path}" + OUTPUT_VARIABLE _cmake_path + ERROR_VARIABLE _cygpath_error + RESULT_VARIABLE _cygpath_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT _cygpath_result EQUAL 0) + message(FATAL_ERROR "cygpath failed for ${msys_path}: ${_cygpath_error}") + endif() + + set(${out_var} "${_cmake_path}" PARENT_SCOPE) +endfunction() + +function(sunshine_copy_msys_dependency msys_path output_dir out_path) + sunshine_msys_path_to_cmake("${msys_path}" _source_path) + get_filename_component(_dll_name "${_source_path}" NAME) + set(_deployed_path "${output_dir}/${_dll_name}") + + if(NOT EXISTS "${_deployed_path}") + execute_process( + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${_source_path}" + "${_deployed_path}" + ERROR_VARIABLE _copy_error + RESULT_VARIABLE _copy_result) + if(NOT _copy_result EQUAL 0) + message(FATAL_ERROR "Failed to copy ${_source_path}: ${_copy_error}") + endif() + endif() + + set(${out_path} "${_deployed_path}" PARENT_SCOPE) +endfunction() + +file(GLOB_RECURSE _deployed_dlls + LIST_DIRECTORIES false + "${SUNSHINE_RUNTIME_OUTPUT_DIR}/*.dll") + +set(_pending "${SUNSHINE_RUNTIME_TARGET}" ${_deployed_dlls}) +set(_processed "") + +while(_pending) + list(POP_FRONT _pending _runtime_binary) + list(FIND _processed "${_runtime_binary}" _processed_index) + if(NOT _processed_index EQUAL -1) + continue() + endif() + + list(APPEND _processed "${_runtime_binary}") + + execute_process( + COMMAND "${SUNSHINE_LDD_EXECUTABLE}" "${_runtime_binary}" + OUTPUT_VARIABLE _ldd_output + ERROR_VARIABLE _ldd_error + RESULT_VARIABLE _ldd_result) + if(NOT _ldd_result EQUAL 0) + message(FATAL_ERROR "ldd failed for ${_runtime_binary}: ${_ldd_error}") + endif() + + string(REGEX MATCHALL "[^\r\n]+" _ldd_lines "${_ldd_output}") + foreach(_ldd_line IN LISTS _ldd_lines) + if(_ldd_line MATCHES "=>[ \t]+not[ \t]+found") + message(FATAL_ERROR "Runtime dependency not found: ${_ldd_line}") + endif() + + if(_ldd_line MATCHES "=>[ \t]+(/(clangarm64|clang64|mingw32|mingw64|ucrt64)/bin/[^ \t\r\n]+\\.dll)") + sunshine_copy_msys_dependency("${CMAKE_MATCH_1}" "${SUNSHINE_RUNTIME_OUTPUT_DIR}" _deployed_dll) + list(FIND _processed "${_deployed_dll}" _deployed_processed_index) + list(FIND _pending "${_deployed_dll}" _deployed_pending_index) + if(_deployed_processed_index EQUAL -1 AND _deployed_pending_index EQUAL -1) + list(APPEND _pending "${_deployed_dll}") + endif() + endif() + endforeach() +endwhile() \ No newline at end of file