GenEx: add new expressions for string comparisons

This commit is contained in:
Marc Chevrier
2025-10-13 15:13:24 +02:00
parent dab5e6ebb1
commit 7564cbae12
26 changed files with 382 additions and 18 deletions
+52 -17
View File
@@ -284,20 +284,8 @@ This section covers the primary and most widely used comparison types.
Other more specific comparison types are documented in their own separate
sections further below.
String Comparisons
^^^^^^^^^^^^^^^^^^
.. genex:: $<STREQUAL:string1,string2>
``1`` if ``string1`` and ``string2`` are equal, else ``0``.
The comparison is case-sensitive. For a case-insensitive comparison,
combine with a :ref:`string transforming generator expression
<String Transforming Generator Expressions>`. For example, the following
evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
$<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
Numeric Comparisons
^^^^^^^^^^^^^^^^^^^
.. genex:: $<EQUAL:value1,value2>
@@ -330,10 +318,57 @@ Version Comparisons
``1`` if ``v1`` is a version greater than or equal to ``v2``, else ``0``.
.. _`String Transforming Generator Expressions`:
String Expressions
------------------
.. _`String Comparisons Generator Expressions`:
String Comparisons
^^^^^^^^^^^^^^^^^^
The comparisons are case-sensitive. For a case-insensitive comparison,
combine with a :ref:`string transforming generator expression
<String Transforming Generator Expressions>`. For example, the following
evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
$<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
.. genex:: $<STREQUAL:string1,string2>
``1`` if ``string1`` and ``string2`` are lexicographically equal, else ``0``.
.. genex:: $<STRLESS:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically less than ``string2``, else ``0``.
.. genex:: $<STRGREATER:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically greater than ``string2``, else
``0``.
.. genex:: $<STRLESS_EQUAL:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically less than or equal to ``string2``,
else ``0``.
.. genex:: $<STRGREATER_EQUAL:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically greater than or equal to
``string2``, else ``0``.
String Transformations
----------------------
^^^^^^^^^^^^^^^^^^^^^^
.. genex:: $<LOWER_CASE:string>
@@ -343,7 +378,7 @@ String Transformations
Content of ``string`` converted to upper case.
.. genex:: $<MAKE_C_IDENTIFIER:...>
.. genex:: $<MAKE_C_IDENTIFIER:string>
Content of ``...`` converted to a C identifier. The conversion follows the
same behavior as :command:`string(MAKE_C_IDENTIFIER)`.
@@ -0,0 +1,5 @@
GenEx-string-comparisons
------------------------
* CMake gains new :ref:`generator expressions
<String Comparisons Generator Expressions>` for string comparisons.
+81 -1
View File
@@ -26,6 +26,7 @@
#include "cmsys/String.h"
#include "cmCMakePath.h"
#include "cmCMakeString.hxx"
#include "cmComputeLinkInformation.h"
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
@@ -265,9 +266,84 @@ static const struct StrEqualNode : public cmGeneratorExpressionNode
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return parameters.front() == parameters[1] ? "1" : "0";
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::EQUAL, parameters[1])
? "1"
: "0";
}
} strEqualNode;
static const struct StrLessNode : public cmGeneratorExpressionNode
{
StrLessNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::LESS, parameters[1])
? "1"
: "0";
}
} strLessNode;
static const struct StrLessEqualNode : public cmGeneratorExpressionNode
{
StrLessEqualNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::LESS_EQUAL, parameters[1])
? "1"
: "0";
}
} strLessEqualNode;
static const struct StrGreaterNode : public cmGeneratorExpressionNode
{
StrGreaterNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::GREATER, parameters[1])
? "1"
: "0";
}
} strGreaterNode;
static const struct StrGreaterEqualNode : public cmGeneratorExpressionNode
{
StrGreaterEqualNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::GREATER_EQUAL, parameters[1])
? "1"
: "0";
}
} strGreaterEqualNode;
static const struct EqualNode : public cmGeneratorExpressionNode
{
@@ -4872,6 +4948,10 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
{ "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
{ "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
{ "STREQUAL", &strEqualNode },
{ "STRLESS", &strLessNode },
{ "STRLESS_EQUAL", &strLessEqualNode },
{ "STRGREATER", &strGreaterNode },
{ "STRGREATER_EQUAL", &strGreaterEqualNode },
{ "EQUAL", &equalNode },
{ "IN_LIST", &inListNode },
{ "FILTER", &filterNode },
@@ -0,0 +1 @@
1
@@ -0,0 +1,40 @@
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 0
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 1
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:,,>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:something,,>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$
@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRGREATER>
$<STRGREATER:>
$<STRGREATER:,,>
$<STRGREATER:something,,>
VERBATIM)
@@ -0,0 +1 @@
1
@@ -0,0 +1,40 @@
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 0 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 1 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:,,>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:something,,>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$
@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRGREATER_EQUAL>
$<STRGREATER_EQUAL:>
$<STRGREATER_EQUAL:,,>
$<STRGREATER_EQUAL:something,,>
VERBATIM)
@@ -0,0 +1 @@
1
@@ -0,0 +1,40 @@
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS>
\$<STRLESS> expression requires 2 comma separated parameters, but got 0
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:>
\$<STRLESS> expression requires 2 comma separated parameters, but got 1
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:,,>
\$<STRLESS> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:something,,>
\$<STRLESS> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$
@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRLESS>
$<STRLESS:>
$<STRLESS:,,>
$<STRLESS:something,,>
VERBATIM)
@@ -0,0 +1 @@
1
@@ -0,0 +1,40 @@
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
0 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
1 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:,,>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:something,,>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$
@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRLESS_EQUAL>
$<STRLESS_EQUAL:>
$<STRLESS_EQUAL:,,>
$<STRLESS_EQUAL:something,,>
VERBATIM)
@@ -7,6 +7,15 @@ run_cmake(BadOR)
run_cmake(BadAND)
run_cmake(BadNOT)
run_cmake(BadStrEqual)
run_cmake(BadStrLess)
run_cmake(BadStrLessEqual)
run_cmake(BadStrGreater)
run_cmake(BadStrGreaterEqual)
run_cmake(STREQUAL)
run_cmake(STRLESS)
run_cmake(STRLESS_EQUAL)
run_cmake(STRGREATER)
run_cmake(STRGREATER_EQUAL)
run_cmake(BadZero)
run_cmake(BadTargetName)
run_cmake(BadTargetTypeInterface)
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STREQUAL-generated.txt" content)
set(expected "1:0")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STREQUAL>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()
@@ -0,0 +1,3 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STREQUAL-generated.txt" CONTENT "$<STREQUAL:AA,AA>:$<STREQUAL:AA,BB>")
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STRGREATER-generated.txt" content)
set(expected "0:0:0:1")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STRGREATER>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()
@@ -0,0 +1,3 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STRGREATER-generated.txt" CONTENT "$<STRGREATER:A,AA>:$<STRGREATER:AA,BB>:$<STRGREATER:AA,AA>:$<STRGREATER:BB,AA>")
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STRGREATER_EQUAL-generated.txt" content)
set(expected "0:0:1:1")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STRGREATER_EQUAL>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()
@@ -0,0 +1,4 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STRGREATER_EQUAL-generated.txt"
CONTENT "$<STRGREATER_EQUAL:A,AA>:$<STRGREATER_EQUAL:AA,BB>:$<STRGREATER_EQUAL:AA,AA>:$<STRGREATER_EQUAL:BB,AA>")
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STRLESS-generated.txt" content)
set(expected "1:1:0:0")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STRLESS>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()
@@ -0,0 +1,3 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STRLESS-generated.txt" CONTENT "$<STRLESS:A,AA>:$<STRLESS:AA,BB>:$<STRLESS:AA,AA>:$<STRLESS:BB,AA>")
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STRLESS_EQUAL-generated.txt" content)
set(expected "1:1:1:0")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STRLESS_EQUAL>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()
@@ -0,0 +1,4 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STRLESS_EQUAL-generated.txt"
CONTENT "$<STRLESS_EQUAL:A,AA>:$<STRLESS_EQUAL:AA,BB>:$<STRLESS_EQUAL:AA,AA>:$<STRLESS_EQUAL:BB,AA>")