From a3c430f390b379d874916d4c9ff02af5323af1bd Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 26 May 2021 17:19:08 +0200 Subject: [PATCH] CMake: Revise plugin finalizer mode usage Only use plugin finalizer mode if qt_finalize_target is called at the end of the user project (which we can't really check, the user has to ensure that) or when qt_finalize_target is automatically defer-called by CMake 3.19+ (which is done by qt_add_executable). This removes the previous behavior of using the finalizer mode if qt_import_plugins is called. Instead the old regular mode is used if the above preconditions are not met. The removed behavior had ordering issues if qt_import_plugins was called before target_link_libraries. The dependency walking would be done before Qt dependencies were added and thus no plugins would be linked. Amends 6fcc272ac9dcf1d6d65de1bdf3138722ba63a902 Task-number: QTBUG-80863 Task-number: QTBUG-92933 Task-number: QTBUG-94030 Change-Id: I78702b653a35596f5581c2f4282b2336f0124e60 Reviewed-by: Qt CI Bot Reviewed-by: Joerg Bornemann --- cmake/QtPublicPluginHelpers.cmake | 2 ++ src/corelib/Qt6CoreMacros.cmake | 16 +++---------- .../cmake/test_import_plugins/CMakeLists.txt | 24 ++++++++++++++----- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/cmake/QtPublicPluginHelpers.cmake b/cmake/QtPublicPluginHelpers.cmake index 35026fa3957..0c4eebec0ba 100644 --- a/cmake/QtPublicPluginHelpers.cmake +++ b/cmake/QtPublicPluginHelpers.cmake @@ -365,6 +365,8 @@ function(__qt_internal_apply_plugin_imports_finalizer_mode target) __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. + # The precondition for this is that qt_finalize_target was called (either explicitly by the user + # or auto-deferred by CMake 3.19+). if("${use_finalizer_mode}" STREQUAL "") qt_enable_import_plugins_finalizer_mode(${target} TRUE) endif() diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 6cba48dee7e..6dca713ee69 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -591,8 +591,8 @@ function(_qt_internal_finalize_executable target) # 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. + # manually called qt_finalize_target at the end of their CMake project, or it was automatically + # done via a deferred call. get_target_property(is_immediately_finalized "${target}" _qt_is_immediately_finalized) if(NOT is_immediately_finalized) __qt_internal_apply_plugin_imports_finalizer_mode("${target}") @@ -868,15 +868,6 @@ 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_target, 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_target). - __qt_internal_apply_plugin_imports_finalizer_mode(${target}) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) @@ -901,8 +892,7 @@ endif() # of its associated module. # # Finalizer mode is enabled by default if: -# - the project calls qt_import_plugins explicitly or -# - the project calls qt_finalize_target explicitly or +# - the project calls qt_finalize_target explicitly at the end of the project file or # - the project uses qt_add_executable and a CMake version greater than or equal to 3.19 # (which will DEFER CALL qt_finalize_target) function(qt6_enable_import_plugins_finalizer_mode target enabled) diff --git a/tests/auto/cmake/test_import_plugins/CMakeLists.txt b/tests/auto/cmake/test_import_plugins/CMakeLists.txt index bdb62617621..ecfa7a0777a 100644 --- a/tests/auto/cmake/test_import_plugins/CMakeLists.txt +++ b/tests/auto/cmake/test_import_plugins/CMakeLists.txt @@ -72,43 +72,52 @@ create_test_executable(default_link QMock1Plugin QMock2Plugin target_link_libraries(${target} PRIVATE Qt6::MockPlugins2) # Check that both regular and finalizer mode plugin importing pulls in the same set of plugins. +# In regular mode, qt_finalize_target won't execute the finalizer plugin importing, because +# we opt out via qt_enable_import_plugins_finalizer_mode(target FALSE). foreach(import_mode "" "FINALIZER_MODE") create_test_executable(manual QMock1Plugin QMock2Plugin QMock3Plugin QMock4Plugin ${import_mode}) qt_import_plugins(${target} INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin ) + qt_finalize_target(${target}) create_test_executable(manual_genex QMock1Plugin QMock2Plugin QMock3Plugin ${import_mode}) qt_import_plugins(${target} INCLUDE $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin> ) + qt_finalize_target(${target}) create_test_executable(blacklist QMock1Plugin ${import_mode}) qt_import_plugins(${target} EXCLUDE Qt6::QMock2Plugin Qt6::QMock3Plugin ) + qt_finalize_target(${target}) create_test_executable(blacklist_genex QMock1Plugin ${import_mode}) qt_import_plugins(${target} EXCLUDE $<1:Qt6::QMock2Plugin> $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock1Plugin> ) + qt_finalize_target(${target}) create_test_executable(override QMock3Plugin QMock4Plugin ${import_mode}) qt_import_plugins(${target} INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin Qt6::QMock4Plugin ) + qt_finalize_target(${target}) create_test_executable(override_genex QMock3Plugin ${import_mode}) qt_import_plugins(${target} INCLUDE_BY_TYPE mockplugin $<1:Qt6::QMock3Plugin> $<0:Qt6::QMock4Plugin> ) + qt_finalize_target(${target}) create_test_executable(override_mix QMock2Plugin QMock3Plugin ${import_mode}) qt_import_plugins(${target} INCLUDE Qt6::QMock2Plugin INCLUDE_BY_TYPE mockplugin Qt6::QMock3Plugin ) + qt_finalize_target(${target}) if(NOT WIN32) # Compiling an empty static array fails on Windows. @@ -116,6 +125,7 @@ foreach(import_mode "" "FINALIZER_MODE") qt_import_plugins(${target} EXCLUDE_BY_TYPE mockplugin ) + qt_finalize_target(${target}) endif() create_test_executable(none_mix QMock3Plugin QMock4Plugin ${import_mode}) @@ -123,6 +133,7 @@ foreach(import_mode "" "FINALIZER_MODE") INCLUDE Qt6::QMock3Plugin Qt6::QMock4Plugin EXCLUDE_BY_TYPE mockplugin ) + qt_finalize_target(${target}) # QMock5Plugin links against the Qt::MockPlugins3 module, which provides the default plugin # QMock6Plugin which is why it is pulled in. @@ -130,10 +141,11 @@ foreach(import_mode "" "FINALIZER_MODE") qt_import_plugins(${target} INCLUDE_BY_TYPE mockplugin Qt6::QMock5Plugin ) + qt_finalize_target(${target}) endforeach() - -# No call to qt_import_plugins() in finalizer mode means nothing will be linked. +# No call to qt_finalize_target() 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) @@ -145,18 +157,18 @@ create_test_executable(default_finalizer QMock3Plugin # TODO: Should not be linked based on .pro file, see QTBUG-93501 FINALIZER_MODE) qt_import_plugins(${target}) +qt_finalize_target(${target}) -# Check that plugin importing with manual finalization works. +# Check that plugin importing with the qt_add_executable wrapper and manual finalization mode 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_target(${target}) -# Check that plugin importing with automatic finalization works when the CMake version is high -# enough. +# Check that plugin importing with automatic deferred 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