diff options
Diffstat (limited to 'cmake')
| -rw-r--r-- | cmake/QtBuildHelpers.cmake | 2 | ||||
| -rw-r--r-- | cmake/QtBuildRepoHelpers.cmake | 30 | ||||
| -rw-r--r-- | cmake/QtFeature.cmake | 18 | ||||
| -rw-r--r-- | cmake/QtFindPackageHelpers.cmake | 81 | ||||
| -rw-r--r-- | cmake/QtModuleToolsConfig.cmake.in | 14 | ||||
| -rw-r--r-- | cmake/QtPostProcessHelpers.cmake | 44 | ||||
| -rw-r--r-- | cmake/QtProcessConfigureArgs.cmake | 313 | ||||
| -rw-r--r-- | cmake/QtStandaloneTestsConfig.cmake.in | 4 | ||||
| -rw-r--r-- | cmake/QtTargetHelpers.cmake | 6 | ||||
| -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, 1152 insertions, 88 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 5961424c38b..05876c1ad6d 100644 --- a/cmake/QtBuildRepoHelpers.cmake +++ b/cmake/QtBuildRepoHelpers.cmake @@ -732,6 +732,7 @@ macro(qt_internal_find_standalone_parts_qt_packages standalone_parts_args_var_na 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}" @@ -743,6 +744,32 @@ macro(qt_internal_find_standalone_parts_qt_packages standalone_parts_args_var_na # 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} @@ -755,8 +782,11 @@ macro(qt_internal_find_standalone_parts_qt_packages standalone_parts_args_var_na 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 diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index 9bbf6e700b4..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() 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 b8e46085a98..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) @@ -835,9 +840,11 @@ function(qt_internal_create_config_file_for_standalone_tests) endif() endforeach() - # 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 modules) + get_cmake_property(tool_package_base_names _qt_standalone_tool_packages) + + # 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() @@ -846,7 +853,22 @@ function(qt_internal_create_config_file_for_standalone_tests) qt_internal_get_standalone_parts_config_file_name(tests_config_file_name) # Substitution variables. - list(JOIN modules "\n " QT_MODULE_PACKAGES) + 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 9d548d14699..47ee7e8353a 100644 --- a/cmake/QtStandaloneTestsConfig.cmake.in +++ b/cmake/QtStandaloneTestsConfig.cmake.in @@ -2,7 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause set(__standalone_parts_qt_packages_args - QT_MODULE_PACKAGES - @QT_MODULE_PACKAGES@ +@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 397628ba11a..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}) 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 |
