2023-10-30 06:33:08 +08:00
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
include ( QtFeatureCommon )
include ( CheckCXXCompilerFlag )
function ( qt_feature_module_begin )
cmake_parse_arguments ( PARSE_ARGV 0 arg
" N O _ M O D U L E ; O N L Y _ E V A L U A T E _ F E A T U R E S "
" L I B R A R Y ; P R I V A T E _ F I L E ; P U B L I C _ F I L E "
" P U B L I C _ D E P E N D E N C I E S ; P R I V A T E _ D E P E N D E N C I E S " )
_qt_internal_validate_all_args_are_parsed ( arg )
if ( NOT arg_ONLY_EVALUATE_FEATURES )
if ( "${arg_LIBRARY}" STREQUAL "" AND ( NOT ${ arg_NO_MODULE } ) )
message ( FATAL_ERROR
" q t _ f e a t u r e _ b e g i n _ m o d u l e n e e d s a L I B R A R Y n a m e ! ( o r s p e c i f y N O _ M O D U L E ) " )
endif ( )
if ( "${arg_PUBLIC_FILE}" STREQUAL "" )
message ( FATAL_ERROR "qt_feature_begin_module needs a PUBLIC_FILE name!" )
endif ( )
if ( "${arg_PRIVATE_FILE}" STREQUAL "" )
message ( FATAL_ERROR "qt_feature_begin_module needs a PRIVATE_FILE name!" )
endif ( )
set ( __QtFeature_only_evaluate_features OFF PARENT_SCOPE )
else ( )
set ( __QtFeature_only_evaluate_features ON PARENT_SCOPE )
endif ( )
set ( __QtFeature_library "${arg_LIBRARY}" PARENT_SCOPE )
set ( __QtFeature_public_features "" PARENT_SCOPE )
set ( __QtFeature_private_features "" PARENT_SCOPE )
set ( __QtFeature_internal_features "" PARENT_SCOPE )
set ( __QtFeature_private_file "${arg_PRIVATE_FILE}" PARENT_SCOPE )
set ( __QtFeature_public_file "${arg_PUBLIC_FILE}" PARENT_SCOPE )
set ( __QtFeature_private_extra "" PARENT_SCOPE )
set ( __QtFeature_public_extra "" PARENT_SCOPE )
set ( __QtFeature_config_definitions "" PARENT_SCOPE )
set ( __QtFeature_define_definitions "" 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}" )
cmake_parse_arguments ( PARSE_ARGV 1 arg
" P R I V A T E ; P U B L I C "
" L A B E L ; P U R P O S E ; S E C T I O N "
" A U T O D E T E C T ; C O N D I T I O N ; E N A B L E ; D I S A B L E ; E M I T _ I F " )
_qt_internal_validate_all_args_are_parsed ( arg )
set ( _QT_FEATURE_DEFINITION_ ${ feature } ${ ARGN } PARENT_SCOPE )
# Register feature for future use:
if ( arg_PUBLIC )
list ( APPEND __QtFeature_public_features "${feature}" )
endif ( )
if ( arg_PRIVATE )
list ( APPEND __QtFeature_private_features "${feature}" )
endif ( )
if ( NOT arg_PUBLIC AND NOT arg_PRIVATE )
list ( APPEND __QtFeature_internal_features "${feature}" )
endif ( )
set ( __QtFeature_public_features ${ __QtFeature_public_features } PARENT_SCOPE )
set ( __QtFeature_private_features ${ __QtFeature_private_features } PARENT_SCOPE )
set ( __QtFeature_internal_features ${ __QtFeature_internal_features } PARENT_SCOPE )
endfunction ( )
function ( qt_evaluate_to_boolean expressionVar )
if ( ${ ${expressionVar } } )
set ( ${ expressionVar } ON PARENT_SCOPE )
else ( )
set ( ${ expressionVar } OFF PARENT_SCOPE )
endif ( )
endfunction ( )
function ( qt_evaluate_config_expression resultVar )
set ( result "" )
set ( nestingLevel 0 )
set ( skipNext OFF )
set ( expression "${ARGN}" )
list ( LENGTH expression length )
math ( EXPR length "${length}-1" )
foreach ( memberIdx RANGE ${ length } )
if ( ${ skipNext } )
set ( skipNext OFF )
continue ( )
endif ( )
list ( GET expression ${ memberIdx } member )
if ( "${member}" STREQUAL "(" )
if ( ${ nestingLevel } GREATER 0 )
list ( APPEND result ${ member } )
endif ( )
math ( EXPR nestingLevel "${nestingLevel} + 1" )
continue ( )
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 ( )
elseif ( "${member}" STREQUAL "NOT" )
list ( APPEND result ${ member } )
continue ( )
elseif ( "${member}" STREQUAL "AND" )
qt_evaluate_to_boolean ( result )
if ( NOT ${ result } )
break ( )
endif ( )
set ( result "" )
elseif ( "${member}" STREQUAL "OR" )
qt_evaluate_to_boolean ( result )
if ( ${ result } )
break ( )
endif ( )
set ( result "" )
elseif ( "${member}" STREQUAL "STREQUAL" AND memberIdx LESS ${ length } )
# Unfortunately the semantics for STREQUAL in if() are broken when the
# RHS is an empty string and the parameters to if are coming through a variable.
# So we expect people to write the empty string with single quotes and then we
# do the comparison manually here.
list ( LENGTH result lhsIndex )
math ( EXPR lhsIndex "${lhsIndex}-1" )
list ( GET result ${ lhsIndex } lhs )
list ( REMOVE_AT result ${ lhsIndex } )
set ( lhs "${${lhs}}" )
math ( EXPR rhsIndex "${memberIdx}+1" )
set ( skipNext ON )
list ( GET expression ${ rhsIndex } rhs )
# We can't pass through an empty string with double quotes through various
# stages of substitution, so instead it is represented using single quotes
# and resolve here.
string ( REGEX REPLACE "'(.*)'" "\\1" rhs "${rhs}" )
string ( COMPARE EQUAL "${lhs}" "${rhs}" stringCompareResult )
list ( APPEND result ${ stringCompareResult } )
else ( )
string ( FIND "${member}" "QT_FEATURE_" idx )
if ( idx EQUAL 0 )
# Remove the QT_FEATURE_ prefix
string ( SUBSTRING "${member}" 11 -1 feature )
qt_evaluate_feature ( ${ feature } )
endif ( )
list ( APPEND result ${ member } )
endif ( )
endforeach ( )
# The 'TARGET Gui' case is handled by qt_evaluate_to_boolean, by passing those tokens verbatim
# to if().
if ( "${result}" STREQUAL "" )
set ( result ON )
else ( )
qt_evaluate_to_boolean ( result )
endif ( )
set ( ${ resultVar } ${ result } PARENT_SCOPE )
endfunction ( )
2023-11-02 05:23:55 +08:00
function ( _qt_internal_get_feature_condition_keywords out_var )
set ( keywords "EQUAL" "LESS" "LESS_EQUAL" "GREATER" "GREATER_EQUAL" "STREQUAL" "STRLESS"
" S T R L E S S _ E Q U A L " " S T R G R E A T E R " " S T R G R E A T E R _ E Q U A L " " V E R S I O N _ E Q U A L " " V E R S I O N _ L E S S "
" V E R S I O N _ L E S S _ E Q U A L " " V E R S I O N _ G R E A T E R " " V E R S I O N _ G R E A T E R _ E Q U A L " " M A T C H E S "
" E X I S T S " " C O M M A N D " " D E F I N E D " " N O T " " A N D " " O R " " T A R G E T " " E X I S T S " " I N _ L I S T " " ( " " ) " )
set ( ${ out_var } "${keywords}" PARENT_SCOPE )
endfunction ( )
2023-10-30 06:33:08 +08:00
function ( _qt_internal_dump_expression_values expression_dump expression )
set ( dump "" )
set ( skipNext FALSE )
set ( isTargetExpression FALSE )
2023-11-02 05:23:55 +08:00
_qt_internal_get_feature_condition_keywords ( keywords )
2023-10-30 06:33:08 +08:00
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.
#
2023-11-02 05:23:55 +08:00
function ( qt_feature_check_and_save_user_provided_value
r e s u l t V a r f e a t u r e c o n d i t i o n c o n d i t i o n _ e x p r e s s i o n c o m p u t e d l a b e l )
2023-10-30 06:33:08 +08:00
if ( DEFINED "FEATURE_${feature}" )
# Revisit new user provided value
set ( user_value "${FEATURE_${feature}}" )
2023-11-02 05:23:55 +08:00
string ( TOUPPER "${user_value}" user_value_upper )
set ( result "${user_value_upper}" )
2023-10-30 06:33:08 +08:00
2023-11-02 05:23:55 +08:00
# If ${feature} depends on another dirty feature, reset the ${feature} value to
# ${computed}.
2023-10-30 06:33:08 +08:00
get_property ( dirty_build GLOBAL PROPERTY _qt_dirty_build )
2023-11-02 05:23:55 +08:00
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
" A u t o - r e s e t t i n g ' F E A T U R E _ $ { f e a t u r e } ' f r o m ' $ { u s e r _ v a l u e _ u p p e r } ' t o "
" ' $ { c o m p u t e d } ' , "
" b e c a u s e t h e d e p e n d e n t f e a t u r e ' $ { d i r t y _ f e a t u r e } ' w a s m a r k e d d i r t y . " )
# 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 "
" ' $ { c o m p u t e d } ' b e c a u s e i t d o e s n ' t m e e t i t s c o n d i t i o n a f t e r r e c o n f i g u r a t i o n . "
" C o n d i t i o n e x p r e s s i o n i s : ' $ { c o n d i t i o n _ e x p r e s s i o n } ' " )
endif ( )
2023-10-30 06:33:08 +08:00
endif ( )
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
" S a n i t y c h e c k f a i l e d : F E A T U R E _ $ { f e a t u r e } h a s i n v a l i d v a l u e \ " $ { r e s u l t } \ " ! " )
endif ( )
# Fix-up user-provided values
set ( "FEATURE_${feature}" "${result}" CACHE BOOL "${label}" FORCE )
else ( )
# Initial setup:
set ( result "${computed}" )
set ( "FEATURE_${feature}" "${result}" CACHE BOOL "${label}" )
endif ( )
set ( "${resultVar}" "${result}" PARENT_SCOPE )
endfunction ( )
# Saves the final user value to QT_FEATURE_${feature}, after checking that the condition is met.
macro ( qt_feature_check_and_save_internal_value
f e a t u r e s a v e d _ u s e r _ v a l u e c o n d i t i o n l a b e l c o n d i t i o n E x p r e s s i o n )
if ( ${ saved_user_value } )
set ( result ON )
else ( )
set ( result OFF )
endif ( )
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 \
c o n d i t i o n : \ n $ { c o n d i t i o n S t r i n g } \ n C o n d i t i o n v a l u e s d u m p : \ n $ { c o n d i t i o n D u m p } \ n " R E C O R D _ O N _ F E A T U R E _ E V A L U A T I O N )
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 ( )
2023-11-02 05:23:55 +08:00
macro ( _qt_internal_parse_feature_definition feature )
cmake_parse_arguments ( arg
" P R I V A T E ; P U B L I C "
" L A B E L ; P U R P O S E ; S E C T I O N ; "
" A U T O D E T E C T ; C O N D I T I O N ; E N A B L E ; D I S A B L E ; E M I T _ I F "
$ { _ Q T _ F E A T U R E _ D E F I N I T I O N _ $ { f e a t u r e } } )
endmacro ( )
2023-10-30 06:33:08 +08:00
# 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, or derived from INPUT_foo (also set 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 already evaluated as dependency nothing to do here.
if ( DEFINED "QT_FEATURE_${feature}" )
return ( )
endif ( )
if ( NOT DEFINED _QT_FEATURE_DEFINITION_ ${ feature } )
qt_debug_print_variables ( DEDUP MATCH "^QT_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 ( )
2023-11-02 05:23:55 +08:00
_qt_internal_parse_feature_definition ( "${feature}" )
2023-10-30 06:33:08 +08:00
if ( "${arg_ENABLE}" STREQUAL "" )
set ( arg_ENABLE OFF )
endif ( )
if ( "${arg_DISABLE}" STREQUAL "" )
set ( arg_DISABLE OFF )
endif ( )
if ( "${arg_AUTODETECT}" STREQUAL "" )
set ( arg_AUTODETECT ON )
endif ( )
if ( "${arg_CONDITION}" STREQUAL "" )
set ( condition ON )
else ( )
qt_evaluate_config_expression ( condition ${ arg_CONDITION } )
endif ( )
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 ( computed OFF )
elseif ( ( ${ enable_result } ) OR ( ${ auto_detect } ) )
set ( computed ${ condition } )
else ( )
# feature not auto-detected and not explicitly enabled
set ( computed OFF )
endif ( )
if ( "${arg_EMIT_IF}" STREQUAL "" )
set ( emit_if ON )
else ( )
qt_evaluate_config_expression ( emit_if ${ arg_EMIT_IF } )
endif ( )
2023-11-02 05:23:55 +08:00
qt_internal_compute_feature_value_from_possible_input ( "${feature}" )
2023-10-30 06:33:08 +08:00
# 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
" F e a t u r e $ { f e a t u r e } i s i n s i g n i f i c a n t i n t h i s c o n f i g u r a t i o n , "
" i g n o r i n g r e l a t e d c o m m a n d l i n e 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 ( )
# Only save the user provided value if the feature was emitted.
if ( emit_if )
qt_feature_check_and_save_user_provided_value (
2023-11-02 05:23:55 +08:00
s a v e d _ u s e r _ v a l u e
" $ { f e a t u r e } " " $ { c o n d i t i o n } " " $ { a r g _ C O N D I T I O N } " " $ { c o m p u t e d } " " $ { a r g _ L A B E L } " )
2023-10-30 06:33:08 +08:00
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 (
" $ { f e a t u r e } " " $ { s a v e d _ u s e r _ v a l u e } " " $ { c o n d i t i o n } " " $ { a r g _ L A B E L } " " $ { a r g _ C O N D I T I O N } " )
# Store each feature's label for summary info.
set ( QT_FEATURE_LABEL_ ${ feature } "${arg_LABEL}" CACHE INTERNAL "" )
endfunction ( )
2023-11-02 05:23:55 +08:00
# 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 (
o p t i o n _ d e p s " $ { o p t i o n _ v a l u e } " )
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 ( )
2023-10-30 06:33:08 +08:00
function ( qt_feature_config feature config_var_name )
qt_feature_normalize_name ( "${feature}" feature )
cmake_parse_arguments ( PARSE_ARGV 2 arg
" N E G A T E "
" N A M E "
" " )
_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}" )
set ( ${ key_name } "FEATURE;${feature};CONFIG_VAR_NAME;${config_var_name};${ARGN}" PARENT_SCOPE )
# Store the key for later evaluation.
list ( APPEND __QtFeature_config_definitions "${key_name}" )
set ( __QtFeature_config_definitions ${ __QtFeature_config_definitions } PARENT_SCOPE )
endfunction ( )
function ( qt_evaluate_qmake_config_values key )
if ( NOT DEFINED ${ key } )
qt_debug_print_variables ( DEDUP MATCH "^_QT_FEATURE_CONFIG_DEFINITION" )
message ( FATAL_ERROR
" A t t e m p t i n g t o e v a l u a t e f e a t u r e c o n f i g $ { k e y } b u t i t s d e f i n i t i o n i s m i s s i n g . " )
endif ( )
cmake_parse_arguments ( arg
" N E G A T E "
" F E A T U R E ; N A M E ; C O N F I G _ V A R _ N A M E "
" " $ { $ { k e y } } )
# If no custom name is specified, then the config value is the same as the feature name.
if ( NOT arg_NAME )
set ( arg_NAME "${arg_FEATURE}" )
endif ( )
set ( expected "NOT" )
if ( arg_NEGATE )
set ( expected "" )
if ( arg_NAME MATCHES "^no_(.*)" )
set ( arg_NAME "${CMAKE_MATCH_1}" )
else ( )
string ( PREPEND arg_NAME "no_" )
endif ( )
endif ( )
# The feature condition is false, there is no need to export any config values.
if ( ${ expected } ${ QT_FEATURE_${arg_FEATURE } } )
return ( )
endif ( )
if ( arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PUBLIC_CONFIG" )
list ( APPEND __QtFeature_qmake_public_config "${arg_NAME}" )
set ( __QtFeature_qmake_public_config "${__QtFeature_qmake_public_config}" PARENT_SCOPE )
endif ( )
if ( arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PRIVATE_CONFIG" )
list ( APPEND __QtFeature_qmake_private_config "${arg_NAME}" )
set ( __QtFeature_qmake_private_config "${__QtFeature_qmake_private_config}" PARENT_SCOPE )
endif ( )
if ( arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PUBLIC_QT_CONFIG" )
list ( APPEND __QtFeature_qmake_public_qt_config "${arg_NAME}" )
set ( __QtFeature_qmake_public_qt_config "${__QtFeature_qmake_public_qt_config}" PARENT_SCOPE )
endif ( )
endfunction ( )
function ( qt_feature_definition feature name )
qt_feature_normalize_name ( "${feature}" feature )
cmake_parse_arguments ( PARSE_ARGV 2 arg
" N E G A T E "
" V A L U E ; P R E R E Q U I S I T E "
" " )
_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}" )
set ( ${ key_name } "FEATURE;${feature};NAME;${name};${ARGN}" PARENT_SCOPE )
# Store the key for later evaluation and subsequent define generation:
list ( APPEND __QtFeature_define_definitions "${key_name}" )
set ( __QtFeature_define_definitions ${ __QtFeature_define_definitions } PARENT_SCOPE )
endfunction ( )
function ( qt_evaluate_feature_definition key )
if ( NOT DEFINED ${ key } )
qt_debug_print_variables ( DEDUP MATCH "^_QT_FEATURE_DEFINE_DEFINITION" )
message ( FATAL_ERROR "Attempting to evaluate feature define ${key} but its definition is missing. " )
endif ( )
cmake_parse_arguments ( arg
" N E G A T E ; "
" F E A T U R E ; N A M E ; V A L U E ; P R E R E Q U I S I T E " " " $ { $ { k e y } } )
set ( expected ON )
if ( arg_NEGATE )
set ( expected OFF )
endif ( )
set ( actual OFF )
if ( QT_FEATURE_ ${ arg_FEATURE } )
set ( actual ON )
endif ( )
set ( msg "" )
if ( actual STREQUAL expected )
set ( indent "" )
if ( arg_PREREQUISITE )
string ( APPEND msg "#if ${arg_PREREQUISITE}\n" )
set ( indent " " )
endif ( )
if ( arg_VALUE )
string ( APPEND msg "${indent}#define ${arg_NAME} ${arg_VALUE}\n" )
else ( )
string ( APPEND msg "${indent}#define ${arg_NAME}\n" )
endif ( )
if ( arg_PREREQUISITE )
string ( APPEND msg "#endif\n" )
endif ( )
string ( APPEND __QtFeature_public_extra "${msg}" )
endif ( )
set ( __QtFeature_public_extra ${ __QtFeature_public_extra } PARENT_SCOPE )
endfunction ( )
function ( qt_extra_definition name value )
cmake_parse_arguments ( PARSE_ARGV 2 arg
" P U B L I C ; P R I V A T E "
" "
" " )
_qt_internal_validate_all_args_are_parsed ( arg )
if ( arg_PUBLIC )
string ( APPEND __QtFeature_public_extra "\n#define ${name} ${value}\n" )
elseif ( arg_PRIVATE )
string ( APPEND __QtFeature_private_extra "\n#define ${name} ${value}\n" )
endif ( )
set ( __QtFeature_public_extra ${ __QtFeature_public_extra } PARENT_SCOPE )
set ( __QtFeature_private_extra ${ __QtFeature_private_extra } PARENT_SCOPE )
endfunction ( )
function ( qt_internal_generate_feature_line line feature )
string ( TOUPPER "${QT_FEATURE_${feature}}" value )
if ( value STREQUAL "ON" )
set ( line "#define QT_FEATURE_${feature} 1\n\n" PARENT_SCOPE )
elseif ( value STREQUAL "OFF" )
set ( line "#define QT_FEATURE_${feature} -1\n\n" PARENT_SCOPE )
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 } }\ "! "
" V a l i d v a l u e s a r e O N , O F F a n d U N S E T . " )
endif ( )
endfunction ( )
function ( qt_internal_feature_write_file file features extra )
set ( contents "" )
foreach ( it ${ features } )
qt_internal_generate_feature_line ( line "${it}" )
string ( APPEND contents "${line}" )
endforeach ( )
string ( APPEND contents "${extra}" )
file ( GENERATE OUTPUT "${file}" CONTENT "${contents}" )
endfunction ( )
# Helper function which evaluates features from a given list of configure.cmake paths
# and creates the feature cache entries.
# Should not be used directly, unless features need to be available in a directory scope before the
# associated module evaluates the features.
# E.g. qtbase/src.pro needs access to Core features before src/corelib/CMakeLists.txt is parsed.
function ( qt_feature_evaluate_features list_of_paths )
qt_feature_module_begin ( ONLY_EVALUATE_FEATURES )
foreach ( path ${ list_of_paths } )
include ( "${path}" )
endforeach ( )
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 )
set ( multiopts )
cmake_parse_arguments ( arg "${flags}" "${options}" "${multiopts}" ${ ARGN } )
set ( target ${ arg_UNPARSED_ARGUMENTS } )
# The value of OUT_VAR_PREFIX is used as a prefix for output variables that should be
# set in the parent scope.
if ( NOT arg_OUT_VAR_PREFIX )
set ( arg_OUT_VAR_PREFIX "" )
endif ( )
set ( all_features ${ __QtFeature_public_features } ${ __QtFeature_private_features } ${ __QtFeature_internal_features } )
list ( REMOVE_DUPLICATES all_features )
foreach ( feature ${ all_features } )
qt_evaluate_feature ( ${ feature } )
endforeach ( )
# Evaluate custom cache assignments.
foreach ( cache_var_name ${ __QtFeature_custom_enabled_cache_variables } )
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 requirements." FORCE )
endforeach ( )
set ( enabled_public_features "" )
set ( disabled_public_features "" )
set ( enabled_private_features "" )
set ( disabled_private_features "" )
foreach ( feature ${ __QtFeature_public_features } )
if ( QT_FEATURE_ ${ feature } )
list ( APPEND enabled_public_features ${ feature } )
else ( )
list ( APPEND disabled_public_features ${ feature } )
endif ( )
endforeach ( )
foreach ( feature ${ __QtFeature_private_features } )
if ( QT_FEATURE_ ${ feature } )
list ( APPEND enabled_private_features ${ feature } )
else ( )
list ( APPEND disabled_private_features ${ feature } )
endif ( )
endforeach ( )
foreach ( key ${ __QtFeature_config_definitions } )
qt_evaluate_qmake_config_values ( ${ key } )
unset ( ${ key } PARENT_SCOPE )
endforeach ( )
foreach ( key ${ __QtFeature_define_definitions } )
qt_evaluate_feature_definition ( ${ key } )
unset ( ${ key } PARENT_SCOPE )
endforeach ( )
foreach ( feature ${ all_features } )
unset ( _QT_FEATURE_DEFINITION_ ${ feature } PARENT_SCOPE )
endforeach ( )
if ( NOT arg_ONLY_EVALUATE_FEATURES )
qt_internal_feature_write_file ( "${CMAKE_CURRENT_BINARY_DIR}/${__QtFeature_private_file}"
" $ { _ _ Q t F e a t u r e _ p r i v a t e _ f e a t u r e s } " " $ { _ _ Q t F e a t u r e _ p r i v a t e _ e x t r a } "
)
qt_internal_feature_write_file ( "${CMAKE_CURRENT_BINARY_DIR}/${__QtFeature_public_file}"
" $ { _ _ Q t F e a t u r e _ p u b l i c _ f e a t u r e s } " " $ { _ _ Q t F e a t u r e _ p u b l i c _ e x t r a } "
)
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;QT_QMAKE_PUBLIC_CONFIG;QT_QMAKE_PRIVATE_CONFIG;QT_QMAKE_PUBLIC_QT_CONFIG" )
endif ( )
foreach ( visibility public private )
string ( TOUPPER "${visibility}" capitalVisibility )
foreach ( state enabled disabled )
string ( TOUPPER "${state}" capitalState )
set_property ( TARGET "${target}" PROPERTY ${ propertyPrefix } QT_ ${ capitalState } _ ${ capitalVisibility } _FEATURES "${${state}_${visibility}_features}" )
endforeach ( )
endforeach ( )
set_property ( TARGET "${target}"
P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ Q M A K E _ P U B L I C _ C O N F I G
" $ { _ _ Q t F e a t u r e _ q m a k e _ p u b l i c _ c o n f i g } " )
set_property ( TARGET "${target}"
P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ Q M A K E _ P R I V A T E _ C O N F I G
" $ { _ _ Q t F e a t u r e _ q m a k e _ p r i v a t e _ c o n f i g } " )
set_property ( TARGET "${target}"
P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ Q M A K E _ P U B L I C _ Q T _ C O N F I G
" $ { _ _ Q t F e a t u r e _ q m a k e _ p u b l i c _ q t _ c o n f i g } " )
# Config values were the old-school features before actual configure.json features were
# implemented. Therefore "CONFIG+=foo" values should be considered features as well,
# so that CMake can find them when building qtmultimedia for example.
if ( __QtFeature_qmake_public_config )
set_property ( TARGET "${target}"
A P P E N D P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ E N A B L E D _ P U B L I C _ F E A T U R E S
$ { _ _ Q t F e a t u r e _ q m a k e _ p u b l i c _ c o n f i g } )
endif ( )
if ( __QtFeature_qmake_private_config )
set_property ( TARGET "${target}"
A P P E N D P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ E N A B L E D _ P R I V A T E _ F E A T U R E S
$ { _ _ Q t F e a t u r e _ q m a k e _ p r i v a t e _ c o n f i g } )
endif ( )
if ( __QtFeature_qmake_public_qt_config )
set_property ( TARGET "${target}"
A P P E N D P R O P E R T Y $ { p r o p e r t y P r e f i x } Q T _ E N A B L E D _ P U B L I C _ F E A T U R E S
$ { _ _ Q t F e a t u r e _ q m a k e _ p u b l i c _ q t _ c o n f i g } )
endif ( )
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 )
unset ( __QtFeature_internal_features PARENT_SCOPE )
unset ( __QtFeature_private_file PARENT_SCOPE )
unset ( __QtFeature_public_file PARENT_SCOPE )
unset ( __QtFeature_private_extra PARENT_SCOPE )
unset ( __QtFeature_public_extra PARENT_SCOPE )
unset ( __QtFeature_define_definitions PARENT_SCOPE )
unset ( __QtFeature_custom_enabled_features PARENT_SCOPE )
unset ( __QtFeature_custom_disabled_features PARENT_SCOPE )
unset ( __QtFeature_only_evaluate_features PARENT_SCOPE )
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
# See https://gitlab.kitware.com/cmake/cmake/issues/19261.
# To circumvent that, copy the properties from GlobalConfig to Core target.
# This way the global features actually get set in the generated CoreTargets.cmake file.
if ( target STREQUAL Core )
foreach ( visibility public private )
string ( TOUPPER "${visibility}" capitalVisibility )
foreach ( state enabled disabled )
string ( TOUPPER "${state}" capitalState )
set ( core_property_name "QT_${capitalState}_${capitalVisibility}_FEATURES" )
set ( global_property_name "INTERFACE_${core_property_name}" )
get_property ( core_values TARGET Core PROPERTY ${ core_property_name } )
get_property ( global_values TARGET GlobalConfig PROPERTY ${ global_property_name } )
set ( total_values ${ core_values } ${ global_values } )
set_property ( TARGET Core PROPERTY ${ core_property_name } ${ total_values } )
endforeach ( )
endforeach ( )
set ( config_property_names
Q T _ Q M A K E _ P U B L I C _ C O N F I G Q T _ Q M A K E _ P R I V A T E _ C O N F I G Q T _ Q M A K E _ P U B L I C _ Q T _ C O N F I G )
foreach ( property_name ${ config_property_names } )
set ( core_property_name "${property_name}" )
set ( global_property_name "INTERFACE_${core_property_name}" )
get_property ( core_values TARGET Core PROPERTY ${ core_property_name } )
get_property ( global_values TARGET GlobalConfig PROPERTY ${ global_property_name } )
set ( total_values ${ core_values } ${ global_values } )
set_property ( TARGET Core PROPERTY ${ core_property_name } ${ total_values } )
endforeach ( )
endif ( )
endfunction ( )
2023-11-02 05:23:55 +08:00
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 } )
qt_internal_compute_feature_value_from_possible_input ( "${feature}" )
if ( DEFINED "FEATURE_${feature}" AND
N O T " $ { Q T _ F E A T U R E _ $ { f e a t u r e } } " S T R E Q U A L " $ { F E A T U R E _ $ { f e a t u r e } } " )
message ( " '${feature}' was changed from ${QT_FEATURE_${feature}} "
" t o $ { F E A T U R E _ $ { f e a t u r e } } " )
set ( dirty_build TRUE )
set_property ( GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}" )
2023-12-05 01:42:35 +08:00
# If the user changed the value of the feature directly (e.g by editing
# CMakeCache.txt), and not via its associated INPUT variable, unset the INPUT cache
# variable before it is used in feature evaluation, to ensure a stale value doesn't
# influence other feature values, especially when QT_INTERNAL_CALLED_FROM_CONFIGURE
# is TRUE and the INPUT_foo variable is not passed.
# e.g. first configure -no-gui, then manually toggle FEATURE_gui to ON in
# CMakeCache.txt, then reconfigure (with the configure script) without -no-gui.
# Without this unset(), we'd have switched FEATURE_gui to OFF again.
if ( NOT FEATURE_ ${ feature } _computed_from_input )
unset ( "INPUT_${feature}" CACHE )
endif ( )
2023-11-02 05:23:55 +08:00
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
" D u e t o d e t e c t e d f e a t u r e s e t c h a n g e s , d e p e n d e n t f e a t u r e s "
" w i l l b e r e - c o m p u t e d a u t o m a t i c a l l y . T h i s m i g h t c a u s e a l o t o f f i l e s t o b e r e b u i l t . "
" T o d i s a b l e t h i s b e h a v i o r , c o n f i g u r e w i t h - D Q T _ N O _ F E A T U R E _ A U T O _ R E S E T = O N " )
endif ( )
endif ( )
endfunction ( )
2023-10-30 06:33:08 +08:00
function ( qt_config_compile_test name )
if ( DEFINED "TEST_${name}" )
return ( )
endif ( )
cmake_parse_arguments ( arg "" "LABEL;PROJECT_PATH;C_STANDARD;CXX_STANDARD"
" C O M P I L E _ O P T I O N S ; L I B R A R I E S ; C O D E ; P A C K A G E S ; C M A K E _ F L A G S " $ { A R G N } )
if ( arg_PROJECT_PATH )
message ( STATUS "Performing Test ${arg_LABEL}" )
set ( flags "" )
qt_get_platform_try_compile_vars ( platform_try_compile_vars )
list ( APPEND flags ${ platform_try_compile_vars } )
# 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" )
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.
if ( arg_PACKAGES )
set ( packages_list "" )
# Parse the package names, version, etc. An example would be:
# PACKAGE Foo 6 REQUIRED
# PACKAGE Bar 2 COMPONENTS Baz
foreach ( p ${ arg_PACKAGES } )
if ( p STREQUAL PACKAGE )
if ( package_entry )
# Encode the ";" into "\;" to separate the arguments of a find_package call.
string ( REPLACE ";" "\\;" package_entry_string "${package_entry}" )
list ( APPEND packages_list "${package_entry_string}" )
endif ( )
set ( package_entry "" )
else ( )
list ( APPEND package_entry "${p}" )
endif ( )
endforeach ( )
# Parse final entry.
if ( package_entry )
string ( REPLACE ";" "\\;" package_entry_string "${package_entry}" )
list ( APPEND packages_list "${package_entry_string}" )
endif ( )
# Encode the ";" again.
string ( REPLACE ";" "\\;" packages_list "${packages_list}" )
# The flags are separated by ';', the find_package entries by '\;',
# and the package parts of an entry by '\\;'.
# Example:
# WrapFoo\\;6\\;COMPONENTS\\;bar\;WrapBaz\\;5
list ( APPEND flags "-DQT_CONFIG_COMPILE_TEST_PACKAGES:STRING=${packages_list}" )
# Inside the project, the value of QT_CONFIG_COMPILE_TEST_PACKAGES is used in a foreach
# loop that calls find_package() for each package entry, and thus the variable expansion
# ends up calling something like find_package(WrapFoo;6;COMPONENTS;bar) aka
# find_package(WrapFoo 6 COMPONENTS bar).
endif ( )
# Pass which libraries need to be linked against.
if ( arg_LIBRARIES )
set ( link_flags "" )
set ( library_targets "" )
# Separate targets from link flags or paths. This is to prevent configuration failures
# when the targets are not found due to missing packages.
foreach ( lib ${ arg_LIBRARIES } )
string ( FIND "${lib}" "::" is_library_target )
if ( is_library_target EQUAL -1 )
list ( APPEND link_flags "${lib}" )
else ( )
list ( APPEND library_targets "${lib}" )
endif ( )
endforeach ( )
if ( link_flags )
list ( APPEND flags "-DQT_CONFIG_COMPILE_TEST_LIBRARIES:STRING=${link_flags}" )
endif ( )
if ( library_targets )
list ( APPEND flags
" - D Q T _ C O N F I G _ C O M P I L E _ T E S T _ L I B R A R Y _ T A R G E T S : S T R I N G = $ { l i b r a r y _ t a r g e t s } " )
endif ( )
endif ( )
# 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}"
" $ { n a m e } " C M A K E _ F L A G S $ { f l a g s } $ { a r g _ C M A K E _ F L A G S } )
if ( ${ HAVE_${name } } )
set ( status_label "Success" )
else ( )
set ( status_label "Failed" )
endif ( )
message ( STATUS "Performing Test ${arg_LABEL} - ${status_label}" )
else ( )
foreach ( library IN ITEMS ${ arg_LIBRARIES } )
if ( NOT TARGET "${library}" )
# If the dependency looks like a cmake target, then make this compile test
# 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 )
2023-11-02 05:23:55 +08:00
message ( STATUS "Performing Test ${arg_LABEL} - Failed because ${library} not found" )
2023-10-30 06:33:08 +08:00
set ( HAVE_ ${ name } FALSE )
break ( )
endif ( )
endif ( )
endforeach ( )
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 )
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 ( INPUT_stdlib_libcpp OR 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.
# https://developercommunity.visualstudio.com/content/problem/139261/msvc-incorrectly-defines-cplusplus.html
# No support for the flag in upstream CMake as of 3.17.
# https://gitlab.kitware.com/cmake/cmake/issues/18837
if ( CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1913 )
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 } )
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 } "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}" )
endfunction ( )
# This function should be used for passing required try compile platform variables to the
# project-based try_compile() call.
# out_var will be a list of -Dfoo=bar strings, suitable to pass to CMAKE_FLAGS.
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" )
endif ( )
if ( VCPKG_CHAINLOAD_TOOLCHAIN_FILE )
list ( APPEND flags "VCPKG_CHAINLOAD_TOOLCHAIN_FILE" )
endif ( )
# 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 ( INPUT_stdlib_libcpp OR 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 "" )
foreach ( flag ${ flags } )
if ( ${ flag } )
list ( APPEND flags_cmd_line "-D${flag}=${${flag}}" )
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 )
# 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}" )
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 ( )
endif ( )
set ( flags "-DSIMD:string=${extension}" )
qt_get_platform_try_compile_vars ( platform_try_compile_vars )
list ( APPEND flags ${ platform_try_compile_vars } )
message ( STATUS "Performing Test ${label} intrinsics" )
try_compile ( "TEST_X86SIMD_${extension}"
" $ { C M A K E _ C U R R E N T _ B I N A R Y _ D I R } / c o n f i g . t e s t s / x 8 6 _ s i m d _ $ { e x t e n s i o n } "
" $ { C M A K E _ C U R R E N T _ S O U R C E _ D I R } / c o n f i g . t e s t s / x 8 6 _ s i m d "
x 8 6 _ s i m d
C M A K E _ F L A G S $ { f l a g s } )
if ( ${ TEST_X86SIMD_${extension } } )
set ( status_label "Success" )
else ( )
set ( status_label "Failed" )
endif ( )
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 ( LINUX OR HURD ) OR ANDROID )
return ( )
endif ( )
message ( STATUS "Performing Test ${label}" )
execute_process ( COMMAND "${CMAKE_CXX_COMPILER}" -dumpmachine
O U T P U T _ V A R I A B L E o u t p u t
O U T P U T _ S T R I P _ T R A I L I N G _ W H I T E S P A C E
R E S U L T _ V A R I A B L E e x i t _ c o d e )
if ( exit_code EQUAL 0 )
set ( status_label "Success" )
else ( )
set ( status_label "Failed" )
endif ( )
message ( STATUS "Performing Test ${label} - ${status_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
" $ { Q T _ I N T E R N A L _ L I N K E R _ M O L D } "
" $ { m o l d _ l i n k e r _ p a t h } "
S Y M B O L I C )
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 } )
set ( flags "-Wl,${arg_FLAG}" )
# 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." )
endif ( )
if ( NOT TARGET ${ target } )
message ( FATAL_ERROR "${target} not found." )
endif ( )
get_target_property ( target_type "${target}" TYPE )
if ( "${target_type}" STREQUAL "INTERFACE_LIBRARY" )
set ( property_prefix "INTERFACE_" )
else ( )
set ( property_prefix "" )
endif ( )
foreach ( visibility IN ITEMS PUBLIC PRIVATE )
set ( value ON )
foreach ( state IN ITEMS ENABLED DISABLED )
get_target_property ( features "${target}" ${ property_prefix } QT_ ${ state } _ ${ visibility } _FEATURES )
if ( "${features}" STREQUAL "features-NOTFOUND" )
continue ( )
endif ( )
foreach ( feature IN ITEMS ${ features } )
if ( DEFINED "QT_FEATURE_${feature}" AND NOT "${QT_FEATURE_${feature}}" STREQUAL "${value}" )
message ( WARNING
" T h i s p r o j e c t w a s i n i t i a l l y c o n f i g u r e d w i t h t h e Q t f e a t u r e \ " $ { f e a t u r e } \ " "
" s e t t o \ " $ { Q T _ F E A T U R E _ $ { f e a t u r e } } \ " . W h i l e l o a d i n g t h e "
" \ " $ { t a r g e t } \ " p a c k a g e , t h e v a l u e o f t h e f e a t u r e "
" h a s c h a n g e d t o \ " $ { v a l u e } \ " . T h a t m i g h t c a u s e a p r o j e c t r e b u i l d d u e t o "
" u p d a t e d C + + h e a d e r s . \ n "
" I n c a s e o f b u i l d i s s u e s , c o n s i d e r r e m o v i n g t h e C M a k e C a c h e . t x t f i l e a n d "
" r e c o n f i g u r i n g t h e p r o j e c t . "
)
endif ( )
set ( QT_FEATURE_ ${ feature } "${value}" CACHE INTERNAL "Qt feature: ${feature} (from target ${target})" )
endforeach ( )
set ( value OFF )
endforeach ( )
endforeach ( )
endfunction ( )