diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2024-01-22 18:11:31 +0100 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2024-01-26 14:51:35 +0100 |
commit | 49902cc6ce228c9365c54b0dbe777ae63720310c (patch) | |
tree | 072ec119806999c4c8f3f0af64f3560a2a7b1e9d /cmake | |
parent | 783c6c7f8790f54bd2247026a1594f48efbfbe19 (diff) |
CMake: Simplify default CMAKE_BUILD_TYPE logic
Previously we had four-ish locations where the CMAKE_BUILD_TYPE was
force set.
Twice in QtBuildInternalsExtra.cmake via
qt_internal_force_set_cmake_build_type_conditionally(), depending on
some conditions. This was executed right at
find_package(Qt6 COMPONENTS BuildInternals)
time.
And twice in qt_internal_set_default_build_type() via
qt_build_repo_begin() / qt_prepare_standalone_project() that goes
through QtSetup.cmake. This was executed only if the relevant functions
were called, rather than directly at find_package() time.
The exact logic of which build type ended up being set was very
confusing.
Refactor the code to decide the build type in one single location
when qt_build_repo_begin() / qt_prepare_standalone_project() are
called, rather than directly at find_package() time.
The actual logic when we override the build type depends on many
factors:
- when an explicit CMAKE_BUILD_TYPE is given, honor it, unless it's
a multi-config build
- when it's a multi-config build, don't set any CMAKE_BUILD_TYPE,
use the value of CMAKE_CONFIGURATION_TYPES
- when it's a qtbase build, compute a default unless an explicit value
was given
- the default is Debug if FEATURE_developer_build is ON
- otherwise the default is Release
- when it's a top-level build, only choose a build type for qtbase
- when it's another repo build, use the original build type unless
another was given explicitly (including in a top-level build)
- when it's a standalone tests build
- if qt is multi-config, the tests will be single config, due to
various CI failure reasons, this hasn't changed
- if qt is single config, use the original unless an explicit
value was given
- when it's a single standalone test build, use the original unless
an explicit value was given
To determine when an explicit CMAKE_BUILD_TYPE was given in contrast
to when it was default initialized, we now have one single function
that uses a few heuristics.
The heuristics are needed because we can't reliably determine an
explicitly given 'Debug' build on Windows, because CMake default
initializes to that.
The heuristics include:
- checking whether CMAKE_BUILD_TYPE_INIT is different from
CMAKE_BUILD_TYPE
- checking what the CMAKE_BUILD_TYPE was before the first project()
call when CMake default initializes
- we save the previous value in the qt.toolchain.cmake file
- also in QtAutoDetect during qtbase configuration
- also when building the sqldrivers project
- honoring the value of QT_NO_FORCE_SET_CMAKE_BUILD_TYPE
As a result of the above changes, the build type will be set exactly
zero or one times, for a particular build directory.
Note that the configure script also has some logic on which
CMAKE_BUILD_TYPE / CMAKE_CONFIGURATION_TYPES to pass to CMake
depending on whether -debug / -release / -debug-and-release /
-force-debug-info were passed. But once the values are passed,
CMake will honor them.
Amends 48841c34d2e86a741ec9992b9704c0fa5973503c
Amends 8c912cddebe544010e7da3f87af5b21f3328d7ec
Pick-to: 6.7
Task-number: QTBUG-114958
Task-number: QTBUG-120436
Change-Id: I30db14d1e8e9ff9bd2d7ea1d2256cdeb9493ca0d
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'cmake')
-rw-r--r-- | cmake/QtBuildHelpers.cmake | 4 | ||||
-rw-r--r-- | cmake/QtBuildInternalsExtra.cmake.in | 38 | ||||
-rw-r--r-- | cmake/QtBuildOptionsHelpers.cmake | 143 | ||||
-rw-r--r-- | cmake/QtPostProcessHelpers.cmake | 14 |
4 files changed, 122 insertions, 77 deletions
diff --git a/cmake/QtBuildHelpers.cmake b/cmake/QtBuildHelpers.cmake index 26c2baa5aa..dc83954e48 100644 --- a/cmake/QtBuildHelpers.cmake +++ b/cmake/QtBuildHelpers.cmake @@ -381,13 +381,13 @@ macro(qt_internal_setup_build_and_global_variables) qt_internal_compute_features_from_possible_inputs() # Depends on qt_internal_compute_features_from_possible_inputs - qt_internal_set_default_build_type() + qt_internal_set_cmake_build_type() qt_internal_set_message_log_level(CMAKE_MESSAGE_LOG_LEVEL) qt_internal_unset_extra_build_internals_vars() qt_internal_get_generator_is_multi_config() - # Depends on qt_internal_set_default_build_type + # Depends on qt_internal_set_cmake_build_type qt_internal_setup_cmake_config_postfix() qt_internal_setup_position_independent_code() diff --git a/cmake/QtBuildInternalsExtra.cmake.in b/cmake/QtBuildInternalsExtra.cmake.in index c035a216f5..09db646507 100644 --- a/cmake/QtBuildInternalsExtra.cmake.in +++ b/cmake/QtBuildInternalsExtra.cmake.in @@ -132,43 +132,5 @@ if(NOT DEFINED QT_MAX_NEW_POLICY_CMAKE_VERSION) set(QT_MAX_NEW_POLICY_CMAKE_VERSION "@max_new_policy_version@") endif() -get_property(__qt_internal_extras_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - -# We want the same build type to be used when configuring all Qt repos or standalone -# tests or single tests. -# To do that, we need to force-set the CMAKE_BUILD_TYPE cache var because CMake itself -# initializes it with the value of CMAKE_BUILD_TYPE_INIT at the start of project -# configuration, so we need to override it. -# Note the value of CMAKE_BUILD_TYPE_INIT is different based on the platform, most -# Linux and macOS platforms will have it empty, but Windows platforms will have a value. -# -# We can't reliably differentiate between a value set on the command line by the user -# and one set by CMake, so we use a few heuristics: -# 1) When using a qt.toolchain.cmake file, we rely on the toolchain file to tell us -# if a value was set by the user at initial configure time. On a 2nd run there will -# always be a value in the cache, but at that point we've already set it to whatever it needs -# to be. -# 2) If a toolchain file is not used, we rely on the value of the CMake internal -# CMAKE_BUILD_TYPE_INIT variable. -# This won't work reliably on Windows where CMAKE_BUILD_TYPE_INIT is non-empty. -# -# Both cases won't handle an empty "" config set by the user, but we claim that's an -# unsupported config when building Qt. -# -# Allow an opt out when QT_NO_FORCE_SET_CMAKE_BUILD_TYPE is set. -# Finally, don't set the variable if a multi-config generator is used. This can happen -# when qtbase is built with a single config, but a test is built with a multi-config generator. -function(qt_internal_force_set_cmake_build_type_conditionally value) - # STREQUAL check needs to be expanded variables because an undefined var is not equal to an - # empty defined var. - if("${CMAKE_BUILD_TYPE}" STREQUAL "${CMAKE_BUILD_TYPE_INIT}" - AND NOT __qt_toolchain_cmake_build_type_before_project_call - AND NOT QT_NO_FORCE_SET_CMAKE_BUILD_TYPE - AND NOT __qt_internal_extras_is_multi_config) - set(CMAKE_BUILD_TYPE "${value}" CACHE STRING "Choose the type of build." FORCE) - set(__qt_build_internals_cmake_build_type "${value}" PARENT_SCOPE) - endif() -endfunction() - # Extra set of exported variables @QT_EXTRA_BUILD_INTERNALS_VARS@ diff --git a/cmake/QtBuildOptionsHelpers.cmake b/cmake/QtBuildOptionsHelpers.cmake index 1d5002bfd1..fbf2aa2b79 100644 --- a/cmake/QtBuildOptionsHelpers.cmake +++ b/cmake/QtBuildOptionsHelpers.cmake @@ -1,34 +1,129 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -macro(qt_internal_set_default_build_type) +# Try to detect if CMAKE_BUILD_TYPE is default initialized by CMake, or it was set by the user. +# +# CMake initializes CMAKE_BUILD_TYPE to the value of CMAKE_BUILD_TYPE_INIT during the first +# project() call if CMAKE_BUILD_TYPE is empty. +# +# Unfortunately on most Windows platforms, it defaults to 'Debug', so we can't differentiate +# between a 'Debug' value set on the command line by the user, a value set by the project, or if it +# was default initialized. +# We need to rely on heuristics to determine that. +# +# We try to check the value of CMAKE_BUILD_TYPE before the first project() call by inspecting +# various variables: +# 1) When using a qt.toolchain.cmake file, we rely on the toolchain file to tell us +# if a value was set by the user at initial configure time via the +# __qt_toolchain_cmake_build_type_before_project_call variable. On a 2nd run there will +# always be a value in the cache, but at that point we've already set it to whatever it needs +# to be. +# 2) Whe configuring qtbase, a top-level qt, or a standalone project we rely on one of the following +# variables being set: +# - __qt_auto_detect_cmake_build_type_before_project_call (e.g for qtbase) +# - __qt_internal_standalone_project_cmake_build_type_before_project_call (e.g for sqldrivers) +# 3) When using a multi-config generator, we assume that the CMAKE_BUILD_TYPE is not default +# initialized. +# 4) The user can also force the build type to be considered non-default-initialized by setting +# QT_NO_FORCE_SET_CMAKE_BUILD_TYPE to TRUE. It has weird naming that doesn't quite correspond +# to the meaning, but it's been called like that for a while now and I'm hesitant to change +# the name in case it's used by various projects. +# +# The code doesn't handle an empty "" config set by the user, but we claim that's an +# unsupported config when building Qt. +function(qt_internal_is_cmake_build_type_default_initialized_heuristic out_var) + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + get_cmake_property(aready_force_set _qt_build_internals_cmake_build_type_set) + + if( + # Set by CMake's Platform/Windows-MSVC.cmake when CMAKE_BUILD_TYPE is empty + # The STREQUAL check needs to have expanded variables because an undefined var is not equal + # to an empty defined var. + "${CMAKE_BUILD_TYPE}" STREQUAL "${CMAKE_BUILD_TYPE_INIT}" + + # Set by qt_internal_force_set_cmake_build_type() + AND aready_force_set MATCHES "NOTFOUND" + + # Set by qt_auto_detect_cmake_build_type() + AND NOT __qt_auto_detect_cmake_build_type_before_project_call + + # Set by sqldrivers project + AND NOT __qt_internal_standalone_project_cmake_build_type_before_project_call + + # Set by qt.toolchain.cmake + AND NOT __qt_toolchain_cmake_build_type_before_project_call + + # Set by user explicitily + AND NOT QT_NO_FORCE_SET_CMAKE_BUILD_TYPE + + # Set in multi-config builds + AND NOT is_multi_config) + + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(qt_internal_force_set_cmake_build_type value) + cmake_parse_arguments(PARSE_ARGV 1 arg + "SHOW_MESSAGE" + "" + "" + ) + _qt_internal_validate_all_args_are_parsed(arg) + + set(CMAKE_BUILD_TYPE "${value}" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") # Set the possible values for cmake-gui. + if(arg_SHOW_MESSAGE) + message(STATUS "Force setting build type to '${value}'.") + endif() + set_property(GLOBAL PROPERTY _qt_build_internals_cmake_build_type_set "${value}") +endfunction() + +# Only override the build type if it was default initialized by CMake. +function(qt_internal_force_set_cmake_build_type_if_cmake_default_initialized value) + qt_internal_is_cmake_build_type_default_initialized_heuristic(is_default_cmake_build_type) + if(is_default_cmake_build_type) + qt_internal_force_set_cmake_build_type("${value}" SHOW_MESSAGE) + endif() +endfunction() + +function(qt_internal_set_cmake_build_type) + # When building standalone tests against a multi-config Qt, we want to configure the tests with + # the first multi-config configuration, rather than use CMake's default configuration. + # In the case of Windows, we definitely don't want it to default to Debug, because that causes + # issues in the CI. + if(QT_BUILD_STANDALONE_TESTS AND QT_MULTI_CONFIG_FIRST_CONFIG) + qt_internal_force_set_cmake_build_type_if_cmake_default_initialized( + "${QT_MULTI_CONFIG_FIRST_CONFIG}") + + # We want the same build type to be used when configuring all Qt repos or standalone + # tests or single tests, so we reuse the initial build type set by qtbase. + # __qt_internal_initial_qt_cmake_build_type is saved in QtBuildInternalsExtra.cmake.in. + elseif(__qt_internal_initial_qt_cmake_build_type) + qt_internal_force_set_cmake_build_type_if_cmake_default_initialized( + "${__qt_internal_initial_qt_cmake_build_type}") + + # Default to something sensible when configuring qtbase / top-level. + else() + qt_internal_set_qt_appropriate_default_cmake_build_type() + endif() +endfunction() + +# Sets a default cmake build type for qtbase / top-level. +macro(qt_internal_set_qt_appropriate_default_cmake_build_type) set(_default_build_type "Release") if(FEATURE_developer_build) set(_default_build_type "Debug") endif() - # Try to detect if an explicit CMAKE_BUILD_TYPE was set by the user. - # CMake sets CMAKE_BUILD_TYPE_INIT to Debug on most Windows platforms and doesn't set - # anything for UNIXes. CMake assigns CMAKE_BUILD_TYPE_INIT to CMAKE_BUILD_TYPE during - # the first project() call, if CMAKE_BUILD_TYPE had no previous value. - # We use extra information about the state of CMAKE_BUILD_TYPE before the first - # project() call that's set in QtAutoDetect.cmake or manually in a project via the - # __qt_internal_standalone_project_cmake_build_type_before_project_call variable (as done - # for the qtbase sqldrivers project). - # STREQUAL check needs to have expanded variables because an undefined var is not equal - # to an empty defined var. - # See also qt_internal_force_set_cmake_build_type_conditionally which is used - # to set the build type when building other repos or tests. - if("${CMAKE_BUILD_TYPE}" STREQUAL "${CMAKE_BUILD_TYPE_INIT}" - AND NOT __qt_auto_detect_cmake_build_type_before_project_call - AND NOT __qt_build_internals_cmake_build_type - AND NOT __qt_internal_standalone_project_cmake_build_type_before_project_call - AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${_default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${_default_build_type}" CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE - PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") # Set the possible values for cmake-gui. + qt_internal_is_cmake_build_type_default_initialized_heuristic(is_default_cmake_build_type) + if(is_default_cmake_build_type) + qt_internal_force_set_cmake_build_type("${_default_build_type}") + message(STATUS "Setting build type to '${_default_build_type}' as none was specified.") elseif(CMAKE_CONFIGURATION_TYPES) message(STATUS "Building for multiple configurations: ${CMAKE_CONFIGURATION_TYPES}.") message(STATUS "Main configuration is: ${QT_MULTI_CONFIG_FIRST_CONFIG}.") @@ -43,7 +138,7 @@ macro(qt_internal_set_default_build_type) ) endif() else() - message(STATUS "CMAKE_BUILD_TYPE was set to: '${CMAKE_BUILD_TYPE}'") + message(STATUS "CMAKE_BUILD_TYPE was already explicitly set to: '${CMAKE_BUILD_TYPE}'") endif() endmacro() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index b9a8d0d889..0384ee6d44 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -556,9 +556,8 @@ function(qt_generate_build_internals_extra_cmake_code) if(CMAKE_BUILD_TYPE) string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " +# Used by qt_internal_set_cmake_build_type. set(__qt_internal_initial_qt_cmake_build_type \"${CMAKE_BUILD_TYPE}\") -qt_internal_force_set_cmake_build_type_conditionally( - \"\${__qt_internal_initial_qt_cmake_build_type}\") ") endif() if(CMAKE_CONFIGURATION_TYPES) @@ -580,17 +579,6 @@ qt_internal_force_set_cmake_build_type_conditionally( string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS "\nset(QT_MULTI_CONFIG_FIRST_CONFIG \"${QT_MULTI_CONFIG_FIRST_CONFIG}\")\n") endif() - # When building standalone tests against a multi-config Qt, we want to choose the first - # configuration, rather than use CMake's default value. - # In the case of Windows, we definitely don't it to default to Debug, because that causes - # issues in the CI. - if(multi_config_specific) - string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " -if(QT_BUILD_STANDALONE_TESTS) - qt_internal_force_set_cmake_build_type_conditionally( - \"\${QT_MULTI_CONFIG_FIRST_CONFIG}\") -endif()\n") - endif() if(CMAKE_CROSS_CONFIGS) string(APPEND ninja_multi_config_specific |