diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 09f1a4de6b..bfe52ca6d7 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -245,6 +245,7 @@ qt_copy_or_install(FILES set(__public_cmake_helpers cmake/QtFeature.cmake cmake/QtFeatureCommon.cmake + cmake/QtPublicFinalizerHelpers.cmake cmake/QtPublicPluginHelpers.cmake cmake/QtPublicTargetHelpers.cmake cmake/QtPublicWalkLibsHelpers.cmake diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index 7708954b90..063128e3d6 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -55,6 +55,7 @@ file(TO_CMAKE_PATH "$ENV{QT_ADDITIONAL_PACKAGES_PREFIX_PATH}" _qt_additional_pac # Public helpers available to all Qt packages. include("${CMAKE_CURRENT_LIST_DIR}/QtFeature.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtPublicFinalizerHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicPluginHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicTargetHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWalkLibsHelpers.cmake") diff --git a/cmake/QtPublicFinalizerHelpers.cmake b/cmake/QtPublicFinalizerHelpers.cmake new file mode 100644 index 0000000000..24354654c8 --- /dev/null +++ b/cmake/QtPublicFinalizerHelpers.cmake @@ -0,0 +1,20 @@ +# Helper to check if the finalizer mode 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_check_finalizer_mode target out_var finalizer) + get_target_property(value ${target} _qt_${finalizer}_finalizer_mode) + if("${value}" STREQUAL "value-NOTFOUND") + set_property(TARGET "${target}" PROPERTY _qt_${finalizer}_finalizer_mode "TRUE") + set(value TRUE) + endif() + set(${out_var} "${value}" PARENT_SCOPE) +endfunction() + +function(__qt_internal_enable_finalizer_mode target finalizer enabled) + if(enabled) + set(enabled "TRUE") + else() + set(enabled "FALSE") + endif() + set_property(TARGET "${target}" PROPERTY _qt_${finalizer}_finalizer_mode "${enabled}") +endfunction() diff --git a/cmake/QtPublicTargetHelpers.cmake b/cmake/QtPublicTargetHelpers.cmake index 9ce8cbf700..1a01a3066b 100644 --- a/cmake/QtPublicTargetHelpers.cmake +++ b/cmake/QtPublicTargetHelpers.cmake @@ -14,3 +14,37 @@ function(__qt_internal_strip_target_directory_scope_token target out_var) string(REGEX REPLACE "::@<.+>$" "" target "${target}") set("${out_var}" "${target}" PARENT_SCOPE) endfunction() + +function(__qt_internal_process_dependency_resource_objects target) + get_target_property(processed ${target} _qt_resource_object_finalizers_processed) + if(processed) + return() + endif() + set_target_properties(${target} PROPERTIES _qt_resource_object_finalizers_processed TRUE) + + __qt_internal_check_finalizer_mode(${target} use_finalizer_mode resource_objects) + if(NOT use_finalizer_mode) + return() + endif() + + __qt_internal_collect_dependency_resource_objects(${target} resource_objects) + target_sources(${target} PRIVATE "${resource_objects}") +endfunction() + +function(__qt_internal_collect_dependency_resource_objects target out_var) + # TODO: The __qt_internal_collect_all_target_dependencies function is not cheap and called + # multiple times in the finalizer jobs. It make sense to re-use its results. + __qt_internal_collect_all_target_dependencies(${target} dep_targets) + + set(resource_objects "") + foreach(dep IN LISTS dep_targets) + get_target_property(dep_resource_targets ${dep} _qt_resource_object_libraries) + foreach(resource_target IN LISTS dep_resource_targets) + if(resource_target) + list(PREPEND resource_objects "$>") + endif() + endforeach() + endforeach() + + set(${out_var} "${resource_objects}" PARENT_SCOPE) +endfunction() diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 6dca713ee6..91fa4590c7 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -596,6 +596,7 @@ function(_qt_internal_finalize_executable target) get_target_property(is_immediately_finalized "${target}" _qt_is_immediately_finalized) if(NOT is_immediately_finalized) __qt_internal_apply_plugin_imports_finalizer_mode("${target}") + __qt_internal_process_dependency_resource_objects("${target}") endif() set_target_properties(${target} PROPERTIES _qt_executable_is_finalized TRUE) @@ -910,6 +911,20 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) endfunction() endif() +# This function allows enabling or disabling the finalizer mode of resource objects linking in +# static Qt builds. +# It makes sense to manually disable the finalizer of the resource object if you are using +# linkers other than ld, since the dependencies between resource objects and static libraries +# are resolved correctly by them. +function(qt6_enable_resource_objects_finalizer_mode target enabled) + __qt_internal_enable_finalizer_mode(${target} resource_objects ${enabled}) +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_enable_resource_objects_finalizer_mode) + qt6_enable_resource_objects_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 @@ -1434,12 +1449,36 @@ function(__qt_propagate_generated_resource target resource_name generated_source set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_generated_cpp_relative_path "${generated_cpp_file_relative_path}") - # Use TARGET_NAME genex to map to the correct prefixed target name when it is exported - # via qt_install(EXPORT), so that the consumers of the target can find the object library - # as well. - target_sources(${target} INTERFACE - "$>" + # After internal discussion we decided to not rely on the linker order that CMake + # offers, until CMake provides the guaranteed linking order that suites our needs in a + # future CMake version. + # The _qt_resource_object_libraries collects all resource targets owned by the 'target' + # instead of the implicit propagating and is used by the executable finalizer to expose + # objects as the end-point sources. If the user prefers not to use a finalizer or the CMake + # version does not support DEFER calls, fall back to interface linking of the resource + # objects. + # target_link_libraries works well with linkers other than ld. If user didn't enforce + # a finalizer we rely on linker to resolve circular dependencies between resource + # objects and static libraries. + set_property(TARGET ${target} APPEND PROPERTY + _qt_resource_object_libraries ${resource_target} ) + set_property(TARGET ${target} APPEND PROPERTY + EXPORT_PROPERTIES _qt_resource_object_libraries + ) + # Keep the implicit linking if finalizers are not used. + set(finalizer_mode_condition + "$>>" + ) + # Do not litter the static libraries + set(not_static_condition + "$,STATIC_LIBRARY>>" + ) + set(resource_objects "$>") + target_link_libraries(${target} INTERFACE + "$<$:${resource_objects}>" + ) + if(NOT target STREQUAL "Core") # It's necessary to link the object library target, since we want to pass # the object library dependencies to the 'target'. Interface linking doesn't diff --git a/sync.profile b/sync.profile index 92995a06c1..f3805528f8 100644 --- a/sync.profile +++ b/sync.profile @@ -22,6 +22,7 @@ "QtMockPlugins1" => "$basedir/tests/auto/cmake/mockplugins/mockplugins1", "QtMockPlugins2" => "$basedir/tests/auto/cmake/mockplugins/mockplugins2", "QtMockPlugins3" => "$basedir/tests/auto/cmake/mockplugins/mockplugins3", + "QtMockStaticResources1" => "$basedir/tests/auto/cmake/test_static_resources/mock_static_resources1", ); %moduleheaders = ( # restrict the module headers to those found in relative path "QtEglFSDeviceIntegration" => "api", diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index ff3edf922a..b95c530640 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -251,7 +251,9 @@ _qt_internal_test_expect_pass(test_add_resources_binary_generated BINARY test_add_resources_binary_generated) include(test_plugin_shared_static_flavor.cmake) -_qt_internal_test_expect_pass(test_init_resources_static_plugin - BINARY test_init_resources_static_plugin) _qt_internal_test_expect_pass(tst_qaddpreroutine BINARY tst_qaddpreroutine) + +_qt_internal_test_expect_pass(test_static_resources + BINARY "${CMAKE_CTEST_COMMAND}" + BINARY_ARGS "-V") diff --git a/tests/auto/cmake/test_static_resources/.cmake.conf b/tests/auto/cmake/test_static_resources/.cmake.conf new file mode 100644 index 0000000000..4e73b3d607 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/.cmake.conf @@ -0,0 +1 @@ +set(QT_REPO_MODULE_VERSION "6.2.0") diff --git a/tests/auto/cmake/test_static_resources/CMakeLists.txt b/tests/auto/cmake/test_static_resources/CMakeLists.txt new file mode 100644 index 0000000000..06c7195625 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.15.0) + +include(.cmake.conf) + +project(TestStaticResources + DESCRIPTION "Test of the static resources" + HOMEPAGE_URL "https://qt.io/" + LANGUAGES CXX C + VERSION "${QT_REPO_MODULE_VERSION}" +) + +find_package(Qt6 COMPONENTS Core BuildInternals Test CONFIG REQUIRED) +set(QT_NO_INTERNAL_COMPATIBILITY_FUNCTIONS TRUE) + +qt_build_repo_begin() + +enable_testing() +add_subdirectory(mock_static_resources1) +add_subdirectory(test_init_resources_static_plugin) +add_subdirectory(test_static_resources_propagation) + +qt_build_repo_end() diff --git a/tests/auto/cmake/test_static_resources/mock_static_resources1/CMakeLists.txt b/tests/auto/cmake/test_static_resources/mock_static_resources1/CMakeLists.txt new file mode 100644 index 0000000000..973f8b3279 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/mock_static_resources1/CMakeLists.txt @@ -0,0 +1,17 @@ +qt_internal_add_module(MockStaticResources1 + STATIC + PLUGIN_TYPES mockstaticresources + SOURCES + dummy.cpp + LIBRARIES + Qt::CorePrivate + PUBLIC_LIBRARIES + Qt::Core +) + +qt_internal_add_resource(MockStaticResources1 "teststaticmodule1" + PREFIX + "/teststaticmodule1" + FILES + "testfile1.txt" +) diff --git a/tests/auto/cmake/test_static_resources/mock_static_resources1/configure.cmake b/tests/auto/cmake/test_static_resources/mock_static_resources1/configure.cmake new file mode 100644 index 0000000000..46da2db2b8 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/mock_static_resources1/configure.cmake @@ -0,0 +1,2 @@ +# This is needed so that MODULE_PLUGIN_TYPES property is set on the exported target. +# Fun times. diff --git a/tests/auto/cmake/test_static_resources/mock_static_resources1/dummy.cpp b/tests/auto/cmake/test_static_resources/mock_static_resources1/dummy.cpp new file mode 100644 index 0000000000..47a48945a2 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/mock_static_resources1/dummy.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void dummy() { } diff --git a/tests/auto/cmake/test_init_resources_static_plugin/testfile1.txt b/tests/auto/cmake/test_static_resources/mock_static_resources1/testfile1.txt similarity index 100% rename from tests/auto/cmake/test_init_resources_static_plugin/testfile1.txt rename to tests/auto/cmake/test_static_resources/mock_static_resources1/testfile1.txt diff --git a/tests/auto/cmake/test_init_resources_static_plugin/CMakeLists.txt b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/CMakeLists.txt similarity index 54% rename from tests/auto/cmake/test_init_resources_static_plugin/CMakeLists.txt rename to tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/CMakeLists.txt index a0e40585e3..07ce26e052 100644 --- a/tests/auto/cmake/test_init_resources_static_plugin/CMakeLists.txt +++ b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/CMakeLists.txt @@ -1,27 +1,7 @@ -cmake_minimum_required(VERSION 3.16) - -if(DEFINED CMAKE_Core_MODULE_MAJOR_VERSION) - set(project_version "${CMAKE_Core_MODULE_MAJOR_VERSION}.\ -${CMAKE_Core_MODULE_MINOR_VERSION}.${CMAKE_Core_MODULE_PATCH_VERSION}" - ) -else() - set(project_version "6.0.0") -endif() - -project(TestInitResourcesStaticPlugin - LANGUAGES CXX - VERSION "${project_version}" -) - -find_package(Qt6 COMPONENTS Core BuildInternals CONFIG REQUIRED) -qt_prepare_standalone_project() - -find_package(Qt6 COMPONENTS Gui Test CONFIG REQUIRED) # Add gui since Core have no plugin types - qt_internal_add_plugin(TestInitResourcesStaticPlugin STATIC OUTPUT_NAME testinitresourcesstaticplugin - TYPE generic + TYPE mockstaticresources SOURCES pluginmain.cpp SKIP_INSTALL diff --git a/tests/auto/cmake/test_init_resources_static_plugin/pluginmain.cpp b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/pluginmain.cpp similarity index 100% rename from tests/auto/cmake/test_init_resources_static_plugin/pluginmain.cpp rename to tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/pluginmain.cpp diff --git a/tests/auto/cmake/test_init_resources_static_plugin/test_init_resources_static_plugin.cpp b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/test_init_resources_static_plugin.cpp similarity index 100% rename from tests/auto/cmake/test_init_resources_static_plugin/test_init_resources_static_plugin.cpp rename to tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/test_init_resources_static_plugin.cpp diff --git a/tests/auto/cmake/test_init_resources_static_plugin/testfile2.txt b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/testfile1.txt similarity index 100% rename from tests/auto/cmake/test_init_resources_static_plugin/testfile2.txt rename to tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/testfile1.txt diff --git a/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/testfile2.txt b/tests/auto/cmake/test_static_resources/test_init_resources_static_plugin/testfile2.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/auto/cmake/test_static_resources/test_static_resources_propagation/CMakeLists.txt b/tests/auto/cmake/test_static_resources/test_static_resources_propagation/CMakeLists.txt new file mode 100644 index 0000000000..40c922fd8b --- /dev/null +++ b/tests/auto/cmake/test_static_resources/test_static_resources_propagation/CMakeLists.txt @@ -0,0 +1,96 @@ +# Add a dummy library that links the static "Qt" module containing resources +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp" CONTENT "void dummy() { }") +add_library(dummy STATIC "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp") +target_link_libraries(dummy PRIVATE MockStaticResources1) + + +# Add the executable using qt_add_executable that needs to initialize the propagated resources. +# Finalize it implicitly(since CMake version 3.19). +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) + qt_add_executable(test_static_resources_propagation main.cpp) + set_target_properties(test_static_resources_propagation PROPERTIES + AUTOMOC TRUE + ) + target_link_libraries(test_static_resources_propagation + PRIVATE + dummy + Qt::Core + Qt::Test + ) + add_test(NAME test_static_resources_propagation + COMMAND test_static_resources_propagation + ) +endif() + + +# Add the executable using qt_add_executable that needs to initialize the propagated resources. +# Finalize it explicitly. +qt_add_executable(test_static_resources_propagation_manual_finalize main.cpp MANUAL_FINALIZATION) +set_target_properties(test_static_resources_propagation_manual_finalize PROPERTIES + AUTOMOC TRUE +) +target_link_libraries(test_static_resources_propagation_manual_finalize + PRIVATE + dummy + Qt::Core + Qt::Test +) +add_test(NAME test_static_resources_propagation_manual_finalize + COMMAND test_static_resources_propagation_manual_finalize +) +qt_finalize_target(test_static_resources_propagation_manual_finalize) + + +# Add the executable using add_executable that needs to initialize the propagated resources. +# Finalize it explicitly. +add_executable(test_static_resources_propagation_non_qt main.cpp) +set_target_properties(test_static_resources_propagation_non_qt PROPERTIES + AUTOMOC TRUE +) +target_link_libraries(test_static_resources_propagation_non_qt + PRIVATE + dummy + Qt::Core + Qt::Test +) +qt_finalize_target(test_static_resources_propagation_non_qt) +add_test(NAME test_static_resources_propagation_non_qt + COMMAND test_static_resources_propagation_non_qt +) + + +if(NOT GCC AND NOT MINGW AND NOT CLANG) + ## Add the executable using add_executable, expecting resources to be linked regardless of order. + add_executable(test_static_resources_propagation_non_ld main.cpp) + set_target_properties(test_static_resources_propagation_non_ld PROPERTIES + AUTOMOC TRUE + ) + target_link_libraries(test_static_resources_propagation_non_ld + PRIVATE + dummy + Qt::Core + Qt::Test + ) + add_test(NAME test_static_resources_propagation_non_ld + COMMAND test_static_resources_propagation_non_ld + ) + + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) + qt_add_executable(test_static_resources_propagation_not_finalize main.cpp) + qt6_enable_resource_objects_finalizer_mode( + test_static_resources_propagation_not_finalize FALSE + ) + set_target_properties(test_static_resources_propagation_not_finalize PROPERTIES + AUTOMOC TRUE + ) + target_link_libraries(test_static_resources_propagation_not_finalize + PRIVATE + dummy + Qt::Core + Qt::Test + ) + add_test(NAME test_static_resources_propagation_not_finalize + COMMAND test_static_resources_propagation_not_finalize + ) + endif() +endif() diff --git a/tests/auto/cmake/test_static_resources/test_static_resources_propagation/main.cpp b/tests/auto/cmake/test_static_resources/test_static_resources_propagation/main.cpp new file mode 100644 index 0000000000..4dee8eb0c7 --- /dev/null +++ b/tests/auto/cmake/test_static_resources/test_static_resources_propagation/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +class TestStaticResourcePropagation : public QObject +{ + Q_OBJECT +private slots: + void resourceFilesExist(); +}; + +void TestStaticResourcePropagation::resourceFilesExist() +{ + bool result = QFile::exists(":/teststaticmodule1/testfile1.txt"); + QVERIFY(result); +} + +QTEST_MAIN(TestStaticResourcePropagation) +#include "main.moc"