From 814de4c2ce8a4a9198672c68d64a2eb9c8e30e8b Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 21 Sep 2023 08:48:53 +0200 Subject: [PATCH] CMake: Fix config condition evaluator Use recursive descent to handle parentheses in config condition expressions. This fixes cases like 'NOT (A AND B)'. Fixes: QTBUG-117053 Change-Id: Iab1b6173abe00d763808bb972a9a5443ffa0938d Reviewed-by: Alexandru Croitor Reviewed-by: Amir Masoud Abdol --- cmake/QtFeature.cmake | 49 ++++++++++++------- .../test_config_expressions/CMakeLists.txt | 6 +-- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index 2c6fa94c255..ee7e0ab824f 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -81,35 +81,23 @@ 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(expression "${ARGN}") list(LENGTH expression length) - set(memberIdx -1) + math(EXPR memberIdx "${startIdx} - 1") math(EXPR length "${length}-1") 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") + 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() - elseif(${nestingLevel} GREATER 0) - list(APPEND result ${member}) + break() elseif("${member}" STREQUAL "NOT") list(APPEND result ${member}) elseif("${member}" STREQUAL "AND") @@ -166,9 +154,34 @@ 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_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" diff --git a/tests/auto/cmake/test_config_expressions/CMakeLists.txt b/tests/auto/cmake/test_config_expressions/CMakeLists.txt index 4061728113e..e3863b738ad 100644 --- a/tests/auto/cmake/test_config_expressions/CMakeLists.txt +++ b/tests/auto/cmake/test_config_expressions/CMakeLists.txt @@ -88,9 +88,9 @@ assert_F(A AND B) assert_T(A AND (B OR C)) assert_T((A AND B) OR C) assert_T((A AND B) OR (NOT B AND C)) -expect_failure_F(NOT (B OR C)) # QTBUG-117053 -expect_failure_T(NOT (A AND B)) # QTBUG-117053 -expect_failure_F(NOT (B OR C)) # QTBUG-117053 +assert_F(NOT (B OR C)) +assert_T(NOT (A AND B)) +assert_F(NOT (B OR C)) # target check set(lib1_cpp "${CMAKE_CURRENT_BINARY_DIR}/lib1.cpp")