From b9c80ecc08d98052e4757a916a8a55701be63497 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 6 Dec 2022 21:07:38 +0100 Subject: [PATCH] CMake: Add qt 'policy' support This mimics CMake's policy support. The policy state is stored in an internal __QT_INTERNAL_POLICY_ variable; by using normal variables, we gain support for stacking for free. Policies can be explicitly en- or disable via qt6_policy; that command also has a GET mode to retrieve them again. Furthermore, one can now pass min and max version to qt6_standard_project_setup, to opt in to a certain set of defaults introduced in a given Qt version. We add support for policies in QtModuleHelpers, so that we can check for known policies while building Qt itself. No actual policies exist yet; but a follow up commit will introduce one for qt_add_qml_module. Change-Id: I57a0404c9193926dd499f94cc5f73e359355c0b3 Reviewed-by: Marc Mutz Reviewed-by: Alexandru Croitor --- cmake/QtModuleHelpers.cmake | 16 +++ src/corelib/Qt6CoreMacros.cmake | 101 ++++++++++++++++++ src/corelib/doc/src/cmake/qt_policy.qdoc | 49 +++++++++ .../src/cmake/qt_standard_project_setup.qdoc | 16 ++- src/corelib/doc/src/external-resources.qdoc | 5 + 5 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/corelib/doc/src/cmake/qt_policy.qdoc diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index 9a9e3bec1d..8a8912bfb4 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -39,6 +39,7 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi EXTRA_CMAKE_INCLUDES NO_PCH_SOURCES EXTERNAL_HEADERS + POLICIES ${__default_private_args} ${__default_public_args} ${__default_private_module_args} @@ -674,6 +675,21 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") set(extra_cmake_code "") + if(arg_POLICIES) + set(policies "") + foreach(policy IN LISTS arg_POLICIES) + list(APPEND policies "set(QT_KNOWN_POLICY_${policy} TRUE)") + + # When building Qt, tests and examples might expect a policy to be known, but they + # won't be known depending on which scope or when a find_package(Module) with the + # respective policy is called. Check the global list of known policies to accommodate + # that. + set_property(GLOBAL APPEND PROPERTY _qt_global_known_policies "${policy}") + endforeach() + list(JOIN policies "\n" policies_str) + string(APPEND extra_cmake_code "${policies_str}\n") + endif() + # Generate metatypes if(${arg_GENERATE_METATYPES}) # No mention of NO_GENERATE_METATYPES. You should not use it. diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 50ed4bc0d8..1820836ff1 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -2617,6 +2617,64 @@ unset(__qt_deploy_support_files) ") endfunction() +# We basically mirror CMake's policy setup +# A policy can be set to OLD, set to NEW or unset +# unset is the default state +# +function(qt6_policy mode policy behaviorOrVariable) + # When building Qt, tests and examples might expect a policy to be known, but they won't be + # known depending on which scope or when a find_package(Module) with the respective policy + # is called. Check the global list of known policies to accommodate that. + if(QT_BUILDING_QT AND NOT DEFINED QT_KNOWN_POLICY_${policy}) + get_property(global_known_policies GLOBAL PROPERTY _qt_global_known_policies) + if(policy IN_LIST global_known_policies) + set(QT_KNOWN_POLICY_${policy} TRUE) + endif() + endif() + + if (NOT DEFINED QT_KNOWN_POLICY_${policy}) + message(FATAL_ERROR + "${policy} is not a known Qt policy. Did you include the necessary Qt module?" + ) + endif() + if (${mode} STREQUAL "SET") + set(behavior ${behaviorOrVariable}) + if (${behavior} STREQUAL "NEW" OR ${behavior} STREQUAL "OLD") + set(__QT_INTERNAL_POLICY_${policy} ${behavior} PARENT_SCOPE) + else() + message(FATAL_ERROR "Qt policies must be either set to NEW or OLD, but got ${behavior}") + endif() + else(${mode} STREQUAL "GET") + set(variable "${behaviorOrVariable}") + set("${variable}" "${__QT_INTERNAL_POLICY_${policy}}" PARENT_SCOPE) + endif() +endfunction() + +# Internal helper function; can be used in any module before doing a policy check +function(__qt_internal_setup_policy policy sinceversion policyexplanation) + if(DEFINED __QT_INTERNAL_POLICY_${policy}) + if (__QT_INTERNAL_POLICY_${policy} STREQUAL "OLD") + # policy is explicitly disabled + message(DEPRECATION + "Qt policy ${policy} is set to OLD. " + "Support for the old behavior will be removed in a future major version of Qt." + ) + endif() + #else: policy is already enabled, nothing to do + elseif (${sinceversion} VERSION_LESS_EQUAL __qt_policy_check_version) + # we cannot use the public function here as we want to set it in parent scope + set(__QT_INTERNAL_POLICY_${policy} "NEW" PARENT_SCOPE) + elseif(NOT "${QT_NO_SHOW_OLD_POLICY_WARNINGS}") + message(AUTHOR_WARNING + "Qt policy ${policy} is not set. " + "Use the qt6_set_policy command to set it and suppress this warning. " + "You can also silence all policy warnings by setting QT_NO_SHOW_OLD_POLICY_WARNINGS " + "to true.\n" + "${policyexplanation}" + ) + endif() +endfunction() + # Note this needs to be a macro because it sets variables intended for the # calling scope. macro(qt6_standard_project_setup) @@ -2625,6 +2683,46 @@ macro(qt6_standard_project_setup) # They can set this variable to true to effectively disable this function. if(NOT QT_NO_STANDARD_PROJECT_SETUP) + set(__qt_sps_args_option) + set(__qt_sps_args_single + MIN_VERSION + MAX_VERSION + ) + set(__qt_sps_args_multi) + cmake_parse_arguments(__qt_sps_arg + "${__qt_sps_args_option}" + "${__qt_sps_args_single}" + "${__qt_sps_args_multi}" + ${ARGN} + ) + + if(__qt_sps_arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + set(__qt_policy_check_version "6.0.0") + if(Qt6_VERSION_MAJOR) + set(__qt_current_version + "${Qt6_VERSION_MAJOR}.${Qt6_VERSION_MINOR}.${Qt6_VERSION_PATCH}") + elseif(QT_BUILDING_QT) + set(__qt_current_version + "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") + else() + message(FATAL_ERROR "Can not determine Qt version.") + endif() + if (__qt_sps_arg_MIN_VERSION) + if ("${__qt_current_version}" VERSION_LESS "${__qt_sps_arg_MIN_VERSION}") + message(FATAL_ERROR "Project required a Qt minimum version of ${__qt_sps_arg_MIN_VERSION}, but current version is only ${__qt_current_version}") + endif() + set(__qt_policy_check_version "${__qt_sps_arg_MIN_VERSION}") + endif() + if (__qt_sps_arg_MAX_VERSION) + if (${__qt_sps_arg_MAX_VERSION} VERSION_LESS ${__qt_sps_arg_MIN_VERSION}) + message(FATAL_ERROR "MAX_VERSION must be larger or equal than MIN_VERSION") + endif() + set(__qt_policy_check_version "${__qt_sps_arg_MAX_VERSION}") + endif() + # All changes below this point should not result in a change to an # existing value, except for CMAKE_INSTALL_RPATH which may append new # values (but no duplicates). @@ -2689,6 +2787,9 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) macro(qt_standard_project_setup) qt6_standard_project_setup(${ARGV}) endmacro() + macro(qt_policy) + qt6_policy(${ARGV}) + endmacro() endif() function(qt6_generate_deploy_script) diff --git a/src/corelib/doc/src/cmake/qt_policy.qdoc b/src/corelib/doc/src/cmake/qt_policy.qdoc new file mode 100644 index 0000000000..11e761b34a --- /dev/null +++ b/src/corelib/doc/src/cmake/qt_policy.qdoc @@ -0,0 +1,49 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +\page qt-policy.html +\ingroup cmake-commands-qtcore + +\summary {Modify the default behavior of Qt's CMake API.} + +\include cmake-find-package-core.qdocinc + +\cmakecommandsince 6.5 + +\section1 Synopsis + +\badcode +qt_policy( + [SET policy behavior] + [GET policy variable] +) +\endcode + +\versionlessCMakeCommandsNote qt6_policy() + +\section1 Description +This command has two modes: +\list +\li When the \c{SET} keyword is used, this command can be used to opt in to + behavior changes in Qt's CMake API, or to explicitly opt out of them. +\li When the \c{GET} keyword is used, \c{variable} is set to the current + value for the policy. +\endlist +\c{policyname} must be the name of a Qt cmake policy. Using an unknown policy +is an error; code supporting older Qt versions should check with +\badcode +if(QT_KNOWN_POLICY_) +\endcode +whether the policy exists before querying or setting it. + +\c{behavior} can +either be +\list +\li \c{NEW} to opt into the new behavior, or +\li \c{OLD} to explicitly opt-out of it. +\endlist + +\sa qt_standard_project_setup + +*/ diff --git a/src/corelib/doc/src/cmake/qt_standard_project_setup.qdoc b/src/corelib/doc/src/cmake/qt_standard_project_setup.qdoc index 6e91779fb9..ad6541f08a 100644 --- a/src/corelib/doc/src/cmake/qt_standard_project_setup.qdoc +++ b/src/corelib/doc/src/cmake/qt_standard_project_setup.qdoc @@ -17,7 +17,10 @@ \section1 Synopsis \badcode -qt_standard_project_setup() +qt_standard_project_setup( + [MIN_VERSION policy_min] + [MAX_VERSION policy_max] +) \endcode \versionlessCMakeCommandsNote qt6_standard_project_setup() @@ -47,6 +50,16 @@ have been defined. It does the following things: Qt-internal targets in this folder. \endlist +Moreover, since Qt 6.5 it can be used to change the default behavior of Qt's CMake +API, by opting in to changes from newer Qt versions. If \c{MIN_VERSION} is +specified, all suggested changes introduced in Qt up to \c{MIN_VERSION} are enabled, +and using an older Qt version will result in an error. +If additionally \c{MAX_VERSION} has been specified, any new changes introduced +in versions up to \c{MAX_VERSION} are also enabled (but using an older Qt +version is not an error). +This is similar to CMake's policy concept (compare \l{cmake_policy}). + + On platforms that support \c{RPATH} (other than Apple platforms), two values are appended to the \c{CMAKE_INSTALL_RPATH} variable by this command. \c{$ORIGIN} is appended so that libraries will find other libraries they depend @@ -67,6 +80,7 @@ The \c{qt_standard_project_setup()} command can effectively be disabled by setting the \l{QT_NO_STANDARD_PROJECT_SETUP} variable to true. \sa {qt6_generate_deploy_app_script}{qt_generate_deploy_app_script()} +\sa {qt6_policy} \section1 Example diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc index ecda025820..6faae09b69 100644 --- a/src/corelib/doc/src/external-resources.qdoc +++ b/src/corelib/doc/src/external-resources.qdoc @@ -131,3 +131,8 @@ \externalpage https://cmake.org/cmake/help/latest/prop_tgt/FOLDER.html \title FOLDER */ + +/*! + \externalpage https://cmake.org/cmake/help/latest/command/cmake_policy.html + \title cmake_policy +*/