summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2021-04-28 16:27:19 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2021-05-20 00:55:46 +0200
commit6fcc272ac9dcf1d6d65de1bdf3138722ba63a902 (patch)
tree2f36991a56e28332c61a580753c637c301f829e4
parent06689a2d7a18882535819ed13ac7248c81330529 (diff)
CMake: Introduce finalizer mode handling of static plugins
Allow linking all plugin initializer object libraries directly into the final target (executable or shared library). The finalizer mode is triggered when the project adds a call to qt_import_plugins, as well when the project has an explicit call to qt_finalize_executable or when it is defer called by CMake 3.19+. Otherwise the old non-finalizer mode is used, where each plugin initializer object library is propagated via the usage requirements of its associated module. A user can explicitly opt in or out of the new mode by calling qt_enable_import_plugins_finalizer_mode(target TRUE/FALSE) The implementation, at configure time, recursively collects all dependencies of the target to extract a list of used Qt modules. From each module we extract its list of associated plugins and their genex conditions. These genexes are used to conditionally link the plugins and the initializers. Renamed QT_PLUGINS property to _qt_plugins, so we can safely query the property even on INTERFACE libraries with lower CMake versions. QT_PLUGINS is kept for backwards compatibility with projects already using it, but should be removed in Qt 7. The upside of the finalizer mode is that it avoids creating link cycles (e.g. Gui -> SvgPlugin -> Gui case) which causes CMake to duplicate the library on the link line, slowing down link time as well as possibly breaking link order dependencies. The downside is that finalizer mode can't cope with generator expressions at the moment. So if a Qt module target is wrapped in a generator expression, it's plugins will not be detected and thus linked. Task-number: QTBUG-80863 Task-number: QTBUG-92933 Change-Id: Ic40c8ae5807a154ed18fcac18b25f00864c8f143 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--cmake/QtPlugins.cmake.in8
-rw-r--r--cmake/QtPublicPluginHelpers.cmake182
-rw-r--r--src/corelib/Qt6CoreMacros.cmake61
-rw-r--r--tests/auto/cmake/test_import_plugins/CMakeLists.txt170
4 files changed, 363 insertions, 58 deletions
diff --git a/cmake/QtPlugins.cmake.in b/cmake/QtPlugins.cmake.in
index 3a7b8ab30d..a96588cc5e 100644
--- a/cmake/QtPlugins.cmake.in
+++ b/cmake/QtPlugins.cmake.in
@@ -14,7 +14,10 @@ function(__qt_internal_add_static_plugins_once)
set(_module_target ${_aliased_target})
endif()
- # Include all PluginConfig.cmake files and update the QT_PLUGINS property of the module.
+ # Include all PluginConfig.cmake files and update the _qt_plugins and QT_PLUGINS property of
+ # the module. The underscored version is the one we will use going forward to have compatibility
+ # with INTERFACE libraries. QT_PLUGINS is now deprecated and only kept so that we don't break
+ # existing projects using it (like CMake itself).
file(GLOB _qt_plugin_config_files "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@*PluginConfig.cmake")
foreach(_config_file ${_qt_plugin_config_files})
string(REGEX REPLACE "^.*/@INSTALL_CMAKE_NAMESPACE@(.*Plugin)Config.cmake$" "\\1" _qt_plugin "${_config_file}")
@@ -23,6 +26,9 @@ function(__qt_internal_add_static_plugins_once)
list(APPEND _qt_plugins ${_qt_plugin})
endif()
endforeach()
+ set_property(TARGET ${_module_target} PROPERTY _qt_plugins ${_qt_plugins})
+
+ # TODO: Deprecated. Remove in Qt 7.
set_property(TARGET ${_module_target} PROPERTY QT_PLUGINS ${_qt_plugins})
get_target_property(_have_added_plugins_already ${_module_target} __qt_internal_plugins_added)
diff --git a/cmake/QtPublicPluginHelpers.cmake b/cmake/QtPublicPluginHelpers.cmake
index 34790fe6a3..35026fa395 100644
--- a/cmake/QtPublicPluginHelpers.cmake
+++ b/cmake/QtPublicPluginHelpers.cmake
@@ -32,6 +32,13 @@ function(__qt_internal_plugin_has_class_name plugin_target out_var)
set(${out_var} "${classname}" PARENT_SCOPE)
endfunction()
+# Constructs a generator expression which decides whether a plugin will be used.
+#
+# The conditions are based on the various properties set in qt_import_plugins.
+
+# All the TARGET_PROPERTY genexes are evaluated in the context of the currently linked target.
+#
+# The genex is saved into out_var.
function(__qt_internal_get_static_plugin_condition_genex
plugin_target_unprefixed
out_var)
@@ -49,9 +56,10 @@ function(__qt_internal_get_static_plugin_condition_genex
# Plugin genex marker for prl processing.
set(_is_plugin_marker_genex "$<BOOL:QT_IS_PLUGIN_GENEX>")
- # The code in here uses the properties defined in qt_import_plugins (Qt6CoreMacros.cmake)
set(_plugin_is_default "$<TARGET_PROPERTY:${plugin_target},QT_DEFAULT_PLUGIN>")
+ # The code in here uses the properties defined in qt_import_plugins (Qt6CoreMacros.cmake)
+
# INCLUDE
set(_plugin_is_whitelisted "$<IN_LIST:${plugin_target},${_manual_plugins_genex}>")
set(_plugin_versionless_is_whitelisted
@@ -120,14 +128,23 @@ function(__qt_internal_get_static_plugin_condition_genex
set(${out_var} "${_plugin_condition}" PARENT_SCOPE)
endfunction()
+# Wraps the genex condition to evaluate to true only when using the regular plugin importing mode
+# (not finalizer mode).
+function(__qt_internal_get_plugin_condition_regular_mode plugin_condition out_var)
+ set(not_finalizer_mode "$<NOT:$<BOOL:$<TARGET_PROPERTY:_qt_static_plugins_use_finalizer_mode>>>")
+ set(full_plugin_condition "$<AND:${plugin_condition},${not_finalizer_mode}>")
+ set(${out_var} "${full_plugin_condition}" PARENT_SCOPE)
+endfunction()
+
# Link plugin via usage requirements of associated Qt module.
function(__qt_internal_add_static_plugin_linkage plugin_target qt_module_target)
__qt_internal_get_static_plugin_condition_genex("${plugin_target}" plugin_condition)
+ __qt_internal_get_plugin_condition_regular_mode("${plugin_condition}" full_plugin_condition)
set(plugin_target "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}")
# If this condition is true, we link against the plug-in
- set(plugin_genex "$<${plugin_condition}:${plugin_target}>")
+ set(plugin_genex "$<${full_plugin_condition}:${plugin_target}>")
target_link_libraries(${qt_module_target} INTERFACE "${plugin_genex}")
endfunction()
@@ -205,3 +222,164 @@ function(__qt_internal_add_static_plugin_init_object_library
set(${out_var_plugin_init_target} "${plugin_init_target}" PARENT_SCOPE)
endfunction()
+# Collect a list of genexes to link plugin libraries.
+function(__qt_internal_collect_plugin_libraries plugin_targets out_var)
+ set(plugins_to_link "")
+
+ foreach(plugin_target ${plugin_targets})
+ __qt_internal_get_static_plugin_condition_genex(
+ "${plugin_target}"
+ plugin_condition)
+
+ set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}")
+
+ list(APPEND plugins_to_link "$<${plugin_condition}:${plugin_target_versioned}>")
+ endforeach()
+
+ set("${out_var}" "${plugins_to_link}" PARENT_SCOPE)
+endfunction()
+
+# Collect a list of genexes to link plugin initializer object libraries.
+#
+# The object libraries are only linked if the associated plugins are linked.
+function(__qt_internal_collect_plugin_init_libraries plugin_targets out_var)
+ set(plugin_inits_to_link "")
+
+ foreach(plugin_target ${plugin_targets})
+ __qt_internal_get_static_plugin_condition_genex(
+ "${plugin_target}"
+ plugin_condition)
+
+ __qt_internal_get_static_plugin_init_target_name("${plugin_target}" plugin_init_target)
+ set(plugin_init_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_init_target}")
+
+ list(APPEND plugin_inits_to_link "$<${plugin_condition}:${plugin_init_target_versioned}>")
+ endforeach()
+
+ set("${out_var}" "${plugin_inits_to_link}" PARENT_SCOPE)
+endfunction()
+
+# Collects all plugin targets discovered by walking the dependencies of ${target}.
+#
+# Walks immediate dependencies and their transitive dependencies.
+# Plugins are collected by inspecting the _qt_plugins property found on any dependency Qt target.
+function(__qt_internal_collect_plugin_targets_from_dependencies target out_var)
+ set(dep_targets "")
+
+ __qt_internal_collect_all_target_dependencies("${target}" dep_targets)
+
+ set(plugin_targets "")
+ foreach(dep_target ${dep_targets})
+ get_target_property(plugins ${dep_target} _qt_plugins)
+ if(plugins)
+ list(APPEND plugin_targets ${plugins})
+ endif()
+ endforeach()
+
+ # Plugins that are specified via qt_import_plugin's INCLUDE or INCLUDE_BY_TYPE can have
+ # dependencies on Qt modules. These modules in turn might bring in more default plugins to link
+ # So it's recursive. Do only one pass for now. Try to extract the included static plugins, walk
+ # their public and private dependencies, check if any of them are Qt modules that provide more
+ # plugins and extract the target names of those plugins.
+ __qt_internal_collect_plugin_targets_from_dependencies_of_plugins(
+ "${target}" recursive_plugin_targets)
+ if(recursive_plugin_targets)
+ list(APPEND plugin_targets ${recursive_plugin_targets})
+ endif()
+ list(REMOVE_DUPLICATES plugin_targets)
+
+ set("${out_var}" "${plugin_targets}" PARENT_SCOPE)
+endfunction()
+
+# Helper to collect plugin targets from encountered module dependencies as a result of walking
+# dependencies. These module dependencies might expose additional plugins.
+function(__qt_internal_collect_plugin_targets_from_dependencies_of_plugins target out_var)
+ set(assigned_plugins_overall "")
+
+ get_target_property(assigned_qt_plugins "${target}" QT_PLUGINS)
+
+ if(assigned_qt_plugins)
+ foreach(assigned_qt_plugin ${assigned_qt_plugins})
+ if(TARGET "${assigned_qt_plugin}")
+ list(APPEND assigned_plugins_overall "${assigned_qt_plugin}")
+ endif()
+ endforeach()
+ endif()
+
+ get_target_property(assigned_qt_plugins_by_type "${target}" _qt_plugins_by_type)
+
+ if(assigned_qt_plugins_by_type)
+ foreach(assigned_qt_plugin ${assigned_qt_plugins_by_type})
+ if(TARGET "${assigned_qt_plugin}")
+ list(APPEND assigned_plugins_overall "${assigned_qt_plugin}")
+ endif()
+ endforeach()
+ endif()
+
+ set(plugin_targets "")
+ foreach(target ${assigned_plugins_overall})
+ __qt_internal_walk_libs(
+ "${target}"
+ dep_targets
+ _discarded_out_var
+ "qt_private_link_library_targets"
+ "collect_targets")
+
+ foreach(dep_target ${dep_targets})
+ get_target_property(plugins ${dep_target} _qt_plugins)
+ if(plugins)
+ list(APPEND plugin_targets ${plugins})
+ endif()
+ endforeach()
+ endforeach()
+
+ list(REMOVE_DUPLICATES plugin_targets)
+
+ set("${out_var}" "${plugin_targets}" PARENT_SCOPE)
+endfunction()
+
+# Helper to check if the finalizer mode of plugin importing should be used.
+# If true or unset, use finalizer mode.
+# If false, use regular mode (usage requirement propagation via associated Qt module)
+function(__qt_internal_get_plugin_imports_finalizer_mode target out_var)
+ get_target_property(value ${target} _qt_static_plugins_use_finalizer_mode)
+ if("${value}" STREQUAL "value-NOTFOUND")
+ set(value "")
+ endif()
+ set(${out_var} "${value}" PARENT_SCOPE)
+endfunction()
+
+# Main logic of finalizer mode.
+function(__qt_internal_apply_plugin_imports_finalizer_mode target)
+ # Nothing to do in a shared build.
+ if(QT6_IS_SHARED_LIBS_BUILD)
+ return()
+ endif()
+
+ # Process a target only once.
+ get_target_property(processed ${target} _qt_plugin_finalizer_imports_processed)
+ if(processed)
+ return()
+ endif()
+
+ __qt_internal_get_plugin_imports_finalizer_mode(${target} use_finalizer_mode)
+
+ # By default if the project hasn't explicitly opted in or out, use finalizer mode.
+ if("${use_finalizer_mode}" STREQUAL "")
+ qt_enable_import_plugins_finalizer_mode(${target} TRUE)
+ endif()
+
+ # Check if the project chose a mode explicitly.
+ __qt_internal_get_plugin_imports_finalizer_mode(${target} use_finalizer_mode)
+ if(NOT use_finalizer_mode)
+ 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)
+
+ target_link_libraries(${target} PRIVATE "${plugin_libraries}" "${init_libraries}")
+
+ set_target_properties(${target} PROPERTIES _qt_plugin_finalizer_imports_processed TRUE)
+endfunction()
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index 01426cd8ea..d05688c596 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -487,6 +487,7 @@ function(qt6_add_executable target)
# due to special behavior of cmake_language() argument handling
cmake_language(EVAL CODE "cmake_language(DEFER CALL qt6_finalize_executable ${target})")
else()
+ set_target_properties("${target}" PROPERTIES _qt_is_immediately_finalized TRUE)
qt6_finalize_executable("${target}")
endif()
endfunction()
@@ -514,6 +515,13 @@ endfunction()
# This function is currently in Technical Preview.
# It's signature and behavior might change.
function(qt6_finalize_executable target)
+ get_target_property(is_finalized "${target}" _qt_executable_is_finalized)
+ if(is_finalized)
+ message(AUTHOR_WARNING
+ "Tried to call qt6_finalize_executable twice on executable: '${target}'. \
+ Did you forget to specify MANUAL_FINALIZATION to qt6_add_executable?")
+ return()
+ endif()
# We can't evaluate generator expressions at configure time, so we can't
# ask for any transitive properties or even the full library dependency
@@ -567,6 +575,18 @@ function(qt6_finalize_executable target)
if(IOS)
qt6_finalize_ios_app("${target}")
endif()
+
+ # For finalizer mode of plugin importing to work safely, we need to know the list of Qt
+ # dependencies the target has, but those might be added later than the qt_add_executable call.
+ # Most of our examples are like that. Only enable finalizer mode when we are sure that the user
+ # manually finalized the executable, or it was automatically done via a deferred call.
+ # A project can still do it manually by calling qt_import_plugins() explicitly.
+ get_target_property(is_immediately_finalized "${target}" _qt_is_immediately_finalized)
+ if(NOT is_immediately_finalized)
+ __qt_internal_apply_plugin_imports_finalizer_mode("${target}")
+ endif()
+
+ set_target_properties(${target} PROPERTIES _qt_executable_is_finalized TRUE)
endfunction()
function(_qt_internal_find_ios_development_team_id out_var)
@@ -806,6 +826,7 @@ function(qt6_import_plugins target)
# Check if passed plugin target name is a version-less one, and make a version-full
# one.
set_property(TARGET "${target}" APPEND PROPERTY "QT_PLUGINS_${_current_type}" "${_arg}")
+ set_property(TARGET "${target}" APPEND PROPERTY "_qt_plugins_by_type" "${_arg}")
_qt_get_plugin_name_with_version("${_arg}" qt_plugin_with_version)
# TODO: Do we really need this check? We didn't have it in Qt5, and plugin targets
@@ -822,6 +843,15 @@ function(qt6_import_plugins target)
string(REGEX REPLACE "[-/]" "_" _plugin_type "${_arg}")
set_property(TARGET "${target}" PROPERTY "QT_PLUGINS_${_plugin_type}" "-")
endforeach()
+
+ # If the project called qt_import_plugins, use this as an event to enable finalizer mode for
+ # plugin importing.
+ #
+ # This is done in addition to the code in qt_finalize_executable, to ensure pre-existing
+ # projects that use qt_import_plugins activate finalizer mode even with an older CMake version
+ # that doesn't support deferred calls (and projects that don't explicitly call
+ # qt_finalize_executable).
+ __qt_internal_apply_plugin_imports_finalizer_mode(${target})
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
@@ -834,6 +864,37 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+# This function allows enabling or disabling the finalizer mode of plugin importing in static Qt
+# builds.
+#
+# When finalizer mode is enabled, all plugins initializer object libraries are directly linked to
+# the given '${target}' (executable or shared library).
+# This prevents cycles between Qt provided static libraries and reduces link time, due to the
+# libraries not being repeated because they are not part of a cycle anymore.
+#
+# When finalizer mode is disabled, each plugin initializer is propagated via usage requirements
+# of its associated module.
+#
+# Finalizer mode is enabled by default if:
+# - the project calls qt_import_plugins explicitly or
+# - the project calls qt_finalize_executable explicitly or
+# - the project uses qt_add_executable and a CMake version greater than or equal to 3.19
+# (which will DEFER CALL qt_finalize_executable)
+function(qt6_enable_import_plugins_finalizer_mode target enabled)
+ if(enabled)
+ set(enabled "TRUE")
+ else()
+ set(enabled "FALSE")
+ endif()
+ set_property(TARGET "${target}" PROPERTY _qt_static_plugins_use_finalizer_mode "${enabled}")
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_enable_import_plugins_finalizer_mode)
+ qt6_enable_import_plugins_finalizer_mode(${ARGV})
+ endfunction()
+endif()
+
# Extracts metatypes from a Qt target and generates a metatypes.json for it.
# By default we check whether AUTOMOC has been enabled and we extract the information from the
diff --git a/tests/auto/cmake/test_import_plugins/CMakeLists.txt b/tests/auto/cmake/test_import_plugins/CMakeLists.txt
index 3db7f012db..bc3580760f 100644
--- a/tests/auto/cmake/test_import_plugins/CMakeLists.txt
+++ b/tests/auto/cmake/test_import_plugins/CMakeLists.txt
@@ -6,21 +6,45 @@ enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt6 COMPONENTS REQUIRED MockPlugins1 MockPlugins2)
-# MockPlugins3 is automatically find_dependency'd by QMock5Plugin which depends on MockPlugins3.
-# QMock5Plugin itself is loaded by QtMockPlugins1Plugins.cmake.
+# MockPlugins3 package is automatically found by the find_dependency call in
+# QMock5PluginConfig.cmake which depends on MockPlugins3.
+# QMock5Plugin itself is loaded by QtMockPlugins1Plugins.cmake, so via QtMockPlugins1Config.cmake.
-function(create_test_executable TARGET_NAME)
- set(CHECK_FILE ${CMAKE_BINARY_DIR}/${TARGET_NAME}_check.cpp)
+function(create_test_executable target)
+ cmake_parse_arguments(arg "FINALIZER_MODE;QT_WRAPPER" "" "ADD_EXECUTABLE_ARGS" ${ARGN})
+
+ set(target_name_adjusted "${target}")
+ if(arg_FINALIZER_MODE)
+ set(target_name_adjusted "${target}_finalizer")
+ endif()
+
+ set(CHECK_FILE ${CMAKE_BINARY_DIR}/${target_name_adjusted}_check.cpp)
set(EXPECTED_PLUGINS)
- foreach(_p ${ARGN})
+ foreach(_p ${arg_UNPARSED_ARGUMENTS})
string(APPEND EXPECTED_PLUGINS " \"${_p}\",\n")
endforeach()
configure_file("${CMAKE_SOURCE_DIR}/check.cpp.in" ${CHECK_FILE})
- add_executable(${TARGET_NAME} main.cpp ${CHECK_FILE})
- target_link_libraries(${TARGET_NAME} PRIVATE Qt6::MockPlugins1)
- add_test(test_${TARGET_NAME} ${TARGET_NAME})
+ set(add_executable_args ${target_name_adjusted} main.cpp ${CHECK_FILE}
+ ${arg_ADD_EXECUTABLE_ARGS})
+
+ if(arg_QT_WRAPPER)
+ qt_add_executable(${add_executable_args})
+ else()
+ add_executable(${add_executable_args})
+ endif()
+ target_link_libraries(${target_name_adjusted} PRIVATE Qt6::MockPlugins1)
+ add_test(test_${target_name_adjusted} ${target_name_adjusted})
+
+ if(arg_FINALIZER_MODE)
+ set(finalizer_mode "TRUE")
+ else()
+ set(finalizer_mode "FALSE")
+ endif()
+ qt_enable_import_plugins_finalizer_mode(${target_name_adjusted} ${finalizer_mode})
+
+ set(target ${target_name_adjusted} PARENT_SCOPE)
endfunction()
# No call to qt_import_plugins() for the default case.
@@ -28,6 +52,7 @@ create_test_executable(default
QMock1Plugin QMock2Plugin
QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501
+ ${import_mode}
)
# No call to qt_import_plugins() for the default_link case.
@@ -42,64 +67,99 @@ create_test_executable(default_link QMock1Plugin QMock2Plugin
# suggests that MockPlugins1 OR MockPlugins2 is sufficient to link the plugin, not both.
# See QTBUG-93501
QMock3Plugin
+ ${import_mode}
)
-target_link_libraries(default_link PRIVATE Qt6::MockPlugins2)
+target_link_libraries(${target} PRIVATE Qt6::MockPlugins2)
+
+# Check that both regular and finalizer mode plugin importing pulls in the same set of plugins.
+foreach(import_mode "" "FINALIZER_MODE")
+ create_test_executable(manual QMock1Plugin QMock2Plugin QMock3Plugin QMock4Plugin
+ ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
+ )
-create_test_executable(manual QMock1Plugin QMock2Plugin QMock3Plugin QMock4Plugin)
-qt_import_plugins(manual
- INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
-)
+ create_test_executable(manual_genex QMock1Plugin QMock2Plugin QMock3Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
+ )
-create_test_executable(manual_genex QMock1Plugin QMock2Plugin QMock3Plugin)
-qt_import_plugins(manual_genex
- INCLUDE $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
-)
+ create_test_executable(blacklist QMock1Plugin ${import_mode})
+ qt_import_plugins(${target}
+ EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin
+ )
-create_test_executable(blacklist QMock1Plugin)
-qt_import_plugins(blacklist
- EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin
-)
+ create_test_executable(blacklist_genex QMock1Plugin ${import_mode})
+ qt_import_plugins(${target}
+ EXCLUDE $<1:Qt6::QMock2Plugin> $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock1Plugin>
+ )
-create_test_executable(blacklist_genex QMock1Plugin)
-qt_import_plugins(blacklist_genex
- EXCLUDE $<1:Qt6::QMock2Plugin> $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock1Plugin>
-)
+ create_test_executable(override QMock3Plugin QMock4Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin Qt6::QMock4Plugin
+ )
-create_test_executable(override QMock3Plugin QMock4Plugin)
-qt_import_plugins(override
- INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin Qt6::QMock4Plugin
-)
+ create_test_executable(override_genex QMock3Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE_BY_TYPE mockplugin $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
+ )
-create_test_executable(override_genex QMock3Plugin)
-qt_import_plugins(override_genex
- INCLUDE_BY_TYPE mockplugin $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin>
-)
-get_target_property(prop_plugs override_genex QT_PLUGINS)
-get_target_property(prop_types override_genex QT_PLUGINS_mockplugin)
+ create_test_executable(override_mix QMock2Plugin QMock3Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE Qt6::QMock2Plugin
+ INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin
+ )
+
+ if(NOT WIN32)
+ # Compiling an empty static array fails on Windows.
+ create_test_executable(none ${import_mode})
+ qt_import_plugins(${target}
+ EXCLUDE_BY_TYPE mockplugin
+ )
+ endif()
+
+ create_test_executable(none_mix QMock3Plugin QMock4Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
+ EXCLUDE_BY_TYPE mockplugin
+ )
+
+ # QMock5Plugin links against the Qt::MockPlugins3 module, which provides the default plugin
+ # QMock6Plugin which is why it is pulled in.
+ create_test_executable(recursive QMock5Plugin QMock6Plugin ${import_mode})
+ qt_import_plugins(${target}
+ INCLUDE_BY_TYPE mockplugin Qt6::QMock5Plugin
+ )
+endforeach()
-create_test_executable(override_mix QMock2Plugin QMock3Plugin)
-qt_import_plugins(override_mix
- INCLUDE Qt6::QMock2Plugin
- INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin
-)
+# No call to qt_import_plugins() in finalizer mode means nothing will be linked.
if(NOT WIN32)
# Compiling an empty static array fails on Windows.
- create_test_executable(none)
- qt_import_plugins(none
- EXCLUDE_BY_TYPE mockplugin
- )
+ create_test_executable(default_finalizer_missing_import_call FINALIZER_MODE)
endif()
-create_test_executable(none_mix QMock3Plugin QMock4Plugin)
-qt_import_plugins(none_mix
- INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin
- EXCLUDE_BY_TYPE mockplugin
-)
+# Empty call to qt_import_plugins() in finalizer mode means default plugins are linked.
+create_test_executable(default_finalizer
+ QMock1Plugin QMock2Plugin
+ QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501
+ FINALIZER_MODE)
+qt_import_plugins(${target})
-# QMock5Plugin links against the Qt::MockPlugins3 module, which provides the default plugin
-# QMock6Plugin which is why it is pulled in.
-create_test_executable(recursive QMock5Plugin QMock6Plugin)
-qt_import_plugins(recursive
- INCLUDE_BY_TYPE mockplugin Qt6::QMock5Plugin
-)
+# Check that plugin importing with manual finalization works.
+create_test_executable(default_qt_add_executable_manually_finalized
+ QMock1Plugin QMock2Plugin
+ QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501
+ FINALIZER_MODE QT_WRAPPER
+ ADD_EXECUTABLE_ARGS MANUAL_FINALIZATION)
+
+qt_finalize_executable(${target})
+
+# Check that plugin importing with automatic finalization works when the CMake version is high
+# enough.
+if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19)
+ create_test_executable(default_qt_add_executable_auto_finalized
+ QMock1Plugin QMock2Plugin
+ QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501
+ FINALIZER_MODE QT_WRAPPER)
+endif()