CMake: Add qt 'policy' support

This mimics CMake's policy support. The policy state is stored in an
internal __QT_INTERNAL_POLICY_<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 <marc.mutz@qt.io>
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
bb10
Fabian Kosmale 2022-12-06 21:07:38 +01:00 committed by Marc Mutz
parent 8aae821b5a
commit b9c80ecc08
5 changed files with 186 additions and 1 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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_<policy_name>)
\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
*/

View File

@ -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

View File

@ -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
*/