Skip to content

Commit

Permalink
CMake: Record used package version for each target dependency
Browse files Browse the repository at this point in the history
When recording which package version to look for in
QtFooModuleDependencies.cmake and other files like it,
instead of using PROJECT_VERSION, use the version of the
package that contains the dependency.

For example if we're hypothetically building the qtdeclarative repo
from the 6.4 branch, against an installed 6.2 qtbase, then
the Qt6QmlModuleDependencies.cmake file will have a
find_package(Qt6Core 6.2) call because qtdeclarative's
find_package(Qt6Core) call found a 6.2 Core when it was configured.

This allows switching the versioning scheme of specific Qt modules
that might not want to follow the general Qt versioning scheme.

The first candidate would be QtWebEngine which might want to
follow the Chromium versioning scheme, something like
Qt 6.94.0 where 94 is the Chromium major version.

Implementation notes.
We now record the package version of a target in a property
called _qt_package_version. We do it for qt modules, plugins,
3rd party libraries, tools and the Platform target.

When we try to look up which version to write into the
QtFooModuleDependencies.cmake file (or the equivalent Plugins and
Tools file), we try to find the version
from a few sources: the property mentioned above, then the
Qt6{target}_VERSION variable, and finally PROJECT_VERSION.
In the latter case, we issue a warning because technically that should
never have to happen, and it's a bug or an unforeseen case if it does.

A few more places also need adjustments:
 - package versions to look for when configuring standalone
   tests and generating standalone tests Config files
 - handling of tools packages
 - The main Qt6 package lookup in each Dependencies.cmake files

Note that there are some requirements and consequences in case a
module wants to use a different versioning scheme like 6.94.0.

Requirements.
 - The root CMakeLists.txt file needs to call find_package with a
   version different from the usual PROJECT_VERSION. Ideally it
   should look for a few different Qt versions which are known to be
   compatible, for example the last stable and LTS versions, or just
   the lowest supported Qt version, e.g. 6.2.6 or whenever this change
   would land in the 6.2 branch.
 - If the repository has multiple modules, some of which need to
   follow the Qt versioning scheme and some not,
   project(VERSION x.y.z) calls need to be carefully placed in
   subdirectory scopes with appropriate version numbers, so that
   qt_internal_add_module / _tool / _plugin pick up the correct
   version.

Consequences.
 - The .so / .dylib names will contain the new version, e.g. .so.6.94
 - Linux ELF symbols will contain the new versions
 - syncqt private headers will now exist under a
   include/QtFoo/6.94.0/QtFoo/private folder
 - pri and prl files will also contain the new version numbers
 - pkg-config .pc files contain the new version numbers
 - It won't be possible to write
   find_package(Qt6 6.94 COMPONENTS WebEngineWidgets) in user code.
   One would have to write find_package(Qt6WebEngineWidgets 6.94)
   otherwise CMake will try to look for Qt6Config 6.94 which won't
   exist.
 - Similarly, a
   find_package(Qt6 6.4 COMPONENTS Widgets WebEngineWidgets) call
   would always find any kind of WebEngine package that is higher than
   6.4, which might be 6.94, 6.95, etc.
 - In the future, if we fix Qt6Config to pass EXACT to its
   subcomponent find_package calls,
   a find_package(Qt6 6.5.0 EXACT COMPONENTS Widgets WebEngineWidgets)
   would fail to find WebEngineWidgets, because its 6.94.0 version
   will not be equal to 6.5.0. Currently we don't pass through EXACT,
   so it's not an issue.

Augments 5ffc744

Task-number: QTBUG-103500
Change-Id: I8bdb56bfcbc7f7f6484d1e56651ffc993fd30bab
Reviewed-by: Michal Klocek <[email protected]>
Reviewed-by: Alexey Edelev <[email protected]>
Reviewed-by: Jörg Bornemann <[email protected]>
  • Loading branch information
alcroito committed Jul 1, 2022
1 parent 74d8321 commit dd1030a
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 13 deletions.
6 changes: 6 additions & 0 deletions cmake/Qt3rdPartyLibraryHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@ function(qt_internal_add_3rdparty_library target)

set_target_properties(${target} PROPERTIES
_qt_module_interface_name "${target}"
_qt_package_version "${PROJECT_VERSION}"
)

set_property(TARGET ${target}
APPEND PROPERTY
EXPORT_PROPERTIES "_qt_module_interface_name;_qt_package_version")

qt_internal_add_qt_repo_known_module(${target})
qt_internal_add_target_aliases(${target})
_qt_internal_apply_strict_cpp(${target})
Expand Down
23 changes: 21 additions & 2 deletions cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,18 @@ macro(qt_build_repo_impl_find_package_tests)
# Do this before adding src, because there might be test related conditions
# in source.
if (QT_BUILD_TESTS AND NOT QT_BUILD_STANDALONE_TESTS)
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Test)
# When looking for the Test package, do it using the Qt6 package version, in case if
# PROJECT_VERSION is following a different versioning scheme.
if(Qt6_VERSION)
set(_qt_build_repo_impl_find_package_tests_version "${Qt6_VERSION}")
else()
set(_qt_build_repo_impl_find_package_tests_version "${PROJECT_VERSION}")
endif()

find_package(Qt6
"${_qt_build_repo_impl_find_package_tests_version}"
CONFIG REQUIRED COMPONENTS Test)
unset(_qt_build_repo_impl_find_package_tests_version)
endif()
endmacro()

Expand Down Expand Up @@ -625,7 +636,15 @@ macro(qt_build_tests)
unset(_qt_tests_config_file_name)

# Of course we always need the test module as well.
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Test)
# When looking for the Test package, do it using the Qt6 package version, in case if
# PROJECT_VERSION is following a different versioning scheme.
if(Qt6_VERSION)
set(_qt_build_tests_package_version "${Qt6_VERSION}")
else()
set(_qt_build_tests_package_version "${PROJECT_VERSION}")
endif()
find_package(Qt6 "${_qt_build_tests_package_version}" CONFIG REQUIRED COMPONENTS Test)
unset(_qt_build_tests_package_version)

# Set language standards after finding Core, because that's when the relevant
# feature variables are available, and the call in QtSetup is too early when building
Expand Down
71 changes: 68 additions & 3 deletions cmake/QtFindPackageHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,68 @@ function(qt_internal_is_lib_part_of_qt6_package lib out_var)
endif()
endfunction()

# Try to get the CMake package version of a Qt target.
#
# Query the target's _qt_package_version property, or try to read it from the CMake package version
# variable set from calling find_package(Qt6${target}).
# Not all targets will have a find_package _VERSION variable, for example if the target is an
# executable.
# A heuristic is used to handle QtFooPrivate module targets.
# If no version can be found, fall back to ${PROJECT_VERSION} and issue a warning.
function(qt_internal_get_package_version_of_target target package_version_out_var)
qt_internal_is_lib_part_of_qt6_package("${target}" is_part_of_qt6)

if(is_part_of_qt6)
# When building qtbase, Qt6_VERSION is not set (unless examples are built in-tree,
# non-ExternalProject). Use the Platform target's version instead which would be the
# equivalent.
if(TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::Platform")
get_target_property(package_version
"${QT_CMAKE_EXPORT_NAMESPACE}::Platform" _qt_package_version)
endif()
if(NOT package_version)
set(package_version "${${QT_CMAKE_EXPORT_NAMESPACE}_VERSION}")
endif()
else()
# Try to get the version from the target.
# Try the Private target first and if it doesn't exist, try the non-Private target later.
if(TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${target}")
get_target_property(package_version
"${QT_CMAKE_EXPORT_NAMESPACE}::${target}" _qt_package_version)
endif()

# Try to get the version from the corresponding package version variable.
if(NOT package_version)
set(package_version "${${QT_CMAKE_EXPORT_NAMESPACE}${target}_VERSION}")
endif()

# Try non-Private target.
if(NOT package_version AND target MATCHES "(.*)Private$")
set(target "${CMAKE_MATCH_1}")
endif()

if(NOT package_version AND TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${target}")
get_target_property(package_version
"${QT_CMAKE_EXPORT_NAMESPACE}::${target}" _qt_package_version)
endif()

if(NOT package_version)
set(package_version "${${QT_CMAKE_EXPORT_NAMESPACE}${target}_VERSION}")
endif()
endif()

if(NOT package_version)
set(package_version "${PROJECT_VERSION}")
if(FEATURE_developer_build)
message(WARNING
"Could not determine package version of target ${target}. "
"Defaulting to project version ${PROJECT_VERSION}.")
endif()
endif()

set(${package_version_out_var} "${package_version}" PARENT_SCOPE)
endfunction()

# This function stores the list of Qt targets a library depend on,
# along with their version info, for usage in ${target}Depends.cmake file
function(qt_register_target_dependencies target public_libs private_libs)
Expand Down Expand Up @@ -346,10 +408,12 @@ function(qt_register_target_dependencies target public_libs private_libs)
if ("${lib}" MATCHES "^Qt::(.*)")
set(lib "${CMAKE_MATCH_1}")
qt_internal_is_lib_part_of_qt6_package("${lib}" is_part_of_qt6)

qt_internal_get_package_version_of_target("${lib}" package_version)
if (is_part_of_qt6)
list(APPEND target_deps "Qt6\;${PROJECT_VERSION}")
list(APPEND target_deps "Qt6\;${package_version}")
else()
list(APPEND target_deps "${INSTALL_CMAKE_NAMESPACE}${lib}\;${PROJECT_VERSION}")
list(APPEND target_deps "${INSTALL_CMAKE_NAMESPACE}${lib}\;${package_version}")
endif()
endif()
endforeach()
Expand All @@ -370,7 +434,8 @@ function(qt_register_target_dependencies target public_libs private_libs)
qt_internal_is_lib_part_of_qt6_package("${lib}" is_part_of_qt6)
get_target_property(lib_type "${lib_namespaced}" TYPE)
if(NOT lib_type STREQUAL "STATIC_LIBRARY" AND NOT is_part_of_qt6)
list(APPEND target_deps "${INSTALL_CMAKE_NAMESPACE}${lib}\;${PROJECT_VERSION}")
qt_internal_get_package_version_of_target("${lib}" package_version)
list(APPEND target_deps "${INSTALL_CMAKE_NAMESPACE}${lib}\;${package_version}")
endif()
endif()
endforeach()
Expand Down
3 changes: 2 additions & 1 deletion cmake/QtModuleDependencies.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ 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_dependency(@INSTALL_CMAKE_NAMESPACE@ @PROJECT_VERSION@

find_dependency(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@
PATHS
"${CMAKE_CURRENT_LIST_DIR}/.."
"${_qt_cmake_dir}"
Expand Down
12 changes: 9 additions & 3 deletions cmake/QtModuleHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,11 @@ function(qt_internal_add_module target)

set_target_properties(${target} PROPERTIES
_qt_module_interface_name "${arg_MODULE_INTERFACE_NAME}"
_qt_package_version "${PROJECT_VERSION}"
)
set_property(TARGET ${target} APPEND PROPERTY EXPORT_PROPERTIES _qt_module_interface_name)
set_property(TARGET ${target}
APPEND PROPERTY
EXPORT_PROPERTIES "_qt_module_interface_name;_qt_package_version")

qt_internal_module_info(module "${target}")
qt_internal_add_qt_repo_known_module("${target}")
Expand Down Expand Up @@ -238,9 +241,12 @@ function(qt_internal_add_module target)
add_library("${target_private}" INTERFACE)
qt_internal_add_target_aliases("${target_private}")
set_target_properties(${target_private} PROPERTIES
_qt_config_module_name ${arg_CONFIG_MODULE_NAME}_private)
_qt_config_module_name ${arg_CONFIG_MODULE_NAME}_private
_qt_package_version "${PROJECT_VERSION}"
)
set_property(TARGET "${target_private}" APPEND PROPERTY
EXPORT_PROPERTIES _qt_config_module_name)
EXPORT_PROPERTIES "_qt_config_module_name;_qt_package_version"
)
endif()

if(NOT arg_HEADER_MODULE)
Expand Down
7 changes: 7 additions & 0 deletions cmake/QtPlatformTargetHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ function(qt_internal_setup_public_platform_target)
)
target_compile_definitions(Platform INTERFACE ${QT_PLATFORM_DEFINITIONS})

set_target_properties(Platform PROPERTIES
_qt_package_version "${PROJECT_VERSION}"
)
set_property(TARGET Platform
APPEND PROPERTY
EXPORT_PROPERTIES "_qt_package_version")

# When building on android we need to link against the logging library
# in order to satisfy linker dependencies. Both of these libraries are part of
# the NDK.
Expand Down
7 changes: 7 additions & 0 deletions cmake/QtPluginHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ function(qt_internal_add_plugin target)

qt_set_target_info_properties(${target} ${ARGN} TARGET_VERSION "${arg_VERSION}")

set_target_properties(${target} PROPERTIES
_qt_package_version "${PROJECT_VERSION}"
)
set_property(TARGET ${target}
APPEND PROPERTY
EXPORT_PROPERTIES "_qt_package_version")

# Override the OUTPUT_NAME that qt6_add_plugin() set, we need to account for
# QT_LIBINFIX, which is specific to building Qt.
# Make sure the Qt6 plugin library names are like they were in Qt5 qmake land.
Expand Down
21 changes: 19 additions & 2 deletions cmake/QtPostProcessHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ function(qt_internal_create_module_depends_file target)
# Make the ModuleTool package depend on dep's ModuleTool package.
list(FIND tool_deps_seen ${dep} dep_seen)
if(dep_seen EQUAL -1 AND ${dep} IN_LIST QT_KNOWN_MODULES_WITH_TOOLS)
qt_internal_get_package_version_of_target("${dep}" dep_package_version)
list(APPEND tool_deps_seen ${dep})
list(APPEND tool_deps
"${INSTALL_CMAKE_NAMESPACE}${dep}Tools\;${PROJECT_VERSION}")
"${INSTALL_CMAKE_NAMESPACE}${dep}Tools\;${dep_package_version}")
endif()
endif()
endforeach()
Expand All @@ -220,8 +221,9 @@ function(qt_internal_create_module_depends_file target)

# Add dependency to the main ModuleTool package to ModuleDependencies file.
if(${target} IN_LIST QT_KNOWN_MODULES_WITH_TOOLS)
qt_internal_get_package_version_of_target("${target}" main_module_tool_package_version)
list(APPEND main_module_tool_deps
"${INSTALL_CMAKE_NAMESPACE}${target}Tools\;${PROJECT_VERSION}")
"${INSTALL_CMAKE_NAMESPACE}${target}Tools\;${main_module_tool_package_version}")
endif()

foreach(dep ${target_deps})
Expand Down Expand Up @@ -259,6 +261,10 @@ function(qt_internal_create_module_depends_file target)
qt_path_join(config_build_dir ${QT_CONFIG_BUILD_DIR} ${path_suffix})
qt_path_join(config_install_dir ${QT_CONFIG_INSTALL_DIR} ${path_suffix})

# All module packages should look for the Qt6 package version that qtbase was originally
# built as.
qt_internal_get_package_version_of_target(Platform main_qt_package_version)

# Configure and install ModuleDependencies file.
configure_file(
"${QT_CMAKE_DIR}/QtModuleDependencies.cmake.in"
Expand All @@ -272,6 +278,9 @@ function(qt_internal_create_module_depends_file target)
COMPONENT Devel
)

message(TRACE "Recorded dependencies for module: ${target}\n"
" Qt dependencies: ${target_deps}\n"
" 3rd-party dependencies: ${third_party_deps}")
endif()
if(tool_deps)
# The value of the property will be used by qt_export_tools.
Expand Down Expand Up @@ -323,6 +332,10 @@ function(qt_internal_create_plugin_depends_file target)
DESTINATION "${config_install_dir}"
COMPONENT Devel
)

message(TRACE "Recorded dependencies for plugin: ${target}\n"
" Qt dependencies: ${target_deps}\n"
" 3rd-party dependencies: ${third_party_deps}")
endif()
endfunction()

Expand Down Expand Up @@ -767,6 +780,10 @@ function(qt_internal_create_config_file_for_standalone_tests)
# Create a Config file that calls find_package on the modules that were built as part
# of the current repo. This is used for standalone tests.
qt_internal_get_standalone_tests_config_file_name(tests_config_file_name)

# Standalone tests Config files should follow the main versioning scheme.
qt_internal_get_package_version_of_target(Platform main_qt_package_version)

configure_file(
"${QT_CMAKE_DIR}/QtStandaloneTestsConfig.cmake.in"
"${config_build_dir}/${tests_config_file_name}"
Expand Down
5 changes: 4 additions & 1 deletion cmake/QtStandaloneTestsConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
find_package(@INSTALL_CMAKE_NAMESPACE@ @PROJECT_VERSION@
# TODO: Ideally this should look for each Qt module separately, with each module's specific version,
# bypassing the Qt6 Config file, aka find_package(Qt6SpecificFoo) repated x times. But it's not
# critical.
find_package(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@
REQUIRED COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@)
39 changes: 38 additions & 1 deletion cmake/QtToolHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ function(qt_internal_add_tool target_name)
_qt_internal_apply_strict_cpp("${target_name}")
qt_internal_adjust_main_config_runtime_output_dir("${target_name}" "${output_dir}")

set_target_properties(${target_name} PROPERTIES
_qt_package_version "${PROJECT_VERSION}"
)
set_property(TARGET ${target_name}
APPEND PROPERTY
EXPORT_PROPERTIES "_qt_package_version")

if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19.0" AND QT_FEATURE_debug_and_release)
set_property(TARGET "${target_name}"
PROPERTY EXCLUDE_FROM_ALL "$<NOT:$<CONFIG:${QT_MULTI_CONFIG_FIRST_CONFIG}>>")
Expand Down Expand Up @@ -335,6 +342,8 @@ function(qt_export_tools module_name)
set(extra_cmake_files "")
set(extra_cmake_includes "")

set(first_tool_package_version "")

foreach(tool_name ${QT_KNOWN_MODULE_${module_name}_TOOLS})
# Specific tools can have package dependencies.
# e.g. qtwaylandscanner depends on WaylandScanner (non-qt package).
Expand Down Expand Up @@ -366,6 +375,13 @@ endif()
")
list(APPEND tool_targets "${QT_CMAKE_EXPORT_NAMESPACE}::${tool_name}")
list(APPEND tool_targets_non_prefixed "${tool_name}")

if(NOT first_tool_package_version)
qt_internal_get_package_version_of_target("${tool_name}" tool_package_version)
if(tool_package_version)
set(first_tool_package_version "${tool_package_version}")
endif()
endif()
endforeach()

string(APPEND extra_cmake_statements
Expand Down Expand Up @@ -411,9 +427,30 @@ endif()
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
INSTALL_DESTINATION "${config_install_dir}"
)

# There might be Tools packages which don't have a corresponding real module_name target, like
# WaylandScannerTools.
# In that case we'll use the package version of the first tool that belongs to that package.
if(TARGET "${module_name}")
qt_internal_get_package_version_of_target("${module_name}" tools_package_version)
elseif(first_tool_package_version)
set(tools_package_version "${first_tool_package_version}")
else()
# This should never happen, because tools_package_version should always have at least some
# value. Issue an assertion message just in case the pre-condition ever changes.
set(tools_package_version "${PROJECT_VERSION}")
if(FEATURE_developer_build)
message(WARNING
"Could not determine package version of tools package ${module_name}. "
"Defaulting to project version ${PROJECT_VERSION}.")
endif()
endif()
message(TRACE
"Exporting tools package ${module_name}Tools with package version ${tools_package_version}"
"\n included targets: ${tool_targets_non_prefixed}")
write_basic_package_version_file(
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake"
VERSION ${PROJECT_VERSION}
VERSION "${tools_package_version}"
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
Expand Down

0 comments on commit dd1030a

Please sign in to comment.