Implement propagation of object files to the end-point executable

This proposal collects all the resource objects to the qt-specific
property of the static libraries. This is done to avoid littering
of other static libraries and put resource object files to the
source part of the linker line when linking the end-point
executable.
The way we link object resource libraries is changed back to the
target_link_libraries approach as we may omit using finalizers
with linkers other than ld. Users may enforce finalizers by calling
the qt6_enable_resource_objects_finalizer_mode function if need.

Refactor tests related to the static resources.

Amends ddaa7150d8

Task-number: QTBUG-93002
Change-Id: I74135e291cd82fb54d1b284b4b4a1e002b1fef98
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Alexey Edelev 2021-05-19 19:46:24 +02:00
parent 029425478a
commit 19e789bace
20 changed files with 332 additions and 28 deletions

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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 "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>")
endif()
endforeach()
endforeach()
set(${out_var} "${resource_objects}" PARENT_SCOPE)
endfunction()

View File

@ -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
"$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>"
# 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
"$<NOT:$<BOOL:$<TARGET_PROPERTY:_qt_resource_objects_finalizer_mode>>>"
)
# Do not litter the static libraries
set(not_static_condition
"$<NOT:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>>"
)
set(resource_objects "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>")
target_link_libraries(${target} INTERFACE
"$<$<AND:${finalizer_mode_condition},${not_static_condition}>:${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

View File

@ -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",

View File

@ -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")

View File

@ -0,0 +1 @@
set(QT_REPO_MODULE_VERSION "6.2.0")

View File

@ -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()

View File

@ -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"
)

View File

@ -0,0 +1,2 @@
# This is needed so that MODULE_PLUGIN_TYPES property is set on the exported target.
# Fun times.

View File

@ -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() { }

View File

@ -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

View File

@ -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()

View File

@ -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 <QtTest/QtTest>
#include <QtCore/qfile.h>
#include <QtCore/qobject.h>
#include <QtPlugin>
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"