qt5base-lts/cmake/QtFeature.cmake
Kevin Funk 99e14037e2 cmake: QtFeature add debugging aid
Persist the target name in the CMake cache description

Change-Id: I124a8b89e5845d12301993b0546c8d5c91b8d8bb
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2019-02-11 10:53:50 +00:00

443 lines
16 KiB
CMake

function(qt_feature_module_begin)
qt_parse_all_arguments(arg "qt_feature_module_begin"
"" "LIBRARY;PRIVATE_FILE;PUBLIC_FILE" "PUBLIC_DEPENDENCIES;PRIVATE_DEPENDENCIES" ${ARGN})
if ("${arg_LIBRARY}" STREQUAL "")
message(FATAL_ERROR "qt_feature_begin_module needs a LIBRARY name!")
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_library "${arg_LIBRARY}" PARENT_SCOPE)
set(__QtFeature_private_features "" PARENT_SCOPE)
set(__QtFeature_public_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)
endfunction()
function(qt_feature feature)
qt_parse_all_arguments(arg "qt_feature"
"PRIVATE;PUBLIC"
"LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${ARGN})
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()
set(__QtFeature_public_features ${__QtFeature_public_features} PARENT_SCOPE)
set(__QtFeature_private_features ${__QtFeature_private_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()
if("${result}" STREQUAL "")
set(result ON)
else()
qt_evaluate_to_boolean(result)
endif()
set(${resultVar} ${result} PARENT_SCOPE)
endfunction()
function(qt_feature_set_cache_value resultVar feature emit_if calculated 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.")
endif()
# Revisit value:
set(cache "${FEATURE_${feature}}")
if ((cache STREQUAL "ON") OR (cache STREQUAL "OFF"))
set(result "${cache}")
else()
message(FATAL_ERROR "Sanity check failed: FEATURE_${feature} has invalid value \"${cache}\"!")
endif()
# Fix-up user-provided values
set("FEATURE_${feature}" "${cache}" CACHE BOOL "${label}")
else()
# Initial setup:
if (emit_if)
set("FEATURE_${feature}" "${calculated}" CACHE BOOL "${label}")
set(result "${calculated}")
else()
set(result OFF)
endif()
endif()
set("${resultVar}" "${result}" PARENT_SCOPE)
endfunction()
macro(qt_feature_set_value feature cache emit_if condition label)
set(result "${cache}")
if (NOT (condition) AND (cache))
message(SEND_ERROR "Feature \"${feature}\": Forcing to \"${cache}\" breaks its condition.")
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}")
endmacro()
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(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()
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()
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()
if(${arg_DISABLE})
set(result OFF)
elseif((${arg_ENABLE}) OR (${arg_AUTODETECT}))
set(result ${condition})
else()
# feature not auto-detected and not explicitly enabled
set(result OFF)
endif()
if("${arg_EMIT_IF}" STREQUAL "")
set(emit_if ON)
else()
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.")
endif()
qt_feature_set_cache_value(cache "${feature}" "${emit_if}" "${result}" "${arg_LABEL}")
qt_feature_set_value("${feature}" "${cache}" "${emit_if}" "${condition}" "${arg_LABEL}")
endfunction()
function(qt_feature_definition feature name)
qt_parse_all_arguments(arg "qt_feature_definition" "NEGATE" "VALUE" "" ${ARGN})
# Generate code:
set(expected 1)
if (arg_NEGATE)
set(expected -1)
endif()
set(msg "\n#if defined(QT_FEATURE_${feature}) && QT_FEATURE_${feature} == ${expected}\n")
if (arg_VALUE)
string(APPEND msg "# define ${name} ${arg_VALUE}\n")
else()
string(APPEND msg "# define ${name}\n")
endif()
string(APPEND msg "#endif\n")
# Store for later use:
list(FIND __QtFeature_public_features "${feature}" public_index)
if (public_index GREATER -1)
string(APPEND __QtFeature_public_extra "${msg}")
endif()
list(FIND __QtFeature_private_features "${feature}" private_index)
if (private_index GREATER -1)
string(APPEND __QtFeature_private_extra "${msg}")
endif()
set(__QtFeature_public_extra ${__QtFeature_public_extra} PARENT_SCOPE)
set(__QtFeature_private_extra ${__QtFeature_private_extra} PARENT_SCOPE)
endfunction()
function(qt_extra_definition name value)
qt_parse_all_arguments(arg "qt_extra_definition" "PUBLIC;PRIVATE" "" "" ${ARGN})
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)
if (QT_FEATURE_${feature} STREQUAL "ON")
set(line "#define QT_FEATURE_${feature} 1\n\n" PARENT_SCOPE)
elseif(QT_FEATURE_${feature} STREQUAL "OFF")
set(line "#define QT_FEATURE_${feature} -1\n\n" PARENT_SCOPE)
elseif(QT_FEATURE_${feature} STREQUAL "UNSET")
set(line "#define QT_FEATURE_${feature} 0\n\n" PARENT_SCOPE)
else()
message(FATAL_ERROR "${feature} has unexpected value \"${QT_FEATURE_${feature}}\"!")
endif()
endfunction()
function(qt_internal_feature_write_file file features extra)
message("Generating file ${file}.")
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()
function(qt_feature_module_end target)
set(all_features ${__QtFeature_public_features} ${__QtFeature_private_features})
list(REMOVE_DUPLICATES all_features)
foreach(feature ${all_features})
qt_evaluate_feature(${feature})
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(feature ${all_features})
unset(_QT_FEATURE_DEFINITION_${feature} PARENT_SCOPE)
endforeach()
qt_internal_feature_write_file("${CMAKE_CURRENT_BINARY_DIR}/${__QtFeature_private_file}"
"${__QtFeature_private_features}" "${__QtFeature_private_extra}"
)
qt_generate_forwarding_headers("${__QtFeature_library}" SOURCE "${__QtFeature_private_file}" PRIVATE)
qt_internal_feature_write_file("${CMAKE_CURRENT_BINARY_DIR}/${__QtFeature_public_file}"
"${__QtFeature_public_features}" "${__QtFeature_public_extra}"
)
qt_generate_forwarding_headers("${__QtFeature_library}" SOURCE "${__QtFeature_public_file}")
get_target_property(targetType "${target}" TYPE)
if("${targetType}" STREQUAL "INTERFACE_LIBRARY")
set(propertyPrefix "INTERFACE_")
else()
set(propertyPrefix "")
set_target_properties("${target}" PROPERTIES EXPORT_PROPERTIES "QT_ENABLED_PUBLIC_FEATURES;QT_DISABLED_PUBLIC_FEATURES;QT_ENABLED_PRIVATE_FEATURES;QT_DISABLED_PRIVATE_FEATURES")
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()
unset(__QtFeature_library PARENT_SCOPE)
unset(__QtFeature_private_features PARENT_SCOPE)
unset(__QtFeature_public_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)
endfunction()
function(qt_config_compile_test name)
cmake_parse_arguments(arg "" "LABEL" "LIBRARIES;CODE" ${ARGN})
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(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}")
endfunction()
function(qt_config_compile_test_x86simd extension label)
string(TOUPPER ${extension} extension_uppercase)
if (DEFINED TEST_X86SIMD_${extension})
return()
endif()
try_compile(TEST_X86SIMD_${extension} "${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/config.tests/x86_simd/main.cpp"
COMPILE_DEFINITIONS -DQT_COMPILER_SUPPORTS_${extension_uppercase}
OUTPUT_VARIABLE foo
)
set(TEST_subarch_${extension} "${TEST_X86SIMD_${extension}}" CACHE INTERNAL "${label}" )
endfunction()
function(qt_make_features_available target)
if(NOT "${target}" MATCHES "^Qt::[a-zA-z][a-zA-Z0-9_]*$")
message(FATAL_ERROR "${target} does not match Qt::[a-zA-z][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}")
message(FATAL_ERROR "Feature ${feature} is already defined when importing features from ${target}.")
endif()
set(QT_FEATURE_${feature} "${value}" CACHE INTERNAL "Qt feature: ${feature} (from target ${target})")
endforeach()
set(value OFF)
endforeach()
endforeach()
endfunction()