Browse Source

Merge topic 'list-TRANSFORM'

dca2347980 list: Add TRANSFORM sub-command
cdae12f8f8 string() Refactoring: creates an helper for REGEX REPLACE

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1938
Brad King 7 years ago
parent
commit
8c2d87dc01
100 changed files with 1322 additions and 109 deletions
  1. 73 0
      Help/command/list.rst
  2. 5 0
      Help/release/dev/list-transform.rst
  3. 1 0
      Source/CMakeLists.txt
  4. 556 0
      Source/cmListCommand.cxx
  5. 1 0
      Source/cmListCommand.h
  6. 14 86
      Source/cmStringCommand.cxx
  7. 0 23
      Source/cmStringCommand.h
  8. 117 0
      Source/cmStringReplaceHelper.cxx
  9. 69 0
      Source/cmStringReplaceHelper.h
  10. 41 0
      Tests/RunCMake/list/RunCMakeTest.cmake
  11. 1 0
      Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments-result.txt
  12. 4 0
      Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments-stderr.txt
  13. 2 0
      Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments.cmake
  14. 1 0
      Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments-result.txt
  15. 4 0
      Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments-stderr.txt
  16. 2 0
      Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments.cmake
  17. 48 0
      Tests/RunCMake/list/TRANSFORM-APPEND.cmake
  18. 1 0
      Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments-result.txt
  19. 4 0
      Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments-stderr.txt
  20. 2 0
      Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments.cmake
  21. 49 0
      Tests/RunCMake/list/TRANSFORM-GENEX_STRIP.cmake
  22. 1 0
      Tests/RunCMake/list/TRANSFORM-InvalidAction-result.txt
  23. 4 0
      Tests/RunCMake/list/TRANSFORM-InvalidAction-stderr.txt
  24. 2 0
      Tests/RunCMake/list/TRANSFORM-InvalidAction.cmake
  25. 1 0
      Tests/RunCMake/list/TRANSFORM-NoAction-result.txt
  26. 4 0
      Tests/RunCMake/list/TRANSFORM-NoAction-stderr.txt
  27. 2 0
      Tests/RunCMake/list/TRANSFORM-NoAction.cmake
  28. 1 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments-result.txt
  29. 4 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments-stderr.txt
  30. 2 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments.cmake
  31. 1 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments-result.txt
  32. 4 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments-stderr.txt
  33. 2 0
      Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments.cmake
  34. 1 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments-result.txt
  35. 4 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments-stderr.txt
  36. 2 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments.cmake
  37. 1 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments-result.txt
  38. 4 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments-stderr.txt
  39. 2 0
      Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments.cmake
  40. 48 0
      Tests/RunCMake/list/TRANSFORM-PREPEND.cmake
  41. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex-result.txt
  42. 5 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex-stderr.txt
  43. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex.cmake
  44. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace-result.txt
  45. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1-result.txt
  46. 5 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1-stderr.txt
  47. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1.cmake
  48. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2-result.txt
  49. 5 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2-stderr.txt
  50. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2.cmake
  51. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments-result.txt
  52. 4 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments-stderr.txt
  53. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments.cmake
  54. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments-result.txt
  55. 4 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments-stderr.txt
  56. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments.cmake
  57. 1 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments-result.txt
  58. 4 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments-stderr.txt
  59. 2 0
      Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments.cmake
  60. 48 0
      Tests/RunCMake/list/TRANSFORM-REPLACE.cmake
  61. 1 0
      Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments-result.txt
  62. 4 0
      Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments-stderr.txt
  63. 2 0
      Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments.cmake
  64. 49 0
      Tests/RunCMake/list/TRANSFORM-STRIP.cmake
  65. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument-result.txt
  66. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument-stderr.txt
  67. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument.cmake
  68. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex-result.txt
  69. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex-stderr.txt
  70. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex.cmake
  71. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments-result.txt
  72. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments-stderr.txt
  73. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments.cmake
  74. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument-result.txt
  75. 5 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument-stderr.txt
  76. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument.cmake
  77. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex-result.txt
  78. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex-stderr.txt
  79. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex.cmake
  80. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments-result.txt
  81. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments-stderr.txt
  82. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments.cmake
  83. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments-result.txt
  84. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments-stderr.txt
  85. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments.cmake
  86. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments-result.txt
  87. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments-stderr.txt
  88. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments.cmake
  89. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-InvalidRegex-result.txt
  90. 5 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-InvalidRegex-stderr.txt
  91. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-InvalidRegex.cmake
  92. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments-result.txt
  93. 5 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments-stderr.txt
  94. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments.cmake
  95. 1 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-TooManyArguments-result.txt
  96. 4 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-TooManyArguments-stderr.txt
  97. 2 0
      Tests/RunCMake/list/TRANSFORM-Selector-REGEX-TooManyArguments.cmake
  98. 1 0
      Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments-result.txt
  99. 4 0
      Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments-stderr.txt
  100. 2 0
      Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments.cmake

+ 73 - 0
Help/command/list.rst

@@ -151,6 +151,79 @@ REMOVE_DUPLICATES
 
 Removes duplicated items in the list.
 
+TRANSFORM
+"""""""""
+
+::
+
+  list(TRANSFORM <list> <ACTION> [<SELECTOR>]
+                        [OUTPUT_VARIABLE <output variable>])
+
+Transforms the list by applying an action to all or, by specifying a
+``<SELECTOR>``, to the selected elements of the list, storing result in-place
+or in the specified output variable.
+
+.. note::
+
+   ``TRANSFORM`` sub-command does not change the number of elements of the
+   list. If a ``<SELECTOR>`` is specified, only some elements will be changed,
+   the other ones will remain same as before the transformation.
+
+``<ACTION>`` specify the action to apply to the elements of list.
+The actions have exactly the same semantics as sub-commands of
+:command:`string` command.
+
+The ``<ACTION>`` may be one of:
+
+``APPEND``, ``PREPEND``: Append, prepend specified value to each element of
+the list. ::
+
+  list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
+
+``TOUPPER``, ``TOLOWER``: Convert each element of the list to upper, lower
+characters. ::
+
+  list(TRANSFORM <list> <TOLOWER|TOUPPER> ...)
+
+``STRIP``: Remove leading and trailing spaces from each element of the
+list. ::
+
+  list(TRANSFORM <list> STRIP ...)
+
+``GENEX_STRIP``: Strip any
+:manual:`generator expressions <cmake-generator-expressions(7)>` from each
+element of the list. ::
+
+  list(TRANSFORM <list> GENEX_STRIP ...)
+
+``REPLACE``: Match the regular expression as many times as possible and
+substitute the replacement expression for the match for each element
+of the list
+(Same semantic as ``REGEX REPLACE`` from :command:`string` command). ::
+
+  list(TRANSFORM <list> REPLACE <regular_expression>
+                                <replace_expression> ...)
+
+``<SELECTOR>`` select which elements of the list will be transformed. Only one
+type of selector can be specified at a time.
+
+The ``<SELECTOR>`` may be one of:
+
+``AT``: Specify a list of indexes. ::
+
+  list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
+
+``FOR``: Specify a range with, optionaly, an incerment used to iterate over
+the range. ::
+
+  list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
+
+``REGEX``: Specify a regular expression. Only elements matching the regular
+expression will be transformed. ::
+
+  list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
+
+
 Sorting
 ^^^^^^^
 

+ 5 - 0
Help/release/dev/list-transform.rst

@@ -0,0 +1,5 @@
+list-transform
+--------------
+
+* The :command:`list` command learned a ``TRANSFORM`` sub-command
+  to apply various string transformation to list's elements.

+ 1 - 0
Source/CMakeLists.txt

@@ -557,6 +557,7 @@ set(SRCS
   cmSiteNameCommand.h
   cmSourceGroupCommand.cxx
   cmSourceGroupCommand.h
+  cmStringReplaceHelper.cxx
   cmStringCommand.cxx
   cmStringCommand.h
   cmSubdirCommand.cxx

+ 556 - 0
Source/cmListCommand.cxx

@@ -5,14 +5,19 @@
 #include "cmsys/RegularExpression.hxx"
 #include <algorithm>
 #include <assert.h>
+#include <functional>
 #include <iterator>
+#include <set>
 #include <sstream>
+#include <stdexcept>
 #include <stdio.h>
 #include <stdlib.h> // required for atoi
 
 #include "cmAlgorithms.h"
+#include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
+#include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -54,6 +59,9 @@ bool cmListCommand::InitialPass(std::vector<std::string> const& args,
   if (subCommand == "REMOVE_DUPLICATES") {
     return this->HandleRemoveDuplicatesCommand(args);
   }
+  if (subCommand == "TRANSFORM") {
+    return this->HandleTransformCommand(args);
+  }
   if (subCommand == "SORT") {
     return this->HandleSortCommand(args);
   }
@@ -407,6 +415,554 @@ bool cmListCommand::HandleRemoveDuplicatesCommand(
   return true;
 }
 
+// Helpers for list(TRANSFORM <list> ...)
+namespace {
+using transform_type = std::function<std::string(const std::string&)>;
+
+class transform_error : public std::runtime_error
+{
+public:
+  transform_error(const std::string& error)
+    : std::runtime_error(error)
+  {
+  }
+};
+
+class TransformSelector
+{
+public:
+  virtual ~TransformSelector() {}
+
+  std::string Tag;
+
+  virtual bool Validate(std::size_t count = 0) = 0;
+
+  virtual bool InSelection(const std::string&) = 0;
+
+  virtual void Transform(std::vector<std::string>& list,
+                         const transform_type& transform)
+  {
+    std::transform(list.begin(), list.end(), list.begin(), transform);
+  }
+
+protected:
+  TransformSelector(std::string&& tag)
+    : Tag(std::move(tag))
+  {
+  }
+};
+class TransformNoSelector : public TransformSelector
+{
+public:
+  TransformNoSelector()
+    : TransformSelector("NO SELECTOR")
+  {
+  }
+
+  bool Validate(std::size_t) override { return true; }
+
+  bool InSelection(const std::string&) override { return true; }
+};
+class TransformSelectorRegex : public TransformSelector
+{
+public:
+  TransformSelectorRegex(const std::string& regex)
+    : TransformSelector("REGEX")
+    , Regex(regex)
+  {
+  }
+
+  bool Validate(std::size_t) override { return this->Regex.is_valid(); }
+
+  bool InSelection(const std::string& value) override
+  {
+    return this->Regex.find(value);
+  }
+
+  cmsys::RegularExpression Regex;
+};
+class TransformSelectorIndexes : public TransformSelector
+{
+public:
+  std::vector<int> Indexes;
+
+  bool InSelection(const std::string&) override { return true; }
+
+  void Transform(std::vector<std::string>& list,
+                 const transform_type& transform) override
+  {
+    this->Validate(list.size());
+
+    for (auto index : this->Indexes) {
+      list[index] = transform(list[index]);
+    }
+  }
+
+protected:
+  TransformSelectorIndexes(std::string&& tag)
+    : TransformSelector(std::move(tag))
+  {
+  }
+  TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
+    : TransformSelector(std::move(tag))
+    , Indexes(indexes)
+  {
+  }
+
+  int NormalizeIndex(int index, std::size_t count)
+  {
+    if (index < 0) {
+      index = static_cast<int>(count) + index;
+    }
+    if (index < 0 || count <= static_cast<std::size_t>(index)) {
+      std::ostringstream str;
+      str << "sub-command TRANSFORM, selector " << this->Tag
+          << ", index: " << index << " out of range (-" << count << ", "
+          << count - 1 << ").";
+      throw transform_error(str.str());
+    }
+    return index;
+  }
+};
+class TransformSelectorAt : public TransformSelectorIndexes
+{
+public:
+  TransformSelectorAt(std::vector<int>&& indexes)
+    : TransformSelectorIndexes("AT", std::move(indexes))
+  {
+  }
+
+  bool Validate(std::size_t count) override
+  {
+    decltype(Indexes) indexes;
+
+    for (auto index : Indexes) {
+      indexes.push_back(this->NormalizeIndex(index, count));
+    }
+    this->Indexes = std::move(indexes);
+
+    return true;
+  }
+};
+class TransformSelectorFor : public TransformSelectorIndexes
+{
+public:
+  TransformSelectorFor(int start, int stop, int step)
+    : TransformSelectorIndexes("FOR")
+    , Start(start)
+    , Stop(stop)
+    , Step(step)
+  {
+  }
+
+  bool Validate(std::size_t count) override
+  {
+    this->Start = this->NormalizeIndex(this->Start, count);
+    this->Stop = this->NormalizeIndex(this->Stop, count);
+
+    // compute indexes
+    auto size = (this->Stop - this->Start + 1) / this->Step;
+    if ((this->Stop - this->Start + 1) % this->Step != 0) {
+      size += 1;
+    }
+
+    this->Indexes.resize(size);
+    auto start = this->Start, step = this->Step;
+    std::generate(this->Indexes.begin(), this->Indexes.end(),
+                  [&start, step]() -> int {
+                    auto r = start;
+                    start += step;
+                    return r;
+                  });
+
+    return true;
+  }
+
+private:
+  int Start, Stop, Step;
+};
+
+class TransformAction
+{
+public:
+  virtual ~TransformAction() {}
+
+  virtual std::string Transform(const std::string& input) = 0;
+};
+class TransformReplace : public TransformAction
+{
+public:
+  TransformReplace(const std::vector<std::string>& arguments,
+                   cmMakefile* makefile)
+    : ReplaceHelper(arguments[0], arguments[1], makefile)
+  {
+    makefile->ClearMatches();
+
+    if (!this->ReplaceHelper.IsRegularExpressionValid()) {
+      std::ostringstream error;
+      error
+        << "sub-command TRANSFORM, action REPLACE: Failed to compile regex \""
+        << arguments[0] << "\".";
+      throw transform_error(error.str());
+    }
+    if (!this->ReplaceHelper.IsReplaceExpressionValid()) {
+      std::ostringstream error;
+      error << "sub-command TRANSFORM, action REPLACE: "
+            << this->ReplaceHelper.GetError() << ".";
+      throw transform_error(error.str());
+    }
+  }
+
+  std::string Transform(const std::string& input) override
+  {
+    // Scan through the input for all matches.
+    std::string output;
+
+    if (!this->ReplaceHelper.Replace(input, output)) {
+      std::ostringstream error;
+      error << "sub-command TRANSFORM, action REPLACE: "
+            << this->ReplaceHelper.GetError() << ".";
+      throw transform_error(error.str());
+    }
+
+    return output;
+  }
+
+private:
+  cmStringReplaceHelper ReplaceHelper;
+};
+}
+
+bool cmListCommand::HandleTransformCommand(
+  std::vector<std::string> const& args)
+{
+  if (args.size() < 3) {
+    this->SetError(
+      "sub-command TRANSFORM requires an action to be specified.");
+    return false;
+  }
+
+  // Structure collecting all elements of the command
+  struct Command
+  {
+    Command(const std::string& listName)
+      : ListName(listName)
+      , OutputName(listName)
+    {
+    }
+
+    std::string Name;
+    std::string ListName;
+    std::vector<std::string> Arguments;
+    std::unique_ptr<TransformAction> Action;
+    std::unique_ptr<TransformSelector> Selector;
+    std::string OutputName;
+  } command(args[1]);
+
+  // Descriptor of action
+  // Arity: number of arguments required for the action
+  // Transform: lambda function implementing the action
+  struct ActionDescriptor
+  {
+    ActionDescriptor(const std::string& name)
+      : Name(name)
+    {
+    }
+    ActionDescriptor(const std::string& name, int arity,
+                     const transform_type& transform)
+      : Name(name)
+      , Arity(arity)
+      , Transform(transform)
+    {
+    }
+
+    operator const std::string&() const { return Name; }
+
+    std::string Name;
+    int Arity = 0;
+    transform_type Transform;
+  };
+
+  // Build a set of supported actions.
+  std::set<ActionDescriptor,
+           std::function<bool(const std::string&, const std::string&)>>
+  descriptors(
+    [](const std::string& x, const std::string& y) { return x < y; });
+  descriptors = { { "APPEND", 1,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return s + command.Arguments[0];
+                      }
+
+                      return s;
+                    } },
+                  { "PREPEND", 1,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return command.Arguments[0] + s;
+                      }
+
+                      return s;
+                    } },
+                  { "TOUPPER", 0,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return cmSystemTools::UpperCase(s);
+                      }
+
+                      return s;
+                    } },
+                  { "TOLOWER", 0,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return cmSystemTools::LowerCase(s);
+                      }
+
+                      return s;
+                    } },
+                  { "STRIP", 0,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return cmSystemTools::TrimWhitespace(s);
+                      }
+
+                      return s;
+                    } },
+                  { "GENEX_STRIP", 0,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return cmGeneratorExpression::Preprocess(
+                          s,
+                          cmGeneratorExpression::StripAllGeneratorExpressions);
+                      }
+
+                      return s;
+                    } },
+                  { "REPLACE", 2,
+                    [&command](const std::string& s) -> std::string {
+                      if (command.Selector->InSelection(s)) {
+                        return command.Action->Transform(s);
+                      }
+
+                      return s;
+                    } } };
+
+  using size_type = std::vector<std::string>::size_type;
+  size_type index = 2;
+
+  // Parse all possible function parameters
+  auto descriptor = descriptors.find(args[index]);
+
+  if (descriptor == descriptors.end()) {
+    std::ostringstream error;
+    error << " sub-command TRANSFORM, " << args[index] << " invalid action.";
+    this->SetError(error.str());
+    return false;
+  }
+
+  // Action arguments
+  index += 1;
+  if (args.size() < index + descriptor->Arity) {
+    std::ostringstream error;
+    error << "sub-command TRANSFORM, action " << descriptor->Name
+          << " expects " << descriptor->Arity << " argument(s).";
+    this->SetError(error.str());
+    return false;
+  }
+
+  command.Name = descriptor->Name;
+  index += descriptor->Arity;
+  if (descriptor->Arity > 0) {
+    command.Arguments =
+      std::vector<std::string>(args.begin() + 3, args.begin() + index);
+  }
+
+  if (command.Name == "REPLACE") {
+    try {
+      command.Action =
+        cm::make_unique<TransformReplace>(command.Arguments, this->Makefile);
+    } catch (const transform_error& e) {
+      this->SetError(e.what());
+      return false;
+    }
+  }
+
+  const std::string REGEX{ "REGEX" }, AT{ "AT" }, FOR{ "FOR" },
+    OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
+
+  // handle optional arguments
+  while (args.size() > index) {
+    if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
+        command.Selector) {
+      std::ostringstream error;
+      error << "sub-command TRANSFORM, selector already specified ("
+            << command.Selector->Tag << ").";
+      this->SetError(error.str());
+      return false;
+    }
+
+    // REGEX selector
+    if (args[index] == REGEX) {
+      if (args.size() == ++index) {
+        this->SetError("sub-command TRANSFORM, selector REGEX expects "
+                       "'regular expression' argument.");
+        return false;
+      }
+
+      command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]);
+      if (!command.Selector->Validate()) {
+        std::ostringstream error;
+        error << "sub-command TRANSFORM, selector REGEX failed to compile "
+                 "regex \"";
+        error << args[index] << "\".";
+        this->SetError(error.str());
+        return false;
+      }
+
+      index += 1;
+      continue;
+    }
+
+    // AT selector
+    if (args[index] == AT) {
+      // get all specified indexes
+      std::vector<int> indexes;
+      while (args.size() > ++index) {
+        std::size_t pos;
+        int value;
+
+        try {
+          value = std::stoi(args[index], &pos);
+          if (pos != args[index].length()) {
+            // this is not a number, stop processing
+            break;
+          }
+          indexes.push_back(value);
+        } catch (const std::invalid_argument&) {
+          // this is not a number, stop processing
+          break;
+        }
+      }
+
+      if (indexes.empty()) {
+        this->SetError(
+          "sub-command TRANSFORM, selector AT expects at least one "
+          "numeric value.");
+        return false;
+      }
+
+      command.Selector =
+        cm::make_unique<TransformSelectorAt>(std::move(indexes));
+
+      continue;
+    }
+
+    // FOR selector
+    if (args[index] == FOR) {
+      if (args.size() <= ++index + 1) {
+        this->SetError("sub-command TRANSFORM, selector FOR expects, at least,"
+                       " two arguments.");
+        return false;
+      }
+
+      int start = 0, stop = 0, step = 1;
+      bool valid = true;
+      try {
+        std::size_t pos;
+
+        start = std::stoi(args[index], &pos);
+        if (pos != args[index].length()) {
+          // this is not a number
+          valid = false;
+        } else {
+          stop = std::stoi(args[++index], &pos);
+          if (pos != args[index].length()) {
+            // this is not a number
+            valid = false;
+          }
+        }
+      } catch (const std::invalid_argument&) {
+        // this is not numbers
+        valid = false;
+      }
+      if (!valid) {
+        this->SetError("sub-command TRANSFORM, selector FOR expects, "
+                       "at least, two numeric values.");
+        return false;
+      }
+      // try to read a third numeric value for step
+      if (args.size() > ++index) {
+        try {
+          std::size_t pos;
+
+          step = std::stoi(args[index], &pos);
+          if (pos != args[index].length()) {
+            // this is not a number
+            step = 1;
+          } else {
+            index += 1;
+          }
+        } catch (const std::invalid_argument&) {
+          // this is not number, ignore exception
+        }
+      }
+
+      if (step < 0) {
+        this->SetError("sub-command TRANSFORM, selector FOR expects "
+                       "non negative numeric value for <step>.");
+      }
+
+      command.Selector =
+        cm::make_unique<TransformSelectorFor>(start, stop, step);
+
+      continue;
+    }
+
+    // output variable
+    if (args[index] == OUTPUT_VARIABLE) {
+      if (args.size() == ++index) {
+        this->SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
+                       "expects variable name argument.");
+        return false;
+      }
+
+      command.OutputName = args[index++];
+      continue;
+    }
+
+    std::ostringstream error;
+    error << "sub-command TRANSFORM, '"
+          << cmJoin(cmMakeRange(args).advance(index), " ")
+          << "': unexpected argument(s).";
+    this->SetError(error.str());
+    return false;
+  }
+
+  // expand the list variable
+  std::vector<std::string> varArgsExpanded;
+  if (!this->GetList(varArgsExpanded, command.ListName)) {
+    this->Makefile->AddDefinition(command.OutputName, "");
+    return true;
+  }
+
+  if (!command.Selector) {
+    // no selector specified, apply transformation to all elements
+    command.Selector = cm::make_unique<TransformNoSelector>();
+  }
+
+  try {
+    command.Selector->Transform(varArgsExpanded, descriptor->Transform);
+  } catch (const transform_error& e) {
+    this->SetError(e.what());
+    return false;
+  }
+
+  this->Makefile->AddDefinition(command.OutputName,
+                                cmJoin(varArgsExpanded, ";").c_str());
+
+  return true;
+}
+
 bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
 {
   assert(args.size() >= 2);

+ 1 - 0
Source/cmListCommand.h

@@ -41,6 +41,7 @@ protected:
   bool HandleRemoveAtCommand(std::vector<std::string> const& args);
   bool HandleRemoveItemCommand(std::vector<std::string> const& args);
   bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
+  bool HandleTransformCommand(std::vector<std::string> const& args);
   bool HandleSortCommand(std::vector<std::string> const& args);
   bool HandleSublistCommand(std::vector<std::string> const& args);
   bool HandleReverseCommand(std::vector<std::string> const& args);

+ 14 - 86
Source/cmStringCommand.cxx

@@ -13,6 +13,7 @@
 #include "cmCryptoHash.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
+#include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmUuid.h"
@@ -344,46 +345,17 @@ bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
   std::string const& regex = args[2];
   std::string const& replace = args[3];
   std::string const& outvar = args[4];
+  cmStringReplaceHelper replaceHelper(regex, replace, this->Makefile);
 
-  // Pull apart the replace expression to find the escaped [0-9] values.
-  std::vector<RegexReplacement> replacement;
-  std::string::size_type l = 0;
-  while (l < replace.length()) {
-    std::string::size_type r = replace.find('\\', l);
-    if (r == std::string::npos) {
-      r = replace.length();
-      replacement.push_back(replace.substr(l, r - l));
-    } else {
-      if (r - l > 0) {
-        replacement.push_back(replace.substr(l, r - l));
-      }
-      if (r == (replace.length() - 1)) {
-        this->SetError("sub-command REGEX, mode REPLACE: "
-                       "replace-expression ends in a backslash.");
-        return false;
-      }
-      if ((replace[r + 1] >= '0') && (replace[r + 1] <= '9')) {
-        replacement.push_back(replace[r + 1] - '0');
-      } else if (replace[r + 1] == 'n') {
-        replacement.push_back("\n");
-      } else if (replace[r + 1] == '\\') {
-        replacement.push_back("\\");
-      } else {
-        std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \"";
-        e += replace.substr(r, 2);
-        e += "\" in replace-expression.";
-        this->SetError(e);
-        return false;
-      }
-      r += 2;
-    }
-    l = r;
+  if (!replaceHelper.IsReplaceExpressionValid()) {
+    this->SetError("sub-command REGEX, mode REPLACE: " +
+                   replaceHelper.GetError() + ".");
+    return false;
   }
 
   this->Makefile->ClearMatches();
-  // Compile the regular expression.
-  cmsys::RegularExpression re;
-  if (!re.compile(regex.c_str())) {
+
+  if (!replaceHelper.IsRegularExpressionValid()) {
     std::string e =
       "sub-command REGEX, mode REPLACE failed to compile regex \"" + regex +
       "\".";
@@ -392,60 +364,16 @@ bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
   }
 
   // Concatenate all the last arguments together.
-  std::string input = cmJoin(cmMakeRange(args).advance(5), std::string());
-
-  // Scan through the input for all matches.
+  const std::string input =
+    cmJoin(cmMakeRange(args).advance(5), std::string());
   std::string output;
-  std::string::size_type base = 0;
-  while (re.find(input.c_str() + base)) {
-    this->Makefile->ClearMatches();
-    this->Makefile->StoreMatches(re);
-    std::string::size_type l2 = re.start();
-    std::string::size_type r = re.end();
-
-    // Concatenate the part of the input that was not matched.
-    output += input.substr(base, l2);
-
-    // Make sure the match had some text.
-    if (r - l2 == 0) {
-      std::string e = "sub-command REGEX, mode REPLACE regex \"" + regex +
-        "\" matched an empty string.";
-      this->SetError(e);
-      return false;
-    }
 
-    // Concatenate the replacement for the match.
-    for (RegexReplacement const& i : replacement) {
-      if (i.number < 0) {
-        // This is just a plain-text part of the replacement.
-        output += i.value;
-      } else {
-        // Replace with part of the match.
-        int n = i.number;
-        std::string::size_type start = re.start(n);
-        std::string::size_type end = re.end(n);
-        std::string::size_type len = input.length() - base;
-        if ((start != std::string::npos) && (end != std::string::npos) &&
-            (start <= len) && (end <= len)) {
-          output += input.substr(base + start, end - start);
-        } else {
-          std::string e =
-            "sub-command REGEX, mode REPLACE: replace expression \"" +
-            replace + "\" contains an out-of-range escape for regex \"" +
-            regex + "\".";
-          this->SetError(e);
-          return false;
-        }
-      }
-    }
-
-    // Move past the match.
-    base += r;
+  if (!replaceHelper.Replace(input, output)) {
+    this->SetError("sub-command REGEX, mode REPLACE: " +
+                   replaceHelper.GetError() + ".");
+    return false;
   }
 
-  // Concatenate the text after the last match.
-  output += input.substr(base, input.length() - base);
-
   // Store the output in the provided variable.
   this->Makefile->AddDefinition(outvar, output.c_str());
   return true;

+ 0 - 23
Source/cmStringCommand.h

@@ -60,29 +60,6 @@ protected:
 
   bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
                 size_t varIdx);
-
-  class RegexReplacement
-  {
-  public:
-    RegexReplacement(const char* s)
-      : number(-1)
-      , value(s)
-    {
-    }
-    RegexReplacement(const std::string& s)
-      : number(-1)
-      , value(s)
-    {
-    }
-    RegexReplacement(int n)
-      : number(n)
-      , value()
-    {
-    }
-    RegexReplacement() {}
-    int number;
-    std::string value;
-  };
 };
 
 #endif

+ 117 - 0
Source/cmStringReplaceHelper.cxx

@@ -0,0 +1,117 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmStringReplaceHelper.h"
+
+#include "cmMakefile.h"
+#include <sstream>
+
+cmStringReplaceHelper::cmStringReplaceHelper(const std::string& regex,
+                                             const std::string& replace_expr,
+                                             cmMakefile* makefile)
+  : RegExString(regex)
+  , RegularExpression(regex)
+  , ReplaceExpression(replace_expr)
+  , Makefile(makefile)
+{
+  this->ParseReplaceExpression();
+}
+
+bool cmStringReplaceHelper::Replace(const std::string& input,
+                                    std::string& output)
+{
+  output.clear();
+
+  // Scan through the input for all matches.
+  std::string::size_type base = 0;
+  while (this->RegularExpression.find(input.c_str() + base)) {
+    if (this->Makefile != nullptr) {
+      this->Makefile->ClearMatches();
+      this->Makefile->StoreMatches(this->RegularExpression);
+    }
+    auto l2 = this->RegularExpression.start();
+    auto r = this->RegularExpression.end();
+
+    // Concatenate the part of the input that was not matched.
+    output += input.substr(base, l2);
+
+    // Make sure the match had some text.
+    if (r - l2 == 0) {
+      std::ostringstream error;
+      error << "regex \"" << this->RegExString << "\" matched an empty string";
+      this->ErrorString = error.str();
+      return false;
+    }
+
+    // Concatenate the replacement for the match.
+    for (const auto& replacement : this->Replacements) {
+      if (replacement.Number < 0) {
+        // This is just a plain-text part of the replacement.
+        output += replacement.Value;
+      } else {
+        // Replace with part of the match.
+        auto n = replacement.Number;
+        auto start = this->RegularExpression.start(n);
+        auto end = this->RegularExpression.end(n);
+        auto len = input.length() - base;
+        if ((start != std::string::npos) && (end != std::string::npos) &&
+            (start <= len) && (end <= len)) {
+          output += input.substr(base + start, end - start);
+        } else {
+          std::ostringstream error;
+          error << "replace expression \"" << this->ReplaceExpression
+                << "\" contains an out-of-range escape for regex \""
+                << this->RegExString << "\"";
+          this->ErrorString = error.str();
+          return false;
+        }
+      }
+    }
+
+    // Move past the match.
+    base += r;
+  }
+
+  // Concatenate the text after the last match.
+  output += input.substr(base, input.length() - base);
+
+  return true;
+}
+
+void cmStringReplaceHelper::ParseReplaceExpression()
+{
+  std::string::size_type l = 0;
+  while (l < this->ReplaceExpression.length()) {
+    auto r = this->ReplaceExpression.find('\\', l);
+    if (r == std::string::npos) {
+      r = this->ReplaceExpression.length();
+      this->Replacements.push_back(this->ReplaceExpression.substr(l, r - l));
+    } else {
+      if (r - l > 0) {
+        this->Replacements.push_back(this->ReplaceExpression.substr(l, r - l));
+      }
+      if (r == (this->ReplaceExpression.length() - 1)) {
+        this->ValidReplaceExpression = false;
+        this->ErrorString = "replace-expression ends in a backslash";
+        return;
+      }
+      if ((this->ReplaceExpression[r + 1] >= '0') &&
+          (this->ReplaceExpression[r + 1] <= '9')) {
+        this->Replacements.push_back(this->ReplaceExpression[r + 1] - '0');
+      } else if (this->ReplaceExpression[r + 1] == 'n') {
+        this->Replacements.push_back("\n");
+      } else if (this->ReplaceExpression[r + 1] == '\\') {
+        this->Replacements.push_back("\\");
+      } else {
+        this->ValidReplaceExpression = false;
+        std::ostringstream error;
+        error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2)
+              << "\" in replace-expression";
+        this->ErrorString = error.str();
+        return;
+      }
+      r += 2;
+    }
+    l = r;
+  }
+}

+ 69 - 0
Source/cmStringReplaceHelper.h

@@ -0,0 +1,69 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmStringReplaceHelper_h
+#define cmStringReplaceHelper_h
+
+#include "cmsys/RegularExpression.hxx"
+
+#include <string>
+#include <vector>
+
+class cmMakefile;
+
+class cmStringReplaceHelper
+{
+public:
+  cmStringReplaceHelper(const std::string& regex,
+                        const std::string& replace_expr,
+                        cmMakefile* makefile = nullptr);
+
+  bool IsRegularExpressionValid() const
+  {
+    return this->RegularExpression.is_valid();
+  }
+  bool IsReplaceExpressionValid() const
+  {
+    return this->ValidReplaceExpression;
+  }
+
+  bool Replace(const std::string& input, std::string& output);
+
+  const std::string& GetError() { return this->ErrorString; }
+
+private:
+  class RegexReplacement
+  {
+  public:
+    RegexReplacement(const char* s)
+      : Number(-1)
+      , Value(s)
+    {
+    }
+    RegexReplacement(const std::string& s)
+      : Number(-1)
+      , Value(s)
+    {
+    }
+    RegexReplacement(int n)
+      : Number(n)
+      , Value()
+    {
+    }
+    RegexReplacement() {}
+
+    int Number;
+    std::string Value;
+  };
+
+  void ParseReplaceExpression();
+
+  std::string ErrorString;
+  std::string RegExString;
+  cmsys::RegularExpression RegularExpression;
+  bool ValidReplaceExpression = true;
+  std::string ReplaceExpression;
+  std::vector<RegexReplacement> Replacements;
+  cmMakefile* Makefile = nullptr;
+};
+
+#endif

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

@@ -43,3 +43,44 @@ run_cmake(SUBLIST-NoArguments)
 run_cmake(SUBLIST-NoVariable)
 run_cmake(SUBLIST-InvalidLength)
 run_cmake(SUBLIST)
+
+run_cmake(TRANSFORM-NoAction)
+run_cmake(TRANSFORM-InvalidAction)
+# 'action' oriented tests
+run_cmake(TRANSFORM-TOUPPER-TooManyArguments)
+run_cmake(TRANSFORM-TOLOWER-TooManyArguments)
+run_cmake(TRANSFORM-STRIP-TooManyArguments)
+run_cmake(TRANSFORM-GENEX_STRIP-TooManyArguments)
+run_cmake(TRANSFORM-APPEND-NoArguments)
+run_cmake(TRANSFORM-APPEND-TooManyArguments)
+run_cmake(TRANSFORM-PREPEND-NoArguments)
+run_cmake(TRANSFORM-PREPEND-TooManyArguments)
+run_cmake(TRANSFORM-REPLACE-NoArguments)
+run_cmake(TRANSFORM-REPLACE-NoEnoughArguments)
+run_cmake(TRANSFORM-REPLACE-TooManyArguments)
+run_cmake(TRANSFORM-REPLACE-InvalidRegex)
+run_cmake(TRANSFORM-REPLACE-InvalidReplace1)
+run_cmake(TRANSFORM-REPLACE-InvalidReplace2)
+# 'selector' oriented tests
+run_cmake(TRANSFORM-Selector-REGEX-NoArguments)
+run_cmake(TRANSFORM-Selector-REGEX-TooManyArguments)
+run_cmake(TRANSFORM-Selector-REGEX-InvalidRegex)
+run_cmake(TRANSFORM-Selector-AT-NoArguments)
+run_cmake(TRANSFORM-Selector-AT-BadArgument)
+run_cmake(TRANSFORM-Selector-AT-InvalidIndex)
+run_cmake(TRANSFORM-Selector-FOR-NoArguments)
+run_cmake(TRANSFORM-Selector-FOR-NoEnoughArguments)
+run_cmake(TRANSFORM-Selector-FOR-TooManyArguments)
+run_cmake(TRANSFORM-Selector-FOR-BadArgument)
+run_cmake(TRANSFORM-Selector-FOR-InvalidIndex)
+# 'output' oriented tests
+run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments)
+run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments)
+# Successful tests
+run_cmake(TRANSFORM-TOUPPER)
+run_cmake(TRANSFORM-TOLOWER)
+run_cmake(TRANSFORM-STRIP)
+run_cmake(TRANSFORM-GENEX_STRIP)
+run_cmake(TRANSFORM-APPEND)
+run_cmake(TRANSFORM-PREPEND)
+run_cmake(TRANSFORM-REPLACE)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-APPEND-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, action APPEND expects 1 argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist APPEND)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-APPEND-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-APPEND-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist APPEND delta one_too_many)

+ 48 - 0
Tests/RunCMake/list/TRANSFORM-APPEND.cmake

@@ -0,0 +1,48 @@
+set(mylist alpha bravo charlie delta)
+
+list(TRANSFORM mylist APPEND "_A" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha_A;bravo_A;charlie_A;delta_A")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha_A;bravo_A;charlie_A;delta_A\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" AT 1 3 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo_A;charlie;delta_A")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha;bravo_A;charlie;delta_A\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" AT 1 -2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo_A;charlie_A;delta")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha;bravo_A;charlie_A;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" FOR 1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo_A;charlie_A;delta")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha;bravo_A;charlie_A;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" FOR 1 -1 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo_A;charlie_A;delta_A")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha;bravo_A;charlie_A;delta_A\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" FOR 0 -1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha_A;bravo;charlie_A;delta")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha_A;bravo;charlie_A;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" REGEX "(r|t)a" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo_A;charlie;delta_A")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${output}\", expected is \"alpha;bravo_A;charlie;delta_A\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist APPEND "_A" REGEX "(r|t)a")
+if (NOT mylist STREQUAL "alpha;bravo_A;charlie;delta_A")
+  message (FATAL_ERROR "TRANSFORM(APPEND) is \"${mylist}\", expected is \"alpha;bravo_A;charlie;delta_A\"")
+endif()

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-GENEX_STRIP-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-GENEX_STRIP-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist GENEX_STRIP one_too_many)

+ 49 - 0
Tests/RunCMake/list/TRANSFORM-GENEX_STRIP.cmake

@@ -0,0 +1,49 @@
+set(mylist one "$<1:two\;three>" four "$<TARGET_OBJECTS:some_target>")
+
+list(TRANSFORM mylist GENEX_STRIP OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one;;four;")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one;;four;\"")
+endif()
+
+set(mylist "one $<CONFIG>" "$<1:two\;three>-$<PLATFORM_ID>" "$<ANGLE-R>four" "$<TARGET_OBJECTS:some_target>")
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP AT 1 3 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one $<CONFIG>;-;$<ANGLE-R>four;")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one $<CONFIG>;-;$<ANGLE-R>four;\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP AT 1 -2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one $<CONFIG>;-;four;$<TARGET_OBJECTS:some_target>")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one $<CONFIG>;-;four;$<TARGET_OBJECTS:some_target>\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP FOR 1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one $<CONFIG>;-;four;$<TARGET_OBJECTS:some_target>")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one $<CONFIG>;-;four;$<TARGET_OBJECTS:some_target>\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP FOR 1 -1 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one $<CONFIG>;-;four;")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one $<CONFIG>;-;four;\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP FOR 0 -1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one ;$<1:two;three>-$<PLATFORM_ID>;four;$<TARGET_OBJECTS:some_target>")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one ;$<1:two;three>-$<PLATFORM_ID>;four;$<TARGET_OBJECTS:some_target>\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP REGEX "(D|G)>" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "one ;-;$<ANGLE-R>four;$<TARGET_OBJECTS:some_target>")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${output}\", expected is \"one ;-;$<ANGLE-R>four;$<TARGET_OBJECTS:some_target>\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist GENEX_STRIP REGEX "(D|G)>")
+if (NOT mylist STREQUAL "one ;-;$<ANGLE-R>four;$<TARGET_OBJECTS:some_target>")
+  message (FATAL_ERROR "TRANSFORM(GENEX_STRIP) is \"${mylist}\", expected is \"one ;-;$<ANGLE-R>four;$<TARGET_OBJECTS:some_target>\"")
+endif()

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-InvalidAction-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-InvalidAction-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-InvalidAction.cmake:2 \(list\):
+  list sub-command TRANSFORM, BAD_ACTION invalid action.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-InvalidAction.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist BAD_ACTION)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-NoAction-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-NoAction-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-NoAction.cmake:2 \(list\):
+  list sub-command TRANSFORM requires an action to be specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-NoAction.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, OUTPUT_VARIABLE expects variable name argument.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER OUTPUT_VARIABLE)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER OUTPUT_VARIABLE output one_too_many)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-PREPEND-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, action PREPEND expects 1 argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist PREPEND)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-PREPEND-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist PREPEND delta one_too_many)

+ 48 - 0
Tests/RunCMake/list/TRANSFORM-PREPEND.cmake

@@ -0,0 +1,48 @@
+set(mylist alpha bravo charlie delta)
+
+list(TRANSFORM mylist PREPEND "P_" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "P_alpha;P_bravo;P_charlie;P_delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"P_alpha;P_bravo;P_charlie;P_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" AT 1 3 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;P_bravo;charlie;P_delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"alpha;P_bravo;charlie;P_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" AT 1 -2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;P_bravo;P_charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"alpha;P_bravo;P_charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" FOR 1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;P_bravo;P_charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"alpha;P_bravo;P_charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" FOR 1 -1 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;P_bravo;P_charlie;P_delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"alpha;P_bravo;P_charlie;P_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" FOR 0 -1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "P_alpha;bravo;P_charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"P_alpha;bravo;P_charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" REGEX "(r|t)a" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;P_bravo;charlie;P_delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${output}\", expected is \"alpha;P_bravo;charlie;P_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist PREPEND "P_" REGEX "(r|t)a")
+if (NOT mylist STREQUAL "alpha;P_bravo;charlie;P_delta")
+  message (FATAL_ERROR "TRANSFORM(PREPEND) is \"${mylist}\", expected is \"alpha;P_bravo;charlie;P_delta\"")
+endif()

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex-result.txt

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-REPLACE-InvalidRegex.cmake:2 \(list\):
+  list sub-command TRANSFORM, action REPLACE: Failed to compile regex
+  "\^\(alpha\$".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidRegex.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE "^(alpha$" "zulu")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace-result.txt

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

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1-result.txt

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-REPLACE-InvalidReplace1.cmake:2 \(list\):
+  list sub-command TRANSFORM, action REPLACE: replace-expression ends in a
+  backslash.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace1.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE "^alpha$" "zulu\\")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2-result.txt

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-REPLACE-InvalidReplace2.cmake:2 \(list\):
+  list sub-command TRANSFORM, action REPLACE: Unknown escape "\\z" in
+  replace-expression.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-InvalidReplace2.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE "^alpha$" "\\zulu")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-REPLACE-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, action REPLACE expects 2 argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-REPLACE-NoEnoughArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, action REPLACE expects 2 argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-NoEnoughArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE "^alpha$")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-REPLACE-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist REPLACE "^alpha$" "zulu" "one_too_many")

+ 48 - 0
Tests/RunCMake/list/TRANSFORM-REPLACE.cmake

@@ -0,0 +1,48 @@
+set(mylist alpha bravo charlie delta)
+
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha_alpha;bravo;charlie;delta_delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha_alpha;bravo;charlie;delta_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" AT 1 3 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie;delta_delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha;bravo;charlie;delta_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+e)$" "\\1_\\1" AT 1 -2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie_charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha;bravo;charlie_charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+e)$" "\\1_\\1" FOR 1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie_charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha;bravo;charlie_charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" FOR 1 -1 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie;delta_delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha;bravo;charlie_A;delta_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" FOR 0 -1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha_alpha;bravo;charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha_alpha;bravo;charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" REGEX "(r|t)a" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie;delta_delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"alpha;bravo;charlie;delta_delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(.+a)$" "\\1_\\1" REGEX "(r|t)a")
+if (NOT mylist STREQUAL "alpha;bravo;charlie;delta_delta")
+  message (FATAL_ERROR "TRANSFORM(REPLACE) is \"${mylist}\", expected is \"alpha;bravo;charlie;delta_delta\"")
+endif()

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-STRIP-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-STRIP-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist STRIP one_too_many)

+ 49 - 0
Tests/RunCMake/list/TRANSFORM-STRIP.cmake

@@ -0,0 +1,49 @@
+set(mylist " alpha" "bravo " " charlie " delta)
+
+list(TRANSFORM mylist STRIP OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo;charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \"alpha;bravo;charlie;delta\"")
+endif()
+
+set(mylist " alpha" "bravo " " charlie " "delta ")
+unset(output)
+list(TRANSFORM mylist STRIP AT 1 3 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL " alpha;bravo; charlie ;delta")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \" alpha;bravo; charlie ;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP AT 1 -2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL " alpha;bravo;charlie;delta ")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \" alpha;bravo;charlie;delta \"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP FOR 1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL " alpha;bravo;charlie;delta ")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \" alpha;bravo;charlie;delta \"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP FOR 1 -1 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL " alpha;bravo;charlie;delta")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \" alpha;bravo;charlie;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP FOR 0 -1 2 OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "alpha;bravo ;charlie;delta ")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \"alpha;bravo ;charlie;delta \"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP REGEX "(r|t)a" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL " alpha;bravo; charlie ;delta")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${output}\", expected is \" alpha;bravo; charlie ;delta\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist STRIP REGEX "(r|t)a")
+if (NOT mylist STREQUAL " alpha;bravo; charlie ;delta")
+  message (FATAL_ERROR "TRANSFORM(STRIP) is \"${mylist}\", expected is \" alpha;bravo; charlie ;delta\"")
+endif()

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-AT-BadArgument.cmake:2 \(list\):
+  list sub-command TRANSFORM, '1x 2': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-BadArgument.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER AT 0 1x 2)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-AT-InvalidIndex.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector AT, index: 3 out of range \(-3, 2\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-InvalidIndex.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER AT 0 3 2)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-AT-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector AT expects at least one numeric value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-AT-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER AT)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument-result.txt

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-FOR-BadArgument.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector FOR expects, at least, two numeric
+  values.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-BadArgument.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER FOR 0 1x 2)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-FOR-InvalidIndex.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector FOR, index: 6 out of range \(-3, 2\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-InvalidIndex.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER FOR 0 6 2)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-FOR-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector FOR expects, at least, two arguments.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER FOR)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-FOR-NoEnoughArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector FOR expects, at least, two arguments.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-NoEnoughArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER FOR 1)

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-FOR-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, '3': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-FOR-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER FOR 0 1 2 3)

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

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-Selector-REGEX-InvalidRegex-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-REGEX-InvalidRegex.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector REGEX failed to compile regex
+  "\^\(alpha\$".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

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

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER REGEX "^(alpha$")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments-result.txt

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

+ 5 - 0
Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-REGEX-NoArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector REGEX expects 'regular expression'
+  argument.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-Selector-REGEX-NoArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER REGEX)

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

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

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

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-Selector-REGEX-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

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

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER REGEX "^alpha$" "one_too_many")

+ 1 - 0
Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments-result.txt

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

+ 4 - 0
Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at TRANSFORM-TOLOWER-TooManyArguments.cmake:2 \(list\):
+  list sub-command TRANSFORM, 'one_too_many': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 2 - 0
Tests/RunCMake/list/TRANSFORM-TOLOWER-TooManyArguments.cmake

@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOLOWER one_too_many)

Some files were not shown because too many files changed in this diff