summaryrefslogtreecommitdiffstats
path: root/cmake/QtFeature.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtFeature.cmake')
-rw-r--r--cmake/QtFeature.cmake777
1 files changed, 644 insertions, 133 deletions
diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake
index 870ae0d194..96cb308b2c 100644
--- a/cmake/QtFeature.cmake
+++ b/cmake/QtFeature.cmake
@@ -1,7 +1,14 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(CheckCXXCompilerFlag)
+
function(qt_feature_module_begin)
- qt_parse_all_arguments(arg "qt_feature_module_begin"
+ cmake_parse_arguments(PARSE_ARGV 0 arg
"NO_MODULE;ONLY_EVALUATE_FEATURES"
- "LIBRARY;PRIVATE_FILE;PUBLIC_FILE" "PUBLIC_DEPENDENCIES;PRIVATE_DEPENDENCIES" ${ARGN})
+ "LIBRARY;PRIVATE_FILE;PUBLIC_FILE"
+ "PUBLIC_DEPENDENCIES;PRIVATE_DEPENDENCIES")
+ _qt_internal_validate_all_args_are_parsed(arg)
if(NOT arg_ONLY_EVALUATE_FEATURES)
if ("${arg_LIBRARY}" STREQUAL "" AND (NOT ${arg_NO_MODULE}))
@@ -35,24 +42,16 @@ function(qt_feature_module_begin)
set(__QtFeature_define_definitions "" PARENT_SCOPE)
endfunction()
-function(qt_feature_normalize_name name out_var)
- # Normalize the feature name to something CMake can deal with.
- if(name MATCHES "c\\+\\+")
- string(REGEX REPLACE "[^a-zA-Z0-9_]" "x" name "${name}")
- else()
- string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" name "${name}")
- endif()
- set(${out_var} "${name}" PARENT_SCOPE)
-endfunction()
-
function(qt_feature feature)
set(original_name "${feature}")
qt_feature_normalize_name("${feature}" feature)
set_property(GLOBAL PROPERTY QT_FEATURE_ORIGINAL_NAME_${feature} "${original_name}")
- qt_parse_all_arguments(arg "qt_feature"
+ cmake_parse_arguments(PARSE_ARGV 1 arg
"PRIVATE;PUBLIC"
- "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${ARGN})
+ "LABEL;PURPOSE;SECTION"
+ "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF")
+ _qt_internal_validate_all_args_are_parsed(arg)
set(_QT_FEATURE_DEFINITION_${feature} ${ARGN} PARENT_SCOPE)
@@ -81,45 +80,25 @@ function(qt_evaluate_to_boolean expressionVar)
endif()
endfunction()
-function(qt_evaluate_config_expression resultVar)
+function(qt_internal_evaluate_config_expression resultVar outIdx startIdx)
set(result "")
- set(nestingLevel 0)
- set(skipNext OFF)
set(expression "${ARGN}")
list(LENGTH expression length)
+ math(EXPR memberIdx "${startIdx} - 1")
math(EXPR length "${length}-1")
- foreach(memberIdx RANGE ${length})
- if(${skipNext})
- set(skipNext OFF)
- continue()
- endif()
-
+ while(memberIdx LESS ${length})
+ math(EXPR memberIdx "${memberIdx} + 1")
list(GET expression ${memberIdx} member)
if("${member}" STREQUAL "(")
- if(${nestingLevel} GREATER 0)
- list(APPEND result ${member})
- endif()
- math(EXPR nestingLevel "${nestingLevel} + 1")
- continue()
+ math(EXPR memberIdx "${memberIdx} + 1")
+ qt_internal_evaluate_config_expression(sub_result memberIdx ${memberIdx} ${expression})
+ list(APPEND result ${sub_result})
elseif("${member}" STREQUAL ")")
- math(EXPR nestingLevel "${nestingLevel} - 1")
- if(nestingLevel LESS 0)
- break()
- endif()
- if(${nestingLevel} EQUAL 0)
- qt_evaluate_config_expression(result ${result})
- else()
- list(APPEND result ${member})
- endif()
- continue()
- elseif(${nestingLevel} GREATER 0)
- list(APPEND result ${member})
- continue()
+ break()
elseif("${member}" STREQUAL "NOT")
list(APPEND result ${member})
- continue()
elseif("${member}" STREQUAL "AND")
qt_evaluate_to_boolean(result)
if(NOT ${result})
@@ -144,7 +123,7 @@ function(qt_evaluate_config_expression resultVar)
set(lhs "${${lhs}}")
math(EXPR rhsIndex "${memberIdx}+1")
- set(skipNext ON)
+ set(memberIdx ${rhsIndex})
list(GET expression ${rhsIndex} rhs)
# We can't pass through an empty string with double quotes through various
@@ -164,7 +143,7 @@ function(qt_evaluate_config_expression resultVar)
list(APPEND result ${member})
endif()
- endforeach()
+ endwhile()
# The 'TARGET Gui' case is handled by qt_evaluate_to_boolean, by passing those tokens verbatim
# to if().
@@ -174,55 +153,220 @@ function(qt_evaluate_config_expression resultVar)
qt_evaluate_to_boolean(result)
endif()
+ # When in recursion, we must skip to the next closing parenthesis on nesting level 0. The outIdx
+ # must point to the matching closing parenthesis, and that's not the case if we're early exiting
+ # in AND/OR.
+ if(startIdx GREATER 0)
+ set(nestingLevel 1)
+ while(TRUE)
+ list(GET expression ${memberIdx} member)
+ if("${member}" STREQUAL ")")
+ math(EXPR nestingLevel "${nestingLevel} - 1")
+ if(nestingLevel EQUAL 0)
+ break()
+ endif()
+ elseif("${member}" STREQUAL "(")
+ math(EXPR nestingLevel "${nestingLevel} + 1")
+ endif()
+ math(EXPR memberIdx "${memberIdx} + 1")
+ endwhile()
+ endif()
+
+ set(${outIdx} ${memberIdx} PARENT_SCOPE)
set(${resultVar} ${result} PARENT_SCOPE)
endfunction()
-function(qt_feature_set_cache_value resultVar feature emit_if calculated label)
+function(qt_evaluate_config_expression resultVar)
+ qt_internal_evaluate_config_expression(result unused 0 ${ARGN})
+ set("${resultVar}" "${result}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_feature_condition_keywords out_var)
+ set(keywords "EQUAL" "LESS" "LESS_EQUAL" "GREATER" "GREATER_EQUAL" "STREQUAL" "STRLESS"
+ "STRLESS_EQUAL" "STRGREATER" "STRGREATER_EQUAL" "VERSION_EQUAL" "VERSION_LESS"
+ "VERSION_LESS_EQUAL" "VERSION_GREATER" "VERSION_GREATER_EQUAL" "MATCHES"
+ "EXISTS" "COMMAND" "DEFINED" "NOT" "AND" "OR" "TARGET" "EXISTS" "IN_LIST" "(" ")")
+ set(${out_var} "${keywords}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_dump_expression_values expression_dump expression)
+ set(dump "")
+ set(skipNext FALSE)
+ set(isTargetExpression FALSE)
+
+ _qt_internal_get_feature_condition_keywords(keywords)
+
+ list(LENGTH expression length)
+ math(EXPR length "${length}-1")
+
+ if(${length} LESS 0)
+ return()
+ endif()
+
+ foreach(memberIdx RANGE ${length})
+ if(${skipNext})
+ set(skipNext FALSE)
+ continue()
+ endif()
+
+ list(GET expression ${memberIdx} member)
+ if(NOT "${member}" IN_LIST keywords)
+ string(FIND "${member}" "QT_FEATURE_" idx)
+ if(idx EQUAL 0)
+ if(NOT DEFINED ${member})
+ list(APPEND dump "${member} not evaluated")
+ else()
+ list(APPEND dump "${member} = \"${${member}}\"")
+ endif()
+ elseif(isTargetExpression)
+ set(targetExpression "TARGET;${member}")
+ if(${targetExpression})
+ list(APPEND dump "TARGET ${member} found")
+ else()
+ list(APPEND dump "TARGET ${member} not found")
+ endif()
+ else()
+ list(APPEND dump "${member} = \"${${member}}\"")
+ endif()
+ set(isTargetExpression FALSE)
+ set(skipNext FALSE)
+ elseif("${member}" STREQUAL "TARGET")
+ set(isTargetExpression TRUE)
+ elseif("${member}" STREQUAL "STREQUAL")
+ set(skipNext TRUE)
+ else()
+ set(skipNext FALSE)
+ set(isTargetExpression FALSE)
+ endif()
+ endforeach()
+ string(JOIN "\n " ${expression_dump} ${dump})
+ set(${expression_dump} "${${expression_dump}}" PARENT_SCOPE)
+endfunction()
+
+# Stores the user provided value to FEATURE_${feature} if provided.
+# If not provided, stores ${computed} instead.
+# ${computed} is also stored when reconfiguring and the condition does not align with the user
+# provided value.
+#
+function(qt_feature_check_and_save_user_provided_value
+ resultVar feature condition condition_expression computed label)
if (DEFINED "FEATURE_${feature}")
- # Must set up the cache
- if (NOT (emit_if))
- message(FATAL_ERROR "Sanity check failed: FEATURE_${feature} that was not emitted was found in the CMakeCache.")
+ # Revisit new user provided value
+ set(user_value "${FEATURE_${feature}}")
+ string(TOUPPER "${user_value}" user_value_upper)
+ set(result "${user_value_upper}")
+
+ # If ${feature} depends on another dirty feature, reset the ${feature} value to
+ # ${computed}.
+ get_property(dirty_build GLOBAL PROPERTY _qt_dirty_build)
+ if(dirty_build)
+ _qt_internal_feature_compute_feature_dependencies(deps "${feature}")
+ if(deps)
+ get_property(dirty_features GLOBAL PROPERTY _qt_dirty_features)
+ foreach(dirty_feature ${dirty_features})
+ if(dirty_feature IN_LIST deps AND NOT "${result}" STREQUAL "${computed}")
+ set(result "${computed}")
+ message(WARNING
+ "Auto-resetting 'FEATURE_${feature}' from '${user_value_upper}' to "
+ "'${computed}', "
+ "because the dependent feature '${dirty_feature}' was marked dirty.")
+
+ # Append ${feature} as a new dirty feature.
+ set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}")
+ break()
+ endif()
+ endforeach()
+ endif()
+
+ # If the build is marked as dirty and the feature doesn't meet its condition,
+ # reset its value to the computed one, which is likely OFF.
+ if(NOT condition AND result)
+ set(result "${computed}")
+ message(WARNING "Resetting 'FEATURE_${feature}' from '${user_value_upper}' to "
+ "'${computed}' because it doesn't meet its condition after reconfiguration. "
+ "Condition expression is: '${condition_expression}'")
+ endif()
endif()
- # Revisit value:
- set(cache "${FEATURE_${feature}}")
- set(booly_values OFF NO FALSE N ON YES TRUE Y)
- if ((cache IN_LIST booly_values) OR (cache GREATER_EQUAL 0))
- set(result "${cache}")
+ set(bool_values OFF NO FALSE N ON YES TRUE Y)
+ if ((result IN_LIST bool_values) OR (result GREATER_EQUAL 0))
+ # All good!
else()
- message(FATAL_ERROR "Sanity check failed: FEATURE_${feature} has invalid value \"${cache}\"!")
+ message(FATAL_ERROR
+ "Sanity check failed: FEATURE_${feature} has invalid value \"${result}\"!")
endif()
+
# Fix-up user-provided values
- set("FEATURE_${feature}" "${cache}" CACHE BOOL "${label}")
+ set("FEATURE_${feature}" "${result}" CACHE BOOL "${label}" FORCE)
else()
# Initial setup:
- if (emit_if)
- set("FEATURE_${feature}" "${calculated}" CACHE BOOL "${label}")
- set(result "${calculated}")
- else()
- set(result OFF)
- endif()
+ set(result "${computed}")
+ set("FEATURE_${feature}" "${result}" CACHE BOOL "${label}")
endif()
set("${resultVar}" "${result}" PARENT_SCOPE)
endfunction()
-macro(qt_feature_set_value feature cache emit_if condition label)
- set(result "${cache}")
+# Saves the final user value to QT_FEATURE_${feature}, after checking that the condition is met.
+macro(qt_feature_check_and_save_internal_value
+ feature saved_user_value condition label conditionExpression)
+ if(${saved_user_value})
+ set(result ON)
+ else()
+ set(result OFF)
+ endif()
- if (NOT (condition) AND (cache))
- message(SEND_ERROR "Feature \"${feature}\": Forcing to \"${cache}\" breaks its condition.")
+ if ((NOT condition) AND result)
+ _qt_internal_dump_expression_values(conditionDump "${conditionExpression}")
+ string(JOIN " " conditionString ${conditionExpression})
+ qt_configure_add_report_error("Feature \"${feature}\": Forcing to \"${result}\" breaks its \
+condition:\n ${conditionString}\nCondition values dump:\n ${conditionDump}\n" RECORD_ON_FEATURE_EVALUATION)
endif()
if (DEFINED "QT_FEATURE_${feature}")
message(FATAL_ERROR "Feature ${feature} is already defined when evaluating configure.cmake features for ${target}.")
endif()
set(QT_FEATURE_${feature} "${result}" CACHE INTERNAL "Qt feature: ${feature}")
+
+ # Add feature to build feature collection
+ list(APPEND QT_KNOWN_FEATURES "${feature}")
+ set(QT_KNOWN_FEATURES "${QT_KNOWN_FEATURES}" CACHE INTERNAL "" FORCE)
endmacro()
+macro(_qt_internal_parse_feature_definition feature)
+ cmake_parse_arguments(arg
+ "PRIVATE;PUBLIC"
+ "LABEL;PURPOSE;SECTION;"
+ "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF"
+ ${_QT_FEATURE_DEFINITION_${feature}})
+endmacro()
+
+
+# The build system stores 2 CMake cache variables for each feature, to allow detecting value changes
+# during subsequent reconfigurations.
+#
+#
+# `FEATURE_foo` stores the user provided feature value for the current configuration run.
+# It can be set directly by the user.
+#
+# If a value is not provided on initial configuration, the value will be auto-computed based on the
+# various conditions of the feature.
+# TODO: Document the various conditions and how they relate to each other.
+#
+#
+# `QT_FEATURE_foo` stores the value of the feature from the previous configuration run.
+# Its value is updated once with the newest user provided value after some checks are performed.
+#
+# This variable also serves as the main source of truth throughout the build system code to check
+# if the feature is enabled, e.g. if(QT_FEATURE_foo)
+#
+# It is not meant to be set by the user. It is only modified by the build system.
+#
+# Comparing the values of QT_FEATURE_foo and FEATURE_foo, the build system can detect whether
+# the user changed the value for a feature and thus recompute any dependent features.
+#
function(qt_evaluate_feature feature)
- # If the feature was set explicitly by the user to be on or off, in the cache, then
- # there's nothing for us to do.
+ # If the feature was already evaluated as dependency nothing to do here.
if(DEFINED "QT_FEATURE_${feature}")
return()
endif()
@@ -232,13 +376,7 @@ function(qt_evaluate_feature feature)
message(FATAL_ERROR "Attempting to evaluate feature ${feature} but its definition is missing. Either the feature does not exist or a dependency to the module that defines it is missing")
endif()
- cmake_parse_arguments(arg
- "PRIVATE;PUBLIC"
- "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${_QT_FEATURE_DEFINITION_${feature}})
-
- if(DEFINED QT_FEATURE_${feature})
- return()
- endif()
+ _qt_internal_parse_feature_definition("${feature}")
if("${arg_ENABLE}" STREQUAL "")
set(arg_ENABLE OFF)
@@ -260,13 +398,14 @@ function(qt_evaluate_feature feature)
qt_evaluate_config_expression(disable_result ${arg_DISABLE})
qt_evaluate_config_expression(enable_result ${arg_ENABLE})
+ qt_evaluate_config_expression(auto_detect ${arg_AUTODETECT})
if(${disable_result})
- set(result OFF)
- elseif((${enable_result}) OR (${arg_AUTODETECT}))
- set(result ${condition})
+ set(computed OFF)
+ elseif((${enable_result}) OR (${auto_detect}))
+ set(computed ${condition})
else()
# feature not auto-detected and not explicitly enabled
- set(result OFF)
+ set(computed OFF)
endif()
if("${arg_EMIT_IF}" STREQUAL "")
@@ -275,20 +414,97 @@ function(qt_evaluate_feature feature)
qt_evaluate_config_expression(emit_if ${arg_EMIT_IF})
endif()
- if (NOT (condition) AND (calculated))
- message(FATAL_ERROR "Sanity check failed: Feature ${feature} is enabled but condition does not hold true.")
+ # Warn about a feature which is not emitted, but the user explicitly provided a value for it.
+ if(NOT emit_if AND DEFINED FEATURE_${feature})
+ set(msg "")
+ string(APPEND msg
+ "Feature ${feature} is insignificant in this configuration, "
+ "ignoring related command line option(s).")
+ qt_configure_add_report_entry(TYPE WARNING MESSAGE "${msg}")
+
+ # Remove the cache entry so that the warning is not persisted and shown on every
+ # reconfiguration.
+ unset(FEATURE_${feature} CACHE)
endif()
- qt_feature_set_cache_value(cache "${feature}" "${emit_if}" "${result}" "${arg_LABEL}")
- qt_feature_set_value("${feature}" "${cache}" "${emit_if}" "${condition}" "${arg_LABEL}")
+ # Only save the user provided value if the feature was emitted.
+ if(emit_if)
+ qt_feature_check_and_save_user_provided_value(
+ saved_user_value
+ "${feature}" "${condition}" "${arg_CONDITION}" "${computed}" "${arg_LABEL}")
+ else()
+ # Make sure the feature internal value is OFF if not emitted.
+ set(saved_user_value OFF)
+ endif()
+
+ qt_feature_check_and_save_internal_value(
+ "${feature}" "${saved_user_value}" "${condition}" "${arg_LABEL}" "${arg_CONDITION}")
# Store each feature's label for summary info.
set(QT_FEATURE_LABEL_${feature} "${arg_LABEL}" CACHE INTERNAL "")
endfunction()
+# Collect feature names that ${feature} depends on, by inspecting the given expression.
+function(_qt_internal_feature_extract_feature_dependencies_from_expression out_var expression)
+ list(LENGTH expression length)
+ math(EXPR length "${length}-1")
+
+ if(length LESS 0)
+ set(${out_var} "" PARENT_SCOPE)
+ return()
+ endif()
+
+ set(deps "")
+
+ foreach(memberIdx RANGE ${length})
+ list(GET expression ${memberIdx} member)
+ if(member MATCHES "^QT_FEATURE_(.+)")
+ list(APPEND deps "${CMAKE_MATCH_1}")
+ endif()
+ endforeach()
+ set(${out_var} "${deps}" PARENT_SCOPE)
+endfunction()
+
+# Collect feature names that ${feature} depends on, based on feature names that appear
+# in the ${feature}'s condition expressions.
+function(_qt_internal_feature_compute_feature_dependencies out_var feature)
+ # Only compute the deps once per feature.
+ get_property(deps_computed GLOBAL PROPERTY _qt_feature_deps_computed_${feature})
+ if(deps_computed)
+ get_property(deps GLOBAL PROPERTY _qt_feature_deps_${feature})
+ set(${out_var} "${deps}" PARENT_SCOPE)
+ return()
+ endif()
+
+ _qt_internal_parse_feature_definition("${feature}")
+
+ set(options_to_check AUTODETECT CONDITION ENABLE DISABLE EMIT_IF)
+ set(deps "")
+
+ # Go through each option that takes condition expressions and collect the feature names.
+ foreach(option ${options_to_check})
+ set(option_value "${arg_${option}}")
+ if(option_value)
+ _qt_internal_feature_extract_feature_dependencies_from_expression(
+ option_deps "${option_value}")
+ if(option_deps)
+ list(APPEND deps ${option_deps})
+ endif()
+ endif()
+ endforeach()
+
+ set_property(GLOBAL PROPERTY _qt_feature_deps_computed_${feature} TRUE)
+ set_property(GLOBAL PROPERTY _qt_feature_deps_${feature} "${deps}")
+ set(${out_var} "${deps}" PARENT_SCOPE)
+endfunction()
+
function(qt_feature_config feature config_var_name)
qt_feature_normalize_name("${feature}" feature)
- qt_parse_all_arguments(arg "qt_feature_config" "NEGATE" "NAME" "" ${ARGN})
+ cmake_parse_arguments(PARSE_ARGV 2 arg
+ "NEGATE"
+ "NAME"
+ "")
+ _qt_internal_validate_all_args_are_parsed(arg)
# Store all the config related info in a unique variable key.
set(key_name "_QT_FEATURE_CONFIG_DEFINITION_${feature}_${config_var_name}")
@@ -348,7 +564,11 @@ endfunction()
function(qt_feature_definition feature name)
qt_feature_normalize_name("${feature}" feature)
- qt_parse_all_arguments(arg "qt_feature_definition" "NEGATE" "VALUE;PREREQUISITE" "" ${ARGN})
+ cmake_parse_arguments(PARSE_ARGV 2 arg
+ "NEGATE"
+ "VALUE;PREREQUISITE"
+ "")
+ _qt_internal_validate_all_args_are_parsed(arg)
# Store all the define related info in a unique variable key.
set(key_name "_QT_FEATURE_DEFINE_DEFINITION_${feature}_${name}")
@@ -375,9 +595,14 @@ function(qt_evaluate_feature_definition key)
set(expected OFF)
endif()
+ set(actual OFF)
+ if(QT_FEATURE_${arg_FEATURE})
+ set(actual ON)
+ endif()
+
set(msg "")
- if(QT_FEATURE_${arg_FEATURE} STREQUAL expected)
+ if(actual STREQUAL expected)
set(indent "")
if(arg_PREREQUISITE)
string(APPEND msg "#if ${arg_PREREQUISITE}\n")
@@ -399,7 +624,11 @@ function(qt_evaluate_feature_definition key)
endfunction()
function(qt_extra_definition name value)
- qt_parse_all_arguments(arg "qt_extra_definition" "PUBLIC;PRIVATE" "" "" ${ARGN})
+ cmake_parse_arguments(PARSE_ARGV 2 arg
+ "PUBLIC;PRIVATE"
+ ""
+ "")
+ _qt_internal_validate_all_args_are_parsed(arg)
if (arg_PUBLIC)
string(APPEND __QtFeature_public_extra "\n#define ${name} ${value}\n")
@@ -412,14 +641,16 @@ function(qt_extra_definition name value)
endfunction()
function(qt_internal_generate_feature_line line feature)
- if (QT_FEATURE_${feature} STREQUAL "ON")
+ string(TOUPPER "${QT_FEATURE_${feature}}" value)
+ if (value STREQUAL "ON")
set(line "#define QT_FEATURE_${feature} 1\n\n" PARENT_SCOPE)
- elseif(QT_FEATURE_${feature} STREQUAL "OFF")
+ elseif(value STREQUAL "OFF")
set(line "#define QT_FEATURE_${feature} -1\n\n" PARENT_SCOPE)
- elseif(QT_FEATURE_${feature} STREQUAL "UNSET")
+ elseif(value STREQUAL "UNSET")
set(line "#define QT_FEATURE_${feature} 0\n\n" PARENT_SCOPE)
else()
- message(FATAL_ERROR "${feature} has unexpected value \"${QT_FEATURE_${feature}}\"!")
+ message(FATAL_ERROR "${feature} has unexpected value \"${QT_FEATURE_${feature}}\"! "
+ "Valid values are ON, OFF and UNSET.")
endif()
endfunction()
@@ -447,6 +678,17 @@ function(qt_feature_evaluate_features list_of_paths)
qt_feature_module_end(ONLY_EVALUATE_FEATURES)
endfunction()
+function(qt_feature_record_summary_entries list_of_paths)
+ # Clean up any stale state just in case.
+ qt_feature_unset_state_vars()
+
+ set(__QtFeature_only_record_summary_entries TRUE)
+ foreach(path ${list_of_paths})
+ include("${path}")
+ endforeach()
+ qt_feature_unset_state_vars()
+endfunction()
+
function(qt_feature_module_end)
set(flags ONLY_EVALUATE_FEATURES)
set(options OUT_VAR_PREFIX)
@@ -469,10 +711,10 @@ function(qt_feature_module_end)
# Evaluate custom cache assignments.
foreach(cache_var_name ${__QtFeature_custom_enabled_cache_variables})
- set(${cache_var_name} ON CACHE BOOL "Force enabled by platform." FORCE)
+ set(${cache_var_name} ON CACHE BOOL "Force enabled by platform requirements." FORCE)
endforeach()
foreach(cache_var_name ${__QtFeature_custom_disabled_cache_variables})
- set(${cache_var_name} OFF CACHE BOOL "Force disabled by platform." FORCE)
+ set(${cache_var_name} OFF CACHE BOOL "Force disabled by platform requirements." FORCE)
endforeach()
set(enabled_public_features "")
@@ -520,29 +762,13 @@ function(qt_feature_module_end)
)
endif()
- # Extra header injections which have to have forwarding headers created by
- # qt_install_injections.
- # Skip creating forwarding headers if qt_feature_module_begin was called with NO_MODULE, aka
- # there is no include/<module_name> so there's no place to put the forwarding headers.
- if(__QtFeature_library)
- set(injections "")
- qt_compute_injection_forwarding_header("${__QtFeature_library}"
- SOURCE "${__QtFeature_public_file}"
- OUT_VAR injections)
- qt_compute_injection_forwarding_header("${__QtFeature_library}"
- SOURCE "${__QtFeature_private_file}" PRIVATE
- OUT_VAR injections)
-
- set(${arg_OUT_VAR_PREFIX}extra_library_injections ${injections} PARENT_SCOPE)
- endif()
-
if (NOT ("${target}" STREQUAL "NO_MODULE") AND NOT arg_ONLY_EVALUATE_FEATURES)
get_target_property(targetType "${target}" TYPE)
if("${targetType}" STREQUAL "INTERFACE_LIBRARY")
set(propertyPrefix "INTERFACE_")
else()
set(propertyPrefix "")
- set_property(TARGET "${target}" APPEND PROPERTY EXPORT_PROPERTIES "QT_ENABLED_PUBLIC_FEATURES;QT_DISABLED_PUBLIC_FEATURES;QT_ENABLED_PRIVATE_FEATURES;QT_DISABLED_PRIVATE_FEATURES;MODULE_PLUGIN_TYPES;QT_PLUGINS;QT_QMAKE_PUBLIC_CONFIG;QT_QMAKE_PRIVATE_CONFIG;QT_QMAKE_PUBLIC_QT_CONFIG")
+ set_property(TARGET "${target}" APPEND PROPERTY EXPORT_PROPERTIES "QT_ENABLED_PUBLIC_FEATURES;QT_DISABLED_PUBLIC_FEATURES;QT_ENABLED_PRIVATE_FEATURES;QT_DISABLED_PRIVATE_FEATURES;QT_QMAKE_PUBLIC_CONFIG;QT_QMAKE_PRIVATE_CONFIG;QT_QMAKE_PUBLIC_QT_CONFIG")
endif()
foreach(visibility public private)
string(TOUPPER "${visibility}" capitalVisibility)
@@ -585,6 +811,10 @@ function(qt_feature_module_end)
qt_feature_copy_global_config_features_to_core(${target})
endif()
+ qt_feature_unset_state_vars()
+endfunction()
+
+macro(qt_feature_unset_state_vars)
unset(__QtFeature_library PARENT_SCOPE)
unset(__QtFeature_public_features PARENT_SCOPE)
unset(__QtFeature_private_features PARENT_SCOPE)
@@ -600,7 +830,8 @@ function(qt_feature_module_end)
unset(__QtFeature_custom_enabled_features PARENT_SCOPE)
unset(__QtFeature_custom_disabled_features PARENT_SCOPE)
unset(__QtFeature_only_evaluate_features PARENT_SCOPE)
-endfunction()
+ unset(__QtFeature_only_record_summary_entries PARENT_SCOPE)
+endmacro()
function(qt_feature_copy_global_config_features_to_core target)
# CMake doesn't support setting custom properties on exported INTERFACE libraries
@@ -639,13 +870,47 @@ function(qt_feature_copy_global_config_features_to_core target)
endif()
endfunction()
+function(qt_internal_detect_dirty_features)
+ # We need to clean up QT_FEATURE_*, but only once per configuration cycle
+ get_property(qt_feature_clean GLOBAL PROPERTY _qt_feature_clean)
+ if(NOT qt_feature_clean AND NOT QT_NO_FEATURE_AUTO_RESET)
+ message(STATUS "Checking for feature set changes")
+ set_property(GLOBAL PROPERTY _qt_feature_clean TRUE)
+ foreach(feature ${QT_KNOWN_FEATURES})
+ if(DEFINED "FEATURE_${feature}" AND
+ NOT "${QT_FEATURE_${feature}}" STREQUAL "${FEATURE_${feature}}")
+ message(" '${feature}' was changed from ${QT_FEATURE_${feature}} "
+ "to ${FEATURE_${feature}}")
+ set(dirty_build TRUE)
+ set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}")
+ endif()
+ unset("QT_FEATURE_${feature}" CACHE)
+ endforeach()
+
+ set(QT_KNOWN_FEATURES "" CACHE INTERNAL "" FORCE)
+
+ if(dirty_build)
+ set_property(GLOBAL PROPERTY _qt_dirty_build TRUE)
+ message(WARNING
+ "Due to detected feature set changes, dependent features "
+ "will be re-computed automatically. This might cause a lot of files to be rebuilt. "
+ "To disable this behavior, configure with -DQT_NO_FEATURE_AUTO_RESET=ON")
+ endif()
+ endif()
+endfunction()
+
+# Builds either a string of source code or a whole project to determine whether the build is
+# successful.
+#
+# Sets a TEST_${name}_OUTPUT variable with the build output, to the scope of the calling function.
+# Sets a TEST_${name} cache variable to either TRUE or FALSE if the build is successful or not.
function(qt_config_compile_test name)
if(DEFINED "TEST_${name}")
return()
endif()
cmake_parse_arguments(arg "" "LABEL;PROJECT_PATH;C_STANDARD;CXX_STANDARD"
- "COMPILE_OPTIONS;LIBRARIES;CODE;PACKAGES" ${ARGN})
+ "COMPILE_OPTIONS;LIBRARIES;CODE;PACKAGES;CMAKE_FLAGS" ${ARGN})
if(arg_PROJECT_PATH)
message(STATUS "Performing Test ${arg_LABEL}")
@@ -657,7 +922,19 @@ function(qt_config_compile_test name)
# If the repo has its own cmake modules, include those in the module path, so that various
# find_package calls work.
if(EXISTS "${PROJECT_SOURCE_DIR}/cmake")
- list(APPEND flags "-DCMAKE_MODULE_PATH:STRING=${PROJECT_SOURCE_DIR}/cmake")
+ set(must_append_module_path_flag TRUE)
+ set(flags_copy "${flags}")
+ set(flags)
+ foreach(flag IN LISTS flags_copy)
+ if(flag MATCHES "^-DCMAKE_MODULE_PATH:STRING=")
+ set(must_append_module_path_flag FALSE)
+ set(flag "${flag}\\;${PROJECT_SOURCE_DIR}/cmake")
+ endif()
+ list(APPEND flags "${flag}")
+ endforeach()
+ if(must_append_module_path_flag)
+ list(APPEND flags "-DCMAKE_MODULE_PATH:STRING=${PROJECT_SOURCE_DIR}/cmake")
+ endif()
endif()
# Pass which packages need to be found.
@@ -724,8 +1001,36 @@ function(qt_config_compile_test name)
endif()
endif()
- try_compile(HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}"
- "${name}" CMAKE_FLAGS ${flags})
+ # Pass override values for CMAKE_SYSTEM_{PREFIX|FRAMEWORK}_PATH.
+ if(DEFINED QT_CMAKE_SYSTEM_PREFIX_PATH_BACKUP)
+ set(path_list ${CMAKE_SYSTEM_PREFIX_PATH})
+ string(REPLACE ";" "\\;" path_list "${path_list}")
+ list(APPEND flags "-DQT_CONFIG_COMPILE_TEST_CMAKE_SYSTEM_PREFIX_PATH=${path_list}")
+ endif()
+ if(DEFINED QT_CMAKE_SYSTEM_FRAMEWORK_PATH_BACKUP)
+ set(path_list ${CMAKE_SYSTEM_FRAMEWORK_PATH})
+ string(REPLACE ";" "\\;" path_list "${path_list}")
+ list(APPEND flags "-DQT_CONFIG_COMPILE_TEST_CMAKE_SYSTEM_FRAMEWORK_PATH=${path_list}")
+ endif()
+
+ if(NOT arg_CMAKE_FLAGS)
+ set(arg_CMAKE_FLAGS "")
+ endif()
+
+ # CI passes the project dir of the Qt repository as absolute path without drive letter:
+ # \Users\qt\work\qt\qtbase
+ # Ensure that arg_PROJECT_PATH is an absolute path with drive letter:
+ # C:/Users/qt/work/qt/qtbase
+ # This works around CMake upstream issue #22534.
+ if(CMAKE_HOST_WIN32)
+ get_filename_component(arg_PROJECT_PATH "${arg_PROJECT_PATH}" REALPATH)
+ endif()
+
+ try_compile(
+ HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" "${name}"
+ CMAKE_FLAGS ${flags} ${arg_CMAKE_FLAGS}
+ OUTPUT_VARIABLE try_compile_output
+ )
if(${HAVE_${name}})
set(status_label "Success")
@@ -740,6 +1045,7 @@ function(qt_config_compile_test name)
# fail instead of cmake abort later via CMAKE_REQUIRED_LIBRARIES.
string(FIND "${library}" "::" cmake_target_namespace_separator)
if(NOT cmake_target_namespace_separator EQUAL -1)
+ message(STATUS "Performing Test ${arg_LABEL} - Failed because ${library} not found")
set(HAVE_${name} FALSE)
break()
endif()
@@ -748,19 +1054,31 @@ function(qt_config_compile_test name)
if(NOT DEFINED HAVE_${name})
set(_save_CMAKE_C_STANDARD "${CMAKE_C_STANDARD}")
+ set(_save_CMAKE_C_STANDARD_REQUIRED "${CMAKE_C_STANDARD_REQUIRED}")
set(_save_CMAKE_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
+ set(_save_CMAKE_CXX_STANDARD_REQUIRED "${CMAKE_CXX_STANDARD_REQUIRED}")
set(_save_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+ set(_save_CMAKE_TRY_COMPILE_PLATFORM_VARIABLES "${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}")
if(arg_C_STANDARD)
set(CMAKE_C_STANDARD "${arg_C_STANDARD}")
+ set(CMAKE_C_STANDARD_REQUIRED OFF)
endif()
if(arg_CXX_STANDARD)
- set(CMAKE_CXX_STANDARD "${arg_CXX_STANDARD}")
+ if(${arg_CXX_STANDARD} LESS 23 OR ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20")
+ set(CMAKE_CXX_STANDARD "${arg_CXX_STANDARD}")
+ set(CMAKE_CXX_STANDARD_REQUIRED OFF)
+ endif()
endif()
set(CMAKE_REQUIRED_FLAGS ${arg_COMPILE_OPTIONS})
+ # Pass -stdlib=libc++ on if necessary
+ if (QT_FEATURE_stdlib_libcpp)
+ list(APPEND CMAKE_REQUIRED_FLAGS "-stdlib=libc++")
+ endif()
+
# For MSVC we need to explicitly pass -Zc:__cplusplus to get correct __cplusplus
# define values. According to common/msvc-version.conf the flag is supported starting
# with 1913.
@@ -771,17 +1089,38 @@ function(qt_config_compile_test name)
list(APPEND CMAKE_REQUIRED_FLAGS "-Zc:__cplusplus")
endif()
+ # Let CMake load our custom platform modules.
+ if(NOT QT_AVOID_CUSTOM_PLATFORM_MODULES)
+ list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_MODULE_PATH)
+ endif()
+
set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}")
set(CMAKE_REQUIRED_LIBRARIES "${arg_LIBRARIES}")
- check_cxx_source_compiles("${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name})
+
+ # OUTPUT_VARIABLE is an internal undocumented variable of check_cxx_source_compiles
+ # since 3.23. Allow an opt out in case this breaks in the future.
+ set(try_compile_output "")
+ set(output_var "")
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23"
+ AND NOT QT_INTERNAL_NO_TRY_COMPILE_OUTPUT_VARIABLE)
+ set(output_var OUTPUT_VARIABLE try_compile_output)
+ endif()
+
+ check_cxx_source_compiles(
+ "${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name} ${output_var}
+ )
set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}")
set(CMAKE_C_STANDARD "${_save_CMAKE_C_STANDARD}")
+ set(CMAKE_C_STANDARD_REQUIRED "${_save_CMAKE_C_STANDARD_REQUIRED}")
set(CMAKE_CXX_STANDARD "${_save_CMAKE_CXX_STANDARD}")
+ set(CMAKE_CXX_STANDARD_REQUIRED "${_save_CMAKE_CXX_STANDARD_REQUIRED}")
set(CMAKE_REQUIRED_FLAGS "${_save_CMAKE_REQUIRED_FLAGS}")
+ set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES "${_save_CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}")
endif()
endif()
+ set(TEST_${name}_OUTPUT "${try_compile_output}" PARENT_SCOPE)
set(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}")
endfunction()
@@ -792,6 +1131,18 @@ function(qt_get_platform_try_compile_vars out_var)
# Use the regular variables that are used for source-based try_compile() calls.
set(flags "${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}")
+ # Pass custom flags.
+ list(APPEND flags "CMAKE_C_FLAGS")
+ list(APPEND flags "CMAKE_C_FLAGS_DEBUG")
+ list(APPEND flags "CMAKE_C_FLAGS_RELEASE")
+ list(APPEND flags "CMAKE_C_FLAGS_RELWITHDEBINFO")
+ list(APPEND flags "CMAKE_CXX_FLAGS")
+ list(APPEND flags "CMAKE_CXX_FLAGS_DEBUG")
+ list(APPEND flags "CMAKE_CXX_FLAGS_RELEASE")
+ list(APPEND flags "CMAKE_CXX_FLAGS_RELWITHDEBINFO")
+ list(APPEND flags "CMAKE_OBJCOPY")
+ list(APPEND flags "CMAKE_EXE_LINKER_FLAGS")
+
# Pass toolchain files.
if(CMAKE_TOOLCHAIN_FILE)
list(APPEND flags "CMAKE_TOOLCHAIN_FILE")
@@ -802,7 +1153,18 @@ function(qt_get_platform_try_compile_vars out_var)
# Pass language standard flags.
list(APPEND flags "CMAKE_C_STANDARD")
+ list(APPEND flags "CMAKE_C_STANDARD_REQUIRED")
list(APPEND flags "CMAKE_CXX_STANDARD")
+ list(APPEND flags "CMAKE_CXX_STANDARD_REQUIRED")
+
+ # Pass -stdlib=libc++ on if necessary
+ if (QT_FEATURE_stdlib_libcpp)
+ if(CMAKE_CXX_FLAGS)
+ string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++")
+ else()
+ set(CMAKE_CXX_FLAGS "-stdlib=libc++")
+ endif()
+ endif()
# Assemble the list with regular options.
set(flags_cmd_line "")
@@ -812,26 +1174,45 @@ function(qt_get_platform_try_compile_vars out_var)
endif()
endforeach()
+ # Let CMake load our custom platform modules.
+ if(NOT QT_AVOID_CUSTOM_PLATFORM_MODULES)
+ list(APPEND flags_cmd_line "-DCMAKE_MODULE_PATH:STRING=${QT_CMAKE_DIR}/platforms")
+ endif()
+
# Pass darwin specific options.
+ # The architectures need to be passed explicitly to project-based try_compile calls even on
+ # macOS, so that arm64 compilation works on Apple silicon.
+ qt_internal_get_first_osx_arch(osx_first_arch)
+ if(osx_first_arch)
+ # Do what qmake does, aka when doing a simulator_and_device build, build the
+ # target architecture test only with the first given architecture, which should be the
+ # device architecture, aka some variation of "arm" (armv7, arm64).
+ list(APPEND flags_cmd_line "-DCMAKE_OSX_ARCHITECTURES:STRING=${osx_first_arch}")
+ endif()
if(UIKIT)
- if(CMAKE_OSX_ARCHITECTURES)
- list(GET CMAKE_OSX_ARCHITECTURES 0 osx_first_arch)
-
- # Do what qmake does, aka when doing a simulator_and_device build, build the
- # target architecture test only with the first given architecture, which should be the
- # device architecture, aka some variation of "arm" (armv7, arm64).
- list(APPEND flags_cmd_line "-DCMAKE_OSX_ARCHITECTURES:STRING=${osx_first_arch}")
- endif()
- # Also specify the sysroot, but only if not doing a simulator_and_device build.
+ # Specify the sysroot, but only if not doing a simulator_and_device build.
# So keep the sysroot empty for simulator_and_device builds.
- if(QT_UIKIT_SDK)
- list(APPEND flags_cmd_line "-DCMAKE_OSX_SYSROOT:STRING=${QT_UIKIT_SDK}")
+ if(QT_APPLE_SDK)
+ list(APPEND flags_cmd_line "-DCMAKE_OSX_SYSROOT:STRING=${QT_APPLE_SDK}")
endif()
endif()
+ if(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH)
+ list(APPEND flags_cmd_line "-DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH:BOOL=OFF")
+ endif()
set("${out_var}" "${flags_cmd_line}" PARENT_SCOPE)
endfunction()
+# Set out_var to the first value of CMAKE_OSX_ARCHITECTURES.
+# Sets an empty string if no architecture is present.
+function(qt_internal_get_first_osx_arch out_var)
+ set(value "")
+ if(CMAKE_OSX_ARCHITECTURES)
+ list(GET CMAKE_OSX_ARCHITECTURES 0 value)
+ endif()
+ set(${out_var} "${value}" PARENT_SCOPE)
+endfunction()
+
function(qt_config_compile_test_x86simd extension label)
if (DEFINED TEST_X86SIMD_${extension})
return()
@@ -842,7 +1223,7 @@ function(qt_config_compile_test_x86simd extension label)
qt_get_platform_try_compile_vars(platform_try_compile_vars)
list(APPEND flags ${platform_try_compile_vars})
- message(STATUS "Performing SIMD Test ${label}")
+ message(STATUS "Performing Test ${label} intrinsics")
try_compile("TEST_X86SIMD_${extension}"
"${CMAKE_CURRENT_BINARY_DIR}/config.tests/x86_simd_${extension}"
"${CMAKE_CURRENT_SOURCE_DIR}/config.tests/x86_simd"
@@ -853,12 +1234,12 @@ function(qt_config_compile_test_x86simd extension label)
else()
set(status_label "Failed")
endif()
- message(STATUS "Performing SIMD Test ${label} - ${status_label}")
+ message(STATUS "Performing Test ${label} intrinsics - ${status_label}")
set(TEST_subarch_${extension} "${TEST_X86SIMD_${extension}}" CACHE INTERNAL "${label}")
endfunction()
function(qt_config_compile_test_machine_tuple label)
- if(DEFINED TEST_MACHINE_TUPLE OR NOT (GCC OR CLANG))
+ if(DEFINED TEST_MACHINE_TUPLE OR NOT (LINUX OR HURD) OR ANDROID)
return()
endif()
@@ -876,6 +1257,130 @@ function(qt_config_compile_test_machine_tuple label)
set(TEST_machine_tuple "${output}" CACHE INTERNAL "${label}")
endfunction()
+function(qt_config_compiler_supports_flag_test name)
+ if(DEFINED "TEST_${name}")
+ return()
+ endif()
+
+ cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN})
+ check_cxx_compiler_flag("${arg_FLAG}" TEST_${name})
+ set(TEST_${name} "${TEST_${name}}" CACHE INTERNAL "${label}")
+endfunction()
+
+# gcc expects -fuse-ld=mold (no absolute path can be given) (gcc >= 12.1)
+# or an 'ld' symlink to 'mold' in a dir that is passed via -B flag (gcc < 12.1)
+#
+# clang expects -fuse-ld=mold
+# or -fuse-ld=<mold-abs-path>
+# or --ldpath=<mold-abs-path> (clang >= 12)
+# https://github.com/rui314/mold/#how-to-use
+# TODO: In the gcc < 12.1 case, the qt_internal_check_if_linker_is_available(mold) check will
+# always return TRUE because gcc will not error out if it is given a -B flag pointing to an
+# invalid dir, as well as when the the symlink to the linker in the -B dir is not actually
+# a valid linker.
+# It would be nice to handle that case in a better way, but it's not that important
+# given that gcc > 12.1 now supports -fuse-ld=mold
+# NOTE: In comparison to clang, in the gcc < 12.1 case, we pass the full path to where mold is
+# and that is recorded in PlatformCommonInternal's INTERFACE_LINK_OPTIONS target.
+# Moving such a Qt to a different machine and trying to build another repo won't
+# work because the recorded path will be invalid. This is not a problem with
+# the gcc >= 12.1 case
+function(qt_internal_get_mold_linker_flags out_var)
+ cmake_parse_arguments(PARSE_ARGV 1 arg "ERROR_IF_EMPTY" "" "")
+
+ find_program(QT_INTERNAL_LINKER_MOLD mold)
+
+ set(flag "")
+ if(QT_INTERNAL_LINKER_MOLD)
+ if(GCC)
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
+ set(flag "-fuse-ld=mold")
+ else()
+ set(mold_linker_dir "${CMAKE_CURRENT_BINARY_DIR}/.qt_linker")
+ set(mold_linker_path "${mold_linker_dir}/ld")
+ if(NOT EXISTS "${mold_linker_dir}")
+ file(MAKE_DIRECTORY "${mold_linker_dir}")
+ endif()
+ if(NOT EXISTS "${mold_linker_path}")
+ file(CREATE_LINK
+ "${QT_INTERNAL_LINKER_MOLD}"
+ "${mold_linker_path}"
+ SYMBOLIC)
+ endif()
+ set(flag "-B${mold_linker_dir}")
+ endif()
+ elseif(CLANG)
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12")
+ set(flag "--ld-path=mold")
+ else()
+ set(flag "-fuse-ld=mold")
+ endif()
+ endif()
+ endif()
+ if(arg_ERROR_IS_EMPTY AND NOT flag)
+ message(FATAL_ERROR "Could not determine the flags to use the mold linker.")
+ endif()
+ set(${out_var} "${flag}" PARENT_SCOPE)
+endfunction()
+
+function(qt_internal_get_active_linker_flags out_var)
+ set(flags "")
+ if(GCC OR CLANG)
+ if(QT_FEATURE_use_gold_linker)
+ list(APPEND flags "-fuse-ld=gold")
+ elseif(QT_FEATURE_use_bfd_linker)
+ list(APPEND flags "-fuse-ld=bfd")
+ elseif(QT_FEATURE_use_lld_linker)
+ list(APPEND flags "-fuse-ld=lld")
+ elseif(QT_FEATURE_use_mold_linker)
+ qt_internal_get_mold_linker_flags(mold_flags ERROR_IF_EMPTY)
+ list(APPEND flags "${mold_flags}")
+ endif()
+ endif()
+ set(${out_var} "${flags}" PARENT_SCOPE)
+endfunction()
+
+function(qt_internal_check_if_linker_is_available name)
+ if(DEFINED "TEST_${name}")
+ return()
+ endif()
+
+ cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN})
+ set(flags "${arg_FLAG}")
+
+ set(CMAKE_REQUIRED_LINK_OPTIONS ${flags})
+ check_cxx_source_compiles("int main() { return 0; }" TEST_${name})
+ set(TEST_${name} "${TEST_${name}}" CACHE INTERNAL "${label}")
+endfunction()
+
+function(qt_config_linker_supports_flag_test name)
+ if(DEFINED "TEST_${name}")
+ return()
+ endif()
+
+ cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN})
+ if(GCC OR CLANG)
+ set(flags "-Wl,--fatal-warnings,${arg_FLAG}")
+ elseif(MSVC)
+ set(flags "${arg_FLAG}")
+ else()
+ # We don't know how to pass linker options in a way that
+ # it reliably fails, so assume the detection failed.
+ set(TEST_${name} "0" CACHE INTERNAL "${label}")
+ return()
+ endif()
+
+ # Pass the linker that the main project uses to the compile test.
+ qt_internal_get_active_linker_flags(linker_flags)
+ if(linker_flags)
+ list(PREPEND flags ${linker_flags})
+ endif()
+
+ set(CMAKE_REQUIRED_LINK_OPTIONS ${flags})
+ check_cxx_source_compiles("int main() { return 0; }" TEST_${name})
+ set(TEST_${name} "${TEST_${name}}" CACHE INTERNAL "${label}")
+endfunction()
+
function(qt_make_features_available target)
if(NOT "${target}" MATCHES "^${QT_CMAKE_EXPORT_NAMESPACE}::[a-zA-Z0-9_-]*$")
message(FATAL_ERROR "${target} does not match ${QT_CMAKE_EXPORT_NAMESPACE}::[a-zA-Z0-9_-]*. INVALID NAME.")
@@ -899,7 +1404,15 @@ function(qt_make_features_available target)
endif()
foreach(feature IN ITEMS ${features})
if (DEFINED "QT_FEATURE_${feature}" AND NOT "${QT_FEATURE_${feature}}" STREQUAL "${value}")
- message(FATAL_ERROR "Feature ${feature} is already defined and has a different value when importing features from ${target}.")
+ message(WARNING
+ "This project was initially configured with the Qt feature \"${feature}\" "
+ "set to \"${QT_FEATURE_${feature}}\". While loading the "
+ "\"${target}\" package, the value of the feature "
+ "has changed to \"${value}\". That might cause a project rebuild due to "
+ "updated C++ headers. \n"
+ "In case of build issues, consider removing the CMakeCache.txt file and "
+ "reconfiguring the project."
+ )
endif()
set(QT_FEATURE_${feature} "${value}" CACHE INTERNAL "Qt feature: ${feature} (from target ${target})")
endforeach()
@@ -907,5 +1420,3 @@ function(qt_make_features_available target)
endforeach()
endforeach()
endfunction()
-
-