CMake: Properly handle CONFIG += thread aka Threads::Threads

mkspecs/features/qt.prf adds a dependency on the system threading
library if the Qt Core thread feature is enabled. Because qt.prf is
loaded by any public or internal Qt project, it's essentially a public
dependency for any Qt consumer.

To mimic that in CMake, we check if the thread feature is enabled, and
and set the Threads::Threads library as a dependency of Qt6::Platform,
which is a public target used by all Qt modules and plugins and Qt
consumers.

We also need to create a Qt6Dependencies.cmake file so we
find_package(Threads) every time find_package(Qt6) is called.

For the .prl files to be usable, we have to filter out some
CMake implementation specific directory separator tokens
'CMAKE_DIRECTORY_ID_SEP' aka '::@', which are added because we call
target_link_libraries() with a target created in a different scope
(I think).

As a result of this change, we shouldn't have to hardcode
Threads::Threads in other projects, because it's now a global public
dependency.

Task-number: QTBUG-85801
Task-number: QTBUG-85877
Change-Id: Ib5d662c43b28e63f7da49d3bd77d0ad751220b31
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Alexandru Croitor 2020-08-03 16:28:16 +02:00
parent bd2bcd4e1d
commit 92ee9bd6b8
17 changed files with 109 additions and 24 deletions

View File

@ -353,6 +353,7 @@ qt_copy_or_install(FILES
cmake/QtBuildInformation.cmake
cmake/QtCompilerFlags.cmake
cmake/QtCompilerOptimization.cmake
cmake/QtConfigDependencies.cmake.in
cmake/QtFeature.cmake
cmake/QtFinishPrlFile.cmake
cmake/QtFindWrapHelper.cmake

View File

@ -3256,7 +3256,12 @@ function(qt_internal_walk_libs target out_var dict_name operation)
set(lib_target ${lib})
endif()
if(TARGET ${lib_target})
# 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.
if(lib_target MATCHES "^::@")
continue()
elseif(TARGET ${lib_target})
if ("${lib_target}" MATCHES "^Qt::(.*)")
# If both, Qt::Foo and Foo targets exist, prefer the target name without
# namespace. Which one is preferred doesn't really matter. This code exists to

View File

@ -49,6 +49,19 @@ if(APPLE AND (NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
list(APPEND CMAKE_MODULE_PATH "${_qt_import_prefix}/macos")
endif()
# Find required dependencies, if any.
include(CMakeFindDependencyMacro)
set(@INSTALL_CMAKE_NAMESPACE@_DEPENDENCIES_FOUND TRUE)
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@Dependencies.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@Dependencies.cmake")
endif()
if(NOT @INSTALL_CMAKE_NAMESPACE@_DEPENDENCIES_FOUND)
set(@INSTALL_CMAKE_NAMESPACE@_FOUND False)
message(FATAL_ERROR
"Failed to find Qt Platform dependency:
${@INSTALL_CMAKE_NAMESPACE@_DEPENDENCY_NOT_FOUND_MESSAGE}")
endif()
foreach(module ${@INSTALL_CMAKE_NAMESPACE@_FIND_COMPONENTS})
find_package(@INSTALL_CMAKE_NAMESPACE@${module}
${_@INSTALL_CMAKE_NAMESPACE@_FIND_PARTS_QUIET}

View File

@ -0,0 +1,35 @@
# note: _third_party_deps example: "ICU\\;1.0\\;i18n uc data;ZLIB\\;\\;"
set(_third_party_deps "@third_party_deps@")
@third_party_extra@
foreach(_target_dep ${_third_party_deps})
list(GET _target_dep 0 pkg)
list(GET _target_dep 1 version)
list(GET _target_dep 2 components)
set(find_package_args "${pkg}")
if(version)
list(APPEND find_package_args "${version}")
endif()
if(components)
string(REPLACE " " ";" components "${components}")
find_dependency(${find_package_args} COMPONENTS ${components})
else()
find_dependency(${find_package_args})
endif()
if (NOT ${pkg}_FOUND)
set(@INSTALL_CMAKE_NAMESPACE@_DEPENDENCIES_FOUND FALSE)
set(__@INSTALL_CMAKE_NAMESPACE@_message "\nPackage: ${pkg}")
if(version)
string(APPEND __@INSTALL_CMAKE_NAMESPACE@_message "\nVersion: ${version}")
endif()
if(components)
string(APPEND __@INSTALL_CMAKE_NAMESPACE@_message "\nComponents: ${components}")
endif()
set(@INSTALL_CMAKE_NAMESPACE@_DEPENDENCY_NOT_FOUND_MESSAGE
"${__@INSTALL_CMAKE_NAMESPACE@_message}")
return()
endif()
endforeach()

View File

@ -262,10 +262,57 @@ function(qt_internal_create_plugin_depends_file target)
endif()
endfunction()
function(qt_internal_create_qt6_dependencies_file)
# This is used for substitution in the configured file.
set(target "${INSTALL_CMAKE_NAMESPACE}")
# This is the actual target we're querying.
set(actual_target Platform)
get_target_property(public_depends "${actual_target}" INTERFACE_LINK_LIBRARIES)
# We need to collect third party deps that are set on the public Platform target,
# like Threads::Threads.
# This mimics find_package part of the CONFIG += thread assignment in mkspecs/features/qt.prf.
qt_collect_third_party_deps(${actual_target})
# For Threads we also need to write an extra variable assignment.
set(third_party_extra "")
if(third_party_deps MATCHES "Threads")
string(APPEND third_party_extra "if(NOT QT_NO_THREADS_PREFER_PTHREAD_FLAG)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
endif()")
endif()
if(third_party_deps)
# Setup build and install paths.
set(path_suffix "${INSTALL_CMAKE_NAMESPACE}")
qt_path_join(config_build_dir ${QT_CONFIG_BUILD_DIR} ${path_suffix})
qt_path_join(config_install_dir ${QT_CONFIG_INSTALL_DIR} ${path_suffix})
# Configure and install QtDependencies file.
configure_file(
"${QT_CMAKE_DIR}/QtConfigDependencies.cmake.in"
"${config_build_dir}/${target}Dependencies.cmake"
@ONLY
)
qt_install(FILES
"${config_build_dir}/${target}Dependencies.cmake"
DESTINATION "${config_install_dir}"
COMPONENT Devel
)
endif()
endfunction()
# Create Depends.cmake & Depends.h files for all modules and plug-ins.
function(qt_internal_create_depends_files)
qt_internal_get_qt_repo_known_modules(repo_known_modules)
if(PROJECT_NAME STREQUAL "QtBase")
qt_internal_create_qt6_dependencies_file()
endif()
foreach (target ${repo_known_modules})
qt_internal_create_module_depends_file(${target})
endforeach()

View File

@ -249,7 +249,6 @@ qt_add_module(Core
../3rdparty/tinycbor/src
LIBRARIES
Qt::GlobalConfigPrivate # special case
Threads::Threads # special case
PRECOMPILED_HEADER
"global/qt_pch.h"
NO_PCH_SOURCES
@ -276,6 +275,13 @@ endif()
qt_generate_qconfig_cpp()
# Handle qtConfig(thread): CONFIG += thread like in qt.prf.
# Aka if the feature is enabled, publically link against the threading library.
# This also ensures the link flag is in the .prl file.
if(QT_FEATURE_thread)
target_link_libraries(Platform INTERFACE Threads::Threads)
endif()
# Handle QObject: Automoc does not work for this as it would
# require to spill internals into users:
qt_add_module(Core_qobject STATIC

View File

@ -33,7 +33,6 @@ qt_extend_target(QSQLiteDriverPlugin CONDITION QT_FEATURE_system_sqlite
# special case begin
if (NOT QT_FEATURE_system_sqlite)
qt_find_package(Threads REQUIRED PROVIDED_TARGETS Threads::Threads)
# On newer compilers compiling sqlite.c produces warnings
qt_disable_warnings(QSQLiteDriverPlugin)
endif()
@ -52,8 +51,6 @@ qt_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_system_sqlite
SQLITE_OMIT_COMPLETE
INCLUDE_DIRECTORIES
../../../3rdparty/sqlite
PUBLIC_LIBRARIES # special case
Threads::Threads # special case
)
qt_extend_target(QSQLiteDriverPlugin CONDITION CMAKE_BUILD_TYPE STREQUAL Release AND NOT QT_FEATURE_system_sqlite

View File

@ -62,7 +62,6 @@ qt_add_module(Test
QT_BUILD_TESTLIB_LIB # special case
LIBRARIES
Qt::CorePrivate
Threads::Threads # special case
PUBLIC_LIBRARIES
Qt::Core
PRIVATE_MODULE_INTERFACE

View File

@ -6,7 +6,6 @@ if(QT_BUILD_STANDALONE_TESTS)
# special case begin
qt_find_package(WrapDBus1 PROVIDED_TARGETS dbus-1)
qt_find_package(ICU COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data)
qt_find_package(Threads PROVIDED_TARGETS Threads::Threads)
qt_find_package(WrapOpenSSL PROVIDED_TARGETS WrapOpenSSL::WrapOpenSSL)
qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders)
# special case end

View File

@ -16,7 +16,6 @@ qt_add_test(tst_qmetatype
../../../other/qvariant_common
PUBLIC_LIBRARIES
Qt::CorePrivate
Threads::Threads # special case
TESTDATA ${test_data}
)

View File

@ -9,6 +9,4 @@ qt_add_test(tst_qpromise
tst_qpromise.cpp
PUBLIC_LIBRARIES
Qt::CorePrivate
LIBRARIES # special case
Threads::Threads # solves issue with libpthread linkage # special case
)

View File

@ -10,8 +10,6 @@ qt_add_test(tst_qthread
tst_qthread.cpp
INCLUDE_DIRECTORIES
../../../../shared
LIBRARIES # special case
Threads::Threads # special case
)
## Scopes:

View File

@ -8,9 +8,6 @@
add_qt_test(tst_qthreadstorage
SOURCES
tst_qthreadstorage.cpp
LIBRARIES # special case
Threads::Threads # special case
)
## Scopes:

View File

@ -28,7 +28,6 @@ qt_add_test(tst_qguiapplication
PUBLIC_LIBRARIES
Qt::CorePrivate
Qt::GuiPrivate
Threads::Threads # special case
)
# Resources:

View File

@ -10,5 +10,4 @@ qt_add_test(tst_qguitimer
PUBLIC_LIBRARIES
Qt::CorePrivate
Qt::Gui
Threads::Threads # special case
)

View File

@ -21,12 +21,6 @@ qt_extend_target(tst_qtcpsocket CONDITION WIN32
ws2_32
)
# special case begin
set(THREADS_PREFER_PTHREAD_FLAG 1)
find_package(Threads)
target_link_libraries(tst_qtcpsocket PRIVATE Threads::Threads)
# special case end
#### Keys ignored in scope 4:.:.:test.pro:(CMAKE_BUILD_TYPE STREQUAL Debug):
# DESTDIR = "../debug"

View File

@ -11,7 +11,6 @@ qt_add_benchmark(qtimer_vs_qmetaobject
.
PUBLIC_LIBRARIES
Qt::Test
Threads::Threads # special case
)
#### Keys ignored in scope 1:.:.:qtimer_vs_qmetaobject.pro:<TRUE>: