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 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> (cherry picked from commit 49902cc6ce228c9365c54b0dbe777ae63720310c) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
384e56e764
commit
22ffc088e8
@ -382,13 +382,13 @@ macro(qt_internal_setup_build_and_global_variables)
|
|||||||
qt_internal_compute_features_from_possible_inputs()
|
qt_internal_compute_features_from_possible_inputs()
|
||||||
|
|
||||||
# Depends on 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_set_message_log_level(CMAKE_MESSAGE_LOG_LEVEL)
|
||||||
qt_internal_unset_extra_build_internals_vars()
|
qt_internal_unset_extra_build_internals_vars()
|
||||||
qt_internal_get_generator_is_multi_config()
|
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_cmake_config_postfix()
|
||||||
|
|
||||||
qt_internal_setup_position_independent_code()
|
qt_internal_setup_position_independent_code()
|
||||||
|
@ -132,43 +132,5 @@ if(NOT DEFINED QT_MAX_NEW_POLICY_CMAKE_VERSION)
|
|||||||
set(QT_MAX_NEW_POLICY_CMAKE_VERSION "@max_new_policy_version@")
|
set(QT_MAX_NEW_POLICY_CMAKE_VERSION "@max_new_policy_version@")
|
||||||
endif()
|
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
|
# Extra set of exported variables
|
||||||
@QT_EXTRA_BUILD_INTERNALS_VARS@
|
@QT_EXTRA_BUILD_INTERNALS_VARS@
|
||||||
|
@ -1,34 +1,129 @@
|
|||||||
# Copyright (C) 2023 The Qt Company Ltd.
|
# Copyright (C) 2023 The Qt Company Ltd.
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# 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")
|
set(_default_build_type "Release")
|
||||||
if(FEATURE_developer_build)
|
if(FEATURE_developer_build)
|
||||||
set(_default_build_type "Debug")
|
set(_default_build_type "Debug")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Try to detect if an explicit CMAKE_BUILD_TYPE was set by the user.
|
qt_internal_is_cmake_build_type_default_initialized_heuristic(is_default_cmake_build_type)
|
||||||
# CMake sets CMAKE_BUILD_TYPE_INIT to Debug on most Windows platforms and doesn't set
|
if(is_default_cmake_build_type)
|
||||||
# anything for UNIXes. CMake assigns CMAKE_BUILD_TYPE_INIT to CMAKE_BUILD_TYPE during
|
qt_internal_force_set_cmake_build_type("${_default_build_type}")
|
||||||
# the first project() call, if CMAKE_BUILD_TYPE had no previous value.
|
message(STATUS "Setting build type to '${_default_build_type}' as none was specified.")
|
||||||
# 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.
|
|
||||||
elseif(CMAKE_CONFIGURATION_TYPES)
|
elseif(CMAKE_CONFIGURATION_TYPES)
|
||||||
message(STATUS "Building for multiple configurations: ${CMAKE_CONFIGURATION_TYPES}.")
|
message(STATUS "Building for multiple configurations: ${CMAKE_CONFIGURATION_TYPES}.")
|
||||||
message(STATUS "Main configuration is: ${QT_MULTI_CONFIG_FIRST_CONFIG}.")
|
message(STATUS "Main configuration is: ${QT_MULTI_CONFIG_FIRST_CONFIG}.")
|
||||||
@ -43,7 +138,7 @@ macro(qt_internal_set_default_build_type)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
else()
|
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()
|
endif()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
@ -556,9 +556,8 @@ function(qt_generate_build_internals_extra_cmake_code)
|
|||||||
if(CMAKE_BUILD_TYPE)
|
if(CMAKE_BUILD_TYPE)
|
||||||
string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS
|
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}\")
|
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()
|
endif()
|
||||||
if(CMAKE_CONFIGURATION_TYPES)
|
if(CMAKE_CONFIGURATION_TYPES)
|
||||||
@ -580,17 +579,6 @@ qt_internal_force_set_cmake_build_type_conditionally(
|
|||||||
string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS
|
string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS
|
||||||
"\nset(QT_MULTI_CONFIG_FIRST_CONFIG \"${QT_MULTI_CONFIG_FIRST_CONFIG}\")\n")
|
"\nset(QT_MULTI_CONFIG_FIRST_CONFIG \"${QT_MULTI_CONFIG_FIRST_CONFIG}\")\n")
|
||||||
endif()
|
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)
|
if(CMAKE_CROSS_CONFIGS)
|
||||||
string(APPEND ninja_multi_config_specific
|
string(APPEND ninja_multi_config_specific
|
||||||
|
@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
if (NOT CMAKE_PROJECT_NAME STREQUAL "QtBase" AND NOT CMAKE_PROJECT_NAME STREQUAL "Qt")
|
if (NOT CMAKE_PROJECT_NAME STREQUAL "QtBase" AND NOT CMAKE_PROJECT_NAME STREQUAL "Qt")
|
||||||
include(.cmake.conf)
|
include(.cmake.conf)
|
||||||
# Store initial build type (if any is specified) to be read by
|
# Store initial build type (if any is specified) to be read by
|
||||||
# qt_internal_set_default_build_type().
|
# qt_internal_set_cmake_build_type().
|
||||||
# See qt_internal_set_default_build_type() for details.
|
# See qt_internal_set_cmake_build_type() for details.
|
||||||
if(DEFINED CACHE{CMAKE_BUILD_TYPE})
|
if(DEFINED CACHE{CMAKE_BUILD_TYPE})
|
||||||
set(__qt_internal_standalone_project_cmake_build_type_before_project_call
|
set(__qt_internal_standalone_project_cmake_build_type_before_project_call
|
||||||
"${CMAKE_BUILD_TYPE}")
|
"${CMAKE_BUILD_TYPE}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user