summaryrefslogtreecommitdiffstats
path: root/src/corelib/Qt6CoreMacros.cmake
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2023-04-17 13:36:27 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2023-08-14 14:05:32 +0200
commit47b879aa0b32c5072dba585dbe8438964efc108c (patch)
treed8d5971a240ae086408ab9a944b00d4ebef4378e /src/corelib/Qt6CoreMacros.cmake
parent139f3f61bd9d68422aa05a8d82ee3aa7897918d2 (diff)
CMake: Place resources into static libraries, not object libraries
Take 2. Re-land previously reverted commit, due to not handling resource names that are not valid c++ identifiers. Now we sanitize the resource names just like rcc does by replacing non-alphanumeric characters with underscores. Original commit message. During the Qt 5 -> Qt 6 and qmake -> CMake porting time frame, it was decided to keep resources in an object file (object library), rather than putting them directly into a static library when doing a static Qt build, so that the build system can take care of linking the object file directly into the executable and thus not forcing project developers to manually initialize resources with the Q_INIT_RESOURCE() macro in project code. This worked for most qmake and cmake projects, but it created difficulties for other build systems, in the sense that these projects would have to manually link to the resource object files, otherwise they would get link time errors about undefined resource symbols, assuming they kept the Q_INIT_RESOURCE() calls. If the project code didn't contain Q_INIT_RESOURCE calls, the situation would be even worse, the linker would not error out, and the missing resources would only be discovered at runtime. It's also an issue in CMake projects that try to link to the library files directly instead of using the library target names, which means the object files would not be automatically linked in. Many projects try to do that because we don't yet offer a convenient way to install libraries and reuse them in other projects (the SDK case), so projects end up shipping only the libraries, without the resource object files. We can improve the situation by moving the resources back into their associated static libraries, and only keeping a static initializer as a separate object file / object library, which references the actual resource initializer symbol, to ensure it does not get discarded during linking. This way, projects that link using targets get no behavior difference, whereas projects linking to static libraries directly can still successfully build as long as their sources have all the necessary Q_INIT_RESOURCE calls. To ensure the resource symbols do not get discarded, we use a few new private macros. We declare the resource init symbols we want to keep as extern symbols and then assign the symbol addresses to volatile variables. This prevents discarding the symbols with the compilers / linkers we care about. It comes at the cost of an additional static initializer per resource, but we would get the same + a bigger performance hit if we just used Q_INIT_RESOURCE twice (once in the object lib and once in project code), which internally needs to traverse a linked list of all resources to check if a resource was initialized or not. For GHS / Integrity, we also need to use a GHS-specific pragma to keep the symbols, which we currently use in qtdeclarative to ensure qml plugin symbols are not discarded. The same macros will be used in a qtdeclarative change to prevent discarding of resources when linking to static qml plugins. A cmake-based test case is added to verify that linking to static libraries directly, without linking to the resource initializer object libraries, works fine as long as the project code calls Q_INIT_RESOURCE for the relevant resource. This reverts commit bc88bb34caf1185a25eda77ee022843c0ca988b0. Fixes: QTBUG-91448 Task-number: QTBUG-110243 Change-Id: Idce69db0cf79d3e32916750bfa61774ced977a7e Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
Diffstat (limited to 'src/corelib/Qt6CoreMacros.cmake')
-rw-r--r--src/corelib/Qt6CoreMacros.cmake50
1 files changed, 47 insertions, 3 deletions
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index cb58bbef40c..a4b1ba4c987 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -1846,11 +1846,24 @@ function(__qt_propagate_generated_resource target resource_name generated_source
math(EXPR resource_count "${resource_count} + 1")
set_target_properties(${target} PROPERTIES _qt_generated_resource_target_count ${resource_count})
+ __qt_internal_generate_init_resource_source_file(
+ resource_init_file ${target} ${resource_name})
+
set(resource_target "${target}_resources_${resource_count}")
- add_library("${resource_target}" OBJECT "${generated_source_code}")
+ add_library("${resource_target}" OBJECT "${resource_init_file}")
+ # Needed so that qtsymbolmacros.h and its dependent headers are already created / syncqt'ed.
+ if(TARGET Core_sync_headers)
+ set(headers_available_target "Core_sync_headers")
+ else()
+ set(headers_available_target "${QT_CMAKE_EXPORT_NAMESPACE}::Core")
+ endif()
+ add_dependencies(${resource_target} ${headers_available_target})
target_compile_definitions("${resource_target}" PRIVATE
"$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_COMPILE_DEFINITIONS>"
)
+ target_include_directories("${resource_target}" PRIVATE
+ "$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_INCLUDE_DIRECTORIES>"
+ )
_qt_internal_set_up_static_runtime_library("${resource_target}")
# Special handling is required for the Core library resources. The linking of the Core
@@ -1869,7 +1882,7 @@ function(__qt_propagate_generated_resource target resource_name generated_source
# .rcc/qrc_qprintdialog.cpp
file(RELATIVE_PATH generated_cpp_file_relative_path
"${CMAKE_CURRENT_BINARY_DIR}"
- "${generated_source_code}")
+ "${resource_init_file}")
set_property(TARGET ${resource_target} APPEND PROPERTY
_qt_resource_generated_cpp_relative_path "${generated_cpp_file_relative_path}")
@@ -1883,8 +1896,39 @@ function(__qt_propagate_generated_resource target resource_name generated_source
set(${output_generated_target} "${resource_target}" PARENT_SCOPE)
else()
set(${output_generated_target} "" PARENT_SCOPE)
- target_sources(${target} PRIVATE ${generated_source_code})
endif()
+
+ target_sources(${target} PRIVATE ${generated_source_code})
+endfunction()
+
+function(__qt_internal_sanitize_resource_name out_var name)
+ # The sanitized output should match RCCResourceLibrary::writeInitializer()'s
+ # isAsciiLetterOrNumber-based substituion.
+ # MAKE_C_IDENTIFIER matches that, it replaces non-alphanumeric chars with underscores.
+ string(MAKE_C_IDENTIFIER "${name}" sanitized_resource_name)
+ set(${out_var} "${sanitized_resource_name}" PARENT_SCOPE)
+endfunction()
+
+function(__qt_internal_generate_init_resource_source_file out_var target resource_name)
+ set(template_file "${__qt_core_macros_module_base_dir}/Qt6CoreResourceInit.in.cpp")
+
+ # Gets replaced in the template
+ __qt_internal_sanitize_resource_name(RESOURCE_NAME "${resource_name}")
+ set(resource_init_path "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resource_name}_init.cpp")
+
+ configure_file("${template_file}" "${resource_init_path}" @ONLY)
+
+ set(scope_args "")
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set(scope_args TARGET_DIRECTORY ${target})
+ endif()
+ set_source_files_properties(${resource_init_path} ${scope_args} PROPERTIES
+ SKIP_AUTOGEN TRUE
+ SKIP_UNITY_BUILD_INCLUSION TRUE
+ SKIP_PRECOMPILE_HEADERS TRUE
+ )
+
+ set(${out_var} "${resource_init_path}" PARENT_SCOPE)
endfunction()
# Make file visible in IDEs.