list: Add FILTER subcommand (#3986)
Create a `list(FILTER)` command to filter lists by regular expression.
This commit is contained in:
parent
a58abc69c2
commit
0205f882ae
|
@ -9,6 +9,7 @@ List operations.
|
||||||
list(GET <list> <element index> [<element index> ...]
|
list(GET <list> <element index> [<element index> ...]
|
||||||
<output variable>)
|
<output variable>)
|
||||||
list(APPEND <list> [<element> ...])
|
list(APPEND <list> [<element> ...])
|
||||||
|
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
|
||||||
list(FIND <list> <value> <output variable>)
|
list(FIND <list> <value> <output variable>)
|
||||||
list(INSERT <list> <element_index> <element> [<element> ...])
|
list(INSERT <list> <element_index> <element> [<element> ...])
|
||||||
list(REMOVE_ITEM <list> <value> [<value> ...])
|
list(REMOVE_ITEM <list> <value> [<value> ...])
|
||||||
|
@ -23,6 +24,12 @@ List operations.
|
||||||
|
|
||||||
``APPEND`` will append elements to the list.
|
``APPEND`` will append elements to the list.
|
||||||
|
|
||||||
|
``FILTER`` will include or remove 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 see also the :command:`string`
|
||||||
|
command.
|
||||||
|
|
||||||
``FIND`` will return the index of the element specified in the list or -1
|
``FIND`` will return the index of the element specified in the list or -1
|
||||||
if it wasn't found.
|
if it wasn't found.
|
||||||
|
|
||||||
|
@ -38,9 +45,9 @@ difference is that ``REMOVE_ITEM`` will remove the given items, while
|
||||||
|
|
||||||
``SORT`` sorts the list in-place alphabetically.
|
``SORT`` sorts the list in-place alphabetically.
|
||||||
|
|
||||||
The list subcommands ``APPEND``, ``INSERT``, ``REMOVE_AT``, ``REMOVE_ITEM``,
|
The list subcommands ``APPEND``, ``INSERT``, ``FILTER``, ``REMOVE_AT``,
|
||||||
``REMOVE_DUPLICATES``, ``REVERSE`` and ``SORT`` may create new values for
|
``REMOVE_ITEM``, ``REMOVE_DUPLICATES``, ``REVERSE`` and ``SORT`` may create new
|
||||||
the list within the current CMake variable scope. Similar to the
|
values for the list within the current CMake variable scope. Similar to the
|
||||||
:command:`set` command, the LIST command creates new variable values in the
|
:command:`set` command, the LIST command creates new variable values in the
|
||||||
current scope, even if the list itself is actually defined in a parent
|
current scope, even if the list itself is actually defined in a parent
|
||||||
scope. To propagate the results of these operations upwards, use
|
scope. To propagate the results of these operations upwards, use
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
list-FILTER-command
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* The :command:`list` command gained a ``FILTER`` sub-command to filter
|
||||||
|
list elements by regular expression.
|
|
@ -14,6 +14,7 @@
|
||||||
#include <cmsys/SystemTools.hxx>
|
#include <cmsys/SystemTools.hxx>
|
||||||
#include "cmAlgorithms.h"
|
#include "cmAlgorithms.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <stdlib.h> // required for atoi
|
#include <stdlib.h> // required for atoi
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -68,6 +69,10 @@ bool cmListCommand
|
||||||
{
|
{
|
||||||
return this->HandleReverseCommand(args);
|
return this->HandleReverseCommand(args);
|
||||||
}
|
}
|
||||||
|
if(subCommand == "FILTER")
|
||||||
|
{
|
||||||
|
return this->HandleFilterCommand(args);
|
||||||
|
}
|
||||||
|
|
||||||
std::string e = "does not recognize sub-command "+subCommand;
|
std::string e = "does not recognize sub-command "+subCommand;
|
||||||
this->SetError(e);
|
this->SetError(e);
|
||||||
|
@ -517,3 +522,107 @@ bool cmListCommand::HandleRemoveAtCommand(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmListCommand::HandleFilterCommand(
|
||||||
|
std::vector<std::string> const& args)
|
||||||
|
{
|
||||||
|
if(args.size() < 2)
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER requires a list to be specified.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.size() < 3)
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER requires an operator to be specified.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.size() < 4)
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER requires a mode to be specified.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& listName = args[1];
|
||||||
|
// expand the variable
|
||||||
|
std::vector<std::string> varArgsExpanded;
|
||||||
|
if ( !this->GetList(varArgsExpanded, listName) )
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER requires list to be present.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& op = args[2];
|
||||||
|
bool includeMatches;
|
||||||
|
if(op == "INCLUDE")
|
||||||
|
{
|
||||||
|
includeMatches = true;
|
||||||
|
}
|
||||||
|
else if(op == "EXCLUDE")
|
||||||
|
{
|
||||||
|
includeMatches = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER does not recognize operator " + op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& mode = args[3];
|
||||||
|
if(mode == "REGEX")
|
||||||
|
{
|
||||||
|
if(args.size() != 5)
|
||||||
|
{
|
||||||
|
this->SetError("sub-command FILTER, mode REGEX "
|
||||||
|
"requires five arguments.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this->FilterRegex(args, includeMatches, listName, varArgsExpanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->SetError("sub-command FILTER does not recognize mode " + mode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
class MatchesRegex {
|
||||||
|
public:
|
||||||
|
MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
|
||||||
|
: regex(in_regex), includeMatches(in_includeMatches) {}
|
||||||
|
|
||||||
|
bool operator()(const std::string& target) {
|
||||||
|
return regex.find(target) ^ includeMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cmsys::RegularExpression& regex;
|
||||||
|
const bool includeMatches;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool cmListCommand::FilterRegex(std::vector<std::string> const& args,
|
||||||
|
bool includeMatches,
|
||||||
|
std::string const& listName,
|
||||||
|
std::vector<std::string>& varArgsExpanded)
|
||||||
|
{
|
||||||
|
const std::string& pattern = args[4];
|
||||||
|
cmsys::RegularExpression regex(pattern);
|
||||||
|
if(!regex.is_valid())
|
||||||
|
{
|
||||||
|
std::string error = "sub-command FILTER, mode REGEX ";
|
||||||
|
error += "failed to compile regex \"";
|
||||||
|
error += pattern;
|
||||||
|
error += "\".";
|
||||||
|
this->SetError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator argsBegin = varArgsExpanded.begin();
|
||||||
|
std::vector<std::string>::iterator argsEnd = varArgsExpanded.end();
|
||||||
|
std::vector<std::string>::iterator newArgsEnd =
|
||||||
|
std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
|
||||||
|
|
||||||
|
std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
|
||||||
|
this->Makefile->AddDefinition(listName, value.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,12 @@ protected:
|
||||||
bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
|
bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
|
||||||
bool HandleSortCommand(std::vector<std::string> const& args);
|
bool HandleSortCommand(std::vector<std::string> const& args);
|
||||||
bool HandleReverseCommand(std::vector<std::string> const& args);
|
bool HandleReverseCommand(std::vector<std::string> const& args);
|
||||||
|
bool HandleFilterCommand(std::vector<std::string> const& args);
|
||||||
|
bool FilterRegex(std::vector<std::string> const& args,
|
||||||
|
bool includeMatches,
|
||||||
|
std::string const& listName,
|
||||||
|
std::vector<std::string>& varArgsExpanded
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
bool GetList(std::vector<std::string>& list, const std::string& var);
|
bool GetList(std::vector<std::string>& list, const std::string& var);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(mylist "")
|
||||||
|
list(FILTER mylist INCLUDE REGEX "^FILTER_THIS_.+")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
^CMake Error at FILTER-NotList.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER requires list to be present.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)$
|
|
@ -0,0 +1,2 @@
|
||||||
|
unset(nosuchlist)
|
||||||
|
list(FILTER nosuchlist EXCLUDE REGEX "^FILTER_THIS_.+")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
^CMake Error at FILTER-REGEX-InvalidMode.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER does not recognize mode NOOP
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)$
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
list(FILTER mylist EXCLUDE NOOP "^FILTER_THIS_.+")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
^CMake Error at FILTER-REGEX-InvalidOperator.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER does not recognize operator RM
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)$
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
list(FILTER mylist RM REGEX "^FILTER_THIS_.+")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
^CMake Error at FILTER-REGEX-InvalidRegex.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER, mode REGEX failed to compile regex "UHOH!\)\(".
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)$
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
list(FILTER mylist EXCLUDE REGEX "UHOH!)(")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
^CMake Error at FILTER-REGEX-TooManyArguments.cmake:2 \(list\):
|
||||||
|
list sub-command FILTER, mode REGEX requires five arguments.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:3 \(include\)$
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
list(FILTER mylist EXCLUDE REGEX "^FILTER_THIS_.+" one_too_many)
|
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -0,0 +1,2 @@
|
||||||
|
^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
|
||||||
|
mylist is: DO_NOT_FILTER_THIS;thisisanitem$
|
|
@ -0,0 +1,4 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
message("mylist was: ${mylist}")
|
||||||
|
list(FILTER mylist EXCLUDE REGEX "^FILTER_THIS_.+")
|
||||||
|
message("mylist is: ${mylist}")
|
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -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$
|
|
@ -0,0 +1,4 @@
|
||||||
|
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||||
|
message("mylist was: ${mylist}")
|
||||||
|
list(FILTER mylist INCLUDE REGEX "^FILTER_THIS_.+")
|
||||||
|
message("mylist is: ${mylist}")
|
|
@ -1,5 +1,6 @@
|
||||||
include(RunCMake)
|
include(RunCMake)
|
||||||
|
|
||||||
|
run_cmake(EmptyFilterRegex)
|
||||||
run_cmake(EmptyGet0)
|
run_cmake(EmptyGet0)
|
||||||
run_cmake(EmptyRemoveAt0)
|
run_cmake(EmptyRemoveAt0)
|
||||||
run_cmake(EmptyInsert-1)
|
run_cmake(EmptyInsert-1)
|
||||||
|
@ -8,17 +9,25 @@ run_cmake(NoArguments)
|
||||||
run_cmake(InvalidSubcommand)
|
run_cmake(InvalidSubcommand)
|
||||||
run_cmake(GET-CMP0007-WARN)
|
run_cmake(GET-CMP0007-WARN)
|
||||||
|
|
||||||
|
run_cmake(FILTER-REGEX-InvalidRegex)
|
||||||
run_cmake(GET-InvalidIndex)
|
run_cmake(GET-InvalidIndex)
|
||||||
run_cmake(INSERT-InvalidIndex)
|
run_cmake(INSERT-InvalidIndex)
|
||||||
run_cmake(REMOVE_AT-InvalidIndex)
|
run_cmake(REMOVE_AT-InvalidIndex)
|
||||||
|
|
||||||
|
run_cmake(FILTER-REGEX-TooManyArguments)
|
||||||
run_cmake(LENGTH-TooManyArguments)
|
run_cmake(LENGTH-TooManyArguments)
|
||||||
run_cmake(REMOVE_DUPLICATES-TooManyArguments)
|
run_cmake(REMOVE_DUPLICATES-TooManyArguments)
|
||||||
run_cmake(REVERSE-TooManyArguments)
|
run_cmake(REVERSE-TooManyArguments)
|
||||||
run_cmake(SORT-TooManyArguments)
|
run_cmake(SORT-TooManyArguments)
|
||||||
|
|
||||||
|
run_cmake(FILTER-NotList)
|
||||||
run_cmake(REMOVE_AT-NotList)
|
run_cmake(REMOVE_AT-NotList)
|
||||||
run_cmake(REMOVE_DUPLICATES-NotList)
|
run_cmake(REMOVE_DUPLICATES-NotList)
|
||||||
run_cmake(REMOVE_ITEM-NotList)
|
run_cmake(REMOVE_ITEM-NotList)
|
||||||
run_cmake(REVERSE-NotList)
|
run_cmake(REVERSE-NotList)
|
||||||
run_cmake(SORT-NotList)
|
run_cmake(SORT-NotList)
|
||||||
|
|
||||||
|
run_cmake(FILTER-REGEX-InvalidMode)
|
||||||
|
run_cmake(FILTER-REGEX-InvalidOperator)
|
||||||
|
run_cmake(FILTER-REGEX-Valid0)
|
||||||
|
run_cmake(FILTER-REGEX-Valid1)
|
||||||
|
|
Loading…
Reference in New Issue