build: Improve support for Windows cross-compilation. (#7389)

* build: Improve support for Windows cross-compilation.

* build: Move linuxdeploy download to bundle target execution time.
This commit is contained in:
Steveice10 2024-02-05 10:09:50 -08:00 committed by GitHub
parent aa6a29d7e1
commit 19784355f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 308 additions and 115 deletions

View File

@ -85,8 +85,6 @@ option(ENABLE_VULKAN "Enables the Vulkan renderer" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
CMAKE_DEPENDENT_OPTION(CITRA_ENABLE_BUNDLE_TARGET "Enable the distribution bundling target." ON "NOT ANDROID AND NOT IOS" OFF)
# Compile options # Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF) CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO}) option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
@ -249,6 +247,26 @@ if (ENABLE_QT)
if (ENABLE_QT_TRANSLATION) if (ENABLE_QT_TRANSLATION)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools) find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
endif() endif()
if (NOT DEFINED QT_TARGET_PATH)
# Determine the location of the compile target's Qt.
get_target_property(qtcore_path Qt6::Core LOCATION_Release)
string(FIND "${qtcore_path}" "/bin/" qtcore_path_bin_pos REVERSE)
string(FIND "${qtcore_path}" "/lib/" qtcore_path_lib_pos REVERSE)
if (qtcore_path_bin_pos GREATER qtcore_path_lib_pos)
string(SUBSTRING "${qtcore_path}" 0 ${qtcore_path_bin_pos} QT_TARGET_PATH)
else()
string(SUBSTRING "${qtcore_path}" 0 ${qtcore_path_lib_pos} QT_TARGET_PATH)
endif()
endif()
if (NOT DEFINED QT_HOST_PATH)
# Use the same for host Qt if none is defined.
set(QT_HOST_PATH "${QT_TARGET_PATH}")
endif()
message(STATUS "Using target Qt at ${QT_TARGET_PATH}")
message(STATUS "Using host Qt at ${QT_HOST_PATH}")
endif() endif()
# Use system tsl::robin_map if available (otherwise we fallback to version bundled with dynarmic) # Use system tsl::robin_map if available (otherwise we fallback to version bundled with dynarmic)
@ -424,7 +442,8 @@ else()
endif() endif()
# Create target for outputting distributable bundles. # Create target for outputting distributable bundles.
if (CITRA_ENABLE_BUNDLE_TARGET) # Not supported for mobile platforms as distributables are built differently.
if (NOT ANDROID AND NOT IOS)
include(BundleTarget) include(BundleTarget)
if (ENABLE_SDL2_FRONTEND) if (ENABLE_SDL2_FRONTEND)
bundle_target(citra) bundle_target(citra)

View File

@ -2,37 +2,104 @@
if (BUNDLE_TARGET_EXECUTE) if (BUNDLE_TARGET_EXECUTE)
# --- Bundling method logic --- # --- Bundling method logic ---
function(symlink_safe_copy from to)
if (WIN32)
# Use cmake copy for maximum compatibility.
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${from}" "${to}"
RESULT_VARIABLE cp_result)
else()
# Use native copy to turn symlinks into normal files.
execute_process(COMMAND cp -L "${from}" "${to}"
RESULT_VARIABLE cp_result)
endif()
if (NOT cp_result EQUAL "0")
message(FATAL_ERROR "cp \"${from}\" \"${to}\" failed: ${cp_result}")
endif()
endfunction()
function(bundle_qt executable_path) function(bundle_qt executable_path)
if (WIN32) if (WIN32)
# Perform standalone bundling first to copy over all used libraries, as windeployqt does not do this.
bundle_standalone("${executable_path}" "${EXECUTABLE_PATH}" "${BUNDLE_LIBRARY_PATHS}")
get_filename_component(executable_parent_dir "${executable_path}" DIRECTORY) get_filename_component(executable_parent_dir "${executable_path}" DIRECTORY)
find_program(windeployqt_executable windeployqt6)
# Create a qt.conf file pointing to the app directory. # Create a qt.conf file pointing to the app directory.
# This ensures Qt can find its plugins. # This ensures Qt can find its plugins.
file(WRITE "${executable_parent_dir}/qt.conf" "[Paths]\nprefix = .") file(WRITE "${executable_parent_dir}/qt.conf" "[Paths]\nPrefix = .")
find_program(windeployqt_executable windeployqt6 PATHS "${QT_HOST_PATH}/bin")
find_program(qtpaths_executable qtpaths6 PATHS "${QT_HOST_PATH}/bin")
# TODO: Hack around windeployqt's poor cross-compilation support by
# TODO: making a local copy with a prefix pointing to the target Qt.
if (NOT "${QT_HOST_PATH}" STREQUAL "${QT_TARGET_PATH}")
set(windeployqt_dir "${BINARY_PATH}/windeployqt_copy")
file(MAKE_DIRECTORY "${windeployqt_dir}")
symlink_safe_copy("${windeployqt_executable}" "${windeployqt_dir}/windeployqt.exe")
symlink_safe_copy("${qtpaths_executable}" "${windeployqt_dir}/qtpaths.exe")
symlink_safe_copy("${QT_HOST_PATH}/bin/Qt6Core.dll" "${windeployqt_dir}")
if (EXISTS "${QT_TARGET_PATH}/share")
# Unix-style Qt; we need to wire up the paths manually.
file(WRITE "${windeployqt_dir}/qt.conf" "\
[Paths]\n
Prefix = ${QT_TARGET_PATH}\n \
ArchData = ${QT_TARGET_PATH}/share/qt6\n \
Binaries = ${QT_TARGET_PATH}/bin\n \
Data = ${QT_TARGET_PATH}/share/qt6\n \
Documentation = ${QT_TARGET_PATH}/share/qt6/doc\n \
Headers = ${QT_TARGET_PATH}/include/qt6\n \
Libraries = ${QT_TARGET_PATH}/lib\n \
LibraryExecutables = ${QT_TARGET_PATH}/share/qt6/bin\n \
Plugins = ${QT_TARGET_PATH}/share/qt6/plugins\n \
QmlImports = ${QT_TARGET_PATH}/share/qt6/qml\n \
Translations = ${QT_TARGET_PATH}/share/qt6/translations\n \
")
else()
# Windows-style Qt; the defaults should suffice.
file(WRITE "${windeployqt_dir}/qt.conf" "[Paths]\nPrefix = ${QT_TARGET_PATH}")
endif()
set(windeployqt_executable "${windeployqt_dir}/windeployqt.exe")
set(qtpaths_executable "${windeployqt_dir}/qtpaths.exe")
endif()
message(STATUS "Executing windeployqt for executable ${executable_path}") message(STATUS "Executing windeployqt for executable ${executable_path}")
execute_process(COMMAND "${windeployqt_executable}" "${executable_path}" execute_process(COMMAND "${windeployqt_executable}" "${executable_path}"
--qtpaths "${qtpaths_executable}"
--no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --no-translations --no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --no-translations
--plugindir "${executable_parent_dir}/plugins") --plugindir "${executable_parent_dir}/plugins"
RESULT_VARIABLE windeployqt_result)
if (NOT windeployqt_result EQUAL "0")
message(FATAL_ERROR "windeployqt failed: ${windeployqt_result}")
endif()
# Remove the FFmpeg multimedia plugin as we don't include FFmpeg. # Remove the FFmpeg multimedia plugin as we don't include FFmpeg.
# We want to use the Windows media plugin instead, which is also included. # We want to use the Windows media plugin instead, which is also included.
file(REMOVE "${executable_parent_dir}/plugins/multimedia/ffmpegmediaplugin.dll") file(REMOVE "${executable_parent_dir}/plugins/multimedia/ffmpegmediaplugin.dll")
elseif (APPLE) elseif (APPLE)
get_filename_component(executable_name "${executable_path}" NAME_WE) get_filename_component(executable_name "${executable_path}" NAME_WE)
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt6) find_program(macdeployqt_executable macdeployqt6 PATHS "${QT_HOST_PATH}/bin")
message(STATUS "Executing macdeployqt for executable ${executable_path}") message(STATUS "Executing macdeployqt at \"${macdeployqt_executable}\" for executable \"${executable_path}\"")
execute_process( execute_process(
COMMAND "${MACDEPLOYQT_EXECUTABLE}" COMMAND "${macdeployqt_executable}"
"${executable_path}" "${executable_path}"
"-executable=${executable_path}/Contents/MacOS/${executable_name}" "-executable=${executable_path}/Contents/MacOS/${executable_name}"
-always-overwrite) -always-overwrite
RESULT_VARIABLE macdeployqt_result)
if (NOT macdeployqt_result EQUAL "0")
message(FATAL_ERROR "macdeployqt failed: ${macdeployqt_result}")
endif()
# Bundling libraries can rewrite path information and break code signatures of system libraries. # Bundling libraries can rewrite path information and break code signatures of system libraries.
# Perform an ad-hoc re-signing on the whole app bundle to fix this. # Perform an ad-hoc re-signing on the whole app bundle to fix this.
execute_process(COMMAND codesign --deep -fs - "${executable_path}") execute_process(COMMAND codesign --deep -fs - "${executable_path}"
RESULT_VARIABLE codesign_result)
if (NOT codesign_result EQUAL "0")
message(FATAL_ERROR "codesign failed: ${codesign_result}")
endif()
else() else()
message(FATAL_ERROR "Unsupported OS for Qt bundling.") message(FATAL_ERROR "Unsupported OS for Qt bundling.")
endif() endif()
@ -44,9 +111,9 @@ if (BUNDLE_TARGET_EXECUTE)
if (enable_qt) if (enable_qt)
# Find qmake to make sure the plugin uses the right version of Qt. # Find qmake to make sure the plugin uses the right version of Qt.
find_program(QMAKE_EXECUTABLE qmake6) find_program(qmake_executable qmake6 PATHS "${QT_HOST_PATH}/bin")
set(extra_linuxdeploy_env "QMAKE=${QMAKE_EXECUTABLE}") set(extra_linuxdeploy_env "QMAKE=${qmake_executable}")
set(extra_linuxdeploy_args --plugin qt) set(extra_linuxdeploy_args --plugin qt)
endif() endif()
@ -59,7 +126,11 @@ if (BUNDLE_TARGET_EXECUTE)
--executable "${executable_path}" --executable "${executable_path}"
--icon-file "${source_path}/dist/citra.svg" --icon-file "${source_path}/dist/citra.svg"
--desktop-file "${source_path}/dist/${executable_name}.desktop" --desktop-file "${source_path}/dist/${executable_name}.desktop"
--appdir "${appdir_path}") --appdir "${appdir_path}"
RESULT_VARIABLE linuxdeploy_appdir_result)
if (NOT linuxdeploy_appdir_result EQUAL "0")
message(FATAL_ERROR "linuxdeploy failed to create AppDir: ${linuxdeploy_appdir_result}")
endif()
if (enable_qt) if (enable_qt)
set(qt_hook_file "${appdir_path}/apprun-hooks/linuxdeploy-plugin-qt-hook.sh") set(qt_hook_file "${appdir_path}/apprun-hooks/linuxdeploy-plugin-qt-hook.sh")
@ -82,7 +153,11 @@ if (BUNDLE_TARGET_EXECUTE)
"OUTPUT=${bundle_dir}/${executable_name}.AppImage" "OUTPUT=${bundle_dir}/${executable_name}.AppImage"
"${linuxdeploy_executable}" "${linuxdeploy_executable}"
--output appimage --output appimage
--appdir "${appdir_path}") --appdir "${appdir_path}"
RESULT_VARIABLE linuxdeploy_appimage_result)
if (NOT linuxdeploy_appimage_result EQUAL "0")
message(FATAL_ERROR "linuxdeploy failed to create AppImage: ${linuxdeploy_appimage_result}")
endif()
endfunction() endfunction()
function(bundle_standalone executable_path original_executable_path bundle_library_paths) function(bundle_standalone executable_path original_executable_path bundle_library_paths)
@ -109,16 +184,23 @@ if (BUNDLE_TARGET_EXECUTE)
file(MAKE_DIRECTORY ${lib_dir}) file(MAKE_DIRECTORY ${lib_dir})
foreach (lib_file IN LISTS resolved_deps) foreach (lib_file IN LISTS resolved_deps)
message(STATUS "Bundling library ${lib_file}") message(STATUS "Bundling library ${lib_file}")
# Use native copy to turn symlinks into normal files. symlink_safe_copy("${lib_file}" "${lib_dir}")
execute_process(COMMAND cp -L "${lib_file}" "${lib_dir}")
endforeach() endforeach()
endif() endif()
# Add libs directory to executable rpath where applicable. # Add libs directory to executable rpath where applicable.
if (APPLE) if (APPLE)
execute_process(COMMAND install_name_tool -add_rpath "@loader_path/libs" "${executable_path}") execute_process(COMMAND install_name_tool -add_rpath "@loader_path/libs" "${executable_path}"
RESULT_VARIABLE install_name_tool_result)
if (NOT install_name_tool_result EQUAL "0")
message(FATAL_ERROR "install_name_tool failed: ${install_name_tool_result}")
endif()
elseif (UNIX) elseif (UNIX)
execute_process(COMMAND patchelf --set-rpath '$ORIGIN/../libs' "${executable_path}") execute_process(COMMAND patchelf --set-rpath '$ORIGIN/../libs' "${executable_path}"
RESULT_VARIABLE patchelf_result)
if (NOT patchelf_result EQUAL "0")
message(FATAL_ERROR "patchelf failed: ${patchelf_result}")
endif()
endif() endif()
endfunction() endfunction()
@ -127,7 +209,7 @@ if (BUNDLE_TARGET_EXECUTE)
set(bundle_dir ${BINARY_PATH}/bundle) set(bundle_dir ${BINARY_PATH}/bundle)
# On Linux, always bundle an AppImage. # On Linux, always bundle an AppImage.
if (DEFINED LINUXDEPLOY) if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
if (IN_PLACE) if (IN_PLACE)
message(FATAL_ERROR "Cannot bundle for Linux in-place.") message(FATAL_ERROR "Cannot bundle for Linux in-place.")
endif() endif()
@ -146,14 +228,12 @@ if (BUNDLE_TARGET_EXECUTE)
if (BUNDLE_QT) if (BUNDLE_QT)
bundle_qt("${bundled_executable_path}") bundle_qt("${bundled_executable_path}")
endif() else()
if (WIN32 OR NOT BUNDLE_QT)
bundle_standalone("${bundled_executable_path}" "${EXECUTABLE_PATH}" "${BUNDLE_LIBRARY_PATHS}") bundle_standalone("${bundled_executable_path}" "${EXECUTABLE_PATH}" "${BUNDLE_LIBRARY_PATHS}")
endif() endif()
endif() endif()
else() elseif (BUNDLE_TARGET_DOWNLOAD_LINUXDEPLOY)
# --- Bundling target creation logic --- # --- linuxdeploy download logic ---
# Downloads and extracts a linuxdeploy component. # Downloads and extracts a linuxdeploy component.
function(download_linuxdeploy_component base_dir name executable_name) function(download_linuxdeploy_component base_dir name executable_name)
@ -161,7 +241,7 @@ else()
if (NOT EXISTS "${executable_file}") if (NOT EXISTS "${executable_file}")
message(STATUS "Downloading ${executable_name}") message(STATUS "Downloading ${executable_name}")
file(DOWNLOAD file(DOWNLOAD
"https://github.com/linuxdeploy/${name}/releases/download/continuous/${executable_name}" "https://github.com/${name}/releases/download/continuous/${executable_name}"
"${executable_file}" SHOW_PROGRESS) "${executable_file}" SHOW_PROGRESS)
file(CHMOD "${executable_file}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) file(CHMOD "${executable_file}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
@ -170,7 +250,11 @@ else()
message(STATUS "Extracting ${executable_name}") message(STATUS "Extracting ${executable_name}")
execute_process( execute_process(
COMMAND "${executable_file}" --appimage-extract COMMAND "${executable_file}" --appimage-extract
WORKING_DIRECTORY "${base_dir}") WORKING_DIRECTORY "${base_dir}"
RESULT_VARIABLE extract_result)
if (NOT extract_result EQUAL "0")
message(FATAL_ERROR "AppImage extract failed: ${extract_result}")
endif()
else() else()
message(STATUS "Copying ${executable_name}") message(STATUS "Copying ${executable_name}")
file(COPY "${executable_file}" DESTINATION "${base_dir}/squashfs-root/usr/bin/") file(COPY "${executable_file}" DESTINATION "${base_dir}/squashfs-root/usr/bin/")
@ -178,11 +262,15 @@ else()
endif() endif()
endfunction() endfunction()
# Adds a target to the bundle target, packing in required libraries. # Download plugins first so they don't overwrite linuxdeploy's AppRun file.
# If in_place is true, the bundling will be done in-place as part of the specified target. download_linuxdeploy_component("${LINUXDEPLOY_PATH}" "linuxdeploy/linuxdeploy-plugin-qt" "linuxdeploy-plugin-qt-${LINUXDEPLOY_ARCH}.AppImage")
function(bundle_target_internal target_name in_place) download_linuxdeploy_component("${LINUXDEPLOY_PATH}" "darealshinji/linuxdeploy-plugin-checkrt" "linuxdeploy-plugin-checkrt.sh")
# Create base bundle target if it does not exist. download_linuxdeploy_component("${LINUXDEPLOY_PATH}" "linuxdeploy/linuxdeploy" "linuxdeploy-${LINUXDEPLOY_ARCH}.AppImage")
if (NOT in_place AND NOT TARGET bundle) else()
# --- Bundling target creation logic ---
# Creates the base bundle target with common files and pre-bundle steps.
function(create_base_bundle_target)
message(STATUS "Creating base bundle target") message(STATUS "Creating base bundle target")
add_custom_target(bundle) add_custom_target(bundle)
@ -204,63 +292,72 @@ else()
add_custom_command( add_custom_command(
TARGET bundle TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/dist/scripting" "${CMAKE_BINARY_DIR}/bundle/scripting") COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/dist/scripting" "${CMAKE_BINARY_DIR}/bundle/scripting")
# On Linux, add a command to prepare linuxdeploy and any required plugins before any bundling occurs.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_TARGET_DOWNLOAD_LINUXDEPLOY=1"
"-DLINUXDEPLOY_PATH=${CMAKE_BINARY_DIR}/externals/linuxdeploy"
"-DLINUXDEPLOY_ARCH=${CMAKE_HOST_SYSTEM_PROCESSOR}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
endfunction()
# Adds a target to the bundle target, packing in required libraries.
# If in_place is true, the bundling will be done in-place as part of the specified target.
function(bundle_target_internal target_name in_place)
# Create base bundle target if it does not exist.
if (NOT in_place AND NOT TARGET bundle)
create_base_bundle_target()
endif() endif()
set(BUNDLE_EXECUTABLE_PATH "$<TARGET_FILE:${target_name}>") set(bundle_executable_path "$<TARGET_FILE:${target_name}>")
if (target_name MATCHES ".*qt") if (target_name MATCHES ".*qt")
set(BUNDLE_QT ON) set(bundle_qt ON)
if (APPLE) if (APPLE)
# For Qt targets on Apple, expect an app bundle. # For Qt targets on Apple, expect an app bundle.
set(BUNDLE_EXECUTABLE_PATH "$<TARGET_BUNDLE_DIR:${target_name}>") set(bundle_executable_path "$<TARGET_BUNDLE_DIR:${target_name}>")
endif() endif()
else() else()
set(BUNDLE_QT OFF) set(bundle_qt OFF)
endif() endif()
# Build a list of library search paths from prefix paths. # Build a list of library search paths from prefix paths.
foreach(prefix_path IN LISTS CMAKE_PREFIX_PATH CMAKE_SYSTEM_PREFIX_PATH) foreach(prefix_path IN LISTS CMAKE_FIND_ROOT_PATH CMAKE_PREFIX_PATH CMAKE_SYSTEM_PREFIX_PATH)
if (WIN32) if (WIN32)
list(APPEND BUNDLE_LIBRARY_PATHS "${prefix_path}/bin") list(APPEND bundle_library_paths "${prefix_path}/bin")
endif() endif()
list(APPEND BUNDLE_LIBRARY_PATHS "${prefix_path}/lib") list(APPEND bundle_library_paths "${prefix_path}/lib")
endforeach() endforeach()
foreach(library_path IN LISTS CMAKE_SYSTEM_LIBRARY_PATH) foreach(library_path IN LISTS CMAKE_SYSTEM_LIBRARY_PATH)
list(APPEND BUNDLE_LIBRARY_PATHS "${library_path}") list(APPEND bundle_library_paths "${library_path}")
endforeach() endforeach()
# On Linux, prepare linuxdeploy and any required plugins.
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUXDEPLOY_BASE "${CMAKE_BINARY_DIR}/externals/linuxdeploy")
# Download plugins first so they don't overwrite linuxdeploy's AppRun file.
download_linuxdeploy_component("${LINUXDEPLOY_BASE}" "linuxdeploy-plugin-qt" "linuxdeploy-plugin-qt-x86_64.AppImage")
download_linuxdeploy_component("${LINUXDEPLOY_BASE}" "linuxdeploy-plugin-checkrt" "linuxdeploy-plugin-checkrt-x86_64.sh")
download_linuxdeploy_component("${LINUXDEPLOY_BASE}" "linuxdeploy" "linuxdeploy-x86_64.AppImage")
set(EXTRA_BUNDLE_ARGS "-DLINUXDEPLOY=${LINUXDEPLOY_BASE}/squashfs-root/AppRun")
endif()
if (in_place) if (in_place)
message(STATUS "Adding in-place bundling to ${target_name}") message(STATUS "Adding in-place bundling to ${target_name}")
set(DEST_TARGET ${target_name}) set(dest_target ${target_name})
else() else()
message(STATUS "Adding ${target_name} to bundle target") message(STATUS "Adding ${target_name} to bundle target")
set(DEST_TARGET bundle) set(dest_target bundle)
add_dependencies(bundle ${target_name}) add_dependencies(bundle ${target_name})
endif() endif()
add_custom_command(TARGET ${DEST_TARGET} POST_BUILD add_custom_command(TARGET ${dest_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} COMMAND ${CMAKE_COMMAND}
"-DCMAKE_PREFIX_PATH=\"${CMAKE_PREFIX_PATH}\"" "-DQT_HOST_PATH=\"${QT_HOST_PATH}\""
"-DQT_TARGET_PATH=\"${QT_TARGET_PATH}\""
"-DBUNDLE_TARGET_EXECUTE=1" "-DBUNDLE_TARGET_EXECUTE=1"
"-DTARGET=${target_name}" "-DTARGET=${target_name}"
"-DSOURCE_PATH=${CMAKE_SOURCE_DIR}" "-DSOURCE_PATH=${CMAKE_SOURCE_DIR}"
"-DBINARY_PATH=${CMAKE_BINARY_DIR}" "-DBINARY_PATH=${CMAKE_BINARY_DIR}"
"-DEXECUTABLE_PATH=${BUNDLE_EXECUTABLE_PATH}" "-DEXECUTABLE_PATH=${bundle_executable_path}"
"-DBUNDLE_LIBRARY_PATHS=\"${BUNDLE_LIBRARY_PATHS}\"" "-DBUNDLE_LIBRARY_PATHS=\"${bundle_library_paths}\""
"-DBUNDLE_QT=${BUNDLE_QT}" "-DBUNDLE_QT=${bundle_qt}"
"-DIN_PLACE=${in_place}" "-DIN_PLACE=${in_place}"
${EXTRA_BUNDLE_ARGS} "-DLINUXDEPLOY=${CMAKE_BINARY_DIR}/externals/linuxdeploy/squashfs-root/AppRun"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake" -P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
endfunction() endfunction()

View File

@ -1,21 +1,20 @@
set(CURRENT_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) set(CURRENT_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR})
# This function downloads Qt using aqt. The path of the downloaded content will be added to the CMAKE_PREFIX_PATH. # Determines parameters based on the host and target for downloading the right Qt binaries.
# Params: function(determine_qt_parameters target host_out type_out arch_out arch_path_out host_type_out host_arch_out host_arch_path_out)
# target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool.
function(download_qt target)
if (target MATCHES "tools_.*") if (target MATCHES "tools_.*")
set(DOWNLOAD_QT_TOOL ON) set(tool ON)
else() else()
set(DOWNLOAD_QT_TOOL OFF) set(tool OFF)
endif() endif()
# Determine installation parameters for OS, architecture, and compiler # Determine installation parameters for OS, architecture, and compiler
if (WIN32) if (WIN32)
set(host "windows") set(host "windows")
set(type "desktop") set(type "desktop")
if (NOT DOWNLOAD_QT_TOOL)
if (NOT tool)
if (MINGW) if (MINGW)
set(arch "win64_mingw") set(arch "win64_mingw")
set(arch_path "mingw_64") set(arch_path "mingw_64")
@ -28,21 +27,35 @@ function(download_qt target)
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.") message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
endif() endif()
set(arch "win64_${arch_path}") set(arch "win64_${arch_path}")
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
set(host_arch_path "msvc2019_64")
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
# set(host_arch_path "msvc2019_arm64")
set(host_arch_path "msvc2019_64")
endif()
set(host_arch "win64_${host_arch_path}")
else() else()
message(FATAL_ERROR "Unsupported bundled Qt toolchain. Enable USE_SYSTEM_QT and provide your own.") message(FATAL_ERROR "Unsupported bundled Qt toolchain. Enable USE_SYSTEM_QT and provide your own.")
endif() endif()
endif() endif()
elseif (APPLE) elseif (APPLE)
set(host "mac") set(host "mac")
if (IOS AND NOT DOWNLOAD_QT_TOOL)
set(type "ios")
set(arch "ios")
set(arch_path "ios")
set(host_arch_path "macos")
else()
set(type "desktop") set(type "desktop")
set(arch "clang_64") set(arch "clang_64")
set(arch_path "macos") set(arch_path "macos")
if (IOS AND NOT tool)
set(host_type "${type}")
set(host_arch "${arch}")
set(host_arch_path "${arch_path}")
set(type "ios")
set(arch "ios")
set(arch_path "ios")
endif() endif()
else() else()
set(host "linux") set(host "linux")
@ -51,38 +64,64 @@ function(download_qt target)
set(arch_path "linux") set(arch_path "linux")
endif() endif()
get_external_prefix(qt base_path) set(${host_out} "${host}" PARENT_SCOPE)
file(MAKE_DIRECTORY "${base_path}") set(${type_out} "${type}" PARENT_SCOPE)
set(${arch_out} "${arch}" PARENT_SCOPE)
set(${arch_path_out} "${arch_path}" PARENT_SCOPE)
if (DEFINED host_type)
set(${host_type_out} "${host_type}" PARENT_SCOPE)
else()
set(${host_type_out} "${type}" PARENT_SCOPE)
endif()
if (DEFINED host_arch)
set(${host_arch_out} "${host_arch}" PARENT_SCOPE)
else()
set(${host_arch_out} "${arch}" PARENT_SCOPE)
endif()
if (DEFINED host_arch_path)
set(${host_arch_path_out} "${host_arch_path}" PARENT_SCOPE)
else()
set(${host_arch_path_out} "${arch_path}" PARENT_SCOPE)
endif()
endfunction()
# Download Qt binaries for a specifc configuration.
function(download_qt_configuration prefix_out target host type arch arch_path base_path)
if (target MATCHES "tools_.*")
set(tool ON)
else()
set(tool OFF)
endif()
set(install_args -c "${CURRENT_MODULE_DIR}/aqt_config.ini") set(install_args -c "${CURRENT_MODULE_DIR}/aqt_config.ini")
if (DOWNLOAD_QT_TOOL) if (tool)
set(prefix "${base_path}/Tools") set(prefix "${base_path}/Tools")
set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target})
else() else()
set(prefix "${base_path}/${target}/${arch_path}") set(prefix "${base_path}/${target}/${arch_path}")
if (host_arch_path) set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch}
set(host_flag "--autodesktop")
set(host_prefix "${base_path}/${target}/${host_arch_path}")
endif()
set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag}
-m qtmultimedia --archives qttranslations qttools qtsvg qtbase) -m qtmultimedia --archives qttranslations qttools qtsvg qtbase)
endif() endif()
if (NOT EXISTS "${prefix}") if (NOT EXISTS "${prefix}")
message(STATUS "Downloading binaries for Qt...") message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.9") set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.9")
if (WIN32) if (WIN32)
set(aqt_path "${base_path}/aqt.exe") set(aqt_path "${base_path}/aqt.exe")
if (NOT EXISTS "${aqt_path}")
file(DOWNLOAD file(DOWNLOAD
${AQT_PREBUILD_BASE_URL}/aqt.exe ${AQT_PREBUILD_BASE_URL}/aqt.exe
${aqt_path} SHOW_PROGRESS) ${aqt_path} SHOW_PROGRESS)
endif()
execute_process(COMMAND ${aqt_path} ${install_args} execute_process(COMMAND ${aqt_path} ${install_args}
WORKING_DIRECTORY ${base_path}) WORKING_DIRECTORY ${base_path})
elseif (APPLE) elseif (APPLE)
set(aqt_path "${base_path}/aqt-macos") set(aqt_path "${base_path}/aqt-macos")
if (NOT EXISTS "${aqt_path}")
file(DOWNLOAD file(DOWNLOAD
${AQT_PREBUILD_BASE_URL}/aqt-macos ${AQT_PREBUILD_BASE_URL}/aqt-macos
${aqt_path} SHOW_PROGRESS) ${aqt_path} SHOW_PROGRESS)
endif()
execute_process(COMMAND chmod +x ${aqt_path}) execute_process(COMMAND chmod +x ${aqt_path})
execute_process(COMMAND ${aqt_path} ${install_args} execute_process(COMMAND ${aqt_path} ${install_args}
WORKING_DIRECTORY ${base_path}) WORKING_DIRECTORY ${base_path})
@ -96,18 +135,38 @@ function(download_qt target)
execute_process(COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${aqt_install_path} python3 -m aqt ${install_args} execute_process(COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${aqt_install_path} python3 -m aqt ${install_args}
WORKING_DIRECTORY ${base_path}) WORKING_DIRECTORY ${base_path})
endif() endif()
message(STATUS "Downloaded Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path} to ${prefix}")
endif() endif()
message(STATUS "Using downloaded Qt binaries at ${prefix}") set(${prefix_out} "${prefix}" PARENT_SCOPE)
endfunction()
# Add the Qt prefix path so CMake can locate it. # This function downloads Qt using aqt.
# The path of the downloaded content will be added to the CMAKE_PREFIX_PATH.
# QT_TARGET_PATH is set to the Qt for the compile target platform.
# QT_HOST_PATH is set to a host-compatible Qt, for running tools.
# Params:
# target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool.
function(download_qt target)
determine_qt_parameters("${target}" host type arch arch_path host_type host_arch host_arch_path)
get_external_prefix(qt base_path)
file(MAKE_DIRECTORY "${base_path}")
download_qt_configuration(prefix "${target}" "${host}" "${type}" "${arch}" "${arch_path}" "${base_path}")
if (DEFINED host_arch_path AND NOT "${host_arch_path}" STREQUAL "${arch_path}")
download_qt_configuration(host_prefix "${target}" "${host}" "${host_type}" "${host_arch}" "${host_arch_path}" "${base_path}")
else()
set(host_prefix "${prefix}")
endif()
set(QT_TARGET_PATH "${prefix}" CACHE STRING "")
set(QT_HOST_PATH "${host_prefix}" CACHE STRING "")
# Add the target Qt prefix path so CMake can locate it.
list(APPEND CMAKE_PREFIX_PATH "${prefix}") list(APPEND CMAKE_PREFIX_PATH "${prefix}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
if (DEFINED host_prefix)
message(STATUS "Using downloaded host Qt binaries at ${host_prefix}")
set(QT_HOST_PATH "${host_prefix}" CACHE STRING "")
endif()
endfunction() endfunction()
function(download_moltenvk) function(download_moltenvk)

View File

@ -57,6 +57,12 @@ if(USE_SYSTEM_CRYPTOPP)
add_library(cryptopp INTERFACE) add_library(cryptopp INTERFACE)
target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp) target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp)
else() else()
if (WIN32 AND NOT MSVC AND "arm64" IN_LIST ARCHITECTURE)
# TODO: CryptoPP ARM64 ASM does not seem to support Windows unless compiled with MSVC.
# TODO: See https://github.com/weidai11/cryptopp/issues/1260
set(CRYPTOPP_DISABLE_ASM ON CACHE BOOL "")
endif()
set(CRYPTOPP_BUILD_DOCUMENTATION OFF CACHE BOOL "") set(CRYPTOPP_BUILD_DOCUMENTATION OFF CACHE BOOL "")
set(CRYPTOPP_BUILD_TESTING OFF CACHE BOOL "") set(CRYPTOPP_BUILD_TESTING OFF CACHE BOOL "")
set(CRYPTOPP_INSTALL OFF CACHE BOOL "") set(CRYPTOPP_INSTALL OFF CACHE BOOL "")
@ -235,6 +241,18 @@ endif()
# DiscordRPC # DiscordRPC
if (USE_DISCORD_PRESENCE) if (USE_DISCORD_PRESENCE)
# rapidjson used by discord-rpc is old and doesn't correctly detect endianness for some platforms.
include(TestBigEndian)
test_big_endian(RAPIDJSON_BIG_ENDIAN)
if(RAPIDJSON_BIG_ENDIAN)
add_compile_definitions(RAPIDJSON_ENDIAN=1)
else()
add_compile_definitions(RAPIDJSON_ENDIAN=0)
endif()
# Apply a dummy CLANG_FORMAT_SUFFIX to disable discord-rpc's unnecessary automatic clang-format.
set(CLANG_FORMAT_SUFFIX "dummy")
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL) add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif() endif()

@ -1 +1 @@
Subproject commit 9327192b0095dc1f420b2082d37bd427b5750d48 Subproject commit a99c80c26686e44eddf0432140ae397f3efbd0b3

2
externals/dynarmic vendored

@ -1 +1 @@
Subproject commit ca0e264f4f962e29baa23a3282ce484625866b98 Subproject commit 30f1a3c6289075ef4af08f5ec502be2fc8627a0c

2
externals/oaknut vendored

@ -1 +1 @@
Subproject commit 9d091109deb445bc6e9289c6195a282b7c993d49 Subproject commit 6b1d57ea7ed4882d32a91eeaa6557b0ecb4da152