cmGeneratorExpressionEvaluator: Short-circuit boolean operators

stage/master/nightly/2023/09/26
Martin Duffy 2023-09-11 09:36:12 -04:00
parent 49e2a4a0a7
commit 634079b86d
20 changed files with 110 additions and 4 deletions

View File

@ -195,6 +195,12 @@ Two forms of conditional generator expressions are supported:
if ``condition`` is ``0``. Any other value for ``condition`` results in an
error.
.. versionadded:: 3.28
This generator expression short-circuits such that generator expressions in
``false_string`` will not evaluate when ``condition`` is ``1``, and generator
expressions in ``true_string`` will not evaluate when condition is ``0``.
Typically, the ``condition`` is itself a generator expression. For instance,
the following expression expands to ``DEBUG_MODE`` when the ``Debug``
configuration is used, and the empty string for all other configurations:
@ -252,6 +258,11 @@ The common boolean logic operators are supported:
``condition`` must be ``0`` or ``1``. The result of the expression is
``0`` if ``condition`` is ``1``, else ``1``.
.. versionadded:: 3.28
Logical operators short-circuit such that generator expressions in the
arguments list will not be evaluated once a return value can be determined.
.. _`Comparison Expressions`:
Primary Comparison Expressions

View File

@ -0,0 +1,5 @@
genexp-no-eval
--------------
* :manual:`generator expressions <cmake-generator-expressions(7)>`
short-circuit to avoid unnecessary evaluation of parameters.

View File

@ -153,10 +153,12 @@ std::string GeneratorExpressionContent::EvaluateParameters(
return std::string();
}
std::string parameter;
for (const auto& pExprEval : *pit) {
parameter += pExprEval->Evaluate(context, dagChecker);
if (context->HadError) {
return std::string();
if (node->ShouldEvaluateNextParameter(parameters, parameter)) {
for (const auto& pExprEval : *pit) {
parameter += pExprEval->Evaluate(context, dagChecker);
if (context->HadError) {
return std::string();
}
}
}
parameters.push_back(std::move(parameter));

View File

@ -128,6 +128,16 @@ struct BooleanOpNode : public cmGeneratorExpressionNode
int NumExpectedParameters() const override { return OneOrMoreParameters; }
bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters,
std::string& def_value) const override
{
if (!parameters.empty() && parameters[0] == failureVal) {
def_value = failureVal;
return false;
}
return true;
}
std::string Evaluate(const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,
@ -195,6 +205,13 @@ static const struct IfNode : public cmGeneratorExpressionNode
int NumExpectedParameters() const override { return 3; }
bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters,
std::string&) const override
{
return (parameters.empty() ||
parameters[0] != cmStrCat(parameters.size() - 1, ""));
}
std::string Evaluate(const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,

View File

@ -33,6 +33,12 @@ struct cmGeneratorExpressionNode
virtual int NumExpectedParameters() const { return 1; }
virtual bool ShouldEvaluateNextParameter(const std::vector<std::string>&,
std::string&) const
{
return true;
}
virtual std::string Evaluate(
const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,

View File

@ -393,6 +393,7 @@ add_RunCMake_test(GenEx-PATH)
add_RunCMake_test(GenEx-PATH_EQUAL)
add_RunCMake_test(GenEx-LIST)
add_RunCMake_test(GeneratorExpression)
add_RunCMake_test(GeneratorExpressionShortCircuit)
add_RunCMake_test(GeneratorInstance)
add_RunCMake_test(GeneratorPlatform)
if(XCODE_VERSION)

View File

@ -0,0 +1,8 @@
CMake Error at BadAND.cmake:2 \(add_custom_target\):
Error evaluating generator expression:
\$<0>
\$<0> expression requires a parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(error $<0>)
add_custom_target(check ALL COMMAND check
$<AND:1,${error}>
)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,8 @@
CMake Error at BadIF.cmake:2 \(add_custom_target\):
Error evaluating generator expression:
\$<0>
\$<0> expression requires a parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(error $<0>)
add_custom_target(check ALL COMMAND check
$<IF:0,1,${error}>
)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,8 @@
CMake Error at BadOR.cmake:2 \(add_custom_target\):
Error evaluating generator expression:
\$<0>
\$<0> expression requires a parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(error $<0>)
add_custom_target(check ALL COMMAND check
$<OR:0,${error}>
)

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.26)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,4 @@
set(error $<0>)
add_custom_target(check ALL COMMAND check
$<AND:0,${error}>
)

View File

@ -0,0 +1,5 @@
set(error $<0>)
add_custom_target(check ALL
COMMAND check $<IF:1,1,${error}>
COMMAND Check $<IF:0,${error},1>
)

View File

@ -0,0 +1,4 @@
set(error $<0>)
add_custom_target(check ALL COMMAND check
$<OR:1,${error}>
)

View File

@ -0,0 +1,9 @@
include(RunCMake)
run_cmake(GoodIF)
run_cmake(GoodAND)
run_cmake(GoodOR)
run_cmake(BadIF)
run_cmake(BadAND)
run_cmake(BadOR)