mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-26 04:09:35 +03:00
Let $<LIST:FILTER> select elements by an arbitrary condition: PREDICATE keeps or drops each element according to a <body> evaluated with $<_0> bound, complementing the existing regular-expression form. Also accept an explicit REGEX keyword before the pattern so the regex and predicate forms read symmetrically; the bare pattern keeps working. Issue: #27892
190 lines
5.0 KiB
C++
190 lines
5.0 KiB
C++
/* 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 <cm/memory>
|
|
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmState.h"
|
|
#include "cmStateDirectory.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmake.h"
|
|
|
|
namespace {
|
|
struct GenExFixture
|
|
{
|
|
cmake CMake{ cmState::Role::Project };
|
|
std::unique_ptr<cmGlobalGenerator> GG;
|
|
std::unique_ptr<cmMakefile> MF;
|
|
std::unique_ptr<cmLocalGenerator> LG;
|
|
|
|
GenExFixture()
|
|
{
|
|
this->GG = cm::make_unique<cmGlobalGenerator>(&this->CMake);
|
|
cmStateSnapshot snapshot = this->CMake.GetCurrentSnapshot();
|
|
snapshot.GetDirectory().SetCurrentBinary(".");
|
|
snapshot.GetDirectory().SetCurrentSource(".");
|
|
this->MF = cm::make_unique<cmMakefile>(this->GG.get(), snapshot);
|
|
this->LG = this->GG->CreateLocalGenerator(this->MF.get());
|
|
}
|
|
|
|
std::string Eval(std::string const& expr)
|
|
{
|
|
return cmGeneratorExpression::Evaluate(expr, this->LG.get(), "Debug");
|
|
}
|
|
};
|
|
|
|
bool expectEq(char const* name, std::string const& got,
|
|
std::string const& want)
|
|
{
|
|
if (got != want) {
|
|
std::cerr << name << ": expected '" << want << "', got '" << got << "'\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool testFilterPredicateInclude()
|
|
{
|
|
GenExFixture fx;
|
|
// Keep only elements equal to "a".
|
|
return expectEq(
|
|
"testFilterPredicateInclude",
|
|
fx.Eval("$<LIST:FILTER,a;b;a;c,INCLUDE,PREDICATE,$<STREQUAL:$<_0>,a>>"),
|
|
"a;a");
|
|
}
|
|
|
|
static bool testFilterPredicateExclude()
|
|
{
|
|
GenExFixture fx;
|
|
// Drop elements equal to "a".
|
|
return expectEq(
|
|
"testFilterPredicateExclude",
|
|
fx.Eval("$<LIST:FILTER,a;b;a;c,EXCLUDE,PREDICATE,$<STREQUAL:$<_0>,a>>"),
|
|
"b;c");
|
|
}
|
|
|
|
static bool testFilterRegexKeyword()
|
|
{
|
|
GenExFixture fx;
|
|
// Explicit REGEX keyword behaves like the bare form.
|
|
return expectEq("testFilterRegexKeyword",
|
|
fx.Eval("$<LIST:FILTER,foo;bar;baz,INCLUDE,REGEX,^ba>"),
|
|
"bar;baz");
|
|
}
|
|
|
|
static bool testFilterBareRegexUnchanged()
|
|
{
|
|
GenExFixture fx;
|
|
// The legacy bare form still works.
|
|
return expectEq("testFilterBareRegexUnchanged",
|
|
fx.Eval("$<LIST:FILTER,foo;bar;baz,EXCLUDE,^ba>"), "foo");
|
|
}
|
|
|
|
static bool testCannedTransformStillWorks()
|
|
{
|
|
GenExFixture fx;
|
|
// Existing canned action + REGEX selector must be unaffected by the
|
|
// refactor.
|
|
return expectEq("testCannedTransformStillWorks",
|
|
fx.Eval("$<LIST:TRANSFORM,foo;bar;baz,TOUPPER,REGEX,^ba>"),
|
|
"foo;BAR;BAZ");
|
|
}
|
|
|
|
static bool testTransformPredicateCanned()
|
|
{
|
|
GenExFixture fx;
|
|
// PREPEND "X-" only to elements equal to "a"; others pass through.
|
|
return expectEq(
|
|
"testTransformPredicateCanned",
|
|
fx.Eval(
|
|
"$<LIST:TRANSFORM,a;b;a,PREPEND,X-,PREDICATE,$<STREQUAL:$<_0>,a>>"),
|
|
"X-a;b;X-a");
|
|
}
|
|
|
|
static bool testTransformPredicateNoneSelected()
|
|
{
|
|
GenExFixture fx;
|
|
// No element matches: the list is returned unchanged.
|
|
return expectEq("testTransformPredicateNoneSelected",
|
|
fx.Eval("$<LIST:TRANSFORM,a;b;c,TOUPPER,PREDICATE,0>"),
|
|
"a;b;c");
|
|
}
|
|
|
|
static bool testTransformPredicateApply()
|
|
{
|
|
GenExFixture fx;
|
|
// Upper-case only elements equal to "a"; "b" passes through unchanged.
|
|
return expectEq("testTransformPredicateApply",
|
|
fx.Eval("$<LIST:TRANSFORM,a;b;a,APPLY,$<UPPER_CASE:$<_0>>,"
|
|
"PREDICATE,$<STREQUAL:$<_0>,a>>"),
|
|
"A;b;A");
|
|
}
|
|
|
|
static bool testTransformPredicateApplyShadowing()
|
|
{
|
|
GenExFixture fx;
|
|
// The apply body and predicate body each independently bind $<_0>.
|
|
return expectEq("testTransformPredicateApplyShadowing",
|
|
fx.Eval("$<LIST:TRANSFORM,a;bb,APPLY,$<_0>$<_0>,PREDICATE,"
|
|
"$<STREQUAL:$<_0>,a>>"),
|
|
"aa;bb");
|
|
}
|
|
|
|
static bool testFilterPredicateEmptyList()
|
|
{
|
|
GenExFixture fx;
|
|
return expectEq("testFilterPredicateEmptyList",
|
|
fx.Eval("$<LIST:FILTER,,INCLUDE,PREDICATE,1>"), "");
|
|
}
|
|
|
|
static bool testTransformPredicateEmptyList()
|
|
{
|
|
GenExFixture fx;
|
|
return expectEq("testTransformPredicateEmptyList",
|
|
fx.Eval("$<LIST:TRANSFORM,,TOUPPER,PREDICATE,1>"), "");
|
|
}
|
|
|
|
int testGenExListPredicate(int /*argc*/, char* /*argv*/[])
|
|
{
|
|
if (!testFilterPredicateInclude()) {
|
|
return 1;
|
|
}
|
|
if (!testFilterPredicateExclude()) {
|
|
return 1;
|
|
}
|
|
if (!testFilterRegexKeyword()) {
|
|
return 1;
|
|
}
|
|
if (!testFilterBareRegexUnchanged()) {
|
|
return 1;
|
|
}
|
|
if (!testCannedTransformStillWorks()) {
|
|
return 1;
|
|
}
|
|
if (!testTransformPredicateCanned()) {
|
|
return 1;
|
|
}
|
|
if (!testTransformPredicateNoneSelected()) {
|
|
return 1;
|
|
}
|
|
if (!testTransformPredicateApply()) {
|
|
return 1;
|
|
}
|
|
if (!testTransformPredicateApplyShadowing()) {
|
|
return 1;
|
|
}
|
|
if (!testFilterPredicateEmptyList()) {
|
|
return 1;
|
|
}
|
|
if (!testTransformPredicateEmptyList()) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|