diff options
Diffstat (limited to 'cmake')
| -rw-r--r-- | cmake/QtBuildHelpers.cmake | 2 | ||||
| -rw-r--r-- | cmake/QtBuildRepoHelpers.cmake | 65 | ||||
| -rw-r--r-- | cmake/QtFeature.cmake | 23 | ||||
| -rw-r--r-- | cmake/QtFindPackageHelpers.cmake | 81 | ||||
| -rw-r--r-- | cmake/QtModuleToolsConfig.cmake.in | 14 | ||||
| -rw-r--r-- | cmake/QtPostProcessHelpers.cmake | 49 | ||||
| -rw-r--r-- | cmake/QtProcessConfigureArgs.cmake | 313 | ||||
| -rw-r--r-- | cmake/QtStandaloneTestsConfig.cmake.in | 10 | ||||
| -rw-r--r-- | cmake/QtTargetHelpers.cmake | 232 | ||||
| -rw-r--r-- | cmake/QtTestHelpers.cmake | 25 | ||||
| -rw-r--r-- | cmake/QtToolHelpers.cmake | 336 | ||||
| -rw-r--r-- | cmake/QtVcpkgManifestHelpers.cmake | 354 | ||||
| -rw-r--r-- | cmake/QtWrapperScriptHelpers.cmake | 13 |
13 files changed, 1367 insertions, 150 deletions
diff --git a/cmake/QtBuildHelpers.cmake b/cmake/QtBuildHelpers.cmake index 7205bad5253..91983636712 100644 --- a/cmake/QtBuildHelpers.cmake +++ b/cmake/QtBuildHelpers.cmake @@ -96,6 +96,7 @@ macro(qt_internal_reset_global_state) qt_internal_set_qt_known_plugins("") set(QT_KNOWN_MODULES_WITH_TOOLS "" CACHE INTERNAL "Known Qt modules with tools" FORCE) + set_property(GLOBAL PROPERTY _qt_standalone_tool_packages "") endmacro() macro(qt_internal_set_qt_path_separator) @@ -263,6 +264,7 @@ function(qt_internal_get_qt_build_private_files_to_install out_var) QtSeparateDebugInfo.Info.plist.in QtSetup.cmake QtStandaloneTestsConfig.cmake.in + QtVcpkgManifestHelpers.cmake QtVersionlessAliasTargets.cmake.in QtVersionlessTargets.cmake.in QtWriteArgsFile.cmake diff --git a/cmake/QtBuildRepoHelpers.cmake b/cmake/QtBuildRepoHelpers.cmake index 1429448c2f8..05876c1ad6d 100644 --- a/cmake/QtBuildRepoHelpers.cmake +++ b/cmake/QtBuildRepoHelpers.cmake @@ -724,6 +724,71 @@ macro(qt_internal_find_standalone_test_config_file) endif() endmacro() +# Used inside the standalone parts config file to find all requested Qt module packages. +# standalone_parts_args_var_name should be the var name in the outer scope that contains +# all the arguments for this function. +macro(qt_internal_find_standalone_parts_qt_packages standalone_parts_args_var_name) + set(__standalone_parts_opt_args "") + set(__standalone_parts_single_args "") + set(__standalone_parts_multi_args + QT_MODULE_PACKAGES + QT_TOOL_PACKAGES + ) + cmake_parse_arguments(__standalone_parts + "${__standalone_parts_opt_args}" + "${__standalone_parts_single_args}" + "${__standalone_parts_multi_args}" + ${${standalone_parts_args_var_name}}) + + # Packages looked up in standalone tests Config files should use the same version as + # the one recorded on the Platform target. + qt_internal_get_package_version_of_target(Platform __standalone_parts_main_qt_package_version) + + if(__standalone_parts_QT_TOOL_PACKAGES) + # Set up QT_HOST_PATH as an extra root path to look for the Tools packages when + # cross-compiling. + if(NOT "${QT_HOST_PATH}" STREQUAL "") + set(__standalone_parts_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) + set(__standalone_parts_CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}) + list(PREPEND CMAKE_PREFIX_PATH "${QT_HOST_PATH_CMAKE_DIR}") + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_HOST_PATH}") + endif() + + foreach(__standalone_parts_package_name IN LISTS __standalone_parts_QT_TOOL_PACKAGES) + find_package(${QT_CMAKE_EXPORT_NAMESPACE}${__standalone_parts_package_name} + "${__standalone_parts_main_qt_package_version}" + PATHS + # These come from Qt6Config.cmake + ${_qt_additional_packages_prefix_path} + ${_qt_additional_packages_prefix_path_env} + ) + endforeach() + + if(NOT "${QT_HOST_PATH}" STREQUAL "") + set(CMAKE_PREFIX_PATH ${__standalone_parts_CMAKE_PREFIX_PATH}) + set(CMAKE_FIND_ROOT_PATH ${__standalone_parts_CMAKE_FIND_ROOT_PATH}) + endif() + endif() + + if(__standalone_parts_QT_MODULE_PACKAGES) + foreach(__standalone_parts_package_name IN LISTS __standalone_parts_QT_MODULE_PACKAGES) + find_package(${QT_CMAKE_EXPORT_NAMESPACE} + "${__standalone_parts_main_qt_package_version}" + COMPONENTS "${__standalone_parts_package_name}") + endforeach() + endif() + + unset(__standalone_parts_opt_args) + unset(__standalone_parts_single_args) + unset(__standalone_parts_multi_args) + unset(__standalone_parts_QT_MODULE_PACKAGES) + unset(__standalone_parts_QT_TOOL_PACKAGES) + unset(__standalone_parts_main_qt_package_version) + unset(__standalone_parts_package_name) + unset(__standalone_parts_CMAKE_PREFIX_PATH) + unset(__standalone_parts_CMAKE_FIND_ROOT_PATH) +endmacro() + # Used by standalone tests and standalone non-ExternalProject examples to find all installed qt # packages. macro(qt_internal_find_standalone_parts_config_files) diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index c4564cfb38d..d8f031f5b27 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -59,6 +59,10 @@ function(qt_feature_module_begin) set(__QtFeature_define_definitions "" PARENT_SCOPE) endfunction() +# Define a Qt feature. +# +# The vcpkg-related arguments are relevant for configure only and are documented +# at the top of the qt_feature implementation in QtProcessConfigureArgs.cmake. function(qt_feature feature) set(original_name "${feature}") qt_feature_normalize_name("${feature}" feature) @@ -68,11 +72,14 @@ function(qt_feature feature) PRIVATE PUBLIC SYSTEM_LIBRARY + VCPKG_DEFAULT + VCPKG_OPTIONAL ) set(single_value_options LABEL PURPOSE SECTION + VCPKG_DESCRIPTION ) set(multi_value_options AUTODETECT @@ -80,6 +87,7 @@ function(qt_feature feature) ENABLE DISABLE EMIT_IF + VCPKG_DEPENDENT_FEATURES ) cmake_parse_arguments(PARSE_ARGV 1 arg "${no_value_options}" "${single_value_options}" "${multi_value_options}" @@ -389,6 +397,10 @@ function(qt_feature_deprecated feature) endif() endfunction() +function(qt_feature_vcpkg_scope name) + # This is just a stub. The real implementation is called at configure script time. +endfunction() + function(qt_evaluate_to_boolean expressionVar) if(${${expressionVar}}) set(${expressionVar} ON PARENT_SCOPE) @@ -816,9 +828,9 @@ endmacro() macro(_qt_internal_parse_feature_definition feature) cmake_parse_arguments(arg - "PRIVATE;PUBLIC;ALIAS_NEGATE" - "LABEL;PURPOSE;SECTION;ALIAS_OF_FEATURE;ALIAS_OF_CACHE" - "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" + "PRIVATE;PUBLIC;ALIAS_NEGATE;VCPKG_DEFAULT;VCPKG_OPTIONAL" + "LABEL;PURPOSE;SECTION;ALIAS_OF_FEATURE;ALIAS_OF_CACHE;VCPKG_DESCRIPTION" + "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF;VCPKG_DEPENDENT_FEATURES" ${_QT_FEATURE_DEFINITION_${feature}}) endmacro() @@ -1302,7 +1314,10 @@ function(qt_feature_module_end) # Before, we didn't use to export the properties at all for INTERFACE_ libraries, # but we need to, because certain GlobalPrivate modules have features which are used # in configure-time conditions for tests. - qt_internal_add_genex_properties_export("${target}" ${properties_to_export}) + qt_internal_add_custom_properties_to_export("${target}" + PROPERTIES_WITHOUT_GENEXES + ${properties_to_export} + ) else() set(propertyPrefix "") set_property(TARGET "${target}" diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 29c12dcd826..82538ab6ffe 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -10,12 +10,39 @@ # - Or remove the <builddir>/CMakeCache.txt file and configure from scratch # - Or remove the QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES cache variable (by # editing CMakeCache.txt) and reconfigure. +# +# It's possible to annotate the qt_find_package with information on how to +# generate a vcpkg manifest to satisfy this qt_find_package call. +# +# VCPKG_PORT <name> +# Name of the vcpkg port (package) to install. +# +# VCPKG_PLATFORM <expression> +# vcpkg platform expression, e.g. "!windows". +# See https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json#platform-expression +# +# VCPKG_VERSION <version> +# The minimum version of the vcpkg port to install +# See https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json#version +# +# VCPKG_ADD_TO_FEATURE <name> +# Add this port to the vcpkg feature with the given name. Create the feature if non-existent. +# +# VCPKG_DEFAULT_FEATURES <ON/OFF> +# ON by default. Set this to OFF to avoid that the vcpkg feature is added to +# vcpkg.json's default features. Only useful if VCPKG_ADD_TO_FEATURE is set. macro(qt_find_package) # Get the target names we expect to be provided by the package. set(find_package_options CONFIG NO_MODULE MODULE REQUIRED) set(options ${find_package_options} MARK_OPTIONAL) - set(oneValueArgs MODULE_NAME QMAKE_LIB) - set(multiValueArgs PROVIDED_TARGETS COMPONENTS OPTIONAL_COMPONENTS) + set(oneValueArgs MODULE_NAME QMAKE_LIB + VCPKG_ADD_TO_FEATURE + VCPKG_DEFAULT_FEATURES + VCPKG_PLATFORM + VCPKG_PORT + VCPKG_VERSION + ) + set(multiValueArgs PROVIDED_TARGETS COMPONENTS OPTIONAL_COMPONENTS VCPKG_FEATURES) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # If some Qt internal project calls qt_find_package(WrapFreeType), but WrapFreeType was already @@ -406,13 +433,13 @@ function(qt_record_extra_main_tools_package_dependency endif() if (TARGET "${main_target_name}") get_target_property(extra_packages "${main_target_name}" - QT_EXTRA_TOOLS_PACKAGE_DEPENDENCIES) + _qt_extra_tools_package_dependencies) if(NOT extra_packages) set(extra_packages "") endif() list(APPEND extra_packages "${dep_package_name}\;${dep_package_version}") - set_target_properties("${main_target_name}" PROPERTIES QT_EXTRA_TOOLS_PACKAGE_DEPENDENCIES + set_target_properties("${main_target_name}" PROPERTIES _qt_extra_tools_package_dependencies "${extra_packages}") endif() endfunction() @@ -457,6 +484,52 @@ function(qt_record_extra_third_party_dependency main_target_name dep_target) endif() endfunction() +# Record a third party dependency for a standalone tools package. +# +# This function records a dependency between the standalone pacakge PACKAGE_BASE_NAME and third +# party dependency DEPENDENCY_PACKAGE_NAME and DEPENDENCY_PACKAGE_VERSION +# at the CMake package level. +# +# E.g. A Qt6GarageTools package with the PACKAGE_BASE_NAME 'Garage', +# needs to call find_package(ZLIB 1.2) (non-qt-package). +function(qt_internal_record_tools_package_extra_third_party_dependency) + set(opt_args "") + set(single_args + PACKAGE_BASE_NAME + ) + set(multi_args + DEPENDENCY_PACKAGE_NAME + DEPENDENCY_PACKAGE_VERSION + ) + 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_BASE_NAME) + message(FATAL_ERROR "PACKAGE_BASE_NAME is required.") + endif() + set(id "${arg_PACKAGE_BASE_NAME}") + + if(NOT arg_DEPENDENCY_PACKAGE_NAME) + message(FATAL_ERROR "DEPENDENCY_PACKAGE_NAME is required.") + endif() + set(dep_package_name "${arg_DEPENDENCY_PACKAGE_NAME}") + + if(arg_DEPENDENCY_PACKAGE_VERSION) + set(dep_package_version "${arg_DEPENDENCY_PACKAGE_VERSION}") + else() + set(dep_package_version "") + endif() + + get_cmake_property(extra_deps _qt_standalone_tool_package_${id}_third_party_dependencies) + if(NOT extra_deps) + set(extra_deps "") + endif() + + list(APPEND extra_deps "${dep_package_name}\;${dep_package_version}") + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_third_party_dependencies + "${extra_deps}") +endfunction() + # Sets out_var to TRUE if the non-namespaced ${lib} target is exported as part of Qt6Targets.cmake. function(qt_internal_is_lib_part_of_qt6_package lib out_var) if (lib STREQUAL "Platform" diff --git a/cmake/QtModuleToolsConfig.cmake.in b/cmake/QtModuleToolsConfig.cmake.in index 43b826c2060..1bd54239edf 100644 --- a/cmake/QtModuleToolsConfig.cmake.in +++ b/cmake/QtModuleToolsConfig.cmake.in @@ -18,10 +18,17 @@ if(NOT DEFINED "@INSTALL_CMAKE_NAMESPACE@@target@_FOUND") set("@INSTALL_CMAKE_NAMESPACE@@target@_FOUND" TRUE) endif() +set(__qt_@target@_should_include_targets_code "@QT_SHOULD_INCLUDE_TARGETS_CODE@") + # Do the checks inside Targets.cmake even when the file is still being generated -include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@TargetsPrecheck.cmake") +if(__qt_@target@_should_include_targets_code) + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@TargetsPrecheck.cmake") +endif() -if(NOT __qt_@target@_skip_include_targets_file AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) +if(NOT __qt_@target@_skip_include_targets_file + AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND + AND __qt_@target@_should_include_targets_code + ) include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@AdditionalTargetInfo.cmake") if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS) @@ -30,7 +37,8 @@ if(NOT __qt_@target@_skip_include_targets_file AND @INSTALL_CMAKE_NAMESPACE@@tar set(__qt_@target@_targets_file_included ON) endif() -foreach(extra_cmake_include @extra_cmake_includes@) +set(__qt_@target@_extra_cmake_includes "@extra_cmake_includes@") +foreach(extra_cmake_include IN LISTS __qt_@target@_extra_cmake_includes) include("${CMAKE_CURRENT_LIST_DIR}/${extra_cmake_include}") endforeach() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 12f5c617960..2fd2ff79478 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -202,12 +202,10 @@ function(qt_internal_create_module_depends_file target) # Extra QtFooModuleTools packages to be added as dependencies to # QtModuleDependencies.cmake. Needed for QtWaylandCompositor / QtWaylandClient. - if(NOT is_interface_lib) - get_target_property(extra_tools_package_dependencies "${target}" - QT_EXTRA_TOOLS_PACKAGE_DEPENDENCIES) - if(extra_tools_package_dependencies) - list(APPEND main_module_tool_deps "${extra_tools_package_dependencies}") - endif() + get_target_property(extra_tools_package_dependencies "${target}" + _qt_extra_tools_package_dependencies) + if(extra_tools_package_dependencies) + list(APPEND main_module_tool_deps "${extra_tools_package_dependencies}") endif() qt_internal_get_qt_all_known_modules(known_modules) @@ -801,8 +799,15 @@ endfunction() function(qt_create_tools_config_files) # Create packages like Qt6CoreTools/Qt6CoreToolsConfig.cmake. foreach(module_name ${QT_KNOWN_MODULES_WITH_TOOLS}) - qt_export_tools("${module_name}") + qt_export_tools(MODULE_NAME "${module_name}") endforeach() + + get_cmake_property(standalone_packages _qt_standalone_tool_packages) + if(standalone_packages) + foreach(package_name IN LISTS standalone_packages) + qt_export_tools(PACKAGE_BASE_NAME "${package_name}") + endforeach() + endif() endfunction() function(qt_internal_create_config_file_for_standalone_tests) @@ -819,7 +824,7 @@ function(qt_internal_create_config_file_for_standalone_tests) # standalone tests, and it can happen that Core or Gui features are not # imported early enough, which means FindWrapPNG will try to find a system PNG library instead # of the bundled one. - set(modules) + set(modules "") foreach(m ${QT_REPO_KNOWN_MODULES}) get_target_property(target_type "${m}" TYPE) @@ -835,12 +840,11 @@ function(qt_internal_create_config_file_for_standalone_tests) endif() endforeach() - list(JOIN modules " " QT_REPO_KNOWN_MODULES_STRING) - string(STRIP "${QT_REPO_KNOWN_MODULES_STRING}" QT_REPO_KNOWN_MODULES_STRING) + get_cmake_property(tool_package_base_names _qt_standalone_tool_packages) - # Skip generating and installing file if no modules were built. This make sure not to install - # anything when build qtx11extras on macOS for example. - if(NOT QT_REPO_KNOWN_MODULES_STRING) + # Skip generating and installing file if no modules or tools were built. This makes sure not + # to install anything when building qtx11extras on macOS for example. + if(NOT modules AND NOT tool_package_base_names) return() endif() @@ -848,8 +852,23 @@ function(qt_internal_create_config_file_for_standalone_tests) # of the current repo. This is used for standalone tests. qt_internal_get_standalone_parts_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) + # Substitution variables. + if(modules) + list(JOIN modules "\n " module_string) + set(QT_MODULE_PACKAGES " QT_MODULE_PACKAGES + ${module_string}") + endif() + + if(tool_package_base_names) + # We only have the base package names, so we need to append Tools to each of the package + # names + set(tool_packages "${tool_package_base_names}") + list(TRANSFORM tool_packages APPEND Tools) + + list(JOIN tool_packages "\n " tool_packages_string) + set(QT_TOOL_PACKAGES " QT_TOOL_PACKAGES + ${tool_packages_string}") + endif() configure_file( "${QT_CMAKE_DIR}/QtStandaloneTestsConfig.cmake.in" diff --git a/cmake/QtProcessConfigureArgs.cmake b/cmake/QtProcessConfigureArgs.cmake index bdedfb0b734..833daceba27 100644 --- a/cmake/QtProcessConfigureArgs.cmake +++ b/cmake/QtProcessConfigureArgs.cmake @@ -11,11 +11,13 @@ # If empty, qtbase/top-level is assumed. # TOP_LEVEL: TRUE, if this is a top-level build. +# The CMake version required for running the configure script. +# This must be less than or equal to the lowest QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_QT_*. +cmake_minimum_required(VERSION 3.19) + include(${CMAKE_CURRENT_LIST_DIR}/QtFeatureCommon.cmake) include(${CMAKE_CURRENT_LIST_DIR}/QtBuildInformation.cmake) - -cmake_policy(SET CMP0007 NEW) -cmake_policy(SET CMP0057 NEW) +include(${CMAKE_CURRENT_LIST_DIR}/QtVcpkgManifestHelpers.cmake) set(cmake_args "") macro(push) @@ -85,8 +87,11 @@ list(TRANSFORM configure_args STRIP) unset(generator) set(auto_detect_compiler TRUE) set(auto_detect_generator ${qtbase_or_top_level_build}) +set(dry_run FALSE) set(no_prefix_option FALSE) set(skipped_qtrepos "") +set(use_vcpkg FALSE) +set(generate_vcpkg_manifest "unknown") unset(device_options) unset(options_json_file) set_property(GLOBAL PROPERTY UNHANDLED_ARGS "") @@ -100,6 +105,20 @@ while(NOT "${configure_args}" STREQUAL "") list(POP_FRONT configure_args generator) elseif(arg STREQUAL "-cmake-use-default-generator") set(auto_detect_generator FALSE) + elseif(arg STREQUAL "-dry-run") + set(dry_run TRUE) + elseif(arg MATCHES "^-(no-)?vcpkg$") + if(CMAKE_MATCH_1 STREQUAL "no-") + set(use_vcpkg FALSE) + else() + set(use_vcpkg TRUE) + endif() + elseif(arg MATCHES "^-(no-)?generate-vcpkg-manifest$") + if(CMAKE_MATCH_1 STREQUAL "no-") + set(generate_vcpkg_manifest FALSE) + else() + set(generate_vcpkg_manifest TRUE) + endif() elseif(arg STREQUAL "-no-guess-compiler") set(auto_detect_compiler FALSE) elseif(arg STREQUAL "-list-features") @@ -171,6 +190,18 @@ while(NOT "${configure_args}" STREQUAL "") endif() endwhile() +# Turn on vcpkg usage if requested. +# Set the manifest directory to where we generate the manifest file. +if(use_vcpkg) + push(-DQT_USE_VCPKG=ON) + push("-DVCPKG_MANIFEST_DIR=${CMAKE_CURRENT_BINARY_DIR}") +endif() + +# By default, generate a manifest if using vcpkg. +if(generate_vcpkg_manifest STREQUAL "unknown") + set(generate_vcpkg_manifest ${use_vcpkg}) +endif() + # Read the specified manually generator value from CMake command line. # The '-cmake-generator' argument has higher priority than CMake command line. if(NOT generator) @@ -225,11 +256,108 @@ endif() set_property(GLOBAL PROPERTY COMMANDLINE_KNOWN_FEATURES "") +# Define a Qt feature. +# +# Arguments that start with VCPKG_ affect the creation of vcpkg features. If +# either VCPKG_DEFAULT or VCPGK_OPTIONAL are given, a corresponding vcpkg +# feature will be created. +# +# VCPKG_DEFAULT +# Specifies to create a vcpkg feature that will be added to the manifest's +# default features. +# +# VCPKG_OPTIONAL +# Specifies to create an optional vcpkg feature. +# +# VCPKG_DESCRIPTION <string> +# Optional description of the vcpkg feature. If not given, the description +# is derived from PURPOSE, or LABEL, or the feature name. +# +# VCPKG_DEPENDENT_FEATURES <features> +# List of vcpkg features this feature depends upon. +# For example, the "jpeg" feature would use "VCPKG_DEPENDENT_FEATURES gui". function(qt_feature feature) - cmake_parse_arguments(arg "" "PURPOSE;SECTION;" "" ${ARGN}) + set(no_value_options + VCPKG_DEFAULT + VCPKG_OPTIONAL + ) + set(single_value_options + LABEL + PURPOSE + SECTION + VCPKG_DESCRIPTION + ) + set(multi_value_options + VCPKG_DEPENDENT_FEATURES + ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + set_property(GLOBAL APPEND PROPERTY COMMANDLINE_KNOWN_FEATURES "${feature}") set_property(GLOBAL PROPERTY COMMANDLINE_FEATURE_PURPOSE_${feature} "${arg_PURPOSE}") set_property(GLOBAL PROPERTY COMMANDLINE_FEATURE_SECTION_${feature} "${arg_SECTION}") + + if(NOT generate_vcpkg_manifest) + return() + endif() + + set(unknown_vcpkg_args "${arg_UNPARSED_ARGUMENTS}") + list(FILTER unknown_vcpkg_args INCLUDE REGEX "^VCPKG_") + if(NOT "${unknown_vcpkg_args}" STREQUAL "") + message(FATAL_ERROR "Unknown arguments passed to qt_feature: ${unknown_vcpkg_args}") + endif() + + set(create_vcpkg_feature FALSE) + if(arg_VCPKG_DEFAULT OR arg_VCPKG_OPTIONAL) + set(create_vcpkg_feature TRUE) + else() + get_cmake_property(vcpkg_features_to_create _QT_VCPKG_FEATURES_TO_CREATE) + if("${feature}" IN_LIST vcpkg_features_to_create) + set(create_vcpkg_feature TRUE) + list(REMOVE_ITEM vcpkg_features_to_create "${feature}") + set_property(GLOBAL PROPERTY + _QT_VCPKG_FEATURES_TO_CREATE ${vcpkg_features_to_create} + ) + endif() + endif() + + if(NOT create_vcpkg_feature) + return() + endif() + + # Determine the description + if(DEFINED arg_VCPKG_DESCRIPTION) + set(description "${arg_VCPKG_DESCRIPTION}") + elseif(DEFINED arg_PURPOSE) + set(description "${arg_PURPOSE}") + elseif(DEFINED arg_LABEL) + set(description "${arg_LABEL}") + else() + set(description "${feature} support") + endif() + + # Determine the dependent features (e.g. gui for freetype) + set(dependent_features "") + get_cmake_property(vcpkg_scope _QT_VCPKG_SCOPE) + if(vcpkg_scope) + list(APPEND dependent_features "${vcpkg_scope}") + endif() + if(DEFINED arg_VCPKG_DEPENDENT_FEATURES) + list(APPEND dependent_features "${arg_VCPKG_DEPENDENT_FEATURES}") + endif() + + # Features that aren't dependencies of other features are added to default-features + # unless VCPKG_DEFAULT or VCPKG_OPTIONAL are specified. + set(additional_args "") + if(NOT arg_VCKG_DEFAULT AND NOT arg_VCPKG_OPTIONAL AND dependent_features STREQUAL "") + list(APPEND additional_args DEFAULT) + endif() + qt_vcpkg_feature("${feature}" "${description}" ${additional_args}) + + foreach(dependent_feature IN LISTS dependent_features) + qt_vcpkg_add_feature_dependencies("${dependent_feature}" "${feature}") + endforeach() endfunction() function(qt_feature_alias feature) @@ -260,11 +388,83 @@ function(qt_feature_deprecated feature) set_property(GLOBAL PROPERTY COMMANDLINE_FEATURE_SECTION_${feature} "${arg_SECTION}") endfunction() +function(qt_feature_vcpkg_scope name) + set_property(GLOBAL PROPERTY _QT_VCPKG_SCOPE ${name}) +endfunction() + function(find_package) message(FATAL_ERROR "find_package must not be used directly in configure.cmake. " "Use qt_find_package or guard the call with an if(NOT QT_CONFIGURE_RUNNING) block.") endfunction() +function(qt_init_vcpkg_manifest_if_needed) + get_property(manifest_initialized GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON SET) + if(manifest_initialized) + return() + endif() + + if(TOP_LEVEL) + set(package_name "qt") + else() + get_filename_component(package_name "${MODULE_ROOT}" NAME) + endif() + qt_vcpkg_manifest_init(NAME "${package_name}") +endfunction() + +function(qt_find_package name) + if(NOT generate_vcpkg_manifest) + return() + endif() + + set(no_value_options "") + set(single_value_options + VCPKG_ADD_TO_FEATURE + VCPKG_DEFAULT_FEATURES + VCPKG_PLATFORM + VCPKG_PORT + VCPKG_VERSION + ) + set(multi_value_options + VCPKG_FEATURES + ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + set(unknown_vcpkg_args "${arg_UNPARSED_ARGUMENTS}") + list(FILTER unknown_vcpkg_args INCLUDE REGEX "^VCPKG_") + if(NOT "${unknown_vcpkg_args}" STREQUAL "") + message(FATAL_ERROR "Unknown arguments passed to qt_find_package: ${unknown_vcpkg_args}") + endif() + + if(NOT DEFINED arg_VCPKG_PORT) + return() + endif() + + qt_init_vcpkg_manifest_if_needed() + + set(dependency_args "${arg_VCPKG_PORT}") + if(DEFINED arg_VCPKG_VERSION) + list(APPEND dependency_args VERSION "${arg_VCPKG_VERSION}") + endif() + if(DEFINED arg_VCPKG_PLATFORM) + list(APPEND dependency_args PLATFORM "${arg_VCPKG_PLATFORM}") + endif() + if(DEFINED arg_VCPKG_DEFAULT_FEATURES) + list(APPEND dependency_args DEFAULT_FEATURES "${arg_VCPKG_DEFAULT_FEATURES}") + endif() + if(DEFINED arg_VCPKG_ADD_TO_FEATURE) + list(APPEND dependency_args ADD_TO_FEATURE "${arg_VCPKG_ADD_TO_FEATURE}") + set_property(GLOBAL APPEND PROPERTY + _QT_VCPKG_FEATURES_TO_CREATE "${arg_VCPKG_ADD_TO_FEATURE}" + ) + endif() + if(DEFINED arg_VCPKG_FEATURES) + list(APPEND dependency_args FEATURES "${arg_VCPKG_FEATURES}") + endif() + qt_vcpkg_add_dependency(${dependency_args}) +endfunction() + macro(defstub name) function(${name}) endfunction() @@ -289,7 +489,6 @@ defstub(qt_configure_end_summary_section) defstub(qt_extra_definition) defstub(qt_feature_config) defstub(qt_feature_definition) -defstub(qt_find_package) defstub(set_package_properties) defstub(qt_qml_find_python) defstub(qt_set01) @@ -391,6 +590,9 @@ endfunction() # stub functions above. set(QT_CONFIGURE_RUNNING ON) +# Make sure that configure sees all qt_find_package calls. +set(QT_FIND_ALL_PACKAGES_ALWAYS ON) + #################################################################################################### # Load qt_cmdline.cmake files @@ -414,6 +616,7 @@ while(commandline_files) unset(commandline_subconfigs) if(EXISTS "${configure_file}") include("${configure_file}") + set_property(GLOBAL PROPERTY _QT_VCPKG_SCOPE) endif() if(EXISTS "${commandline_file}") include("${commandline_file}") @@ -1188,9 +1391,109 @@ if(INPUT_sysroot) "to pass the sysroot to CMake.\n") endif() +function(qt_generate_vcpkg_manifest) + # Check if manifest was initialized (i.e., any dependencies were found) + get_property(manifest_initialized GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON SET) + if(NOT manifest_initialized) + # For repositories that don't need 3rd-party libs, generate an empty vcpkg.json. + qt_init_vcpkg_manifest_if_needed() + endif() + + # Write the manifest file + set(manifest_file "${CMAKE_CURRENT_BINARY_DIR}/vcpkg.json") + qt_vcpkg_write_manifest("${manifest_file}") +endfunction() + +function(qt_select_vcpkg_features out_var cmake_args) + # Extract enabled/disabled features from cmake_args. + set(normalized_enabled_features "") + set(normalized_disabled_features "") + foreach(arg IN LISTS cmake_args) + if(arg MATCHES "^-DFEATURE_([^=]+)=(.*)") + if(CMAKE_MATCH_2) + list(APPEND normalized_enabled_features "${CMAKE_MATCH_1}") + else() + list(APPEND normalized_disabled_features "${CMAKE_MATCH_1}") + endif() + endif() + endforeach() + + if(normalized_enabled_features STREQUAL "" AND normalized_disabled_features STREQUAL "") + return() + endif() + + # Convert normalized feature names back to original names + set(enabled_features "") + set(disabled_features "") + foreach(original_feature IN LISTS commandline_known_features) + qt_feature_normalize_name("${original_feature}" normalized_feature) + if(normalized_feature IN_LIST normalized_enabled_features) + list(APPEND enabled_features "${original_feature}") + endif() + if(normalized_feature IN_LIST normalized_disabled_features) + list(APPEND disabled_features "${original_feature}") + endif() + endforeach() + + set(manifest_file_path "${CMAKE_CURRENT_BINARY_DIR}/vcpkg.json") + if(NOT EXISTS "${manifest_file_path}") + return() + endif() + + file(READ "${manifest_file_path}" json) + qt_vcpkg_set_internal_manifest_data("${json}") + + # Enable all default vcpkg feature that were not explicitly disabled. + qt_vcpkg_get_default_features(vcpkg_default_features "${json}") + set(vcpkg_features_to_enable ${vcpkg_default_features}) + list(REMOVE_ITEM vcpkg_features_to_enable ${disabled_features}) + + # Enable all vcpkg features that were explicitly enabled. Filter out non-vcpkg features. + qt_vcpkg_get_features(all_vcpkg_features "${json}") + set(enabled_features_without_vcpkg_features "${enabled_features}") + list(REMOVE_ITEM enabled_features_without_vcpkg_features ${all_vcpkg_features}) + list(APPEND vcpkg_features_to_enable ${enabled_features}) + list(REMOVE_ITEM vcpkg_features_to_enable ${enabled_features_without_vcpkg_features}) + list(REMOVE_DUPLICATES vcpkg_features_to_enable) + + # If we're enabling exactly the default features, we don't have to explicitly enable them. + if(vcpkg_features_to_enable STREQUAL vcpkg_default_features) + return() + endif() + + list(APPEND cmake_args "-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON") + list(JOIN vcpkg_features_to_enable "[[;]]" manifest_args) + list(APPEND cmake_args "-DVCPKG_MANIFEST_FEATURES=${manifest_args}") + set("${out_var}" "${cmake_args}" PARENT_SCOPE) +endfunction() + +if(generate_vcpkg_manifest) + qt_generate_vcpkg_manifest() +endif() + +if(use_vcpkg) + qt_select_vcpkg_features(cmake_args "${cmake_args}") +endif() + # Restore the escaped semicolons in arguments that are lists list(TRANSFORM cmake_args REPLACE "\\[\\[;\\]\\]" "\\\\;") +if(dry_run) + if(CMAKE_COMMAND MATCHES " ") + set(pretty_command_line "\"${CMAKE_COMMAND}\"") + else() + set(pretty_command_line "${CMAKE_COMMAND}") + endif() + foreach(arg IN LISTS cmake_args) + if(arg MATCHES "[ ;]") + set(arg "\"${arg}\"") + endif() + string(APPEND pretty_command_line " ${arg}") + endforeach() + message("${pretty_command_line}") + return() +endif() + execute_process(COMMAND "${CMAKE_COMMAND}" ${cmake_args} COMMAND_ECHO STDOUT RESULT_VARIABLE exit_code) diff --git a/cmake/QtStandaloneTestsConfig.cmake.in b/cmake/QtStandaloneTestsConfig.cmake.in index 39200167a58..47ee7e8353a 100644 --- a/cmake/QtStandaloneTestsConfig.cmake.in +++ b/cmake/QtStandaloneTestsConfig.cmake.in @@ -1,8 +1,8 @@ # Copyright (C) 2024 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# 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@ - COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@) +set(__standalone_parts_qt_packages_args +@QT_MODULE_PACKAGES@ +@QT_TOOL_PACKAGES@ +) +qt_internal_find_standalone_parts_qt_packages(__standalone_parts_qt_packages_args) diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index eedfdbeba74..da210820bb2 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -925,6 +925,12 @@ function(qt_internal_export_additional_targets_file_finalizer id) list(LENGTH arg_TARGETS num_TARGETS) + if(num_TARGETS EQUAL 0) + # Return early without creating and installing the additional file if there are no targets + # to process. + return() + endif() + # Determine the release configurations we're currently building if(QT_GENERATOR_IS_MULTI_CONFIG) set(active_configurations ${CMAKE_CONFIGURATION_TYPES}) @@ -1688,15 +1694,35 @@ function(qt_internal_get_target_sources_property out_var) set(${out_var} "${${out_var}}" PARENT_SCOPE) endfunction() -# This function collects target properties that contain generator expressions and needs to be -# exported. This function is needed since the CMake EXPORT_PROPERTIES property doesn't support -# properties that contain generator expressions. -# Usage: qt_internal_add_genex_properties_export(target properties...) -function(qt_internal_add_genex_properties_export target) +# This function collects target properties that need to be exported without using CMake's +# EXPORT_PROPERTIES. +# Use cases: +# - Properties named INTERFACE_foo (which CMake doesn't allow exporting) +# - Properties that contain generator expressions (need special handling for multi-config builds) +# Usage: +# qt_internal_add_custom_properties_to_export(target +# PROPERTIES property1 [property2 ...] +# PROPERTIES_WITHOUT_GENEXES property3 [property4 ...] +# ) +# Arguments: +# PROPERTIES +# should contain names of properties that can differ in multi-config builds (e.g. paths) +# PROPERTIES_WITHOUT_GENEXES +# should contain names of properties that will always have the same value in multi config +# builds (e.g, feature values). +function(qt_internal_add_custom_properties_to_export target) + set(opt_args "") + set(single_args "") + set(multi_args + PROPERTIES + PROPERTIES_WITHOUT_GENEXES + ) + cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) - set(config_check_begin "") - set(config_check_end "") + # Prepare multi-config helper genexes. if(is_multi_config) list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) @@ -1704,7 +1730,7 @@ function(qt_internal_add_genex_properties_export target) # The check is only applicable to the 'main' configuration. If user project doesn't use # multi-config generator, then the check supposed to return true and the value from the # 'main' configuration supposed to be used. - string(JOIN "" check_if_config_empty + string(CONCAT check_if_config_empty "$<1:$><NOT:" "$<1:$><BOOL:" "$<1:$><CONFIG$<ANGLE-R>" @@ -1714,7 +1740,7 @@ function(qt_internal_add_genex_properties_export target) # The genex snippet is evaluated to '$<CONFIG:'Qt config type'>' in the generated cmake # file and checks if the config that user uses matches the generated cmake file config. - string(JOIN "" check_user_config + string(CONCAT check_user_config "$<1:$><CONFIG:$<CONFIG>$<ANGLE-R>" ) @@ -1725,34 +1751,70 @@ function(qt_internal_add_genex_properties_export target) # user project according to the user config type. # All genexes need to be escaped properly to protect them from evaluation by the # file(GENERATE call in the qt_internal_export_genex_properties function. - string(JOIN "" config_check_begin + string(CONCAT config_check_begin_multi "$<1:$><" "$<1:$><OR:" "${check_user_config}" "$<$<CONFIG:${first_config_type}>:$<COMMA>${check_if_config_empty}>" "$<ANGLE-R>:" ) - set(config_check_end "$<ANGLE-R>") + set(config_check_end_multi "$<ANGLE-R>") endif() set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") - foreach(property IN LISTS ARGN) - set(target_property_genex "$<TARGET_PROPERTY:${target_name},${property}>") - # All properties that contain lists need to be protected of processing by JOIN genex calls. - # So this escapes the semicolons for these list. - set(target_property_list_escape - "$<JOIN:$<GENEX_EVAL:${target_property_genex}>,\;>") - set(property_value - "\"${config_check_begin}${target_property_list_escape}${config_check_end}\"") - set_property(TARGET ${target} APPEND PROPERTY _qt_export_genex_properties_content - "${property} ${property_value}") + + set(property_sources + PROPERTIES + PROPERTIES_WITHOUT_GENEXES + ) + + foreach(property_source IN LISTS property_sources) + if(property_source STREQUAL "PROPERTIES") + # Properties with genexes need multi-config specific handling. + set(config_check_begin "${config_check_begin_multi}") + set(config_check_end "${config_check_end_multi}") + + set(output_property "_qt_export_custom_properties_content") + elseif(property_source STREQUAL "PROPERTIES_WITHOUT_GENEXES") + # Properties without genexes don't need the config checks. + set(config_check_begin "") + set(config_check_end "") + + set(output_property "_qt_export_custom_properties_no_genexes_content") + else() + message(FATAL_ERROR "Invalid type of property source" ${property_source}"") + endif() + + foreach(property IN LISTS arg_${property_source}) + set(target_property_genex "$<TARGET_PROPERTY:${target_name},${property}>") + # All properties that contain lists need to be protected of processing by JOIN genex + # calls. So this escapes the semicolons for these list. + set(target_property_list_escape + "$<JOIN:$<GENEX_EVAL:${target_property_genex}>,\;>") + set(property_value + "\"${config_check_begin}${target_property_list_escape}${config_check_end}\"") + set_property(TARGET ${target} APPEND PROPERTY "${output_property}" + "${property} ${property_value}") + endforeach() endforeach() endfunction() -# This function executes generator expressions for the properties that are added by the -# qt_internal_add_genex_properties_export function and sets the calculated values to the -# corresponding properties in the generated ExtraProperties.cmake file. The file then needs to be -# included after the target creation routines in Config.cmake files. It also supports Multi-Config -# builds. +# This function generates and installs ${EXPORT_NAME_PREFIX}ExportProperties-$<CONFIG>.cmake files +# to be included from inside a FooConfig.cmake file. +# +# The file contains set_property(TARGET PROPERTY) assignments that append values to a given target's +# properties as added by the qt_internal_add_custom_properties_to_export function. +# +# The assigned values are computed from the result of executing the generator expressions that were +# stored in the properties, and are wrapped in config-specific genexes in a multi-config build. +# +# Example output: +# set_property(TARGET Qt6::Foo PROPERTY MY_GENEX_PROP +# "$<$<OR:$<CONFIG:RelWithDebInfo>,$<NOT:$<BOOL:$<CONFIG>>>>:OneReleaseVal>") +# set_property(TARGET Qt6::Foo PROPERTY MY_REGULAR_PROP "SecondValue") +# include("${CMAKE_CURRENT_LIST_DIR}/Qt6FooExtraProperties-Debug.cmake") +# inside the include +# set_property(TARGET Qt6::Foo APPEND PROPERTY MY_GENEX_PROP "$<$<OR:$<CONFIG:Debug>>:OneDebugVal>") +# # Arguments: # EXPORT_NAME_PREFIX: # The portion of the file name before ExtraProperties.cmake @@ -1761,13 +1823,15 @@ endfunction() # TARGETS: # The internal target names. function(qt_internal_export_genex_properties) - set(option_args "") + set(opt_args "") set(single_args EXPORT_NAME_PREFIX CONFIG_INSTALL_DIR ) - set(multi_args TARGETS) - cmake_parse_arguments(arg "${option_args}" "${single_args}" "${multi_args}" ${ARGN}) + set(multi_args + TARGETS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") if(NOT arg_EXPORT_NAME_PREFIX) message(FATAL_ERROR "qt_internal_export_genex_properties: " @@ -1779,24 +1843,36 @@ function(qt_internal_export_genex_properties) "TARGETS argument must contain at least one target") endif() - foreach(target IN LISTS arg_TARGETS) - get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + # TODO: Handling more than one target won't work correctly atm due to trying to create and + # install the same file name multiple times for each target. + list(LENGTH arg_TARGETS targets_count) + if(targets_count GREATER 1) + message(AUTHOR_WARNING "qt_internal_export_genex_properties: " + "Specifying more than one target is not fully supported yet.") + endif() + + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + + set(should_append "") + set(config_suffix "") + set(is_first_config "1") + if(is_multi_config) + list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) + + # The non-genex properties should only go to the first config file. + set(is_first_config "$<CONFIG:${first_config_type}>") + set(config_suffix "$<$<NOT:${is_first_config}>:-$<CONFIG>>") + # If the generated file belongs to the 'main' config type, we should set property + # but not append it. + string(JOIN "" should_append + "$<$<NOT:${is_first_config}>: APPEND>") + endif() + + foreach(target IN LISTS arg_TARGETS) set(output_file_base_name "${arg_EXPORT_NAME_PREFIX}ExtraProperties") - set(should_append "") - set(config_suffix "") - if(is_multi_config) - list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) - set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>") - # If the generated file belongs to the 'main' config type, we should set property - # but not append it. - string(JOIN "" should_append - "$<$<NOT:$<CONFIG:${first_config_type}>>: APPEND>") - endif() set(file_name "${output_file_base_name}${config_suffix}.cmake") - - qt_path_join(output_file "${arg_CONFIG_INSTALL_DIR}" - "${file_name}") + qt_path_join(output_file "${arg_CONFIG_INSTALL_DIR}" "${file_name}") if(NOT IS_ABSOLUTE "${output_file}") qt_path_join(output_file "${QT_BUILD_DIR}" "${output_file}") @@ -1804,17 +1880,51 @@ function(qt_internal_export_genex_properties) set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") + # Common genex helpers. string(JOIN "" set_property_begin "set_property(TARGET " "${target_name}${should_append} PROPERTY " ) set(set_property_end ")") set(set_property_glue "${set_property_end}\n${set_property_begin}") + set(t_prop "TARGET_PROPERTY:${target}") + + # Handle the properties that contain genexes. set(property_list - "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_export_genex_properties_content>>") - string(JOIN "" set_property_content "${set_property_begin}" + "$<GENEX_EVAL:$<${t_prop},_qt_export_custom_properties_content>>") + set(property_has_values "$<BOOL:${property_list}>") + string(CONCAT set_property_content + "${set_property_begin}" "$<JOIN:${property_list},${set_property_glue}>" "${set_property_end}") + string(CONCAT set_property_content_conditional + "$<${property_has_values}:" + "\n${set_property_content}" + ">") + + # We need to ensure the no genexes content only gets added to the first config file. + set(property_no_genexes_list + "$<GENEX_EVAL:$<${t_prop},_qt_export_custom_properties_no_genexes_content>>") + set(property_no_genexes_has_values "$<BOOL:${property_no_genexes_list}>") + string(CONCAT property_no_genexes_has_values_and_first_config + "$<AND:${property_no_genexes_has_values},${is_first_config}>") + + string(CONCAT set_property_no_genexes_content + "${set_property_begin}" + "$<JOIN:${property_no_genexes_list},${set_property_glue}>" + "${set_property_end}") + + string(CONCAT set_property_no_genexes_content_conditional + "$<${property_no_genexes_has_values_and_first_config}:" + "\n${set_property_no_genexes_content}" + ">") + + # Final content is generated if at least one genex-carrying property has a value, + # or if we are in the first config and at least one no-genex property has a value. + set(content_available_condition + "$<OR:${property_has_values},${property_no_genexes_has_values_and_first_config}>") + + set(config_includes_string "") if(is_multi_config) set(config_includes "") foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES) @@ -1827,19 +1937,33 @@ function(qt_internal_export_genex_properties) endforeach() list(JOIN config_includes "\n" config_includes_string) set(config_includes_string - "\n$<$<CONFIG:${first_config_type}>:${config_includes_string}>") + "\n$<${is_first_config}:${config_includes_string}>") + + # Config includes should be included if we have properties with genexes, which are + # config specific. + string(CONCAT config_includes_string_conditional + "$<${property_has_values}:" + "${config_includes_string}" + ">") endif() + string(CONCAT final_content + "$<${content_available_condition}:" + "${set_property_content_conditional}" + "${set_property_no_genexes_content_conditional}" + "${config_includes_string_conditional}" + ">") + file(GENERATE OUTPUT "${output_file}" - CONTENT "$<$<BOOL:${property_list}>:${set_property_content}${config_includes_string}>" - CONDITION "$<BOOL:${property_list}>" + CONTENT "${final_content}" + CONDITION "${content_available_condition}" ) - endforeach() - qt_install(FILES "$<$<BOOL:${property_list}>:${output_file}>" - DESTINATION "${arg_CONFIG_INSTALL_DIR}" - COMPONENT Devel - ) + qt_install(FILES "$<${content_available_condition}:${output_file}>" + DESTINATION "${arg_CONFIG_INSTALL_DIR}" + COMPONENT Devel + ) + endforeach() endfunction() # A small wrapper for adding the Platform target, and a building block for the PlatformXInternal diff --git a/cmake/QtTestHelpers.cmake b/cmake/QtTestHelpers.cmake index 52183d09203..06228ac41de 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -821,13 +821,22 @@ function(qt_internal_add_test name) qt_internal_collect_command_environment(test_env_path test_env_plugin_path) + set(add_test_args "") + if(test_working_dir) + list(APPEND add_test_args WORKING_DIRECTORY "${test_working_dir}") + endif() + if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) if(QT_BUILD_TESTS_BATCHED) message(FATAL_ERROR "Wrapperless tests are unspupported with test batching") endif() - add_test(NAME "${testname}" COMMAND ${test_executable} ${extra_test_args} - WORKING_DIRECTORY "${test_working_dir}") + + + add_test(NAME "${testname}" + COMMAND ${test_executable} ${extra_test_args} + ${add_test_args} + ) set_property(TEST "${testname}" APPEND PROPERTY ENVIRONMENT "PATH=${test_env_path}" "QT_TEST_RUNNING_IN_CTEST=1" @@ -838,7 +847,7 @@ function(qt_internal_add_test name) qt_internal_create_test_script(NAME "${testname}" COMMAND "${test_executable}" ARGS "${extra_test_args}" - WORKING_DIRECTORY "${test_working_dir}" + ${add_test_args} OUTPUT_FILE "${test_wrapper_file}" ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST" 1 "PATH" "${test_env_path}" @@ -1089,8 +1098,14 @@ for this function. Will be ignored") if(is_in_batch) _qt_internal_test_batch_target_name(executable_name) endif() + + set(add_test_working_dir "") + if(arg_WORKING_DIRECTORY) + list(APPEND add_test_working_dir WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}") + endif() + add_test(NAME "${arg_NAME}" COMMAND "${CMAKE_COMMAND}" "-P" "${arg_OUTPUT_FILE}" - WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}") + ${add_test_working_dir}) # If crosscompiling is enabled, we should avoid run cmake in emulator environment. # Prepend emulator to test command in generated cmake script instead. Keep in mind that @@ -1111,7 +1126,7 @@ for this function. Will be ignored") "\${env_test_args}" ${command_args} OUTPUT_FILE "${arg_OUTPUT_FILE}" - WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}" + ${add_test_working_dir} ENVIRONMENT ${arg_ENVIRONMENT} PRE_RUN "separate_arguments(env_test_args NATIVE_COMMAND \ \"\$ENV{TESTARGS}\")" diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake index fe568d09ee6..5d531818ebd 100644 --- a/cmake/QtToolHelpers.cmake +++ b/cmake/QtToolHelpers.cmake @@ -360,21 +360,181 @@ echo. > ${target_bin_dir}/${target}_try_run_passed" DEPENDS ${target_bin_dir}/${target}_try_run_passed) endfunction() +# Create a standalone FooTools package, similar to the ones automatically generated by +# qt_internal_add_module, e.g. Core -> CoreTools. +# +# A standalone tools package can be useful when one wants to associate some special targets and +# cmake scripts to a package that is not associated with any Qt module, and is not architecture +# dependent (e.g. java binaries). +# +# Arguments +# PACKAGE_BASE_NAME - base name of the package, e.g. for a value of Foo, it will create a +# Qt6FooToolsConfig.cmake file. +# +# PACKAGE_VERSION - version to use for the package, defaults to PROJECT_VERSION. +# +# EXTRA_CMAKE_FILES - list of additional cmake files to install alongside the package +# +# EXTRA_CMAKE_INCLUDES - list of cmake files to include in the package config file +# +# EXTRA_CMAKE_STATEMENTS - list of additional cmake statements (strings) to append to the end of +# the package config file. +# +# TOOL_TARGETS - list of add_executable targets that should be install(EXPORT)ed for this package. +# It can NOT take imported targets or custom targets. +# +# TOOL_PACKAGE_DEPENDENCIES - list of other Qt Tools package dependencies that should be +# find_package'd when using this Tools package. +function(qt_internal_add_tools_package) + set(opt_args "") + set(single_args + PACKAGE_BASE_NAME + PACKAGE_VERSION + ) + set(multi_args + EXTRA_CMAKE_FILES + EXTRA_CMAKE_INCLUDES + EXTRA_CMAKE_STATEMENTS + TOOL_TARGETS + TOOL_PACKAGE_DEPENDENCIES + ) + 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_BASE_NAME) + message(FATAL_ERROR "Missing PACKAGE_BASE_NAME value") + endif() + + if(arg_PACKAGE_VERSION) + set(tools_package_version "${arg_PACKAGE_VERSION}") + else() + set(tools_package_version "${PROJECT_VERSION}") + endif() + + set(id "${arg_PACKAGE_BASE_NAME}") + _qt_internal_append_to_cmake_property_without_duplicates(_qt_standalone_tool_packages "${id}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_package_base_name "${id}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_version + "${tools_package_version}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_list_dir + "${CMAKE_CURRENT_LIST_DIR}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_extra_cmake_files + "${arg_EXTRA_CMAKE_FILES}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_extra_cmake_includes + "${arg_EXTRA_CMAKE_INCLUDES}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_extra_cmake_statements + "${arg_EXTRA_CMAKE_STATEMENTS}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_tool_targets + "${arg_TOOL_TARGETS}") + + set_property(GLOBAL PROPERTY _qt_standalone_tool_package_${id}_tool_package_dependencies + "${arg_TOOL_PACKAGE_DEPENDENCIES}") +endfunction() + +# Creates a FooTools package with information about tool targets. +# The source of information for the created package is taken either from a Qt module and its +# tool targets, or from global properties associated with a standalone tools package. function(qt_export_tools module_name) + set(opt_args "") + set(single_args + MODULE_NAME + PACKAGE_BASE_NAME + ) + set(multi_args "") + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + # Bail out when not building tools. if(NOT QT_WILL_BUILD_TOOLS) return() endif() - # If no tools were defined belonging to this module, don't create a config and targets file. - if(NOT "${module_name}" IN_LIST QT_KNOWN_MODULES_WITH_TOOLS) - return() + if(NOT arg_MODULE_NAME AND NOT arg_PACKAGE_BASE_NAME) + message(FATAL_ERROR "Either MODULE_NAME or PACKAGE_BASE_NAME must be specified") endif() - # The tools target name. For example: CoreTools - set(target "${module_name}Tools") + # Qt module case. + if(arg_MODULE_NAME) + # The tools package name. For example: CoreTools + set(module_name "${arg_MODULE_NAME}") + + # If no tools were defined belonging to this module, don't create a config and targets file. + if(NOT "${module_name}" IN_LIST QT_KNOWN_MODULES_WITH_TOOLS) + return() + endif() + + set(package_name "${module_name}Tools") + set(known_tools ${QT_KNOWN_MODULE_${module_name}_TOOLS}) + endif() + + # Standalone package case. + if(arg_PACKAGE_BASE_NAME) + set(package_name "${arg_PACKAGE_BASE_NAME}Tools") + + set(id "${arg_PACKAGE_BASE_NAME}") + + get_cmake_property(standalone_tools_package_version + _qt_standalone_tool_package_${id}_version) + if(NOT standalone_tools_package_version) + message(FATAL_ERROR "Missing version for standalone tools package ${id}") + endif() + + get_cmake_property(standalone_tools_list_dir + _qt_standalone_tool_package_${id}_list_dir) + if(NOT standalone_tools_list_dir) + set(standalone_tools_list_dir "") + endif() + + get_cmake_property(package_extra_cmake_files + _qt_standalone_tool_package_${id}_extra_cmake_files) + if(NOT package_extra_cmake_files) + set(package_extra_cmake_files "") + endif() - set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${target}") + get_cmake_property(package_extra_cmake_includes + _qt_standalone_tool_package_${id}_extra_cmake_includes) + if(NOT package_extra_cmake_includes) + set(package_extra_cmake_includes "") + endif() + + get_cmake_property(package_extra_cmake_statements + _qt_standalone_tool_package_${id}_extra_cmake_statements) + if(NOT package_extra_cmake_statements) + set(package_extra_cmake_statements "") + endif() + + get_cmake_property(known_tools _qt_standalone_tool_package_${id}_tool_targets) + if(NOT known_tools) + set(known_tools "") + endif() + + get_cmake_property(tool_package_dependencies + _qt_standalone_tool_package_${id}_tool_package_dependencies) + if(NOT tool_package_dependencies) + set(tool_package_dependencies "") + endif() + + get_cmake_property(third_party_package_dependencies + _qt_standalone_tool_package_${id}_third_party_dependencies) + if(NOT third_party_package_dependencies) + set(third_party_package_dependencies "") + endif() + + unset(id) + endif() + + # This is used as a substitution variable, ala @target@ in the config / dependencies / + # etc files. + set(target ${package_name}) + + set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${package_name}") qt_path_join(config_build_dir ${QT_CONFIG_BUILD_DIR} ${path_suffix}) qt_path_join(config_install_dir ${QT_CONFIG_INSTALL_DIR} ${path_suffix}) @@ -395,7 +555,16 @@ function(qt_export_tools module_name) set(first_tool_package_version "") - set(known_tools ${QT_KNOWN_MODULE_${module_name}_TOOLS}) + if(package_extra_cmake_files) + list(APPEND extra_cmake_files ${package_extra_cmake_files}) + foreach(cmake_file ${package_extra_cmake_files}) + file(COPY "${cmake_file}" DESTINATION "${config_build_dir}") + endforeach() + endif() + + if(package_extra_cmake_includes) + list(APPEND extra_cmake_includes ${package_extra_cmake_includes}) + endif() foreach(tool_name IN LISTS known_tools) # Specific tools can have package dependencies. @@ -429,9 +598,10 @@ function(qt_export_tools module_name) if (QT_WILL_RENAME_TOOL_TARGETS) string(REGEX REPLACE "_native$" "" tool_name ${tool_name}) endif() - # `__qt_${target}_targets_file_included` is defined in the QtModuleToolsConfig.cmake.in + # `__qt_${package_name}_targets_file_included` is defined in the + # QtModuleToolsConfig.cmake.in set(extra_cmake_statements "${extra_cmake_statements} -if(__qt_${target}_targets_file_included AND ${INSTALL_CMAKE_NAMESPACE}${target}_FOUND) +if(__qt_${package_name}_targets_file_included AND ${INSTALL_CMAKE_NAMESPACE}${package_name}_FOUND) __qt_internal_promote_target_to_global(${INSTALL_CMAKE_NAMESPACE}::${tool_name}) endif() ") @@ -446,29 +616,64 @@ endif() endif() endforeach() - string(APPEND extra_cmake_statements -"set(${QT_CMAKE_EXPORT_NAMESPACE}${module_name}Tools_TARGETS \"${tool_targets}\")") + if(tool_targets) + string(APPEND extra_cmake_statements +"set(${QT_CMAKE_EXPORT_NAMESPACE}${package_name}Tools_TARGETS \"${tool_targets}\")") + endif() + + if(package_extra_cmake_statements) + foreach(statement ${package_extra_cmake_statements}) + string(APPEND extra_cmake_statements "\n${statement}") + endforeach() + endif() # Extract package dependencies that were determined in QtPostProcess, but only if ${module_name} # is an actual target. # module_name can be a non-existent target, if the tool doesn't have an existing associated # module, e.g. qtwaylandscanner. - if(TARGET "${module_name}") + if(module_name AND TARGET "${module_name}") get_target_property(module_package_deps "${module_name}" _qt_tools_package_deps) if(module_package_deps) list(APPEND package_deps "${module_package_deps}") endif() endif() - # Configure and install dependencies file for the ${module_name}Tools package. + if(tool_package_dependencies) + list(APPEND package_deps "${tool_package_dependencies}") + endif() + + if(third_party_package_dependencies) + foreach(third_party_dep IN LISTS third_party_package_dependencies) + list(GET third_party_dep 0 third_party_dep_name) + list(GET third_party_dep 1 third_party_dep_version) + + # Assume that all tool thirdparty deps are mandatory. + # TODO: Components are not supported + list(APPEND third_party_deps + "${third_party_dep_name}\\\;FALSE\\\;${third_party_dep_version}\\\;\\\;") + endforeach() + endif() + + # Generate ConfigExtras.cmake if a template exists. Include it first. + set(config_extras_path_in + "${standalone_tools_list_dir}/${path_suffix}ConfigExtras.cmake.in") + if(EXISTS "${config_extras_path_in}") + configure_file("${config_extras_path_in}" + "${config_build_dir}/${path_suffix}ConfigExtras.cmake" + @ONLY) + list(PREPEND extra_cmake_files "${config_build_dir}/${path_suffix}ConfigExtras.cmake") + list(PREPEND extra_cmake_includes "${path_suffix}ConfigExtras.cmake") + endif() + + # Configure and install dependencies file for the ${package_name}Tools package. configure_file( "${QT_CMAKE_DIR}/QtModuleToolsDependencies.cmake.in" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Dependencies.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}Dependencies.cmake" @ONLY ) qt_install(FILES - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Dependencies.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}Dependencies.cmake" DESTINATION "${config_install_dir}" COMPONENT Devel ) @@ -481,29 +686,43 @@ endif() ) endif() - # Configure and install the ${module_name}Tools package Config file. + # Used in the template. + set(QT_SHOULD_INCLUDE_TARGETS_CODE FALSE) + if(known_tools) + set(QT_SHOULD_INCLUDE_TARGETS_CODE TRUE) + endif() + + # Configure and install the ${package_name}Tools package Config file. qt_internal_get_min_new_policy_cmake_version(min_new_policy_version) qt_internal_get_max_new_policy_cmake_version(max_new_policy_version) configure_package_config_file( "${QT_CMAKE_DIR}/QtModuleToolsConfig.cmake.in" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}Config.cmake" INSTALL_DESTINATION "${config_install_dir}" ) - qt_configure_file( - OUTPUT "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}TargetsPrecheck.cmake" - CONTENT + if(known_tools) + qt_configure_file( + OUTPUT + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}TargetsPrecheck.cmake" + CONTENT "_qt_internal_should_include_targets( TARGETS ${known_tools} NAMESPACE ${INSTALL_CMAKE_NAMESPACE}:: - OUT_VAR_SHOULD_SKIP __qt_${target}_skip_include_targets_file + OUT_VAR_SHOULD_SKIP __qt_${package_name}_skip_include_targets_file ) ") + endif() - # 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}") + # There are multiple sources where a tools' package version can come from. + # For a standalone tools package, it is explicitly given. + # For a package associated with a Qt module, it comes from a module. + # For a package without a Qt module, like WaylandScannerTools, + # we'll use the package version of the first tool that belongs to that package. + if(standalone_tools_package_version) + # Use the explicitly given package version. + set(tools_package_version "${standalone_tools_package_version}") + elseif(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}") @@ -513,56 +732,63 @@ endif() set(tools_package_version "${PROJECT_VERSION}") if(FEATURE_developer_build) message(WARNING - "Could not determine package version of tools package ${module_name}. " + "Could not determine package version of tools package ${package_name}. " "Defaulting to project version ${PROJECT_VERSION}.") endif() endif() message(TRACE - "Exporting tools package ${module_name}Tools with package version ${tools_package_version}" + "Exporting tools package ${package_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" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}ConfigVersionImpl.cmake" VERSION "${tools_package_version}" COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT ) qt_internal_write_qt_package_version_file( - "${INSTALL_CMAKE_NAMESPACE}${target}" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake" + "${INSTALL_CMAKE_NAMESPACE}${package_name}" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}ConfigVersion.cmake" ) qt_install(FILES - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}TargetsPrecheck.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}Config.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}ConfigVersion.cmake" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}ConfigVersionImpl.cmake" DESTINATION "${config_install_dir}" COMPONENT Devel ) - set(export_name "${INSTALL_CMAKE_NAMESPACE}${target}Targets") - qt_install(EXPORT "${export_name}" - NAMESPACE "${QT_CMAKE_EXPORT_NAMESPACE}::" - DESTINATION "${config_install_dir}") - - qt_internal_export_additional_targets_file( - TARGETS ${QT_KNOWN_MODULE_${module_name}_TOOLS} - TARGET_EXPORT_NAMES ${tool_targets} - EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target} - CONFIG_INSTALL_DIR "${config_install_dir}") + if(known_tools) + qt_install(FILES + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}TargetsPrecheck.cmake" + DESTINATION "${config_install_dir}" + COMPONENT Devel + ) - # Create versionless targets file. - configure_file( - "${QT_CMAKE_DIR}/QtModuleToolsVersionlessTargets.cmake.in" - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}VersionlessTargets.cmake" - @ONLY - ) + set(export_name "${INSTALL_CMAKE_NAMESPACE}${package_name}Targets") + qt_install(EXPORT "${export_name}" + NAMESPACE "${QT_CMAKE_EXPORT_NAMESPACE}::" + DESTINATION "${config_install_dir}") + + qt_internal_export_additional_targets_file( + TARGETS ${known_tools} + TARGET_EXPORT_NAMES ${tool_targets} + EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${package_name} + CONFIG_INSTALL_DIR "${config_install_dir}") + + # Create versionless targets file. + configure_file( + "${QT_CMAKE_DIR}/QtModuleToolsVersionlessTargets.cmake.in" + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}VersionlessTargets.cmake" + @ONLY + ) - qt_install(FILES - "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}VersionlessTargets.cmake" - DESTINATION "${config_install_dir}" - COMPONENT Devel - ) + qt_install(FILES + "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${package_name}VersionlessTargets.cmake" + DESTINATION "${config_install_dir}" + COMPONENT Devel + ) + endif() endfunction() # Returns the target name for the tool with the given name. diff --git a/cmake/QtVcpkgManifestHelpers.cmake b/cmake/QtVcpkgManifestHelpers.cmake new file mode 100644 index 00000000000..1b556610915 --- /dev/null +++ b/cmake/QtVcpkgManifestHelpers.cmake @@ -0,0 +1,354 @@ +# CMake API for generating vcpkg.json manifest files + +# Initialize the internal vcpkg manifest data with the given JSON string. +# +# This function is called internally by qt_vcpkg_manifest_init and may be used to set the internal +# JSON data to a JSON string read from a file. +function(qt_vcpkg_set_internal_manifest_data json) + set_property(GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON "${json}") +endfunction() + +# Initialize a new vcpkg manifest with basic package information. +# +# This function creates the foundation for a vcpkg.json manifest file by setting up +# the basic structure with package name and version. It must be called before any +# other qt_vcpkg_* functions. +# +# Arguments: +# NAME - Required. The package name for the vcpkg manifest +# VERSION - Optional. The package version string +# BUILTIN_BASELINE - Optional. Baseline for version resolution. +function(qt_vcpkg_manifest_init) + set(no_value_options "") + set(single_value_options + BUILTIN_BASELINE + NAME + VERSION + ) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + if(NOT DEFINED arg_NAME) + message(FATAL_ERROR "qt_vcpkg_manifest_init: NAME is required") + endif() + + string(JSON manifest_json SET "{}" name "\"${arg_NAME}\"") + string(JSON manifest_json SET "${manifest_json}" dependencies "[]") + string(JSON manifest_json SET "${manifest_json}" features "{}") + string(JSON manifest_json SET "${manifest_json}" default-features "[]") + if(DEFINED arg_VERSION) + string(JSON manifest_json SET "${manifest_json}" version "\"${arg_VERSION}\"") + endif() + if(DEFINED arg_BUILTIN_BASELINE) + string(JSON manifest_json SET "${manifest_json}" builtin-baseline + "\"${arg_BUILTIN_BASELINE}\"" + ) + endif() + + qt_vcpkg_set_internal_manifest_data("${manifest_json}") +endfunction() + +# Add a dependency to the vcpkg manifest. +# +# This function adds either a simple string dependency or a complex dependency object +# to the manifest's dependencies array or to a specific feature's dependencies. +# +# Arguments: +# dependency - Required. The name of the package dependency +# VERSION - Optional. Specific version constraint for the dependency +# PLATFORM - Optional. Platform expression to limit when dependency is used +# FEATURES - Optional. List of features to enable for this dependency +# DEFAULT_FEATURES - Optional. Boolean to set default-features. +# ADD_TO_FEATURE - Optional. Name of feature to add this dependency to. If not specified, +# adds to the manifest's root dependencies array. +# +# If VERSION, PLATFORM, FEATURES, or DEFAULT_FEATURES are specified, a complex dependency object is +# created. When FEATURES is specified, "default-features": false is automatically set. Otherwise, a +# simple string dependency is added. +# +# If ADD_TO_FEATURE is specified but the feature doesn't exist, it will be created with a default +# description. +function(qt_vcpkg_add_dependency name) + set(no_value_options "") + set(single_value_options + ADD_TO_FEATURE + DEFAULT_FEATURES + PLATFORM + VERSION + ) + set(multi_value_options + FEATURES + ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR "qt_vcpkg_add_dependency: Must call qt_vcpkg_manifest_init first") + endif() + + if(arg_ADD_TO_FEATURE) + # Check if feature exists, create with default description if not + string(JSON feature_obj ERROR_VARIABLE feature_error GET "${manifest_json}" features + ${arg_ADD_TO_FEATURE} + ) + if(feature_error) + string(JSON feature_obj SET "{}" description "\"Use ${arg_ADD_TO_FEATURE}\"") + endif() + + string(JSON deps_exists ERROR_VARIABLE deps_error GET "${feature_obj}" dependencies) + if(deps_error) + string(JSON feature_obj SET "${feature_obj}" dependencies "[]") + endif() + + set(target_obj "${feature_obj}") + set(target_path "features" "${arg_ADD_TO_FEATURE}") + else() + set(target_obj "${manifest_json}") + set(target_path "") + endif() + + string(JSON deps_length LENGTH "${target_obj}" dependencies) + if(DEFINED arg_VERSION OR DEFINED arg_FEATURES OR DEFINED arg_PLATFORM + OR DEFINED arg_DEFAULT_FEATURES) + string(JSON dep_obj SET "{}" name "\"${name}\"") + + if(arg_VERSION) + string(JSON dep_obj SET "${dep_obj}" version>= "\"${arg_VERSION}\"") + endif() + + if(arg_FEATURES) + if(NOT DEFINED arg_DEFAULT_FEATURES) + set(arg_DEFAULT_FEATURES OFF) + endif() + string(JSON dep_obj SET "${dep_obj}" features "[]") + set(feature_index 0) + foreach(feature ${arg_FEATURES}) + string(JSON dep_obj SET "${dep_obj}" features ${feature_index} "\"${feature}\"") + math(EXPR feature_index "${feature_index} + 1") + endforeach() + endif() + + if(DEFINED arg_DEFAULT_FEATURES) + if(arg_DEFAULT_FEATURES) + string(JSON dep_obj SET "${dep_obj}" default-features true) + else() + string(JSON dep_obj SET "${dep_obj}" default-features false) + endif() + endif() + + if(arg_PLATFORM) + string(JSON dep_obj SET "${dep_obj}" platform "\"${arg_PLATFORM}\"") + endif() + + string(JSON target_obj SET "${target_obj}" dependencies ${deps_length} "${dep_obj}") + else() + string(JSON target_obj SET "${target_obj}" dependencies ${deps_length} "\"${name}\"") + endif() + + if(arg_ADD_TO_FEATURE) + string(JSON manifest_json SET "${manifest_json}" features ${arg_ADD_TO_FEATURE} + "${target_obj}" + ) + else() + set(manifest_json "${target_obj}") + endif() + + set_property(GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON "${manifest_json}") +endfunction() + +# Add a feature definition to the vcpkg manifest. +# +# This function creates or updates a basic feature in the manifest with the specified +# description and metadata. It does not modify feature dependencies - use the dedicated +# dependency functions for that purpose. +# +# Arguments: +# name - Required. The feature name +# description - Required. Human-readable description of the feature +# DEFAULT - Optional flag. If set, feature is added to default-features +# SUPPORTS - Optional. Platform expression defining where feature is supported +# +# The function preserves existing dependencies when updating an existing feature. +function(qt_vcpkg_feature name description) + set(no_value_options DEFAULT) + set(single_value_options SUPPORTS) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 2 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR "qt_vcpkg_feature: Must call qt_vcpkg_manifest_init first") + endif() + + string(JSON feature_obj ERROR_VARIABLE feature_error GET "${manifest_json}" features ${name}) + if(feature_error) + set(feature_obj "{}") + endif() + string(JSON feature_obj SET "${feature_obj}" description "\"${description}\"") + + if(arg_SUPPORTS) + string(JSON feature_obj SET "${feature_obj}" supports "\"${arg_SUPPORTS}\"") + endif() + + string(JSON manifest_json SET "${manifest_json}" features ${name} "${feature_obj}") + + if(arg_DEFAULT) + string(JSON default_features_length LENGTH "${manifest_json}" default-features) + string(JSON manifest_json SET "${manifest_json}" default-features ${default_features_length} + "\"${name}\"" + ) + endif() + + set_property(GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON "${manifest_json}") +endfunction() + +# Add feature dependencies to an existing feature. +# +# This function creates self-referential dependencies where one feature in a package +# depends on other features in the same package. +# +# Arguments: +# feature_name - Required. Name of the feature to modify +# dependency_features - Required. List of features this feature depends on +# +# Creates a dependency object with the package's own name and the specified features, +# with "default-features": false to only enable the specified features. +function(qt_vcpkg_add_feature_dependencies feature_name) + set(dependency_features ${ARGN}) + + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR + "qt_vcpkg_add_feature_dependencies: Must call qt_vcpkg_manifest_init first" + ) + endif() + + string(JSON package_name GET "${manifest_json}" name) + qt_vcpkg_add_dependency(${package_name} ADD_TO_FEATURE ${feature_name} + FEATURES ${dependency_features} + ) +endfunction() + +# Write the vcpkg manifest to a file. +# +# This function serializes the current manifest JSON structure to the specified +# output file. +# +# Arguments: +# output_file - Required. Path where the vcpkg.json manifest should be written +# +# The function removes empty top-level elements from the manifest before writing +# to avoid cluttering the output file. +function(qt_vcpkg_write_manifest output_file) + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR "qt_vcpkg_write_manifest: Must call qt_vcpkg_manifest_init first") + endif() + + # Remove empty elements + foreach(element_name IN ITEMS dependencies features default-features) + string(JSON element_length LENGTH "${manifest_json}" "${element_name}") + if(element_length EQUAL 0) + string(JSON manifest_json REMOVE "${manifest_json}" "${element_name}") + endif() + endforeach() + + file(CONFIGURE OUTPUT "${output_file}" CONTENT "${manifest_json}") + message(STATUS "Generated vcpkg manifest: ${output_file}") +endfunction() + +# Return the default-features of the current manifest in out_var. +# +# This function reads the default-features from the internal manifest data and +# writes their names to out_var. +function(qt_vcpkg_get_default_features out_var) + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR "qt_vcpkg_get_default_features: Must call qt_vcpkg_manifest_init first") + endif() + + set("${out_var}" "" PARENT_SCOPE) + string(JSON default_features ERROR_VARIABLE json_error GET "${manifest_json}" default-features) + if(json_error) + return() + endif() + string(JSON default_features_length ERROR_VARIABLE json_error LENGTH "${default_features}") + if(json_error) + return() + endif() + + set(result "") + math(EXPR max_i "${default_features_length} - 1") + foreach(i RANGE 0 ${max_i}) + string(JSON element ERROR_VARIABLE json_error GET "${default_features}" "${i}") + if(json_error) + continue() + endif() + string(JSON element_type ERROR_VARIABLE json_error TYPE "${default_features}" "${i}") + if(json_error) + continue() + endif() + + set(feature "") + if(element_type STREQUAL "STRING") + set(feature "${element}") + elseif(element_type STREQUAL "OBJECT") + string(JSON feature ERROR_VARIABLE json_error GET "${element}" "name") + if(json_error) + continue() + endif() + endif() + + list(APPEND result "${feature}") + endforeach() + + set("${out_var}" "${result}" PARENT_SCOPE) +endfunction() + +# Return the names of all features in the current manifest in out_var. +# +# This function reads the features map from the current manifest and returns its keys +# (the feature names) in out_var. +function(qt_vcpkg_get_features out_var) + get_property(manifest_json GLOBAL PROPERTY _QT_VCPKG_MANIFEST_JSON) + if(NOT manifest_json) + message(FATAL_ERROR "qt_vcpkg_get_features: Must call qt_vcpkg_manifest_init first") + endif() + + set("${out_var}" "" PARENT_SCOPE) + string(JSON features_obj ERROR_VARIABLE json_error GET "${manifest_json}" features) + if(json_error) + return() + endif() + string(JSON features_length ERROR_VARIABLE json_error LENGTH "${features_obj}") + if(json_error) + return() + endif() + + set(result "") + math(EXPR max_i "${features_length} - 1") + foreach(i RANGE 0 ${max_i}) + string(JSON feature ERROR_VARIABLE json_error MEMBER "${features_obj}" ${i}) + if(json_error) + continue() + endif() + list(APPEND result "${feature}") + endforeach() + + set("${out_var}" "${result}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtWrapperScriptHelpers.cmake b/cmake/QtWrapperScriptHelpers.cmake index f76d52bca91..95380386300 100644 --- a/cmake/QtWrapperScriptHelpers.cmake +++ b/cmake/QtWrapperScriptHelpers.cmake @@ -98,6 +98,19 @@ export CMAKE_GENERATOR=Xcode endif() file(TO_NATIVE_PATH "${__relative_path_to_cmake_scripts_dir}" __relative_path_to_cmake_scripts_dir) + + # Store arguments we want to forward from configure to qt-configure-module arguments in a + # side-car file. If there's nothing to forward, create an empty file. + set(initial_configure_args "") + if(QT_USE_VCPKG) + list(APPEND initial_configure_args -vcpkg) + endif() + set(side_car_file_name "qt-configure-module-flags.txt") + set(side_car_file_path "${__GlobalConfig_build_dir}/${side_car_file_name}") + string(JOIN "\n" side_car_file_content "${initial_configure_args}") + file(CONFIGURE OUTPUT "${side_car_file_path}" CONTENT "${side_car_file_content}\n") + qt_copy_or_install(FILES "${side_car_file_path}" DESTINATION "${__GlobalConfig_install_dir}") + if(generate_unix) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-configure-module.in" "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-configure-module" @ONLY |
