mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
GenEx: add bound-operand binding mechanism and $<_0>
Introduce "binding operations": generator expressions that evaluate a <body> once for each value they supply, with $<_0> expanding to that value. This is the foundation the $<LIST:TRANSFORM,...,APPLY> action and the predicate selectors build on, letting a <body> refer to the element being processed. Using $<_0> outside a binding operation is reported as an error rather than silently expanding to nothing. Issue: #27892
This commit is contained in:
@@ -1048,6 +1048,22 @@ List Ordering
|
||||
|
||||
$<LIST:SORT,list,CASE:SENSITIVE,COMPARE:STRING,ORDER:DESCENDING>
|
||||
|
||||
.. _GenEx Bound Operands:
|
||||
|
||||
Bound Operands
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. genex:: $<_0>
|
||||
|
||||
.. versionadded:: 4.5
|
||||
|
||||
``$<_0>`` is the *bound operand* of the enclosing *binding operation*: a
|
||||
generator expression that evaluates a ``<body>`` once for each value it
|
||||
supplies, with ``$<_0>`` expanding to that value.
|
||||
|
||||
``$<_0>`` is only valid inside the body of a binding operation. Using it
|
||||
anywhere else is an error.
|
||||
|
||||
Path Expressions
|
||||
----------------
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/optional>
|
||||
|
||||
@@ -25,9 +26,26 @@ struct Context final
|
||||
void SetCMP0189(cmPolicies::PolicyStatus cmp0189);
|
||||
cmPolicies::PolicyStatus GetCMP0189() const;
|
||||
|
||||
void SetBoundOperand(std::string value);
|
||||
bool HasBoundOperand() const;
|
||||
std::string const& GetBoundOperand() const;
|
||||
|
||||
private:
|
||||
cm::optional<cmPolicies::PolicyStatus> CMP0189;
|
||||
cm::optional<std::string> BoundOperand;
|
||||
};
|
||||
|
||||
inline void Context::SetBoundOperand(std::string value)
|
||||
{
|
||||
this->BoundOperand = std::move(value);
|
||||
}
|
||||
inline bool Context::HasBoundOperand() const
|
||||
{
|
||||
return this->BoundOperand.has_value();
|
||||
}
|
||||
inline std::string const& Context::GetBoundOperand() const
|
||||
{
|
||||
return *this->BoundOperand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,12 @@ struct GeneratorExpressionContent : public cmGeneratorExpressionEvaluator
|
||||
|
||||
std::string GetOriginalExpression() const;
|
||||
|
||||
std::vector<cmGeneratorExpressionEvaluatorVector> const& GetParamChildren()
|
||||
const
|
||||
{
|
||||
return this->ParamChildren;
|
||||
}
|
||||
|
||||
~GeneratorExpressionContent() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -141,6 +141,27 @@ static const struct OneNode : public cmGeneratorExpressionNode
|
||||
}
|
||||
} oneNode;
|
||||
|
||||
static const struct BoundOperandNode : public cmGeneratorExpressionNode
|
||||
{
|
||||
BoundOperandNode() {} // NOLINT(modernize-use-equals-default)
|
||||
|
||||
int NumExpectedParameters() const override { return 0; }
|
||||
|
||||
std::string Evaluate(
|
||||
std::vector<std::string> const& /*parameters*/,
|
||||
cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content,
|
||||
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
||||
{
|
||||
if (!eval->Context.HasBoundOperand()) {
|
||||
reportError(eval, content->GetOriginalExpression(),
|
||||
"$<_0> may only be used inside the body of a binding "
|
||||
"operation.");
|
||||
return std::string();
|
||||
}
|
||||
return eval->Context.GetBoundOperand();
|
||||
}
|
||||
} boundOperandNode;
|
||||
|
||||
static const struct OneNode buildInterfaceNode;
|
||||
|
||||
static const struct ZeroNode installInterfaceNode;
|
||||
@@ -5932,6 +5953,7 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
|
||||
{ "PATH_EQUAL", &pathEqualNode },
|
||||
{ "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
|
||||
{ "BOOL", &boolNode },
|
||||
{ "_0", &boundOperandNode },
|
||||
{ "IF", &ifNode },
|
||||
{ "ANGLE-R", &angle_rNode },
|
||||
{ "COMMA", &commaNode },
|
||||
|
||||
@@ -14,6 +14,7 @@ set(CMakeLib_TESTS
|
||||
testDocumentationFormatter.cxx
|
||||
testGccDepfileReader.cxx
|
||||
testGeneratedFileStream.cxx
|
||||
testGenExBoundOperand.cxx
|
||||
testJSONHelpers.cxx
|
||||
testRST.cxx
|
||||
testRange.cxx
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "cmGenExContext.h"
|
||||
|
||||
static bool testContextBinding()
|
||||
{
|
||||
cm::GenEx::Context ctx(nullptr, "Debug");
|
||||
bool ok = true;
|
||||
if (ctx.HasBoundOperand()) {
|
||||
std::cerr << "binding should start unset\n";
|
||||
ok = false;
|
||||
}
|
||||
ctx.SetBoundOperand("net");
|
||||
if (!ctx.HasBoundOperand() || ctx.GetBoundOperand() != "net") {
|
||||
std::cerr << "binding did not round-trip\n";
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int testGenExBoundOperand(int /*argc*/, char* /*argv*/[])
|
||||
{
|
||||
if (!testContextBinding()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1 @@
|
||||
\$<_0> may only be used inside the body of a binding operation
|
||||
@@ -0,0 +1 @@
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bad.txt" CONTENT "$<_0>")
|
||||
@@ -57,6 +57,7 @@ run_cmake(FILTER-InvalidOperator)
|
||||
run_cmake(FILTER-Exclude)
|
||||
run_cmake(FILTER-Include)
|
||||
run_cmake(LIST-edgecases)
|
||||
run_cmake(BoundOperandOutsideBinding)
|
||||
|
||||
function(run_cmake_build test)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
|
||||
|
||||
Reference in New Issue
Block a user