From 54cb92d58b33a7a8cc387bc84643e5d55e84b88c Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 Jan 2025 19:29:17 +0100 Subject: [PATCH] CMake: Provide a way to find all available Qt module packages A project might want to find_package all available (installed) Qt CMake packages that contain qt modules. A use case might be a qml app that needs to link to all of Qt, and support showing qml files that can load any Qt qml module. Add a new Qt6 COMPONENT called ALL_QT_MODULES. It can be used like this: find_package(Qt6 COMPONENTS ALL_QT_MODULES). The implementation will find all installed Qt modules by globbing over all json files installed in $qt/modules dir, and treat the file names as package names. It will then tell Qt6 to find_package each of those packages. Pick-to: 6.8 Change-Id: I89242307438576a0cbb3cdca80a9cb72818b6035 Reviewed-by: Joerg Bornemann (cherry picked from commit c7027880aa746a01362ac12491910479d7fc67e3) Reviewed-by: Qt Cherry-pick Bot --- cmake/QtConfig.cmake.in | 7 +++++- cmake/QtInstallPaths.cmake.in | 1 + cmake/QtModuleHelpers.cmake | 5 ++++ cmake/QtPublicCMakeHelpers.cmake | 39 ++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index 5c3e79a5b7c..959fbda4268 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -128,7 +128,12 @@ if(QT_DISABLE_NO_DEFAULT_PATH_IN_QT_PACKAGES) set(__qt_use_no_default_path_for_qt_packages "") endif() -foreach(module ${@INSTALL_CMAKE_NAMESPACE@_FIND_COMPONENTS}) +set(__qt_umbrella_find_components ${@INSTALL_CMAKE_NAMESPACE@_FIND_COMPONENTS}) +__qt_internal_handle_find_all_qt_module_packages(__qt_umbrella_find_components + COMPONENTS ${__qt_umbrella_find_components} +) + +foreach(module ${__qt_umbrella_find_components}) if(NOT "${QT_HOST_PATH}" STREQUAL "" AND "${module}" MATCHES "Tools$" AND NOT "${module}" MATCHES "UiTools$" diff --git a/cmake/QtInstallPaths.cmake.in b/cmake/QtInstallPaths.cmake.in index 977fffd0e4d..020aafeeafe 100644 --- a/cmake/QtInstallPaths.cmake.in +++ b/cmake/QtInstallPaths.cmake.in @@ -14,3 +14,4 @@ set(QT@PROJECT_VERSION_MAJOR@_INSTALL_PLUGINS "@INSTALL_PLUGINSDIR@") set(QT@PROJECT_VERSION_MAJOR@_INSTALL_QML "@INSTALL_QMLDIR@") set(QT@PROJECT_VERSION_MAJOR@_INSTALL_TESTS "@INSTALL_TESTSDIR@") set(QT@PROJECT_VERSION_MAJOR@_INSTALL_TRANSLATIONS "@INSTALL_TRANSLATIONSDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_DESCRIPTIONSDIR "@INSTALL_DESCRIPTIONSDIR@") diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index 5d6a16901c0..4320420b49d 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -1343,7 +1343,12 @@ function(qt_describe_module target) qt_path_join(install_dir ${QT_INSTALL_DIR} ${path_suffix}) set(descfile_in "${QT_CMAKE_DIR}/ModuleDescription.json.in") + + # IMPORTANT: If you adjust the file name not to be the exact target name and thus the CMake + # package name, it needs to consider also the code in QtConfig.cmake.in that globs the json + # files. set(descfile_out "${build_dir}/${target}.json") + string(TOLOWER "${PROJECT_NAME}" lower_case_project_name) set(extra_module_information "") set(platforms_information "") diff --git a/cmake/QtPublicCMakeHelpers.cmake b/cmake/QtPublicCMakeHelpers.cmake index ae46128be6b..c06271b8cdf 100644 --- a/cmake/QtPublicCMakeHelpers.cmake +++ b/cmake/QtPublicCMakeHelpers.cmake @@ -597,6 +597,45 @@ function(_qt_internal_remove_args out_var) set(${out_var} "${result}" PARENT_SCOPE) endfunction() +function(__qt_internal_handle_find_all_qt_module_packages out_var) + set(opt_args "") + set(single_args "") + set(multi_args + COMPONENTS + ) + cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_COMPONENTS) + return() + endif() + + set(components ${arg_COMPONENTS}) + + if("ALL_QT_MODULES" IN_LIST components) + list(FIND components "ALL_QT_MODULES" all_qt_modules_index) + list(REMOVE_AT components ${all_qt_modules_index}) + + # Find the path to dir with module.json files. # We consider each file name to be a + # Qt package (component) name that contains a target with the same name. + set(json_modules_path "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DESCRIPTIONSDIR}") + file(GLOB json_modules "${json_modules_path}/*.json") + + set(all_qt_modules "") + + foreach(json_module IN LISTS json_modules) + get_filename_component(module_name "${json_module}" NAME_WE) + list(APPEND all_qt_modules ${module_name}) + endforeach() + + if(all_qt_modules) + list(INSERT components "${all_qt_modules_index}" ${all_qt_modules}) + endif() + endif() + + set(${out_var} "${components}" PARENT_SCOPE) +endfunction() + # Append ${ARGN} to ${target}'s ${property_name} property, removing duplicates. function(_qt_internal_append_to_target_property_without_duplicates target property_name) get_target_property(property "${target}" "${property_name}")