diff options
Diffstat (limited to 'cmake/QtFeature.cmake')
-rw-r--r-- | cmake/QtFeature.cmake | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake new file mode 100644 index 0000000000..a4d9421935 --- /dev/null +++ b/cmake/QtFeature.cmake @@ -0,0 +1,435 @@ +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) + + qt_push_features_into_parent_scope(PUBLIC_FEATURES ${_arg_PUBLIC_DEPENDENCIES}) + qt_push_features_into_parent_scope(PRIVATE_FEATURES ${_arg_PRIVATE_DEPENDENCIES}) +endfunction() + +function(qt_feature _feature) + set(QT_FEATURE_DEFINITION_${_feature} ${ARGN} PARENT_SCOPE) + qt_parse_all_arguments(_arg "qt_feature" + "PRIVATE;PUBLIC" + "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${ARGN}) + + if("${_arg_EMIT_IF}" STREQUAL "") + set(_arg_EMIT_IF 1) + endif() + + if (${_arg_EMIT_IF}) + set(QT_FEATURE_${_feature} "UNSET" CACHE STRING "${_arg_LABEL}") + set_property(CACHE QT_FEATURE_${_feature} PROPERTY STRINGS UNSET ON OFF) + + # 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() + 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 two 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_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(NOT QT_FEATURE_${_feature} STREQUAL UNSET) + return() + endif() + + if(NOT DEFINED QT_FEATURE_DEFINITION_${_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_COMPUTED_VALUE_${_feature}) + set(QT_FEATURE_${_feature} ${QT_FEATURE_COMPUTED_VALUE_${_feature}} PARENT_SCOPE) + 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(_arg_CONDITION ON) + endif() + + if(${_arg_DISABLE}) + set(result OFF) + elseif((${_arg_ENABLE}) OR (${_arg_AUTODETECT})) + qt_evaluate_config_expression(result ${_arg_CONDITION}) + else() + # feature not auto-detected and not explicitly enabled + set(result OFF) + endif() + + set(QT_FEATURE_COMPUTED_VALUE_${_feature} "${result}" CACHE INTERNAL "${_arg_LABEL}") + set(QT_FEATURE_${_feature} "${result}" PARENT_SCOPE) +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_generate_feature_line _line _feature) + set(_line "") + 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_feature_write_file _file _features _extra) + message("Generating file ${_file}.") + set(_contents "") + foreach(_it ${_features}) + _qt_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}) + unset(QT_FEATURE_COMPUTED_VALUE_${feature} CACHE) + endforeach() + + foreach(feature ${all_features}) + qt_evaluate_feature(${feature}) + + set(QT_FEATURE_${feature} ${QT_FEATURE_${feature}} PARENT_SCOPE) + 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_COMPUTED_VALUE_${feature} CACHE) + unset(QT_FEATURE_DEFINITION_${feature} CACHE) + endforeach() + + _qt_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_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" "" ${ARGN}) + + check_cxx_source_compiles("${_arg_UNPARSED_ARGUMENTS}" HAVE_${name}) + set(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${_arg_LABEL}") +endfunction() + +function(qt_config_compile_test_x86simd extension label) + string(TOUPPER ${extension} extension_uppercase) + 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_pull_features_into_current_scope) + cmake_parse_arguments(__arg "PUBLIC_FEATURES;PRIVATE_FEATURES" "FEATURE_PROPERTY_INFIX" "" ${ARGN}) + foreach(__target IN ITEMS ${__arg_UNPARSED_ARGUMENTS}) + if(NOT TARGET ${__target}) + continue() + 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 PUBLIC PRIVATE) + set(__value ON) + foreach(__state ENABLED DISABLED) + if(NOT ${__arg_${__visibility}_FEATURES}) + continue() + endif() + get_target_property(__features "${__target}" ${__property_prefix}QT_${__arg_FEATURE_PROPERTY_INFIX}${__state}_${__visibility}_FEATURES) + if("${__features}" STREQUAL "__features-NOTFOUND") + continue() + endif() + foreach(__feature ${__features}) + set(QT_FEATURE_${__feature} ${__value} PARENT_SCOPE) + endforeach() + set(__value OFF) + endforeach() + endforeach() + endforeach() +endfunction() + +macro(qt_push_features_into_parent_scope) + cmake_parse_arguments(__arg "PUBLIC_FEATURES;PRIVATE_FEATURES" "FEATURE_PROPERTY_INFIX" "" ${ARGN}) + foreach(__target IN ITEMS ${__arg_UNPARSED_ARGUMENTS}) + if(NOT TARGET ${__target}) + continue() + 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 PUBLIC PRIVATE) + set(__value ON) + foreach(__state ENABLED DISABLED) + if(NOT ${__arg_${__visibility}_FEATURES}) + continue() + endif() + get_target_property(__features "${__target}" ${__property_prefix}QT_${__arg_FEATURE_PROPERTY_INFIX}${__state}_${__visibility}_FEATURES) + if("${__features}" STREQUAL "__features-NOTFOUND") + continue() + endif() + foreach(__feature ${__features}) + set(QT_FEATURE_${__feature} ${__value} PARENT_SCOPE) + endforeach() + set(__value OFF) + endforeach() + endforeach() + endforeach() +endmacro() + +macro(qt_load_global_features) + if(NOT TARGET Qt::Core) + find_package(Qt${PROJECT_VERSION_MAJOR}Core QUIET) + endif() + qt_pull_features_into_current_scope(PUBLIC_FEATURES PRIVATE_FEATURES FEATURE_PROPERTY_INFIX "GLOBAL_" Qt::Core) +endmacro() |