diff options
Diffstat (limited to 'src/corelib/Qt6CoreDeploySupport.cmake')
-rw-r--r-- | src/corelib/Qt6CoreDeploySupport.cmake | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/src/corelib/Qt6CoreDeploySupport.cmake b/src/corelib/Qt6CoreDeploySupport.cmake new file mode 100644 index 0000000000..2fc8f8bf1c --- /dev/null +++ b/src/corelib/Qt6CoreDeploySupport.cmake @@ -0,0 +1,680 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# NOTE: This code should only ever be executed in script mode. It expects to be +# used either as part of an install(CODE) call or called by a script +# invoked via cmake -P as a POST_BUILD step. + +cmake_minimum_required(VERSION 3.16...3.21) + +function(qt6_deploy_qt_conf qt_conf_absolute_path) + set(no_value_options "") + set(single_value_options + PREFIX + DOC_DIR + HEADERS_DIR + LIB_DIR + LIBEXEC_DIR + BIN_DIR + PLUGINS_DIR + QML_DIR + ARCHDATA_DIR + DATA_DIR + TRANSLATIONS_DIR + EXAMPLES_DIR + TESTS_DIR + SETTINGS_DIR + ) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unparsed arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + if(NOT IS_ABSOLUTE "${qt_conf_absolute_path}") + message(FATAL_ERROR + "Given qt.conf path is not an absolute path: '${qt_conf_absolute_path}'") + endif() + + # Only write out locations that differ from the defaults + set(contents "[Paths]\n") + if(arg_PREFIX) + string(APPEND contents "Prefix = ${arg_PREFIX}\n") + endif() + if(arg_DOC_DIR AND NOT arg_DOC_DIR STREQUAL "doc") + string(APPEND contents "Documentation = ${arg_DOC_DIR}\n") + endif() + if(arg_HEADERS_DIR AND NOT arg_HEADERS_DIR STREQUAL "include") + string(APPEND contents "Headers = ${arg_HEADERS_DIR}\n") + endif() + if(arg_LIB_DIR AND NOT arg_LIB_DIR STREQUAL "lib") + string(APPEND contents "Libraries = ${arg_LIB_DIR}\n") + endif() + + # This one is special, the default is platform-specific + if(arg_LIBEXEC_DIR AND + ((WIN32 AND NOT arg_LIBEXEC_DIR STREQUAL "bin") OR + (NOT WIN32 AND NOT arg_LIBEXEC_DIR STREQUAL "libexec"))) + string(APPEND contents "LibraryExecutables = ${arg_LIBEXEC_DIR}\n") + endif() + + if(arg_BIN_DIR AND NOT arg_BIN_DIR STREQUAL "bin") + string(APPEND contents "Binaries = ${arg_BIN_DIR}\n") + endif() + if(arg_PLUGINS_DIR AND NOT arg_PLUGINS_DIR STREQUAL "plugins") + string(APPEND contents "Plugins = ${arg_PLUGINS_DIR}\n") + endif() + if(arg_QML_DIR AND NOT arg_QML_DIR STREQUAL "qml") + string(APPEND contents "QmlImports = ${arg_QML_DIR}\n") + endif() + if(arg_ARCHDATA_DIR AND NOT arg_ARCHDATA_DIR STREQUAL ".") + string(APPEND contents "ArchData = ${arg_ARCHDATA_DIR}\n") + endif() + if(arg_DATA_DIR AND NOT arg_DATA_DIR STREQUAL ".") + string(APPEND contents "Data = ${arg_DATA_DIR}\n") + endif() + if(arg_TRANSLATIONS_DIR AND NOT arg_TRANSLATIONS_DIR STREQUAL "translations") + string(APPEND contents "Translations = ${arg_TRANSLATIONS_DIR}\n") + endif() + if(arg_EXAMPLES_DIR AND NOT arg_EXAMPLES_DIR STREQUAL "examples") + string(APPEND contents "Examples = ${arg_EXAMPLES_DIR}\n") + endif() + if(arg_TESTS_DIR AND NOT arg_TESTS_DIR STREQUAL "tests") + string(APPEND contents "Tests = ${arg_TESTS_DIR}\n") + endif() + if(arg_SETTINGS_DIR AND NOT arg_SETTINGS_DIR STREQUAL ".") + string(APPEND contents "Settings = ${arg_SETTINGS_DIR}\n") + endif() + + message(STATUS "Writing ${qt_conf_absolute_path}") + file(WRITE "${qt_conf_absolute_path}" "${contents}") +endfunction() + +if(NOT __QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_deploy_qt_conf) + if(__QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_deploy_qt_conf(${ARGV}) + else() + message(FATAL_ERROR "qt_deploy_qt_conf() is only available in Qt 6.") + endif() + endfunction() +endif() + +# Copied from QtCMakeHelpers.cmake +function(_qt_internal_re_escape out_var str) + string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" regex "${str}") + set(${out_var} ${regex} PARENT_SCOPE) +endfunction() + +function(_qt_internal_set_rpath) + if(NOT CMAKE_HOST_UNIX OR CMAKE_HOST_APPLE) + message(WARNING "_qt_internal_set_rpath is not implemented on this platform.") + return() + endif() + + set(no_value_options "") + set(single_value_options FILE NEW_RPATH) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + if(__QT_DEPLOY_USE_PATCHELF) + message(STATUS "Setting runtime path of '${arg_FILE}' to '${arg_NEW_RPATH}'.") + execute_process( + COMMAND ${__QT_DEPLOY_PATCHELF_EXECUTABLE} --set-rpath "${arg_NEW_RPATH}" "${arg_FILE}" + RESULT_VARIABLE process_result + ) + if(NOT process_result EQUAL "0") + if(process_result MATCHES "^[0-9]+$") + message(FATAL_ERROR "patchelf failed with exit code ${process_result}.") + else() + message(FATAL_ERROR "patchelf failed: ${process_result}.") + endif() + endif() + else() + # Warning: file(RPATH_SET) is CMake-internal API. + file(RPATH_SET + FILE "${arg_FILE}" + NEW_RPATH "${arg_NEW_RPATH}" + ) + endif() +endfunction() + +# Store the platform-dependent $ORIGIN marker in out_var. +function(_qt_internal_get_rpath_origin out_var) + if(__QT_DEPLOY_SYSTEM_NAME STREQUAL "Darwin") + set(rpath_origin "@loader_path") + else() + set(rpath_origin "$ORIGIN") + endif() + set(${out_var} ${rpath_origin} PARENT_SCOPE) +endfunction() + +# Add a function to the list of deployment hooks. +# The hooks are run at the end of _qt_internal_generic_deployqt. +# +# Every hook is passed the parameters of _qt_internal_generic_deployqt plus the following: +# RESOLVED_DEPENDENCIES: list of resolved dependencies that were installed. +function(_qt_internal_add_deployment_hook function_name) + set_property(GLOBAL APPEND PROPERTY QT_INTERNAL_DEPLOYMENT_HOOKS "${function_name}") +endfunction() + +# Run all registered deployment hooks. +function(_qt_internal_run_deployment_hooks) + get_property(hooks GLOBAL PROPERTY QT_INTERNAL_DEPLOYMENT_HOOKS) + foreach(hook IN LISTS hooks) + if(NOT COMMAND "${hook}") + message(AUTHOR_WARNING "'${hook}' is not a command but was added as deployment hook.") + continue() + endif() + if(CMAKE_VERSION GREATER_EQUAL "3.19") + cmake_language(CALL "${hook}" ${ARGV}) + else() + set(temp_file ".qt-run-deploy-hook.cmake") + file(WRITE "${temp_file}" "${hook}(${ARGV})") + include(${temp_file}) + file(REMOVE "${temp_file}") + endif() + endforeach() +endfunction() + +function(_qt_internal_generic_deployqt) + set(no_value_options + NO_TRANSLATIONS + VERBOSE + ) + set(single_value_options + LIB_DIR + PLUGINS_DIR + ) + set(file_GRD_options + EXECUTABLES + LIBRARIES + MODULES + PRE_INCLUDE_REGEXES + PRE_EXCLUDE_REGEXES + POST_INCLUDE_REGEXES + POST_EXCLUDE_REGEXES + POST_INCLUDE_FILES + POST_EXCLUDE_FILES + ) + set(multi_value_options ${file_GRD_options}) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + if(arg_VERBOSE OR __QT_DEPLOY_VERBOSE) + set(verbose TRUE) + endif() + + # Make input file paths absolute + foreach(var IN ITEMS EXECUTABLES LIBRARIES MODULES) + string(PREPEND var arg_) + set(abspaths "") + foreach(path IN LISTS ${var}) + get_filename_component(abspath "${path}" REALPATH BASE_DIR "${QT_DEPLOY_PREFIX}") + list(APPEND abspaths "${abspath}") + endforeach() + set(${var} "${abspaths}") + endforeach() + + # We need to get the runtime dependencies of plugins too. + list(APPEND arg_MODULES ${__QT_DEPLOY_PLUGINS}) + + # Forward the arguments that are exactly the same for file(GET_RUNTIME_DEPENDENCIES). + set(file_GRD_args "") + foreach(var IN LISTS file_GRD_options) + if(NOT "${arg_${var}}" STREQUAL "") + list(APPEND file_GRD_args ${var} ${arg_${var}}) + endif() + endforeach() + + # Compile a list of regular expressions that represent ignored library directories. + if("${arg_POST_EXCLUDE_REGEXES}" STREQUAL "") + set(regexes "") + foreach(path IN LISTS QT_DEPLOY_IGNORED_LIB_DIRS) + _qt_internal_re_escape(path_rex "${path}") + list(APPEND regexes "^${path_rex}") + endforeach() + if(regexes) + list(APPEND file_GRD_args POST_EXCLUDE_REGEXES ${regexes}) + endif() + endif() + + # Get the runtime dependencies recursively. + file(GET_RUNTIME_DEPENDENCIES + ${file_GRD_args} + RESOLVED_DEPENDENCIES_VAR resolved + UNRESOLVED_DEPENDENCIES_VAR unresolved + CONFLICTING_DEPENDENCIES_PREFIX conflicting + ) + if(verbose) + message("file(GET_RUNTIME_DEPENDENCIES ${file_args})") + foreach(file IN LISTS resolved) + message(" resolved: ${file}") + endforeach() + foreach(file IN LISTS unresolved) + message(" unresolved: ${file}") + endforeach() + foreach(file IN LISTS conflicting_FILENAMES) + message(" conflicting: ${file}") + message(" with ${conflicting_${file}}") + endforeach() + endif() + + # Deploy the Qt libraries. + file(INSTALL ${resolved} + DESTINATION "${CMAKE_INSTALL_PREFIX}/${arg_LIB_DIR}" + FOLLOW_SYMLINK_CHAIN + ) + + # Determine the runtime path origin marker if necessary. + if(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH) + _qt_internal_get_rpath_origin(rpath_origin) + endif() + + # Deploy the Qt plugins. + foreach(file_path IN LISTS __QT_DEPLOY_PLUGINS) + file(RELATIVE_PATH destination + "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_PLUGINS}" + "${file_path}" + ) + get_filename_component(destination "${destination}" DIRECTORY) + string(PREPEND destination "${CMAKE_INSTALL_PREFIX}/${arg_PLUGINS_DIR}/") + file(INSTALL ${file_path} DESTINATION ${destination}) + + if(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH) + get_filename_component(file_name ${file_path} NAME) + file(RELATIVE_PATH rel_lib_dir "${destination}" + "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_LIB_DIR}") + _qt_internal_set_rpath( + FILE "${destination}/${file_name}" + NEW_RPATH "${rpath_origin}/${rel_lib_dir}" + ) + endif() + endforeach() + + # Deploy translations. + if(NOT arg_NO_TRANSLATIONS) + qt6_deploy_translations() + endif() + + _qt_internal_run_deployment_hooks(${ARGV} RESOLVED_DEPENDENCIES ${resolved}) +endfunction() + +function(qt6_deploy_runtime_dependencies) + + if(NOT __QT_DEPLOY_TOOL) + message(FATAL_ERROR "No Qt deploy tool available for this target platform") + endif() + + set(no_value_options + GENERATE_QT_CONF + VERBOSE + NO_OVERWRITE + NO_APP_STORE_COMPLIANCE # TODO: Might want a better name + NO_TRANSLATIONS + NO_COMPILER_RUNTIME + ) + set(single_value_options + EXECUTABLE + BIN_DIR + LIB_DIR + LIBEXEC_DIR + PLUGINS_DIR + QML_DIR + ) + set(file_GRD_options + # The following include/exclude options are only used if the "generic deploy tool" is + # used. The options are what file(GET_RUNTIME_DEPENDENCIES) supports. + PRE_INCLUDE_REGEXES + PRE_EXCLUDE_REGEXES + POST_INCLUDE_REGEXES + POST_EXCLUDE_REGEXES + POST_INCLUDE_FILES + POST_EXCLUDE_FILES + ) + set(multi_value_options + # These ADDITIONAL_... options are based on what file(GET_RUNTIME_DEPENDENCIES) + # supports. We differentiate between the types of binaries so that we keep + # open the possibility of switching to a purely CMake implementation of + # the deploy tool based on file(GET_RUNTIME_DEPENDENCIES) instead of the + # individual platform-specific tools (macdeployqt, windeployqt, etc.). + ADDITIONAL_EXECUTABLES + ADDITIONAL_LIBRARIES + ADDITIONAL_MODULES + ${file_GRD_options} + DEPLOY_TOOL_OPTIONS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unparsed arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + if(NOT arg_EXECUTABLE) + message(FATAL_ERROR "EXECUTABLE must be specified") + endif() + + # None of these are used if the executable is a macOS app bundle + if(NOT arg_BIN_DIR) + set(arg_BIN_DIR "${QT_DEPLOY_BIN_DIR}") + endif() + if(NOT arg_LIBEXEC_DIR) + set(arg_LIBEXEC_DIR "${QT_DEPLOY_LIBEXEC_DIR}") + endif() + if(NOT arg_LIB_DIR) + set(arg_LIB_DIR "${QT_DEPLOY_LIB_DIR}") + endif() + if(NOT arg_QML_DIR) + set(arg_QML_DIR "${QT_DEPLOY_QML_DIR}") + endif() + if(NOT arg_PLUGINS_DIR) + set(arg_PLUGINS_DIR "${QT_DEPLOY_PLUGINS_DIR}") + endif() + + # macdeployqt always writes out a qt.conf file. It will complain if one + # already exists, so leave it to create it for us if we will be running it. + if(__QT_DEPLOY_SYSTEM_NAME STREQUAL Darwin) + # We might get EXECUTABLE pointing to either the actual binary under the + # Contents/MacOS directory, or it might be pointing to the top of the + # app bundle (i.e. the <appname>.app directory). We want the latter to + # pass to macdeployqt. + if(arg_EXECUTABLE MATCHES "^((.*/)?(.*).app)/Contents/MacOS/(.*)$") + set(arg_EXECUTABLE "${CMAKE_MATCH_1}") + endif() + elseif(arg_GENERATE_QT_CONF) + set(exe_dir "${QT_DEPLOY_BIN_DIR}") + if(exe_dir STREQUAL "" OR exe_dir STREQUAL ".") + set(exe_dir ".") + set(prefix ".") + else() + string(REPLACE "/" ";" path "${exe_dir}") + list(LENGTH path path_count) + string(REPEAT "../" ${path_count} rel_path) + string(REGEX REPLACE "/+$" "" prefix "${rel_path}") + endif() + qt6_deploy_qt_conf("${QT_DEPLOY_PREFIX}/${exe_dir}/qt.conf" + PREFIX "${prefix}" + BIN_DIR "${arg_BIN_DIR}" + LIB_DIR "${arg_LIB_DIR}" + PLUGINS_DIR "${arg_PLUGINS_DIR}" + QML_DIR "${arg_QML_DIR}" + ) + endif() + + set(extra_binaries_option "") + set(tool_options "") + + if(arg_VERBOSE OR __QT_DEPLOY_VERBOSE) + # macdeployqt supports 0-3: 0=no output, 1=error/warn (default), 2=normal, 3=debug + # windeployqt supports 0-2: 0=error/warn (default), 1=verbose, 2=full_verbose + if(__QT_DEPLOY_SYSTEM_NAME STREQUAL Windows) + list(APPEND tool_options --verbose 2) + elseif(__QT_DEPLOY_SYSTEM_NAME STREQUAL Darwin) + list(APPEND tool_options -verbose=3) + else() + list(APPEND tool_options VERBOSE) + endif() + endif() + + if(__QT_DEPLOY_SYSTEM_NAME STREQUAL Windows) + list(APPEND tool_options + --dir . + --libdir "${arg_BIN_DIR}" # NOTE: Deliberately not arg_LIB_DIR + --plugindir "${arg_PLUGINS_DIR}" + --qml-deploy-dir "${arg_QML_DIR}" + --translationdir "${QT_DEPLOY_TRANSLATIONS_DIR}" + ) + if(NOT arg_NO_OVERWRITE) + list(APPEND tool_options --force) + endif() + if(arg_NO_TRANSLATIONS) + list(APPEND tool_options --no-translations) + endif() + if(arg_NO_COMPILER_RUNTIME) + list(APPEND tool_options --no-compiler-runtime) + endif() + + # Specify path to target Qt's qtpaths .exe or .bat file, so windeployqt deploys the correct + # libraries when cross-compiling from x86_64 to arm64 windows. + if(__QT_DEPLOY_TARGET_QT_PATHS_PATH AND EXISTS "${__QT_DEPLOY_TARGET_QT_PATHS_PATH}") + list(APPEND tool_options --qtpaths "${__QT_DEPLOY_TARGET_QT_PATHS_PATH}") + else() + message(WARNING + "No qtpaths executable found for target Qt " + "at: ${__QT_DEPLOY_TARGET_QT_PATHS_PATH}. " + "Libraries may not be deployed correctly.") + endif() + + list(APPEND tool_options ${arg_DEPLOY_TOOL_OPTIONS}) + elseif(__QT_DEPLOY_SYSTEM_NAME STREQUAL Darwin) + set(extra_binaries_option "-executable=") + if(NOT arg_NO_APP_STORE_COMPLIANCE) + list(APPEND tool_options -appstore-compliant) + endif() + if(NOT arg_NO_OVERWRITE) + list(APPEND tool_options -always-overwrite) + endif() + list(APPEND tool_options ${arg_DEPLOY_TOOL_OPTIONS}) + endif() + + # This is an internal variable. It is normally unset and is only intended + # for debugging purposes. It may be removed at any time without warning. + list(APPEND tool_options ${__qt_deploy_tool_extra_options}) + + if(__QT_DEPLOY_TOOL STREQUAL "GRD") + message(STATUS "Running generic Qt deploy tool on ${arg_EXECUTABLE}") + + if(NOT "${arg_DEPLOY_TOOL_OPTIONS}" STREQUAL "") + message(WARNING + "DEPLOY_TOOL_OPTIONS was specified but has no effect when using the generic " + "deployment tool." + ) + endif() + + # Construct the EXECUTABLES, LIBRARIES and MODULES arguments. + list(APPEND tool_options EXECUTABLES ${arg_EXECUTABLE}) + if(NOT "${arg_ADDITIONAL_EXECUTABLES}" STREQUAL "") + list(APPEND tool_options ${arg_ADDITIONAL_EXECUTABLES}) + endif() + foreach(file_type LIBRARIES MODULES) + if("${arg_ADDITIONAL_${file_type}}" STREQUAL "") + continue() + endif() + list(APPEND tool_options ${file_type} ${arg_ADDITIONAL_${file_type}}) + endforeach() + + # Forward the arguments that are exactly the same for file(GET_RUNTIME_DEPENDENCIES). + foreach(var IN LISTS file_GRD_options) + if(NOT "${arg_${var}}" STREQUAL "") + list(APPEND tool_options ${var} ${arg_${var}}) + endif() + endforeach() + + if(arg_NO_TRANSLATIONS) + list(APPEND tool_options NO_TRANSLATIONS) + endif() + + _qt_internal_generic_deployqt( + EXECUTABLE "${arg_EXECUTABLE}" + LIB_DIR "${arg_LIB_DIR}" + PLUGINS_DIR "${arg_PLUGINS_DIR}" + ${tool_options} + ) + return() + endif() + + # Both windeployqt and macdeployqt don't differentiate between the different + # types of binaries, so we merge the lists and treat them all the same. + set(additional_binaries + ${arg_ADDITIONAL_EXECUTABLES} + ${arg_ADDITIONAL_LIBRARIES} + ${arg_ADDITIONAL_MODULES} + ) + foreach(extra_binary IN LISTS additional_binaries) + list(APPEND tool_options "${extra_binaries_option}${extra_binary}") + endforeach() + + message(STATUS + "Running Qt deploy tool for ${arg_EXECUTABLE} in working directory '${QT_DEPLOY_PREFIX}'") + execute_process( + COMMAND_ECHO STDOUT + COMMAND "${__QT_DEPLOY_TOOL}" "${arg_EXECUTABLE}" ${tool_options} + WORKING_DIRECTORY "${QT_DEPLOY_PREFIX}" + RESULT_VARIABLE result + ) + if(result) + message(FATAL_ERROR "Executing ${__QT_DEPLOY_TOOL} failed: ${result}") + endif() + +endfunction() + +if(NOT __QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_deploy_runtime_dependencies) + if(__QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_deploy_runtime_dependencies(${ARGV}) + else() + message(FATAL_ERROR "qt_deploy_runtime_dependencies() is only available in Qt 6.") + endif() + endfunction() +endif() + +function(_qt_internal_show_skip_runtime_deploy_message qt_build_type_string) + set(no_value_options "") + set(single_value_options + EXTRA_MESSAGE + ) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + message(STATUS + "Skipping runtime deployment steps. " + "Support for installing runtime dependencies is not implemented for " + "this target platform (${__QT_DEPLOY_SYSTEM_NAME}, ${qt_build_type_string}). " + "${arg_EXTRA_MESSAGE}" + ) +endfunction() + +# This function is currently in Technical Preview. +# Its signature and behavior might change. +function(qt6_deploy_translations) + set(no_value_options VERBOSE) + set(single_value_options + LCONVERT + ) + set(multi_value_options + CATALOGS + LOCALES + ) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + set(verbose OFF) + if(arg_VERBOSE OR __QT_DEPLOY_VERBOSE) + set(verbose ON) + endif() + + if(arg_CATALOGS) + set(catalogs ${arg_CATALOGS}) + else() + set(catalogs ${__QT_DEPLOY_I18N_CATALOGS}) + endif() + + get_filename_component(qt_translations_dir "${__QT_DEPLOY_QT_INSTALL_TRANSLATIONS}" ABSOLUTE + BASE_DIR "${__QT_DEPLOY_QT_INSTALL_PREFIX}" + ) + set(locales ${arg_LOCALES}) + if(NOT locales) + file(GLOB locales RELATIVE "${qt_translations_dir}" "${qt_translations_dir}/*.qm") + list(TRANSFORM locales REPLACE "\\.qm$" "") + list(TRANSFORM locales REPLACE "^qt_help" "qt-help") + list(TRANSFORM locales REPLACE "^[^_]+" "") + list(TRANSFORM locales REPLACE "^_" "") + list(REMOVE_DUPLICATES locales) + endif() + + # Ensure existence of the output directory. + set(output_dir "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_TRANSLATIONS_DIR}") + if(NOT EXISTS "${output_dir}") + file(MAKE_DIRECTORY "${output_dir}") + endif() + + # Locate lconvert. + if(arg_LCONVERT) + set(lconvert "${arg_LCONVERT}") + else() + set(lconvert "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_BINS}/lconvert") + if(CMAKE_HOST_WIN32) + string(APPEND lconvert ".exe") + endif() + if(NOT EXISTS ${lconvert}) + message(STATUS "lconvert was not found. Skipping deployment of translations.") + return() + endif() + endif() + + # Find the .qm files for the selected locales + if(verbose) + message(STATUS "Looking for translations in ${qt_translations_dir}") + endif() + foreach(locale IN LISTS locales) + set(qm_files "") + foreach(catalog IN LISTS catalogs) + set(qm_file "${catalog}_${locale}.qm") + if(EXISTS "${qt_translations_dir}/${qm_file}") + list(APPEND qm_files ${qm_file}) + endif() + endforeach() + + if(NOT qm_files) + message(WARNING "No translations found for requested locale '${locale}'.") + continue() + endif() + + if(verbose) + foreach(qm_file IN LISTS qm_files) + message(STATUS "found translation file: ${qm_file}") + endforeach() + endif() + + # Merge the .qm files into one qt_${locale}.qm file. + set(output_file_name "qt_${locale}.qm") + set(output_file_path "${output_dir}/${output_file_name}") + message(STATUS "Creating: ${output_file_path}") + set(extra_options) + if(verbose) + list(APPEND extra_options COMMAND_ECHO STDOUT) + endif() + execute_process( + COMMAND ${lconvert} -if qm -o ${output_file_path} ${qm_files} + WORKING_DIRECTORY ${qt_translations_dir} + RESULT_VARIABLE process_result + ${extra_options} + ) + if(NOT process_result EQUAL "0") + if(process_result MATCHES "^[0-9]+$") + message(WARNING "lconvert failed with exit code ${process_result}.") + else() + message(WARNING "lconvert failed: ${process_result}.") + endif() + endif() + endforeach() +endfunction() + +if(NOT __QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_deploy_translations) + if(__QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_deploy_translations(${ARGV}) + else() + message(FATAL_ERROR "qt_deploy_translations() is only available in Qt 6.") + endif() + endfunction() +endif() |