diff --git a/cmake/Qt3rdPartyLibraryHelpers.cmake b/cmake/Qt3rdPartyLibraryHelpers.cmake index 1db4683c69b..86178d1e409 100644 --- a/cmake/Qt3rdPartyLibraryHelpers.cmake +++ b/cmake/Qt3rdPartyLibraryHelpers.cmake @@ -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}) diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index 6ade39fc8de..5e9969a037b 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -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() @@ -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 diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 07749ebf107..ae581fd75d2 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -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) @@ -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() @@ -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() diff --git a/cmake/QtModuleDependencies.cmake.in b/cmake/QtModuleDependencies.cmake.in index c7c986bfbaa..4786b530e32 100644 --- a/cmake/QtModuleDependencies.cmake.in +++ b/cmake/QtModuleDependencies.cmake.in @@ -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}" diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index bc5d04d3ebb..b345e1797c6 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -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}") @@ -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) diff --git a/cmake/QtPlatformTargetHelpers.cmake b/cmake/QtPlatformTargetHelpers.cmake index 4d5ad793742..e9f40d05656 100644 --- a/cmake/QtPlatformTargetHelpers.cmake +++ b/cmake/QtPlatformTargetHelpers.cmake @@ -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. diff --git a/cmake/QtPluginHelpers.cmake b/cmake/QtPluginHelpers.cmake index 03ff7de1c97..830e6cbb4b0 100644 --- a/cmake/QtPluginHelpers.cmake +++ b/cmake/QtPluginHelpers.cmake @@ -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. diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 0d8f6866bce..cac26d88c5d 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -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() @@ -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}) @@ -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" @@ -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. @@ -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() @@ -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}" diff --git a/cmake/QtStandaloneTestsConfig.cmake.in b/cmake/QtStandaloneTestsConfig.cmake.in index 3d08ae0c122..afa9d2ee79b 100644 --- a/cmake/QtStandaloneTestsConfig.cmake.in +++ b/cmake/QtStandaloneTestsConfig.cmake.in @@ -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@) diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake index 23d6256015b..2f49e4d8726 100644 --- a/cmake/QtToolHelpers.cmake +++ b/cmake/QtToolHelpers.cmake @@ -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 "$>") @@ -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). @@ -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 @@ -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 )