CMake: Rework deployment of Qt plugins
[ChangeLog][CMake] qt6_import_plugins doesn't have any effect anymore on plugin deployment with the CMake deployment API on Linux. [ChangeLog][CMake] qt6_deploy_runtime_dependencies now supports the following plugin selection flags on Linux: INCLUDE_PLUGINS, INCLUDE_PLUGIN_TYPES, EXCLUDE_PLUGINS, EXCLUDE_PLUGIN_TYPES. Before this patch, we were deploying the plugins of every Qt module we discovered when walking the dependencies of the target to be deployed. That doesn't work if the target links against a shared library that privately links against another Qt module, i.e. QtMultimedia like in the bug report. We need to take into account the Qt modules that are discovered when retrieving the runtime dependencies at deployment time and deploy their plugins. Also, we now run file(GET_RUNTIME_DEPENDENCIES) and plugin discovery in multiple passes. Every pass might discover new Qt plugins whose runtime dependencies must be resolved. We stop if a pass did not discover new plugins or after a certain number of passes. Plugin discovery works like this: - identify the Qt modules within resolved libraries, - read the plugin types for the module from the corresponding, module JSON file in Qt's installation, - glob the files in Qt's plugins directory, - select the files according to the plugin types from the Qt modules and the INCLUDE.../EXCLUDE... arguments passed by the user. We also read the default QPA platforms from modules/Gui.json and deploy those. Fixes: QTBUG-117731 Change-Id: I62ec96ab6de8327941c4c5a0ba83fd89f1733768 Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
This commit is contained in:
parent
f2ac985d2f
commit
87f4f0af6e
@ -413,46 +413,6 @@ function(__qt_internal_collect_plugin_targets_from_dependencies_of_plugins targe
|
||||
set("${out_var}" "${plugin_targets}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Generate plugin information files for deployment
|
||||
#
|
||||
# Arguments:
|
||||
# OUT_PLUGIN_TARGETS - Variable name to store the plugin targets that were collected with
|
||||
# __qt_internal_collect_plugin_targets_from_dependencies.
|
||||
function(__qt_internal_generate_plugin_deployment_info target)
|
||||
set(no_value_options "")
|
||||
set(single_value_options "OUT_PLUGIN_TARGETS")
|
||||
set(multi_value_options "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
||||
)
|
||||
|
||||
__qt_internal_collect_plugin_targets_from_dependencies("${target}" plugin_targets)
|
||||
if(NOT "${arg_OUT_PLUGIN_TARGETS}" STREQUAL "")
|
||||
set("${arg_OUT_PLUGIN_TARGETS}" "${plugin_targets}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
get_target_property(marked_for_deployment ${target} _qt_marked_for_deployment)
|
||||
if(NOT marked_for_deployment)
|
||||
return()
|
||||
endif()
|
||||
|
||||
__qt_internal_collect_plugin_library_files(${target} "${plugin_targets}" plugins_files)
|
||||
set(plugins_files "$<FILTER:${plugins_files},EXCLUDE,^$>")
|
||||
|
||||
_qt_internal_get_deploy_impl_dir(deploy_impl_dir)
|
||||
set(file_path "${deploy_impl_dir}/${target}-plugins")
|
||||
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
|
||||
if(is_multi_config)
|
||||
string(APPEND file_path "-$<CONFIG>")
|
||||
endif()
|
||||
string(APPEND file_path ".cmake")
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT ${file_path}
|
||||
CONTENT "set(__QT_DEPLOY_PLUGINS ${plugins_files})"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Main logic of finalizer mode.
|
||||
function(__qt_internal_apply_plugin_imports_finalizer_mode target)
|
||||
# Process a target only once.
|
||||
@ -461,9 +421,6 @@ function(__qt_internal_apply_plugin_imports_finalizer_mode target)
|
||||
return()
|
||||
endif()
|
||||
|
||||
__qt_internal_generate_plugin_deployment_info(${target}
|
||||
OUT_PLUGIN_TARGETS plugin_targets)
|
||||
|
||||
# By default if the project hasn't explicitly opted in or out, use finalizer mode.
|
||||
# The precondition for this is that qt_finalize_target was called (either explicitly by the user
|
||||
# or auto-deferred by CMake 3.19+).
|
||||
@ -477,6 +434,7 @@ function(__qt_internal_apply_plugin_imports_finalizer_mode target)
|
||||
return()
|
||||
endif()
|
||||
|
||||
__qt_internal_collect_plugin_targets_from_dependencies("${target}" plugin_targets)
|
||||
__qt_internal_collect_plugin_init_libraries("${plugin_targets}" init_libraries)
|
||||
__qt_internal_collect_plugin_libraries("${plugin_targets}" plugin_libraries)
|
||||
|
||||
|
@ -24,11 +24,10 @@ set(QT@PROJECT_VERSION_MAJOR@_IS_SHARED_LIBS_BUILD "@BUILD_SHARED_LIBS@")
|
||||
set(QT@PROJECT_VERSION_MAJOR@_DEBUG_POSTFIX "@CMAKE_DEBUG_POSTFIX@")
|
||||
|
||||
set(_Qt6CTestMacros "${CMAKE_CURRENT_LIST_DIR}/Qt6CTestMacros.cmake")
|
||||
@qtcore_extra_cmake_code@
|
||||
|
||||
_qt_internal_setup_deploy_support()
|
||||
|
||||
@qtcore_extra_cmake_code@
|
||||
|
||||
if(ANDROID_PLATFORM)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@AndroidMacros.cmake")
|
||||
_qt_internal_create_global_android_targets()
|
||||
|
@ -181,6 +181,239 @@ function(_qt_internal_run_deployment_hooks)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Return a list of Qt modules from a list of file paths.
|
||||
# Arguments:
|
||||
# LIBRARIES - list of file paths, usually shared objects the to be deployed product links
|
||||
# against.
|
||||
#
|
||||
# Example input: /foo/bar/libQt6Core.so.6;/foo/bar/libQt6Gui.so.6;/foo/bar/somethingelse.so
|
||||
# Output: Core;Gui
|
||||
function(_qt_internal_get_qt_modules_from_resolved_libs out_var)
|
||||
set(no_value_options "")
|
||||
set(single_value_options "")
|
||||
set(multi_value_options LIBRARIES)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg
|
||||
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
||||
)
|
||||
if(arg_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
set(result "")
|
||||
foreach(lib IN LISTS arg_LIBRARIES)
|
||||
if(lib MATCHES "${__QT_CMAKE_EXPORT_NAMESPACE}([^/.]+)${__QT_LIBINFIX}\\.[^/]+$")
|
||||
list(APPEND result "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set("${out_var}" "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_json_array_to_cmake_list out_var json_array)
|
||||
set(result "")
|
||||
string(JSON json_array_length ERROR_VARIABLE error LENGTH "${json_array}")
|
||||
if(error STREQUAL "NOTFOUND")
|
||||
math(EXPR json_array_max_idx "${json_array_length} - 1")
|
||||
foreach(i RANGE "${json_array_max_idx}")
|
||||
string(JSON type ERROR_VARIABLE error GET "${json_array}" "${i}")
|
||||
if(error STREQUAL "NOTFOUND")
|
||||
list(APPEND result "${type}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
set("${out_var}" "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Return the plugin types for a Qt module.
|
||||
# Arguments:
|
||||
# MODULE
|
||||
# The Qt module we'd like to know the plugin types for.
|
||||
# QPA_PLATFORMS_OUT_VAR
|
||||
# Output variable for the list of supported QPA platforms to be deployed.
|
||||
# Only filled for the Gui module.
|
||||
#
|
||||
# Example input: Gui
|
||||
# Output: platforms;iconengines;imageformats;...
|
||||
#
|
||||
# This function reads the module JSON files from the Qt installation prefix.
|
||||
function(_qt_internal_get_plugin_types_for_module out_var)
|
||||
set(no_value_options "")
|
||||
set(single_value_options
|
||||
MODULE
|
||||
QPA_PLATFORMS_OUT_VAR
|
||||
)
|
||||
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 "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT DEFINED arg_MODULE)
|
||||
message(FATAL_ERROR "MODULE argument is required.")
|
||||
endif()
|
||||
|
||||
# Clear output variables to enable usage of early exit.
|
||||
set("${out_var}" "" PARENT_SCOPE)
|
||||
if(DEFINED arg_QPA_PLATFORMS_OUT_VAR)
|
||||
set("${arg_QPA_PLATFORMS_OUT_VAR}" "" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(modules_dir
|
||||
"${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_DESCRIPTIONSDIR}"
|
||||
)
|
||||
set(modules_file "${modules_dir}/${arg_MODULE}.json")
|
||||
if(NOT EXISTS "${modules_file}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${modules_file}" content)
|
||||
string(JSON types_array ERROR_VARIABLE error GET "${content}" "plugin_types")
|
||||
if(NOT error STREQUAL "NOTFOUND")
|
||||
return()
|
||||
endif()
|
||||
_qt_internal_json_array_to_cmake_list(result "${types_array}")
|
||||
set("${out_var}" "${result}" PARENT_SCOPE)
|
||||
|
||||
# Read .qpa.platforms
|
||||
if(DEFINED arg_QPA_PLATFORMS_OUT_VAR)
|
||||
string(JSON qpa_object ERROR_VARIABLE error GET "${content}" "qpa")
|
||||
if(NOT error STREQUAL "NOTFOUND")
|
||||
return()
|
||||
endif()
|
||||
string(JSON platforms_array ERROR_VARIABLE error GET "${qpa_object}" "platforms")
|
||||
if(NOT error STREQUAL "NOTFOUND")
|
||||
return()
|
||||
endif()
|
||||
_qt_internal_json_array_to_cmake_list(result "${platforms_array}")
|
||||
set("${arg_QPA_PLATFORMS_OUT_VAR}" "${result}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_plugin_file_path_match out_var file_path plugin_short_names)
|
||||
set(result FALSE)
|
||||
get_filename_component(file_name "${file_path}" NAME_WE)
|
||||
foreach(plugin_name IN LISTS plugin_short_names)
|
||||
get_filename_component(plugin_name "${plugin_name}" NAME_WE)
|
||||
if(file_name MATCHES "${plugin_name}${__QT_LIBINFIX}$")
|
||||
set(result TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
set("${out_var}" "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Return the Qt plugins to deploy.
|
||||
#
|
||||
# This function iterates over the Qt modules in LIBRARIES and retrieves their plugin types.
|
||||
# Those plugin types are added to INCLUDE_BY_TYPE.
|
||||
# The caller may specify further plugin types to include in INCLUDE_BY_TYPE.
|
||||
#
|
||||
# Further arguments:
|
||||
# NO_DEFAULT
|
||||
# Don't deploy the default plugins.
|
||||
# EXCLUDE_BY_TYPE
|
||||
# Plugin types that should not be deployed.
|
||||
# EXCLUDE
|
||||
# Plugins that should not be deployed. These are the plugin file name infixes (e.g. qxcb).
|
||||
# INCLUDE
|
||||
# Plugins that should be deployed.
|
||||
function(_qt_internal_get_qt_plugins_to_deploy out_var)
|
||||
set(no_value_options
|
||||
NO_DEFAULT
|
||||
)
|
||||
set(single_value_options "")
|
||||
set(multi_value_options
|
||||
EXCLUDE
|
||||
EXCLUDE_BY_TYPE
|
||||
INCLUDE
|
||||
INCLUDE_BY_TYPE
|
||||
LIBRARIES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg
|
||||
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
||||
)
|
||||
if(arg_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
# Locate the Qt modules with the resolved libraries and find their plugin types.
|
||||
_qt_internal_get_qt_modules_from_resolved_libs(modules LIBRARIES ${arg_LIBRARIES})
|
||||
set(plugin_types_from_modules "")
|
||||
set(qpa_platforms "")
|
||||
foreach(module IN LISTS modules)
|
||||
set(additional_args "")
|
||||
if(NOT arg_NO_DEFAULT AND module STREQUAL "Gui")
|
||||
set(additional_args QPA_PLATFORMS_OUT_VAR qpa_platforms)
|
||||
endif()
|
||||
_qt_internal_get_plugin_types_for_module(module_plugin_types
|
||||
MODULE ${module}
|
||||
${additional_args}
|
||||
)
|
||||
if(module_plugin_types)
|
||||
list(APPEND plugin_types_from_modules "${module_plugin_types}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(selected_plugin_types ${arg_INCLUDE_BY_TYPE} ${plugin_types_from_modules})
|
||||
list(REMOVE_DUPLICATES selected_plugin_types)
|
||||
list(REMOVE_ITEM selected_plugin_types ${arg_EXCLUDE_BY_TYPE} platforms)
|
||||
|
||||
# Collect all available plugins and plugin types from Qt's installation root.
|
||||
set(plugins_root_dir "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_PLUGINS}")
|
||||
file(GLOB_RECURSE all_files
|
||||
LIST_DIRECTORIES TRUE
|
||||
RELATIVE "${plugins_root_dir}"
|
||||
"${plugins_root_dir}/*${__QT_DEPLOY_SHARED_LIBRARY_SUFFIX}"
|
||||
)
|
||||
set(available_plugin_types "")
|
||||
set(available_plugin_files "")
|
||||
foreach(file_path IN LISTS all_files)
|
||||
if(IS_DIRECTORY "${plugins_root_dir}/${file_path}")
|
||||
list(APPEND available_plugin_types "${file_path}")
|
||||
else()
|
||||
list(APPEND available_plugin_files "${plugins_root_dir}/${file_path}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(plugins "")
|
||||
foreach(plugin_type IN LISTS available_plugin_types)
|
||||
if(plugin_type IN_LIST selected_plugin_types)
|
||||
# Add the whole plugin directory content.
|
||||
set(plugins_dir "${plugins_root_dir}/${plugin_type}")
|
||||
file(GLOB file_paths LIST_DIRECTORIES FALSE
|
||||
"${plugins_dir}/*${__QT_DEPLOY_SHARED_LIBRARY_SUFFIX}"
|
||||
)
|
||||
foreach(file_path IN LISTS file_paths)
|
||||
_qt_internal_plugin_file_path_match(excluded "${file_path}" "${arg_EXCLUDED}")
|
||||
if(NOT excluded)
|
||||
list(APPEND plugins "${file_path}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(included_plugin_names "${arg_INCLUDE}")
|
||||
if(NOT qpa_platforms STREQUAL "")
|
||||
set(qpa_plugin_names "${qpa_platforms}")
|
||||
list(TRANSFORM qpa_plugin_names PREPEND "q")
|
||||
list(APPEND included_plugin_names "${qpa_plugin_names}")
|
||||
endif()
|
||||
if(NOT included_plugin_names STREQUAL "")
|
||||
if(DEFINED arg_EXCLUDED)
|
||||
list(REMOVE_ITEM included_plugin_names ${arg_EXCLUDED})
|
||||
endif()
|
||||
foreach(file_path IN LISTS available_plugin_files)
|
||||
_qt_internal_plugin_file_path_match(included "${file_path}" "${included_plugin_names}")
|
||||
if(included)
|
||||
list(APPEND plugins "${file_path}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set("${out_var}" "${plugins}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_generic_deployqt)
|
||||
set(no_value_options
|
||||
NO_PLUGINS
|
||||
@ -202,7 +435,13 @@ function(_qt_internal_generic_deployqt)
|
||||
POST_INCLUDE_FILES
|
||||
POST_EXCLUDE_FILES
|
||||
)
|
||||
set(multi_value_options ${file_GRD_options})
|
||||
set(multi_value_options
|
||||
${file_GRD_options}
|
||||
EXCLUDE_PLUGINS
|
||||
EXCLUDE_PLUGIN_TYPES
|
||||
INCLUDE_PLUGINS
|
||||
INCLUDE_PLUGIN_TYPES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
||||
)
|
||||
@ -210,12 +449,6 @@ function(_qt_internal_generic_deployqt)
|
||||
message(FATAL_ERROR "Unparsed arguments: ${arg_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(arg_NO_PLUGINS)
|
||||
set(plugins "")
|
||||
else()
|
||||
set(plugins ${__QT_DEPLOY_PLUGINS})
|
||||
endif()
|
||||
|
||||
if(arg_VERBOSE OR __QT_DEPLOY_VERBOSE)
|
||||
set(verbose TRUE)
|
||||
endif()
|
||||
@ -231,54 +464,106 @@ function(_qt_internal_generic_deployqt)
|
||||
set(${var} "${abspaths}")
|
||||
endforeach()
|
||||
|
||||
# We need to get the runtime dependencies of plugins too.
|
||||
if(NOT plugins STREQUAL "")
|
||||
list(APPEND arg_MODULES ${plugins})
|
||||
endif()
|
||||
|
||||
# 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}")
|
||||
list(APPEND arg_POST_EXCLUDE_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()
|
||||
# Forward arguments for plugin selection.
|
||||
set(plugin_selection_args "")
|
||||
if(DEFINED arg_EXCLUDE_PLUGINS)
|
||||
list(APPEND plugin_selection_args EXCLUDE ${arg_EXCLUDE_PLUGINS})
|
||||
endif()
|
||||
if(DEFINED arg_INCLUDE_PLUGINS)
|
||||
list(APPEND plugin_selection_args INCLUDE ${arg_INCLUDE_PLUGINS})
|
||||
endif()
|
||||
if(DEFINED arg_EXCLUDE_PLUGIN_TYPES)
|
||||
list(APPEND plugin_selection_args EXCLUDE_BY_TYPE ${arg_EXCLUDE_PLUGIN_TYPES})
|
||||
endif()
|
||||
if(DEFINED arg_INCLUDE_PLUGIN_TYPES)
|
||||
list(APPEND plugin_selection_args INCLUDE_BY_TYPE ${arg_INCLUDE_PLUGIN_TYPES})
|
||||
endif()
|
||||
|
||||
set(plugins "")
|
||||
set(runtime_dependencies "")
|
||||
foreach(pass RANGE 9)
|
||||
# We need to get the runtime dependencies of plugins too.
|
||||
if(NOT plugins STREQUAL "")
|
||||
list(APPEND arg_MODULES ${plugins})
|
||||
endif()
|
||||
|
||||
# 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()
|
||||
|
||||
# Get the runtime dependencies recursively.
|
||||
file(GET_RUNTIME_DEPENDENCIES
|
||||
${file_GRD_args}
|
||||
RESOLVED_DEPENDENCIES_VAR resolved
|
||||
UNRESOLVED_DEPENDENCIES_VAR unresolved
|
||||
CONFLICTING_DEPENDENCIES_PREFIX conflicting
|
||||
)
|
||||
list(APPEND runtime_dependencies "${resolved}")
|
||||
if(verbose)
|
||||
message("pass ${pass}\nfile(GET_RUNTIME_DEPENDENCIES ${file_GRD_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()
|
||||
|
||||
# Discover more Qt plugins from resolved libraries.
|
||||
if(arg_NO_PLUGINS)
|
||||
set(more_plugins "")
|
||||
if(verbose)
|
||||
message(STATUS "Skipping plugin deployment.")
|
||||
endif()
|
||||
else()
|
||||
_qt_internal_get_qt_plugins_to_deploy(more_plugins
|
||||
LIBRARIES ${resolved}
|
||||
${plugin_selection_args}
|
||||
)
|
||||
if(NOT more_plugins STREQUAL "")
|
||||
list(REMOVE_ITEM more_plugins ${plugins})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(more_plugins STREQUAL "")
|
||||
# We're done, continue with deployment.
|
||||
if(verbose)
|
||||
message(STATUS "No further plugins discovered.")
|
||||
endif()
|
||||
break()
|
||||
endif()
|
||||
|
||||
# Add the newly discovered plugins. Re-run GRD.
|
||||
if(verbose)
|
||||
foreach(plugin IN LISTS more_plugins)
|
||||
message(STATUS "Discovered plugin: ${plugin}")
|
||||
endforeach()
|
||||
endif()
|
||||
list(APPEND plugins "${more_plugins}")
|
||||
list(REMOVE_DUPLICATES plugins)
|
||||
set(arg_LIBRARIES "")
|
||||
set(arg_MODULES "${more_plugins}")
|
||||
endforeach()
|
||||
|
||||
# Deploy the Qt libraries.
|
||||
file(INSTALL ${resolved}
|
||||
list(REMOVE_DUPLICATES runtime_dependencies)
|
||||
file(INSTALL ${runtime_dependencies}
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${arg_LIB_DIR}"
|
||||
FOLLOW_SYMLINK_CHAIN
|
||||
)
|
||||
@ -321,7 +606,7 @@ function(_qt_internal_generic_deployqt)
|
||||
qt6_deploy_translations()
|
||||
endif()
|
||||
|
||||
_qt_internal_run_deployment_hooks(${ARGV} RESOLVED_DEPENDENCIES ${resolved})
|
||||
_qt_internal_run_deployment_hooks(${ARGV} RESOLVED_DEPENDENCIES ${runtime_dependencies})
|
||||
endfunction()
|
||||
|
||||
function(qt6_deploy_runtime_dependencies)
|
||||
@ -546,6 +831,18 @@ function(qt6_deploy_runtime_dependencies)
|
||||
if(arg_NO_PLUGINS)
|
||||
list(APPEND tool_options NO_PLUGINS)
|
||||
endif()
|
||||
if(DEFINED arg_INCLUDE_PLUGINS)
|
||||
list(APPEND tool_options INCLUDE_PLUGINS ${arg_INCLUDE_PLUGINS})
|
||||
endif()
|
||||
if(DEFINED arg_INCLUDE_PLUGIN_TYPES)
|
||||
list(APPEND tool_options INCLUDE_PLUGIN_TYPES ${arg_INCLUDE_PLUGIN_TYPES})
|
||||
endif()
|
||||
if(DEFINED arg_EXCLUDE_PLUGINS)
|
||||
list(APPEND tool_options EXCLUDE_PLUGINS ${arg_EXCLUDE_PLUGINS})
|
||||
endif()
|
||||
if(DEFINED arg_EXCLUDE_PLUGIN_TYPES)
|
||||
list(APPEND tool_options EXCLUDE_PLUGIN_TYPES ${arg_EXCLUDE_PLUGIN_TYPES})
|
||||
endif()
|
||||
if(arg_NO_TRANSLATIONS)
|
||||
list(APPEND tool_options NO_TRANSLATIONS)
|
||||
endif()
|
||||
|
@ -3165,11 +3165,13 @@ endif()
|
||||
|
||||
# These are internal implementation details. They may be removed at any time.
|
||||
set(__QT_DEPLOY_SYSTEM_NAME \"${CMAKE_SYSTEM_NAME}\")
|
||||
set(__QT_DEPLOY_SHARED_LIBRARY_SUFFIX \"${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
||||
set(__QT_DEPLOY_IS_SHARED_LIBS_BUILD \"${QT6_IS_SHARED_LIBS_BUILD}\")
|
||||
set(__QT_DEPLOY_TOOL \"${__QT_DEPLOY_TOOL}\")
|
||||
set(__QT_DEPLOY_IMPL_DIR \"${deploy_impl_dir}\")
|
||||
set(__QT_DEPLOY_VERBOSE \"${QT_ENABLE_VERBOSE_DEPLOYMENT}\")
|
||||
set(__QT_CMAKE_EXPORT_NAMESPACE \"${QT_CMAKE_EXPORT_NAMESPACE}\")
|
||||
set(__QT_LIBINFIX \"${QT_LIBINFIX}\")
|
||||
set(__QT_DEPLOY_GENERATOR_IS_MULTI_CONFIG \"${is_multi_config}\")
|
||||
set(__QT_DEPLOY_ACTIVE_CONFIG \"$<CONFIG>\")
|
||||
set(__QT_NO_CREATE_VERSIONLESS_FUNCTIONS \"${QT_NO_CREATE_VERSIONLESS_FUNCTIONS}\")
|
||||
@ -3178,11 +3180,11 @@ set(__QT_DEPLOY_QT_ADDITIONAL_PACKAGES_PREFIX_PATH \"${QT_ADDITIONAL_PACKAGES_PR
|
||||
set(__QT_DEPLOY_QT_INSTALL_PREFIX \"${QT6_INSTALL_PREFIX}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_BINS \"${QT6_INSTALL_BINS}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_DATA \"${QT6_INSTALL_DATA}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_DESCRIPTIONSDIR \"${QT6_INSTALL_DESCRIPTIONSDIR}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_LIBEXECS \"${QT6_INSTALL_LIBEXECS}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_PLUGINS \"${QT6_INSTALL_PLUGINS}\")
|
||||
set(__QT_DEPLOY_QT_INSTALL_TRANSLATIONS \"${QT6_INSTALL_TRANSLATIONS}\")
|
||||
set(__QT_DEPLOY_TARGET_QT_PATHS_PATH \"${target_qtpaths_path}\")
|
||||
set(__QT_DEPLOY_PLUGINS \"\")
|
||||
set(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH \"${must_adjust_plugins_rpath}\")
|
||||
set(__QT_DEPLOY_USE_PATCHELF \"${QT_DEPLOY_USE_PATCHELF}\")
|
||||
set(__QT_DEPLOY_PATCHELF_EXECUTABLE \"${QT_DEPLOY_PATCHELF_EXECUTABLE}\")
|
||||
@ -3631,16 +3633,6 @@ function(qt6_generate_deploy_script)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Mark the target as "to be deployed".
|
||||
set_property(TARGET ${arg_TARGET} PROPERTY _qt_marked_for_deployment ON)
|
||||
|
||||
# If the target already was finalized, maybe because it was defined in a subdirectory, generate
|
||||
# the plugin deployment information here.
|
||||
get_target_property(is_finalized "${arg_TARGET}" _qt_is_finalized)
|
||||
if(is_finalized)
|
||||
__qt_internal_generate_plugin_deployment_info(${arg_TARGET})
|
||||
endif()
|
||||
|
||||
# Create a file name that will be unique for this target and the combination
|
||||
# of arguments passed to this command. This allows the project to call us
|
||||
# multiple times with different arguments for the same target (e.g. to
|
||||
|
@ -208,7 +208,7 @@ example \c qjpeg.
|
||||
Qt6::QJpegPlugin target's plugin name is \c qjpeg.
|
||||
|
||||
\note The arguments \c EXCLUDE_PLUGINS, \c EXCLUDE_PLUGIN_TYPES, \c
|
||||
INCLUDE_PLUGINS, and \c INCLUDE_PLUGIN_TYPES only work on Windows.
|
||||
INCLUDE_PLUGINS, and \c INCLUDE_PLUGIN_TYPES only work on Windows and Linux.
|
||||
|
||||
\section1 Example
|
||||
|
||||
|
@ -404,15 +404,22 @@ endif()
|
||||
|
||||
_qt_internal_test_expect_pass(test_qt_extract_metatypes)
|
||||
|
||||
set(deploy_args
|
||||
set(deployment_tests
|
||||
test_plugin_deployment
|
||||
test_widgets_app_deployment
|
||||
BINARY "${CMAKE_CTEST_COMMAND}"
|
||||
BINARY_ARGS "-V"
|
||||
# Need to explicitly specify a writable install prefix.
|
||||
BUILD_OPTIONS
|
||||
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/test_widgets_app_deployment_installed
|
||||
NO_RUN_ENVIRONMENT_PLUGIN_PATH
|
||||
)
|
||||
foreach(test_name IN LISTS deployment_tests)
|
||||
set(${test_name}_args
|
||||
${test_name}
|
||||
BINARY "${CMAKE_CTEST_COMMAND}"
|
||||
BINARY_ARGS "-V"
|
||||
# Need to explicitly specify a writable install prefix.
|
||||
BUILD_OPTIONS
|
||||
-DQT_ENABLE_VERBOSE_DEPLOYMENT=ON
|
||||
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/${test_name}_installed
|
||||
NO_RUN_ENVIRONMENT_PLUGIN_PATH
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(is_desktop_linux FALSE)
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT CMAKE_CROSSCOMPILING)
|
||||
@ -423,11 +430,13 @@ endif()
|
||||
# and fail on other platforms, because there is no support for runtime dependency deployment on
|
||||
# those platforms.
|
||||
# With static builds the runtime dependencies are just skipped, but the test should still pass.
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR is_desktop_linux)
|
||||
_qt_internal_test_expect_pass(${deploy_args})
|
||||
else()
|
||||
_qt_internal_test_expect_fail(${deploy_args})
|
||||
endif()
|
||||
foreach(test_name IN LISTS deployment_tests)
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR is_desktop_linux)
|
||||
_qt_internal_test_expect_pass(${${test_name}_args})
|
||||
else()
|
||||
_qt_internal_test_expect_fail(${${test_name}_args})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
_qt_internal_test_expect_pass(test_config_expressions)
|
||||
_qt_internal_test_expect_pass(test_QTP0003)
|
||||
|
100
tests/auto/cmake/test_plugin_deployment/CMakeLists.txt
Normal file
100
tests/auto/cmake/test_plugin_deployment/CMakeLists.txt
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright (C) 2025 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(test_plugin_deployment)
|
||||
enable_testing()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Test)
|
||||
if(NOT UNIX OR APPLE)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Gui)
|
||||
endif()
|
||||
|
||||
qt6_standard_project_setup()
|
||||
add_subdirectory(mylib)
|
||||
|
||||
function(create_test_executable target)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.19")
|
||||
qt_add_executable(${target} MANUAL_FINALIZATION main.cpp)
|
||||
else()
|
||||
qt_add_executable(${target} main.cpp)
|
||||
endif()
|
||||
|
||||
set_target_properties(${target} PROPERTIES
|
||||
# We explicitly don't set WIN32_EXECUTABLE to ensure we see errors from stderr when
|
||||
# something fails and not having to use DebugView.
|
||||
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
target_link_libraries(${target} PRIVATE MyLib Qt::Test)
|
||||
|
||||
install(TARGETS ${target}
|
||||
BUNDLE DESTINATION .
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# Install MyLib explicitly, because macdeployqt/winddeployqt don't take care of installing
|
||||
# non-Qt dependencies.
|
||||
if(APPLE)
|
||||
install(TARGETS MyLib
|
||||
LIBRARY DESTINATION "${target}.app/Contents/Frameworks"
|
||||
)
|
||||
elseif(WIN32)
|
||||
# windeployqt doesn't take care of installing non-Qt dependencies.
|
||||
install(TARGETS MyLib
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
elseif(UNIX AND NOT QT6_IS_SHARED_LIBS_BUILD)
|
||||
# In a static build, GRD won't run, and we need to install the DSO manually.
|
||||
install(TARGETS MyLib
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT UNIX OR APPLE)
|
||||
# Explicitly link against Qt::Gui, because otherwise, macdeployqt/windeployqt don't deploy
|
||||
# the Gui module.
|
||||
target_link_libraries(${target} PRIVATE Qt::Gui)
|
||||
endif()
|
||||
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET ${target}
|
||||
OUTPUT_SCRIPT deploy_script
|
||||
# Don't fail at configure time on unsupported platforms
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${deploy_script})
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.19")
|
||||
qt_finalize_target(${target})
|
||||
endif()
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
set(installed_app_location "${CMAKE_INSTALL_PREFIX}/${target}.app/Contents/MacOS/${target}")
|
||||
elseif(WIN32)
|
||||
set(installed_app_location "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/${target}.exe")
|
||||
elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT CMAKE_CROSSCOMPILING)
|
||||
set(installed_app_location "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/${target}")
|
||||
endif()
|
||||
|
||||
# There's no nice way to get the location of an installed binary, so we need to construct
|
||||
# the binary install path by hand, somewhat similar to how it's done in
|
||||
# the implementation of qt_deploy_runtime_dependencies.
|
||||
# On unsupported deployment platforms, either the install_ test will fail not finding
|
||||
# the location of the app (because we do not set a installed_app_location value)
|
||||
# or the run_deployed_ test will fail because we didn't deploy the runtime dependencies.
|
||||
# When support for additional platforms is added, these locations will have to be augmented.
|
||||
add_test(install_${target} "${CMAKE_COMMAND}" --install .)
|
||||
set_tests_properties(install_${target} PROPERTIES FIXTURES_SETUP deploy_step)
|
||||
add_test(NAME run_deployed_${target}
|
||||
COMMAND "${installed_app_location}"
|
||||
# Make sure that we don't use the default working directory which is
|
||||
# CMAKE_CURRENT_BINARY_DIR because on Windows the loader might pick up dlls
|
||||
# from the working directory instead of the installed app dir, if the dll is
|
||||
# missing in the app dir.
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}")
|
||||
set_tests_properties(run_deployed_${target} PROPERTIES FIXTURES_REQUIRED deploy_step)
|
||||
endfunction()
|
||||
|
||||
create_test_executable(App)
|
||||
|
26
tests/auto/cmake/test_plugin_deployment/main.cpp
Normal file
26
tests/auto/cmake/test_plugin_deployment/main.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "mylib/mylib.h"
|
||||
|
||||
#include <QtTest/qtest.h>
|
||||
|
||||
class test_plugin_deployment : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void loadsTheRightPlugins();
|
||||
};
|
||||
|
||||
void test_plugin_deployment::loadsTheRightPlugins()
|
||||
{
|
||||
const auto formats = getSupportedImageFormats();
|
||||
if (!formats.contains("gif")) {
|
||||
qDebug() << "supported imageformats: " << formats;
|
||||
QFAIL("Cannot load the qgif plugin. Plugin deployment failed.");
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(test_plugin_deployment)
|
||||
|
||||
#include "main.moc"
|
@ -0,0 +1,7 @@
|
||||
# Copyright (C) 2025 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Gui)
|
||||
qt_add_library(MyLib SHARED mylib.cpp)
|
||||
target_compile_definitions(MyLib PRIVATE BUILD_MYLIB)
|
||||
target_link_libraries(MyLib PRIVATE Qt6::Gui)
|
9
tests/auto/cmake/test_plugin_deployment/mylib/mylib.cpp
Normal file
9
tests/auto/cmake/test_plugin_deployment/mylib/mylib.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#include "mylib.h"
|
||||
#include <QtGui/QImageReader>
|
||||
|
||||
QList<QByteArray> getSupportedImageFormats()
|
||||
{
|
||||
return QImageReader::supportedImageFormats();
|
||||
}
|
14
tests/auto/cmake/test_plugin_deployment/mylib/mylib.h
Normal file
14
tests/auto/cmake/test_plugin_deployment/mylib/mylib.h
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QList>
|
||||
|
||||
#ifdef BUILD_MYLIB
|
||||
# define MYLIB_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define MYLIB_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT QList<QByteArray> getSupportedImageFormats();
|
Loading…
x
Reference in New Issue
Block a user