summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Bornemann <[email protected]>2025-01-03 17:19:22 +0100
committerJoerg Bornemann <[email protected]>2025-01-24 18:53:34 +0100
commitad7b94e163ac5c3959a7e38d7f48536be288a187 (patch)
tree6d92d6cc8853edaccc4441ba7fea74b2b6bc2195
parentcf9c94e85160a9d0f03e284ff92995900076faee (diff)
CMake: Only load Qt6FooPrivate automatically when building Qt
[ChangeLog][CMake] CMake packages of public Qt modules don't provide the targets of their private counterparts anymore. User projects must now call find_package(Qt6 COMPONENTS FooPrivate) to make use of the Qt6::FooPrivate target. User projects that rely on the old behavior can set the CMake variable QT_FIND_PRIVATE_MODULES to ON. For user projects, the warning message we know from QMake is displayed. The warning can be disabled by setting the CMake variable QT_NO_PRIVATE_MODULE_WARNING to ON. Within Qt itself, find_package(Qt6Foo) will still find_package(Qt6FooPrivate). For static Qt builds, we need to wrap usage of private Qt modules in $<BUILD_INTERFACE> or $<BUILD_LOCAL_INTERFACE> (if CMake's version is >= 3.26). Static builds with CMake < 3.26 will always load the private modules if the Qt6FooConfig.cmake from Qt's build tree is loaded. This is the case in non-prefix builds and (in the future) when building examples as external project. This amends commit fbbf4ace0188b9718b6d7808021c0b887fd52d9f. Pick-to: 6.9 Task-number: QTBUG-87776 Change-Id: I78e95248f2b3fa73c3005c61df2fe4f71ad8eeb8 Reviewed-by: Alexandru Croitor <[email protected]>
-rw-r--r--cmake/QtModuleConfig.cmake.in46
-rw-r--r--cmake/QtModuleConfigPrivate.cmake.in14
-rw-r--r--cmake/QtModuleHelpers.cmake35
-rw-r--r--cmake/QtPublicWalkLibsHelpers.cmake12
-rw-r--r--cmake/QtTargetHelpers.cmake45
-rw-r--r--tests/auto/cmake/mockplugins/CMakeLists.txt2
-rw-r--r--tests/auto/cmake/test_private_includes/CMakeLists.txt2
-rw-r--r--tests/auto/cmake/test_private_targets/CMakeLists.txt2
-rw-r--r--tests/auto/cmake/tst_qaddpreroutine/CMakeLists.txt2
9 files changed, 126 insertions, 34 deletions
diff --git a/cmake/QtModuleConfig.cmake.in b/cmake/QtModuleConfig.cmake.in
index 93185857162..de243aff2b3 100644
--- a/cmake/QtModuleConfig.cmake.in
+++ b/cmake/QtModuleConfig.cmake.in
@@ -32,26 +32,35 @@ if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND)
endif()
# Find the private module counterpart.
-if (@INSTALL_CMAKE_NAMESPACE@@target@_FOUND AND NOT @arg_NO_PRIVATE_MODULE@)
- if(NOT @INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND)
- if("${_qt_cmake_dir}" STREQUAL "")
- set(_qt_cmake_dir "${QT_TOOLCHAIN_RELOCATABLE_CMAKE_DIR}")
- endif()
- set(__qt_use_no_default_path_for_qt_packages "NO_DEFAULT_PATH")
- if(QT_DISABLE_NO_DEFAULT_PATH_IN_QT_PACKAGES)
- set(__qt_use_no_default_path_for_qt_packages "")
- endif()
- find_package(@INSTALL_CMAKE_NAMESPACE@@target_private@ "@PROJECT_VERSION@" EXACT
- QUIET
- CONFIG
- PATHS
- ${QT_BUILD_CMAKE_PREFIX_PATH}
- "${CMAKE_CURRENT_LIST_DIR}/.."
- "${_qt_cmake_dir}"
- ${_qt_additional_packages_prefix_paths}
- ${__qt_use_no_default_path_for_qt_packages}
+set(__qt_@target@_always_load_private_module OFF)
+include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@[email protected]" OPTIONAL)
+if (@INSTALL_CMAKE_NAMESPACE@@target@_FOUND
+ AND NOT @INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND
+ AND NOT @arg_NO_PRIVATE_MODULE@
+ AND (
+ __qt_@target@_always_load_private_module
+ OR DEFINED QT_REPO_MODULE_VERSION
+ OR QT_FIND_PRIVATE_MODULES
)
+ )
+ if("${_qt_cmake_dir}" STREQUAL "")
+ set(_qt_cmake_dir "${QT_TOOLCHAIN_RELOCATABLE_CMAKE_DIR}")
endif()
+ set(__qt_use_no_default_path_for_qt_packages "NO_DEFAULT_PATH")
+ if(QT_DISABLE_NO_DEFAULT_PATH_IN_QT_PACKAGES)
+ set(__qt_use_no_default_path_for_qt_packages "")
+ endif()
+ find_package(@INSTALL_CMAKE_NAMESPACE@@target_private@ "@PROJECT_VERSION@" EXACT
+ QUIET
+ CONFIG
+ PATHS
+ ${QT_BUILD_CMAKE_PREFIX_PATH}
+ "${CMAKE_CURRENT_LIST_DIR}/.."
+ "${_qt_cmake_dir}"
+ ${_qt_additional_packages_prefix_paths}
+ ${__qt_use_no_default_path_for_qt_packages}
+ )
+
if(NOT @INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND)
get_property(@INSTALL_CMAKE_NAMESPACE@@target_private@_warning_shown GLOBAL PROPERTY
@INSTALL_CMAKE_NAMESPACE@@target_private@_warning_shown
@@ -68,6 +77,7 @@ if (@INSTALL_CMAKE_NAMESPACE@@target@_FOUND AND NOT @arg_NO_PRIVATE_MODULE@)
endif()
endif()
endif()
+unset(__qt_@target@_always_load_private_module)
if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND)
# DEPRECATED
diff --git a/cmake/QtModuleConfigPrivate.cmake.in b/cmake/QtModuleConfigPrivate.cmake.in
index 94117e6f9d3..31bf64a4783 100644
--- a/cmake/QtModuleConfigPrivate.cmake.in
+++ b/cmake/QtModuleConfigPrivate.cmake.in
@@ -20,6 +20,20 @@ if(NOT DEFINED "@INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND")
set("@INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND" TRUE)
endif()
+if(NOT __qt_@target@_always_load_private_module
+ AND NOT DEFINED QT_REPO_MODULE_VERSION
+ AND NOT QT_NO_PRIVATE_MODULE_WARNING
+ AND NOT __qt_private_module_@target_private@_warning_shown)
+ message(WARNING
+ "This project is using headers of the @target_private@ module and will therefore be tied "
+ "to this specific Qt module build version. "
+ "Running this project against other versions of the Qt modules may crash at any arbitrary "
+ "point. This is not a bug, but a result of using Qt internals. You have been warned! "
+ "\nYou can disable this warning by setting QT_NO_PRIVATE_MODULE_WARNING to ON."
+ )
+ set(__qt_private_module_@target_private@_warning_shown TRUE)
+endif()
+
if(NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target_private@_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@[email protected]")
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@[email protected]")
diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake
index 401d04bc84e..5d6a16901c0 100644
--- a/cmake/QtModuleHelpers.cmake
+++ b/cmake/QtModuleHelpers.cmake
@@ -327,7 +327,7 @@ function(qt_internal_add_module target)
set_target_properties(${target_private} PROPERTIES
_qt_config_module_name ${arg_CONFIG_MODULE_NAME}_private
_qt_package_version "${PROJECT_VERSION}"
- _qt_package_name "${INSTALL_CMAKE_NAMESPACE}${target}"
+ _qt_package_name "${INSTALL_CMAKE_NAMESPACE}${target}Private"
_qt_is_private_module TRUE
_qt_public_module_target_name "${target}"
)
@@ -790,7 +790,14 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)")
qt_internal_get_min_new_policy_cmake_version(min_new_policy_version)
qt_internal_get_max_new_policy_cmake_version(max_new_policy_version)
+
+ if(is_static_lib)
+ set(write_basic_module_package_args IS_STATIC_LIB)
+ else()
+ set(write_basic_module_package_args "")
+ endif()
qt_internal_write_basic_module_package("${target}" "${target_private}"
+ ${write_basic_module_package_args}
CONFIG_BUILD_DIR ${config_build_dir}
CONFIG_INSTALL_DIR ${config_install_dir}
)
@@ -809,7 +816,8 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)")
)
if(NOT arg_NO_PRIVATE_MODULE)
- qt_internal_write_basic_module_package(${target} ${target_private}
+ qt_internal_write_basic_module_package("${target}" "${target_private}"
+ ${write_basic_module_package_args}
PRIVATE
CONFIG_BUILD_DIR ${private_config_build_dir}
CONFIG_INSTALL_DIR ${private_config_install_dir}
@@ -981,11 +989,9 @@ endfunction()
#
# If PRIVATE is specified, write Qt6FooPrivate.
# Otherwise write its public counterpart.
-#
-# Note that this function is supposed to be called from qt_internal_add_module, and depends on
-# variables set in the scope of that function, e.g. target and target_private.
-function(qt_internal_write_basic_module_package)
+function(qt_internal_write_basic_module_package target target_private)
set(no_value_options
+ IS_STATIC_LIB
PRIVATE
)
set(single_value_options
@@ -1005,6 +1011,23 @@ function(qt_internal_write_basic_module_package)
set(module_config_input_file "QtModuleConfig.cmake.in")
endif()
+ if(arg_IS_STATIC_LIB AND NOT arg_PRIVATE AND CMAKE_VERSION VERSION_LESS "3.26")
+ # We auto-load the private module package from the public module package if we have a static
+ # Qt module and CMake's version is < 3.26. This is needed for the case "Qt6Foo links against
+ # Qt6BarPrivate", because CMake generates a check for the target Qt6::BarPrivate in
+ # Qt6FooTargets.cmake. Once we can require CMake 3.26, we can simply link against
+ # $<BUILD_LOCAL_INTERFACE:Qt6BarPrivate> in qt_internal_extend_target.
+ #
+ # For older CMake versions, we create an additional CMake file that's optionally included by
+ # Qt6FooConfig.cmake to work around the lack of BUILD_LOCAL_INTERFACE.
+ file(CONFIGURE
+ OUTPUT "${arg_CONFIG_BUILD_DIR}/${package_name}-build.cmake"
+ CONTENT "# This file marks this directory as part of Qt's build tree.
+set(__qt_${target}_always_load_private_module ON)
+"
+ )
+ endif()
+
configure_package_config_file(
"${QT_CMAKE_DIR}/${module_config_input_file}"
"${arg_CONFIG_BUILD_DIR}/${package_name}Config.cmake"
diff --git a/cmake/QtPublicWalkLibsHelpers.cmake b/cmake/QtPublicWalkLibsHelpers.cmake
index a7bef65fc56..a7c40076921 100644
--- a/cmake/QtPublicWalkLibsHelpers.cmake
+++ b/cmake/QtPublicWalkLibsHelpers.cmake
@@ -172,12 +172,16 @@ function(__qt_internal_walk_libs
if(lib MATCHES "^\\$<TARGET_OBJECTS:")
# Skip object files.
continue()
- elseif(lib MATCHES "^\\$<LINK_ONLY:(.*)>$")
- set(lib_target ${CMAKE_MATCH_1})
- else()
- set(lib_target ${lib})
endif()
+ set(lib_target "${lib}")
+
+ # Unwrap targets like $<LINK_ONLY:$<BUILD_INTERFACE:Qt6::CorePrivate>>
+ while(lib_target
+ MATCHES "^\\$<(LINK_ONLY|BUILD_INTERFACE|BUILD_LOCAL_INTERFACE):(.*)>$")
+ set(lib_target ${CMAKE_MATCH_2})
+ endwhile()
+
# Skip CMAKE_DIRECTORY_ID_SEP. If a target_link_libraries is applied to a target
# that was defined in a different scope, CMake appends and prepends a special directory
# id separator. Filter those out.
diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake
index e29d48359db..261592ab84d 100644
--- a/cmake/QtTargetHelpers.cmake
+++ b/cmake/QtTargetHelpers.cmake
@@ -148,6 +148,7 @@ function(qt_internal_extend_target target)
list(TRANSFORM arg_PUBLIC_LIBRARIES REPLACE "^Qt::" "${QT_CMAKE_EXPORT_NAMESPACE}::")
list(TRANSFORM arg_LIBRARIES REPLACE "^Qt::" "${QT_CMAKE_EXPORT_NAMESPACE}::")
+ qt_internal_wrap_private_modules(arg_LIBRARIES ${arg_LIBRARIES})
# Set-up the target
@@ -257,6 +258,9 @@ function(qt_internal_extend_target target)
if(TARGET "${target_private}")
target_link_libraries("${target_private}"
INTERFACE ${arg_PRIVATE_MODULE_INTERFACE})
+ qt_internal_register_target_dependencies("${target_private}"
+ PUBLIC ${arg_PRIVATE_MODULE_INTERFACE}
+ )
elseif(arg_PRIVATE_MODULE_INTERFACE)
set(warning_message "")
string(APPEND warning_message
@@ -268,9 +272,9 @@ function(qt_internal_extend_target target)
endif()
set(qt_register_target_dependencies_args "")
- if(arg_PUBLIC_LIBRARIES OR arg_PRIVATE_MODULE_INTERFACE)
+ if(arg_PUBLIC_LIBRARIES)
list(APPEND qt_register_target_dependencies_args
- PUBLIC ${arg_PUBLIC_LIBRARIES} ${arg_PRIVATE_MODULE_INTERFACE})
+ PUBLIC ${arg_PUBLIC_LIBRARIES})
endif()
if(qt_libs_private OR arg_LIBRARIES)
list(APPEND qt_register_target_dependencies_args
@@ -337,6 +341,43 @@ function(qt_internal_extend_target target)
endif()
endfunction()
+# Takes an output variable and a list of libraries.
+#
+# Every library that is a private module is wrapped in $<BUILD_INTERFACE> or
+# $<BUILD_LOCAL_INTERFACE> if CMake is new enough.
+#
+# This is necessary for static builds, because if Qt6Foo links to Qt6BarPrivate, this link
+# dependency is purely internal. If we don't do this, CMake adds a target check for Qt6BarPrivate
+# in Qt6FooTargets.cmake. This breaks if Qt6BarPrivate is not find_package'ed before.
+function(qt_internal_wrap_private_modules out_var)
+ set(result "")
+
+ if(CMAKE_VERSION VERSION_LESS "3.26")
+ set(wrapper_genex "BUILD_INTERFACE")
+ else()
+ set(wrapper_genex "BUILD_LOCAL_INTERFACE")
+ endif()
+
+ foreach(lib IN LISTS ARGN)
+ if(TARGET "${lib}")
+ get_target_property(lib_is_private_module ${lib} _qt_is_private_module)
+ if(lib_is_private_module)
+ # Add the public module as non-wrapped link dependency. This is necessary for
+ # targets that link only to the private module. Consumers of this target would then
+ # get a linker error about missing symbols from that Qt module.
+ get_target_property(lib_public_module_target ${lib} _qt_public_module_target_name)
+ list(APPEND result "${INSTALL_CMAKE_NAMESPACE}::${lib_public_module_target}")
+
+ # Wrap the private module in BUILD_LOCAL_INTERFACE.
+ set(lib "$<${wrapper_genex}:${lib}>")
+ endif()
+ endif()
+ list(APPEND result "${lib}")
+ endforeach()
+
+ set("${out_var}" "${result}" PARENT_SCOPE)
+endfunction()
+
# Given CMAKE_CONFIG and ALL_CMAKE_CONFIGS, determines if a directory suffix needs to be appended
# to each destination, and sets the computed install target destination arguments in OUT_VAR.
# Defaults used for each of the destination types, and can be configured per destination type.
diff --git a/tests/auto/cmake/mockplugins/CMakeLists.txt b/tests/auto/cmake/mockplugins/CMakeLists.txt
index 1bde9aedb19..990191285fb 100644
--- a/tests/auto/cmake/mockplugins/CMakeLists.txt
+++ b/tests/auto/cmake/mockplugins/CMakeLists.txt
@@ -11,7 +11,7 @@ project(QtMockPlugins
LANGUAGES CXX C
)
-find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
+find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals CorePrivate)
qt_internal_project_setup()
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Widgets Xml)
diff --git a/tests/auto/cmake/test_private_includes/CMakeLists.txt b/tests/auto/cmake/test_private_includes/CMakeLists.txt
index d4ba8ab888b..bab1143f217 100644
--- a/tests/auto/cmake/test_private_includes/CMakeLists.txt
+++ b/tests/auto/cmake/test_private_includes/CMakeLists.txt
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16)
project(test_private_includes)
-find_package(Qt6Gui REQUIRED)
+find_package(Qt6GuiPrivate REQUIRED)
add_executable(testapp main.cpp)
target_link_libraries(testapp PRIVATE Qt::GuiPrivate)
diff --git a/tests/auto/cmake/test_private_targets/CMakeLists.txt b/tests/auto/cmake/test_private_targets/CMakeLists.txt
index 72e06b044ce..79c5b5e0918 100644
--- a/tests/auto/cmake/test_private_targets/CMakeLists.txt
+++ b/tests/auto/cmake/test_private_targets/CMakeLists.txt
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16)
project(test_private_targets)
-find_package(Qt6Gui REQUIRED)
+find_package(Qt6GuiPrivate REQUIRED)
add_executable(testapp main.cpp)
target_link_libraries(testapp Qt::GuiPrivate)
diff --git a/tests/auto/cmake/tst_qaddpreroutine/CMakeLists.txt b/tests/auto/cmake/tst_qaddpreroutine/CMakeLists.txt
index bc1a6339db8..d03c811b063 100644
--- a/tests/auto/cmake/tst_qaddpreroutine/CMakeLists.txt
+++ b/tests/auto/cmake/tst_qaddpreroutine/CMakeLists.txt
@@ -22,7 +22,7 @@ project(tst_qaddpreroutine
find_package(Qt6 COMPONENTS Core BuildInternals CONFIG REQUIRED)
qt_prepare_standalone_project()
-find_package(Qt6 COMPONENTS Gui Test CONFIG REQUIRED)
+find_package(Qt6 COMPONENTS GuiPrivate Test CONFIG REQUIRED)
qt_internal_add_plugin(QTBUG_90341ThemePlugin
NO_UNITY_BUILD