diff --git a/cmake/QtPlugins.cmake.in b/cmake/QtPlugins.cmake.in index 3a7b8ab30d3..a96588cc5ed 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 34790fe6a3d..35026fa3957 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 "$") - # The code in here uses the properties defined in qt_import_plugins (Qt6CoreMacros.cmake) set(_plugin_is_default "$") + # The code in here uses the properties defined in qt_import_plugins (Qt6CoreMacros.cmake) + # INCLUDE set(_plugin_is_whitelisted "$") 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 "$>>") + set(full_plugin_condition "$") + 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 01426cd8ea4..d05688c596a 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 3db7f012dba..bc3580760f3 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) -create_test_executable(manual QMock1Plugin QMock2Plugin QMock3Plugin QMock4Plugin) -qt_import_plugins(manual - INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin -) +# 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_genex QMock1Plugin QMock2Plugin QMock3Plugin) -qt_import_plugins(manual_genex - INCLUDE $<1:Qt6::QMock3Plugin> $<0: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(blacklist QMock1Plugin) -qt_import_plugins(blacklist - EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin -) + create_test_executable(blacklist QMock1Plugin ${import_mode}) + qt_import_plugins(${target} + EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin + ) -create_test_executable(blacklist_genex QMock1Plugin) -qt_import_plugins(blacklist_genex - EXCLUDE $<1:Qt6::QMock2Plugin> $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock1Plugin> -) + 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(override QMock3Plugin QMock4Plugin) -qt_import_plugins(override - INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin Qt6::QMock4Plugin -) + create_test_executable(override QMock3Plugin QMock4Plugin ${import_mode}) + qt_import_plugins(${target} + INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin 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_genex QMock3Plugin ${import_mode}) + qt_import_plugins(${target} + INCLUDE_BY_TYPE mockplugin $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin> + ) -create_test_executable(override_mix QMock2Plugin QMock3Plugin) -qt_import_plugins(override_mix - INCLUDE Qt6::QMock2Plugin - INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin -) + 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) - qt_import_plugins(none + 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() + + +# 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(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()