mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-04 14:50:23 +00:00
list(FILTER): Add PREDICATE mode
This commit is contained in:
@@ -19,7 +19,7 @@ Synopsis
|
|||||||
|
|
||||||
`Modification`_
|
`Modification`_
|
||||||
list(`APPEND`_ <list> [<element>...])
|
list(`APPEND`_ <list> [<element>...])
|
||||||
list(`FILTER`_ <list> {INCLUDE | EXCLUDE} REGEX <regex>)
|
list(`FILTER`_ <list> <INCLUDE|EXCLUDE> <MODE>)
|
||||||
list(`INSERT`_ <list> <index> [<element>...])
|
list(`INSERT`_ <list> <index> [<element>...])
|
||||||
list(`POP_BACK`_ <list> [<out-var>...])
|
list(`POP_BACK`_ <list> [<out-var>...])
|
||||||
list(`POP_FRONT`_ <list> [<out-var>...])
|
list(`POP_FRONT`_ <list> [<out-var>...])
|
||||||
@@ -120,15 +120,54 @@ Modification
|
|||||||
that empty list.
|
that empty list.
|
||||||
|
|
||||||
.. signature::
|
.. signature::
|
||||||
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
|
list(FILTER <list> <INCLUDE|EXCLUDE> <MODE>)
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
Includes or removes items from the list that match the mode's pattern.
|
Includes or removes items from the list that match the mode's pattern.
|
||||||
In ``REGEX`` mode, items will be matched against the given regular expression.
|
|
||||||
|
|
||||||
For more information on regular expressions look under
|
``<MODE>`` must be one of the following:
|
||||||
:ref:`string(REGEX) <Regex Specification>`.
|
|
||||||
|
``REGEX``
|
||||||
|
Items will be matched against the given regular expression.
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
|
||||||
|
|
||||||
|
For more information on regular expressions look under
|
||||||
|
:ref:`string(REGEX) <Regex Specification>`.
|
||||||
|
|
||||||
|
``PREDICATE``
|
||||||
|
Specify a user-defined callable as a predicate.
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
list(FILTER <list> <INCLUDE|EXCLUDE> PREDICATE <function>)
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
``<function>`` is a user-defined :command:`function` that acts as a
|
||||||
|
unary predicate. The callable must accept exactly two parameters: the
|
||||||
|
input value and the name of an output variable. The callable must set the
|
||||||
|
output variable to a boolean value in the calling scope.
|
||||||
|
The output variable is interpreted using standard CMake boolean evaluation.
|
||||||
|
If the callable does not set the output variable, it is an error.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
function(file_exists path result)
|
||||||
|
if(EXISTS "${path}")
|
||||||
|
set(${result} TRUE PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${result} FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(candidate_files main.c missing.c utils.c)
|
||||||
|
list(FILTER candidate_files INCLUDE PREDICATE file_exists)
|
||||||
|
|
||||||
.. signature::
|
.. signature::
|
||||||
list(INSERT <list> <element_index> <element> [<element> ...])
|
list(INSERT <list> <element_index> <element> [<element> ...])
|
||||||
|
|||||||
10
Help/release/dev/list-PREDICATE.rst
Normal file
10
Help/release/dev/list-PREDICATE.rst
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
list-PREDICATE
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* The :command:`list(TRANSFORM)` command gained a new ``PREDICATE`` selector
|
||||||
|
that invokes a user-defined callable to decide which elements are
|
||||||
|
transformed.
|
||||||
|
|
||||||
|
* The :command:`list(FILTER)` command gained a new ``PREDICATE`` mode
|
||||||
|
that invokes a user-defined callable to decide which elements are
|
||||||
|
included or excluded, complementing the existing ``REGEX`` mode.
|
||||||
@@ -82,15 +82,26 @@ private:
|
|||||||
bool const IncludeMatches;
|
bool const IncludeMatches;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hash of call site (FilePath:Line) for unique variable names across recursive
|
||||||
|
// calls.
|
||||||
|
std::string OutputVarFor(cm::string_view prefix, cmMakefile& makefile)
|
||||||
|
{
|
||||||
|
cmListFileContext context = makefile.GetBacktrace().Top();
|
||||||
|
std::size_t hash =
|
||||||
|
std::hash<std::string>{}(cmStrCat(context.FilePath, ":", context.Line));
|
||||||
|
return cmStrCat(prefix, hash, "_");
|
||||||
|
}
|
||||||
|
|
||||||
class PredicateEvaluator
|
class PredicateEvaluator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PredicateEvaluator(std::string const& functionName, cmMakefile& makefile,
|
PredicateEvaluator(std::string functionName, cmMakefile& makefile,
|
||||||
std::string errorPrefix = "sub-command TRANSFORM, "
|
std::string errorPrefix = "sub-command TRANSFORM, "
|
||||||
"selector PREDICATE")
|
"selector PREDICATE")
|
||||||
: FunctionName(functionName)
|
: FunctionName(std::move(functionName))
|
||||||
, Makefile(&makefile)
|
, Makefile(&makefile)
|
||||||
, ErrorPrefix(std::move(errorPrefix))
|
, ErrorPrefix(std::move(errorPrefix))
|
||||||
|
, OutputVar(OutputVarFor("_cmake_predicate_out_", makefile))
|
||||||
{
|
{
|
||||||
if (!makefile.GetState()->GetCommand(this->FunctionName)) {
|
if (!makefile.GetState()->GetCommand(this->FunctionName)) {
|
||||||
throw cmList::transform_error(cmStrCat(this->ErrorPrefix,
|
throw cmList::transform_error(cmStrCat(this->ErrorPrefix,
|
||||||
@@ -101,13 +112,13 @@ public:
|
|||||||
|
|
||||||
bool operator()(std::string const& value)
|
bool operator()(std::string const& value)
|
||||||
{
|
{
|
||||||
std::string const outputVar = "_list_predicate_out_";
|
this->Makefile->RemoveDefinition(this->OutputVar);
|
||||||
this->Makefile->RemoveDefinition(outputVar);
|
|
||||||
|
|
||||||
cmListFileContext context = this->Makefile->GetBacktrace().Top();
|
cmListFileContext context = this->Makefile->GetBacktrace().Top();
|
||||||
std::vector<cmListFileArgument> funcArgs;
|
std::vector<cmListFileArgument> funcArgs;
|
||||||
funcArgs.emplace_back(value, cmListFileArgument::Quoted, context.Line);
|
funcArgs.emplace_back(value, cmListFileArgument::Quoted, context.Line);
|
||||||
funcArgs.emplace_back(outputVar, cmListFileArgument::Quoted, context.Line);
|
funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
|
||||||
|
context.Line);
|
||||||
cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
|
cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
|
||||||
std::move(funcArgs) };
|
std::move(funcArgs) };
|
||||||
|
|
||||||
@@ -119,7 +130,7 @@ public:
|
|||||||
"\" failed during execution."));
|
"\" failed during execution."));
|
||||||
}
|
}
|
||||||
|
|
||||||
cmValue result = this->Makefile->GetDefinition(outputVar);
|
cmValue result = this->Makefile->GetDefinition(this->OutputVar);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw cmList::transform_error(
|
throw cmList::transform_error(
|
||||||
cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
|
cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
|
||||||
@@ -127,7 +138,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool boolResult = cmIsOn(*result);
|
bool boolResult = cmIsOn(*result);
|
||||||
this->Makefile->RemoveDefinition(outputVar);
|
this->Makefile->RemoveDefinition(this->OutputVar);
|
||||||
return boolResult;
|
return boolResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +146,26 @@ private:
|
|||||||
std::string FunctionName;
|
std::string FunctionName;
|
||||||
cmMakefile* Makefile = nullptr;
|
cmMakefile* Makefile = nullptr;
|
||||||
std::string ErrorPrefix;
|
std::string ErrorPrefix;
|
||||||
|
std::string OutputVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MatchesPredicate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MatchesPredicate(PredicateEvaluator& evaluator, cmList::FilterMode mode)
|
||||||
|
: Evaluator(evaluator)
|
||||||
|
, IncludeMatches(mode == cmList::FilterMode::INCLUDE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(std::string const& target)
|
||||||
|
{
|
||||||
|
return this->Evaluator(target) ^ this->IncludeMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PredicateEvaluator& Evaluator;
|
||||||
|
bool IncludeMatches;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +185,23 @@ cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmList& cmList::filter(std::string const& functionName, FilterMode mode,
|
||||||
|
cmMakefile& makefile)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
PredicateEvaluator evaluator(functionName, makefile,
|
||||||
|
"sub-command FILTER, mode PREDICATE");
|
||||||
|
|
||||||
|
auto it = std::remove_if(this->Values.begin(), this->Values.end(),
|
||||||
|
MatchesPredicate{ evaluator, mode });
|
||||||
|
this->Values.erase(it, this->Values.end());
|
||||||
|
} catch (transform_error& e) {
|
||||||
|
throw std::invalid_argument(e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class StringSorter
|
class StringSorter
|
||||||
{
|
{
|
||||||
@@ -655,6 +703,7 @@ public:
|
|||||||
TransformAction::Initialize(selector);
|
TransformAction::Initialize(selector);
|
||||||
this->FunctionName = functionName;
|
this->FunctionName = functionName;
|
||||||
this->Makefile = &makefile;
|
this->Makefile = &makefile;
|
||||||
|
this->OutputVar = OutputVarFor("_cmake_transform_apply_out_", makefile);
|
||||||
|
|
||||||
// Validate: command must exist
|
// Validate: command must exist
|
||||||
if (!makefile.GetState()->GetCommand(this->FunctionName)) {
|
if (!makefile.GetState()->GetCommand(this->FunctionName)) {
|
||||||
@@ -678,17 +727,15 @@ public:
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a unique output variable name to avoid collisions
|
|
||||||
std::string const outputVar = "_list_transform_apply_out_";
|
|
||||||
|
|
||||||
// Unset the output variable before calling
|
// Unset the output variable before calling
|
||||||
this->Makefile->RemoveDefinition(outputVar);
|
this->Makefile->RemoveDefinition(this->OutputVar);
|
||||||
|
|
||||||
// Build the function call: functionName(s, outputVar)
|
// Build the function call: functionName(s, outputVar)
|
||||||
cmListFileContext context = this->Makefile->GetBacktrace().Top();
|
cmListFileContext context = this->Makefile->GetBacktrace().Top();
|
||||||
std::vector<cmListFileArgument> funcArgs;
|
std::vector<cmListFileArgument> funcArgs;
|
||||||
funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
|
funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
|
||||||
funcArgs.emplace_back(outputVar, cmListFileArgument::Quoted, context.Line);
|
funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
|
||||||
|
context.Line);
|
||||||
cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
|
cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
|
||||||
std::move(funcArgs) };
|
std::move(funcArgs) };
|
||||||
|
|
||||||
@@ -701,7 +748,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read back the output variable
|
// Read back the output variable
|
||||||
cmValue result = this->Makefile->GetDefinition(outputVar);
|
cmValue result = this->Makefile->GetDefinition(this->OutputVar);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw transform_error(
|
throw transform_error(
|
||||||
cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
|
cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
|
||||||
@@ -713,7 +760,7 @@ public:
|
|||||||
std::string output = *result;
|
std::string output = *result;
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
this->Makefile->RemoveDefinition(outputVar);
|
this->Makefile->RemoveDefinition(this->OutputVar);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -721,6 +768,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::string FunctionName;
|
std::string FunctionName;
|
||||||
cmMakefile* Makefile = nullptr;
|
cmMakefile* Makefile = nullptr;
|
||||||
|
std::string OutputVar;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Descriptor of action
|
// Descriptor of action
|
||||||
@@ -980,16 +1028,19 @@ cmList& cmList::transform(TransformAction action, std::string const& arg,
|
|||||||
cmMakefile& makefile,
|
cmMakefile& makefile,
|
||||||
std::unique_ptr<TransformSelector> selector)
|
std::unique_ptr<TransformSelector> selector)
|
||||||
{
|
{
|
||||||
auto descriptor = TransformConfigure(action, selector, 1);
|
// Validate action and arity via the static registry.
|
||||||
|
TransformConfigure(action, selector, 1);
|
||||||
|
|
||||||
auto* applyAction =
|
// Create a local instance rather than reusing the singleton from
|
||||||
static_cast<TransformActionApply*>(descriptor->Transform.get());
|
// Descriptors. A user function invoked by APPLY may itself call
|
||||||
applyAction->Initialize(static_cast<::TransformSelector*>(selector.get()),
|
// list(TRANSFORM ... APPLY ...), which would clobber a shared instance.
|
||||||
arg, makefile);
|
TransformActionApply applyAction;
|
||||||
|
applyAction.Initialize(static_cast<::TransformSelector*>(selector.get()),
|
||||||
|
arg, makefile);
|
||||||
|
|
||||||
static_cast<::TransformSelector&>(*selector).Transform(
|
static_cast<::TransformSelector&>(*selector).Transform(
|
||||||
this->Values, [&descriptor](std::string const& s) -> std::string {
|
this->Values, [&applyAction](std::string const& s) -> std::string {
|
||||||
return (*descriptor->Transform)(s);
|
return applyAction(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|||||||
@@ -821,8 +821,11 @@ public:
|
|||||||
EXCLUDE
|
EXCLUDE
|
||||||
};
|
};
|
||||||
// Includes or removes items from the list
|
// Includes or removes items from the list
|
||||||
// Throw std::invalid_argument if regular expression is invalid
|
// Throw std::invalid_argument if regular expression is invalid or predicate
|
||||||
|
// function is unknown / does not set its output variable
|
||||||
cmList& filter(cm::string_view regex, FilterMode mode);
|
cmList& filter(cm::string_view regex, FilterMode mode);
|
||||||
|
cmList& filter(std::string const& functionName, FilterMode mode,
|
||||||
|
cmMakefile& makefile);
|
||||||
|
|
||||||
cmList& reverse()
|
cmList& reverse()
|
||||||
{
|
{
|
||||||
@@ -912,6 +915,9 @@ public:
|
|||||||
template <typename Type>
|
template <typename Type>
|
||||||
static std::unique_ptr<TransformSelector> New(std::string&&);
|
static std::unique_ptr<TransformSelector> New(std::string&&);
|
||||||
|
|
||||||
|
// NewPREDICATE is public (unlike NewAT/NewFOR/NewREGEX) because it takes
|
||||||
|
// a cmMakefile& parameter that cannot be dispatched through the existing
|
||||||
|
// New<Type>() templates.
|
||||||
static std::unique_ptr<TransformSelector> NewPREDICATE(
|
static std::unique_ptr<TransformSelector> NewPREDICATE(
|
||||||
std::string const& functionName, cmMakefile& makefile);
|
std::string const& functionName, cmMakefile& makefile);
|
||||||
|
|
||||||
|
|||||||
@@ -968,25 +968,46 @@ bool HandleFilterCommand(std::vector<std::string> const& args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string const& mode = args[3];
|
std::string const& mode = args[3];
|
||||||
if (mode != "REGEX") {
|
if (mode == "REGEX") {
|
||||||
status.SetError("sub-command FILTER does not recognize mode " + mode);
|
if (args.size() != 5) {
|
||||||
return false;
|
status.SetError("sub-command FILTER, mode REGEX "
|
||||||
}
|
"requires five arguments.");
|
||||||
if (args.size() != 5) {
|
return false;
|
||||||
status.SetError("sub-command FILTER, mode REGEX "
|
}
|
||||||
"requires five arguments.");
|
std::string const& pattern = args[4];
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::string const& pattern = args[4];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
status.GetMakefile().AddDefinition(
|
status.GetMakefile().AddDefinition(
|
||||||
listName, list->filter(pattern, filterMode).to_string());
|
listName, list->filter(pattern, filterMode).to_string());
|
||||||
return true;
|
return true;
|
||||||
} catch (std::invalid_argument& e) {
|
} catch (std::invalid_argument& e) {
|
||||||
status.SetError(e.what());
|
status.SetError(e.what());
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode == "PREDICATE") {
|
||||||
|
if (args.size() != 5) {
|
||||||
|
status.SetError("sub-command FILTER, mode PREDICATE "
|
||||||
|
"requires five arguments.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string const& functionName = args[4];
|
||||||
|
|
||||||
|
try {
|
||||||
|
status.GetMakefile().AddDefinition(
|
||||||
|
listName,
|
||||||
|
list->filter(functionName, filterMode, status.GetMakefile())
|
||||||
|
.to_string());
|
||||||
|
return true;
|
||||||
|
} catch (std::invalid_argument& e) {
|
||||||
|
status.SetError(e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status.SetError("sub-command FILTER does not recognize mode " + mode);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
2
Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt
Normal file
2
Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
|
||||||
|
mylist is: DO_NOT_FILTER_THIS;thisisanitem$
|
||||||
50
Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake
Normal file
50
Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Predicate: returns TRUE for items starting with "FILTER_THIS_"
|
||||||
|
function(starts_with_filter input result)
|
||||||
|
if(input MATCHES "^FILTER_THIS_")
|
||||||
|
set(${result} TRUE PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${result} FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
message("mylist was: ${mylist}")
|
||||||
|
list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
|
||||||
|
message("mylist is: ${mylist}")
|
||||||
|
|
||||||
|
# EXCLUDE with macro predicate
|
||||||
|
macro(is_short input result)
|
||||||
|
string(LENGTH "${input}" _len)
|
||||||
|
if(_len LESS 6)
|
||||||
|
set(${result} TRUE)
|
||||||
|
else()
|
||||||
|
set(${result} FALSE)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(mylist ab cdefgh ij klmnop qr)
|
||||||
|
list(FILTER mylist EXCLUDE PREDICATE is_short)
|
||||||
|
if(NOT mylist STREQUAL "cdefgh;klmnop")
|
||||||
|
message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE macro) is \"${mylist}\", expected \"cdefgh;klmnop\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# EXCLUDE on empty list
|
||||||
|
set(empty_list "")
|
||||||
|
list(FILTER empty_list EXCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT empty_list STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# EXCLUDE where nothing matches (all elements kept)
|
||||||
|
set(mylist alpha bravo charlie)
|
||||||
|
list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT mylist STREQUAL "alpha;bravo;charlie")
|
||||||
|
message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE no-match) is \"${mylist}\", expected \"alpha;bravo;charlie\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# EXCLUDE where everything matches (all elements removed)
|
||||||
|
set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C)
|
||||||
|
list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT mylist STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE all-match) is \"${mylist}\", expected \"\"")
|
||||||
|
endif()
|
||||||
2
Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt
Normal file
2
Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
|
||||||
|
mylist is: FILTER_THIS_BIT;FILTER_THIS_THING$
|
||||||
51
Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake
Normal file
51
Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Predicate: returns TRUE for items starting with "FILTER_THIS_"
|
||||||
|
function(starts_with_filter input result)
|
||||||
|
if(input MATCHES "^FILTER_THIS_")
|
||||||
|
set(${result} TRUE PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${result} FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Predicate as macro
|
||||||
|
macro(is_short input result)
|
||||||
|
string(LENGTH "${input}" _len)
|
||||||
|
if(_len LESS 6)
|
||||||
|
set(${result} TRUE)
|
||||||
|
else()
|
||||||
|
set(${result} FALSE)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
message("mylist was: ${mylist}")
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
|
||||||
|
message("mylist is: ${mylist}")
|
||||||
|
|
||||||
|
# INCLUDE with macro predicate
|
||||||
|
set(mylist ab cdefgh ij klmnop qr)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE is_short)
|
||||||
|
if(NOT mylist STREQUAL "ab;ij;qr")
|
||||||
|
message(FATAL_ERROR "FILTER(INCLUDE PREDICATE macro) is \"${mylist}\", expected \"ab;ij;qr\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# INCLUDE on empty list
|
||||||
|
set(empty_list "")
|
||||||
|
list(FILTER empty_list INCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT empty_list STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FILTER(INCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# INCLUDE where nothing matches (all elements removed)
|
||||||
|
set(mylist alpha bravo charlie)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT mylist STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FILTER(INCLUDE PREDICATE no-match) is \"${mylist}\", expected \"\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# INCLUDE where everything matches
|
||||||
|
set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
|
||||||
|
if(NOT mylist STREQUAL "FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C")
|
||||||
|
message(FATAL_ERROR "FILTER(INCLUDE PREDICATE all-match) is \"${mylist}\", expected \"FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C\"")
|
||||||
|
endif()
|
||||||
1
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt
Normal file
1
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
5
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt
Normal file
5
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
^CMake Error at FILTER-PREDICATE-NoOutput\.cmake:6 \(list\):
|
||||||
|
list sub-command FILTER, mode PREDICATE: function "bad_predicate" did not
|
||||||
|
set the output variable\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:3 \(include\)$
|
||||||
6
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake
Normal file
6
Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
function(bad_predicate in out)
|
||||||
|
# Deliberately does NOT set ${out}
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(mylist alpha bravo charlie)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE bad_predicate)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
^CMake Error at FILTER-PREDICATE-TooManyArguments\.cmake:6 \(list\):
|
||||||
|
list sub-command FILTER, mode PREDICATE requires five arguments\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:3 \(include\)$
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
function(my_predicate in out)
|
||||||
|
set(${out} TRUE PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(mylist alpha bravo charlie)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE my_predicate extra_arg)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
^CMake Error at FILTER-PREDICATE-UnknownFunction\.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER, mode PREDICATE: unknown function
|
||||||
|
"no_such_function"\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:3 \(include\)$
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
set(mylist alpha bravo charlie)
|
||||||
|
list(FILTER mylist INCLUDE PREDICATE no_such_function)
|
||||||
@@ -37,6 +37,11 @@ run_cmake(FILTER-REGEX-InvalidMode)
|
|||||||
run_cmake(FILTER-REGEX-InvalidOperator)
|
run_cmake(FILTER-REGEX-InvalidOperator)
|
||||||
run_cmake(FILTER-REGEX-Valid0)
|
run_cmake(FILTER-REGEX-Valid0)
|
||||||
run_cmake(FILTER-REGEX-Valid1)
|
run_cmake(FILTER-REGEX-Valid1)
|
||||||
|
run_cmake(FILTER-PREDICATE-UnknownFunction)
|
||||||
|
run_cmake(FILTER-PREDICATE-NoOutput)
|
||||||
|
run_cmake(FILTER-PREDICATE-TooManyArguments)
|
||||||
|
run_cmake(FILTER-PREDICATE-Include)
|
||||||
|
run_cmake(FILTER-PREDICATE-Exclude)
|
||||||
|
|
||||||
run_cmake(JOIN-NoArguments)
|
run_cmake(JOIN-NoArguments)
|
||||||
run_cmake(JOIN-NoVariable)
|
run_cmake(JOIN-NoVariable)
|
||||||
|
|||||||
Reference in New Issue
Block a user