Browse Source

list: Add FILTER subcommand (#3986)

Create a `list(FILTER)` command to filter lists by regular expression.
Ashley Whetter 10 năm trước cách đây
mục cha
commit
0205f882ae
29 tập tin đã thay đổi với 191 bổ sung3 xóa
  1. 10 3
      Help/command/list.rst
  2. 5 0
      Help/release/dev/list-FILTER-command.rst
  3. 109 0
      Source/cmListCommand.cxx
  4. 6 0
      Source/cmListCommand.h
  5. 1 0
      Tests/RunCMake/list/EmptyFilterRegex-result.txt
  6. 0 0
      Tests/RunCMake/list/EmptyFilterRegex-stderr.txt
  7. 2 0
      Tests/RunCMake/list/EmptyFilterRegex.cmake
  8. 1 0
      Tests/RunCMake/list/FILTER-NotList-result.txt
  9. 4 0
      Tests/RunCMake/list/FILTER-NotList-stderr.txt
  10. 2 0
      Tests/RunCMake/list/FILTER-NotList.cmake
  11. 1 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidMode-result.txt
  12. 4 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidMode-stderr.txt
  13. 2 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidMode.cmake
  14. 1 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidOperator-result.txt
  15. 4 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidOperator-stderr.txt
  16. 2 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidOperator.cmake
  17. 1 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidRegex-result.txt
  18. 4 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidRegex-stderr.txt
  19. 2 0
      Tests/RunCMake/list/FILTER-REGEX-InvalidRegex.cmake
  20. 1 0
      Tests/RunCMake/list/FILTER-REGEX-TooManyArguments-result.txt
  21. 4 0
      Tests/RunCMake/list/FILTER-REGEX-TooManyArguments-stderr.txt
  22. 2 0
      Tests/RunCMake/list/FILTER-REGEX-TooManyArguments.cmake
  23. 1 0
      Tests/RunCMake/list/FILTER-REGEX-Valid0-result.txt
  24. 2 0
      Tests/RunCMake/list/FILTER-REGEX-Valid0-stderr.txt
  25. 4 0
      Tests/RunCMake/list/FILTER-REGEX-Valid0.cmake
  26. 1 0
      Tests/RunCMake/list/FILTER-REGEX-Valid1-result.txt
  27. 2 0
      Tests/RunCMake/list/FILTER-REGEX-Valid1-stderr.txt
  28. 4 0
      Tests/RunCMake/list/FILTER-REGEX-Valid1.cmake
  29. 9 0
      Tests/RunCMake/list/RunCMakeTest.cmake

+ 10 - 3
Help/command/list.rst

@@ -9,6 +9,7 @@ List operations.
   list(GET <list> <element index> [<element index> ...]
        <output variable>)
   list(APPEND <list> [<element> ...])
+  list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
   list(FIND <list> <value> <output variable>)
   list(INSERT <list> <element_index> <element> [<element> ...])
   list(REMOVE_ITEM <list> <value> [<value> ...])
@@ -23,6 +24,12 @@ List operations.
 
 ``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
 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.
 
-The list subcommands ``APPEND``, ``INSERT``, ``REMOVE_AT``, ``REMOVE_ITEM``,
-``REMOVE_DUPLICATES``, ``REVERSE`` and ``SORT`` may create new values for
-the list within the current CMake variable scope.  Similar to the
+The list subcommands ``APPEND``, ``INSERT``, ``FILTER``, ``REMOVE_AT``,
+``REMOVE_ITEM``, ``REMOVE_DUPLICATES``, ``REVERSE`` and ``SORT`` may create new
+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
 current scope, even if the list itself is actually defined in a parent
 scope.  To propagate the results of these operations upwards, use

+ 5 - 0
Help/release/dev/list-FILTER-command.rst

@@ -0,0 +1,5 @@
+list-FILTER-command
+-------------------
+
+* The :command:`list` command gained a ``FILTER`` sub-command to filter
+  list elements by regular expression.

+ 109 - 0
Source/cmListCommand.cxx

@@ -14,6 +14,7 @@
 #include <cmsys/SystemTools.hxx>
 #include "cmAlgorithms.h"
 
+#include <algorithm>
 #include <stdlib.h> // required for atoi
 #include <ctype.h>
 #include <assert.h>
@@ -68,6 +69,10 @@ bool cmListCommand
     {
     return this->HandleReverseCommand(args);
     }
+  if(subCommand == "FILTER")
+    {
+    return this->HandleFilterCommand(args);
+    }
 
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e);
@@ -517,3 +522,107 @@ bool cmListCommand::HandleRemoveAtCommand(
   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;
+}

+ 6 - 0
Source/cmListCommand.h

@@ -58,6 +58,12 @@ protected:
   bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
   bool HandleSortCommand(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);

+ 1 - 0
Tests/RunCMake/list/EmptyFilterRegex-result.txt

@@ -0,0 +1 @@
+0

+ 0 - 0
Tests/RunCMake/list/EmptyFilterRegex-stderr.txt


+ 2 - 0
Tests/RunCMake/list/EmptyFilterRegex.cmake

@@ -0,0 +1,2 @@
+set(mylist "")
+list(FILTER mylist INCLUDE REGEX "^FILTER_THIS_.+")

+ 1 - 0
Tests/RunCMake/list/FILTER-NotList-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/list/FILTER-NotList-stderr.txt

@@ -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\)$

+ 2 - 0
Tests/RunCMake/list/FILTER-NotList.cmake

@@ -0,0 +1,2 @@
+unset(nosuchlist)
+list(FILTER nosuchlist EXCLUDE REGEX "^FILTER_THIS_.+")

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidMode-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidMode-stderr.txt

@@ -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\)$

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidMode.cmake

@@ -0,0 +1,2 @@
+set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
+list(FILTER mylist EXCLUDE NOOP "^FILTER_THIS_.+")

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidOperator-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidOperator-stderr.txt

@@ -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\)$

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidOperator.cmake

@@ -0,0 +1,2 @@
+set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
+list(FILTER mylist RM REGEX "^FILTER_THIS_.+")

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidRegex-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidRegex-stderr.txt

@@ -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\)$

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-InvalidRegex.cmake

@@ -0,0 +1,2 @@
+set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
+list(FILTER mylist EXCLUDE REGEX "UHOH!)(")

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-TooManyArguments-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-TooManyArguments-stderr.txt

@@ -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\)$

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-TooManyArguments.cmake

@@ -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)

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid0-result.txt

@@ -0,0 +1 @@
+0

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid0-stderr.txt

@@ -0,0 +1,2 @@
+^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
+mylist is: DO_NOT_FILTER_THIS;thisisanitem$

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid0.cmake

@@ -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}")

+ 1 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid1-result.txt

@@ -0,0 +1 @@
+0

+ 2 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid1-stderr.txt

@@ -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$

+ 4 - 0
Tests/RunCMake/list/FILTER-REGEX-Valid1.cmake

@@ -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}")

+ 9 - 0
Tests/RunCMake/list/RunCMakeTest.cmake

@@ -1,5 +1,6 @@
 include(RunCMake)
 
+run_cmake(EmptyFilterRegex)
 run_cmake(EmptyGet0)
 run_cmake(EmptyRemoveAt0)
 run_cmake(EmptyInsert-1)
@@ -8,17 +9,25 @@ run_cmake(NoArguments)
 run_cmake(InvalidSubcommand)
 run_cmake(GET-CMP0007-WARN)
 
+run_cmake(FILTER-REGEX-InvalidRegex)
 run_cmake(GET-InvalidIndex)
 run_cmake(INSERT-InvalidIndex)
 run_cmake(REMOVE_AT-InvalidIndex)
 
+run_cmake(FILTER-REGEX-TooManyArguments)
 run_cmake(LENGTH-TooManyArguments)
 run_cmake(REMOVE_DUPLICATES-TooManyArguments)
 run_cmake(REVERSE-TooManyArguments)
 run_cmake(SORT-TooManyArguments)
 
+run_cmake(FILTER-NotList)
 run_cmake(REMOVE_AT-NotList)
 run_cmake(REMOVE_DUPLICATES-NotList)
 run_cmake(REMOVE_ITEM-NotList)
 run_cmake(REVERSE-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)