diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index adfb67ffd6b..e7545084c13 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -87,6 +87,7 @@ qt_internal_add_module(Core global/qtpreprocessorsupport.h global/qtrace_p.h global/qtresource.h + global/qtsymbolmacros.h global/qttranslation.h global/qttypetraits.h global/qtversionchecks.h @@ -342,11 +343,12 @@ qt_internal_add_module(Core PUBLIC_LIBRARIES Qt::Platform EXTRA_CMAKE_FILES - "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CTestMacros.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CoreConfigureFileTemplate.in" - "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CoreDeploySupport.cmake" - "${config_build_dir}/QtInstallPaths.cmake" - ${corelib_extra_cmake_files} + "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CTestMacros.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CoreConfigureFileTemplate.in" + "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CoreResourceInit.in.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Qt6CoreDeploySupport.cmake" + "${config_build_dir}/QtInstallPaths.cmake" + ${corelib_extra_cmake_files} POLICIES QTP0002 ) 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_include_directories("${resource_target}" PRIVATE + "$" + ) _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. diff --git a/src/corelib/Qt6CoreResourceInit.in.cpp b/src/corelib/Qt6CoreResourceInit.in.cpp new file mode 100644 index 00000000000..0234ec8232d --- /dev/null +++ b/src/corelib/Qt6CoreResourceInit.in.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +// This file was generated by the qt_add_resources command. + +#include + +QT_DECLARE_EXTERN_RESOURCE(@RESOURCE_NAME@); + +namespace { + struct resourceReferenceKeeper { + resourceReferenceKeeper() { QT_KEEP_RESOURCE(@RESOURCE_NAME@); } + } resourceReferenceKeeperInstance; +} diff --git a/src/corelib/global/qtsymbolmacros.h b/src/corelib/global/qtsymbolmacros.h new file mode 100644 index 00000000000..18cdc85f728 --- /dev/null +++ b/src/corelib/global/qtsymbolmacros.h @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QTSYMBOLMACROS_H +#define QTSYMBOLMACROS_H + +#if 0 +# pragma qt_sync_stop_processing +#endif + +// For GHS symbol keeping. +#include +#include + +// For handling namespaced resources. +#ifdef QT_NAMESPACE +# define QT_RCC_MANGLE_NAMESPACE0(x) x +# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b +# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) +# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ + QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) +#else +# define QT_RCC_MANGLE_NAMESPACE(name) name +#endif + +// GHS needs special handling to keep a symbol around. +#if defined(Q_CC_GHS) +# define Q_GHS_KEEP_REFERENCE(S) QT_DO_PRAGMA(ghs reference S ##__Fv) +#else +# define Q_GHS_KEEP_REFERENCE(S) +#endif + +// Macros to ensure a symbol is not dropped by the linker even if it's not used. +#define QT_DECLARE_EXTERN_SYMBOL(NAME, RETURN_TYPE) \ + extern RETURN_TYPE NAME(); \ + Q_GHS_KEEP_REFERENCE(NAME) + +#define QT_DECLARE_EXTERN_SYMBOL_INT(NAME) \ + QT_DECLARE_EXTERN_SYMBOL(NAME, int) + +#define QT_DECLARE_EXTERN_SYMBOL_VOID(NAME) \ + QT_DECLARE_EXTERN_SYMBOL(NAME, void) + +#define QT_KEEP_SYMBOL_VAR_NAME(NAME) NAME ## _keep + +#define QT_KEEP_SYMBOL_HELPER(NAME, VAR_NAME) \ + volatile auto VAR_NAME = &NAME; \ + Q_UNUSED(VAR_NAME) + +#define QT_KEEP_SYMBOL(NAME) \ + QT_KEEP_SYMBOL_HELPER(NAME, QT_KEEP_SYMBOL_VAR_NAME(NAME)) + + +// Similar to the ones above, but for rcc resource symbols specifically. +#define QT_GET_RESOURCE_INIT_SYMBOL(NAME) \ + QT_RCC_MANGLE_NAMESPACE(qInitResources_ ## NAME) + +#define QT_DECLARE_EXTERN_RESOURCE(NAME) \ + QT_DECLARE_EXTERN_SYMBOL_INT(QT_GET_RESOURCE_INIT_SYMBOL(NAME)) + +#define QT_KEEP_RESOURCE(NAME) \ + QT_KEEP_SYMBOL(QT_GET_RESOURCE_INIT_SYMBOL(NAME)) + +#endif // QTSYMBOLMACROS_H + diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 975cc6fc7d2..1ce6f8a0202 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -222,6 +222,7 @@ _qt_internal_test_expect_pass(test_multiple_find_package) _qt_internal_test_expect_pass(test_add_resources_delayed_file) _qt_internal_test_expect_pass(test_add_binary_resources_delayed_file BINARY test_add_binary_resources_delayed_file) _qt_internal_test_expect_pass(test_qt_add_resources_rebuild) +_qt_internal_test_expect_pass(test_resource_without_obj_lib BINARY test_resource_without_obj_lib) if(NOT NO_GUI) _qt_internal_test_expect_pass(test_private_includes) diff --git a/tests/auto/cmake/test_resource_without_obj_lib/CMakeLists.txt b/tests/auto/cmake/test_resource_without_obj_lib/CMakeLists.txt new file mode 100644 index 00000000000..16563141f4c --- /dev/null +++ b/tests/auto/cmake/test_resource_without_obj_lib/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(test_resource_without_obj_lib) + +if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/FindPackageHints.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/FindPackageHints.cmake") +endif() + +find_package(Qt6 REQUIRED + COMPONENTS Core Test + HINTS ${Qt6Tests_PREFIX_PATH} +) + +qt6_add_library(helper_lib STATIC helper_lib.cpp) +qt6_add_resources(helper_lib "helper_res" FILES resource.txt PREFIX "/") + +# Link to Core, to ensure both the helper_lib and the main executable +# inherit the QT_NAMESPACE if it is set, otherwise we get undefined +# linker errors due to the mismatch in symbol names. +target_link_libraries(helper_lib PRIVATE Qt6::Core) + +set(CMAKE_AUTOMOC ON) + +qt6_add_executable(test_resource_without_obj_lib main.cpp) +target_link_libraries(test_resource_without_obj_lib PRIVATE Qt6::Core Qt6::Test) + +# Link against the library file and not the target, so that we can confirm +# the ability to manually initialize the resource via Q_INIT_RESOURCE. +target_link_libraries(test_resource_without_obj_lib PRIVATE $) + diff --git a/tests/auto/cmake/test_resource_without_obj_lib/helper_lib.cpp b/tests/auto/cmake/test_resource_without_obj_lib/helper_lib.cpp new file mode 100644 index 00000000000..18371786a92 --- /dev/null +++ b/tests/auto/cmake/test_resource_without_obj_lib/helper_lib.cpp @@ -0,0 +1,4 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +void nothing() {} diff --git a/tests/auto/cmake/test_resource_without_obj_lib/main.cpp b/tests/auto/cmake/test_resource_without_obj_lib/main.cpp new file mode 100644 index 00000000000..29ea0f72721 --- /dev/null +++ b/tests/auto/cmake/test_resource_without_obj_lib/main.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include + +class TestManualResourceInit : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void resourceExistsAfterManualInit(); +}; + +void TestManualResourceInit::initTestCase() +{ + // Manually initialize the resource like we used to do it in qt5 + qmake times. + Q_INIT_RESOURCE(helper_res); +} + +void TestManualResourceInit::resourceExistsAfterManualInit() +{ + QVERIFY(QFile::exists(":/resource.txt")); +} + +QTEST_MAIN(TestManualResourceInit) +#include "main.moc" + diff --git a/tests/auto/cmake/test_resource_without_obj_lib/resource.txt b/tests/auto/cmake/test_resource_without_obj_lib/resource.txt new file mode 100644 index 00000000000..7804a324a4d --- /dev/null +++ b/tests/auto/cmake/test_resource_without_obj_lib/resource.txt @@ -0,0 +1 @@ +Test resource