diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 76b6757207..b0d0d8a4b6 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -212,6 +212,15 @@ macro(qt_find_package) ${components_as_string}) endif() + # Record the package + component + optional component provided targets. + qt_internal_record_package_component_provided_targets( + PACKAGE_NAME "${ARGV0}" + ON_TARGET ${qt_find_package_target_name} + PROVIDED_TARGETS ${arg_PROVIDED_TARGETS} + COMPONENTS ${arg_COMPONENTS} + OPTIONAL_COMPONENTS ${arg_OPTIONAL_COMPONENTS} + ) + get_property(is_global TARGET ${qt_find_package_target_name} PROPERTY IMPORTED_GLOBAL) qt_internal_should_not_promote_package_target_to_global( @@ -237,6 +246,56 @@ macro(qt_find_package) endif() endmacro() +# Records information about a package's provided targets, given a specific list of components. +# +# A package might contain multiple components, and create only a subset of targets based on which +# components are looked for. +# This function computes a unique key / id using the package name and the components that are +# passed. +# Then it saves the id in a property on the ON_TARGET target. The ON_TARGET target is one +# of the provided targets for that package id. This allows us to create a relationship to find +# the package id, given a target. +# The function also appends the list of provided targets for that package id to a global property. +# This information will later be saved into the module Dependencies.cmake file. +function(qt_internal_record_package_component_provided_targets) + set(opt_args "") + set(single_args + PACKAGE_NAME + ON_TARGET + ) + set(multi_args + COMPONENTS + OPTIONAL_COMPONENTS + PROVIDED_TARGETS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_PACKAGE_NAME) + message(FATAL_ERROR "PACKAGE_NAME is required.") + endif() + + if(NOT arg_ON_TARGET) + message(FATAL_ERROR "ON_TARGET is required.") + endif() + + _qt_internal_get_package_components_id( + PACKAGE_NAME "${arg_PACKAGE_NAME}" + COMPONENTS ${arg_COMPONENTS} + OPTIONAL_COMPONENTS ${arg_OPTIONAL_COMPONENTS} + OUT_VAR_KEY package_key + ) + + set_target_properties(${arg_ON_TARGET} PROPERTIES + _qt_package_components_id "${package_key}" + ) + + _qt_internal_append_to_cmake_property_without_duplicates( + _qt_find_package_${package_key}_provided_targets + "${arg_PROVIDED_TARGETS}" + ) +endfunction() + # Save found packages in the cache. They will be read on next reconfiguration to skip looking # for packages that were not previously found. # Only applies to -developer-builds by default. diff --git a/cmake/QtModuleDependencies.cmake.in b/cmake/QtModuleDependencies.cmake.in index ba5922d1e2..4a82ad3bd3 100644 --- a/cmake/QtModuleDependencies.cmake.in +++ b/cmake/QtModuleDependencies.cmake.in @@ -30,6 +30,7 @@ endif() # note: _third_party_deps example: "ICU\\;FALSE\\;1.0\\;i18n uc data;ZLIB\\;FALSE\\;\\;" set(__qt_@target@_third_party_deps "@third_party_deps@") +@third_party_deps_extra_info@ _qt_internal_find_third_party_dependencies("@target@" __qt_@target@_third_party_deps) # Find Qt tool package. diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 53ff7e1358..9654b18664 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -67,6 +67,11 @@ macro(qt_collect_third_party_deps target) set(package_optional_components "") endif() + get_target_property(package_components_id ${dep} _qt_package_components_id) + if(package_components_id) + list(APPEND third_party_deps_package_components_ids ${package_components_id}) + endif() + list(APPEND third_party_deps "${package_name}\;${package_is_optional}\;${package_version}\;${package_components}\;${package_optional_components}") endif() @@ -74,6 +79,42 @@ macro(qt_collect_third_party_deps target) endforeach() endmacro() +# Collect provided targets for the given list of package component ids. +# +# ${target} is merely used as a key infix to avoid name clashes in the Dependencies.cmake files. +# package_component_ids is a list of '${package_name}-${components}-${optional_components}' keys +# that are sanitized not to contain spaces or semicolons. +# +# The output is a list of variable assignments to add to the dependencies file. +# Each variable assignment is the list of provided targets for a given package component id. +# +# We use these extra assignments instead of adding the info to the existing 'third_party_deps' list +# to make the information more readable. That list already has 5 items per package, making it +# quite hard to read. +function(qt_internal_collect_third_party_dep_packages_info + target + package_components_ids + out_packages_info) + + # There might be multiple calls to find the same package, so remove the duplicates. + list(REMOVE_DUPLICATES package_components_ids) + + set(packages_info "") + + foreach(package_key IN LISTS package_components_ids) + get_cmake_property(provided_targets _qt_find_package_${package_key}_provided_targets) + if(provided_targets) + set(key "__qt_${target}_third_party_package_${package_key}_provided_targets") + + # Escape the semicolon, so it is preserved in the list(JOIN) below + string(REPLACE ";" "\;" provided_targets "${provided_targets}") + string(APPEND packages_info "set(${key} \"${provided_targets}\")\n") + endif() + endforeach() + + set(${out_packages_info} "${packages_info}" PARENT_SCOPE) +endfunction() + # Filter the dependency targets to collect unique set of the dependencies. # non-Private and Private targets are treated as the single object in this context # since they are defined by the same CMake package. For internal modules @@ -149,6 +190,7 @@ function(qt_internal_create_module_depends_file target) # ModuleDependencies.cmake. set(third_party_deps "") set(third_party_deps_seen "") + set(third_party_deps_package_components_ids "") # Used for collecting Qt tool dependencies that should be find_package()'d in # ModuleToolsDependencies.cmake. @@ -216,6 +258,14 @@ function(qt_internal_create_module_depends_file target) endforeach() qt_collect_third_party_deps(${target}) + qt_internal_collect_third_party_dep_packages_info(${target} + "${third_party_deps_package_components_ids}" + packages_info) + + set(third_party_deps_extra_info "") + if(packages_info) + string(APPEND third_party_deps_extra_info "${packages_info}") + endif() # Add dependency to the main ModuleTool package to ModuleDependencies file. if(${target} IN_LIST QT_KNOWN_MODULES_WITH_TOOLS) diff --git a/cmake/QtPublicFindPackageHelpers.cmake b/cmake/QtPublicFindPackageHelpers.cmake index 69f4f69e40..e463ea8d5d 100644 --- a/cmake/QtPublicFindPackageHelpers.cmake +++ b/cmake/QtPublicFindPackageHelpers.cmake @@ -4,3 +4,52 @@ function(qt_internal_disable_find_package_global_promotion target) set_target_properties("${target}" PROPERTIES _qt_no_promote_global TRUE) endfunction() + +# Transforms a list of package components into a string, so it can serve as a valid infix +# in a property name. +function(_qt_internal_get_package_components_as_valid_key_infix value out_var) + string(REPLACE ";" "_" valid_value "${value}") + set(${out_var} "${valid_value}" PARENT_SCOPE) +endfunction() + +# This function computes a unique key / id using the package name and the components that are +# passed. +# The result is later used as property name, to store provided targets for a specific +# package + components combination. +function(_qt_internal_get_package_components_id) + set(opt_args "") + set(single_args + PACKAGE_NAME + OUT_VAR_KEY + ) + set(multi_args + COMPONENTS + OPTIONAL_COMPONENTS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_PACKAGE_NAME) + message(FATAL_ERROR "PACKAGE_NAME is required") + endif() + + if(NOT arg_OUT_VAR_KEY) + message(FATAL_ERROR "OUT_VAR_KEY is required") + endif() + + set(key "${arg_PACKAGE_NAME}") + + if(arg_COMPONENTS) + _qt_internal_get_package_components_as_valid_key_infix("${arg_COMPONENTS}" + components_as_string) + string(APPEND key "-${components_as_string}") + endif() + + if(arg_OPTIONAL_COMPONENTS) + _qt_internal_get_package_components_as_valid_key_infix("${arg_OPTIONAL_COMPONENTS}" + components_as_string) + string(APPEND key "-${components_as_string}") + endif() + + set(${arg_OUT_VAR_KEY} "${key}" PARENT_SCOPE) +endfunction()