Browse Source

GenEx LIST: list operations

Fixes: #24550, #24547
Marc Chevrier 2 years ago
parent
commit
31675964e7
100 changed files with 2178 additions and 37 deletions
  1. 15 15
      Help/command/list.rst
  2. 262 2
      Help/manual/cmake-generator-expressions.7.rst
  3. 4 0
      Help/release/dev/GenEx-LIST.rst
  4. 705 10
      Source/cmGeneratorExpressionNode.cxx
  5. 10 8
      Source/cmList.cxx
  6. 1 1
      Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt
  7. 1 1
      Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
  8. 1 0
      Tests/RunCMake/CMakeLists.txt
  9. 34 0
      Tests/RunCMake/GenEx-LIST/APPEND.cmake.in
  10. 5 0
      Tests/RunCMake/GenEx-LIST/CMakeLists.txt
  11. 1 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt
  12. 9 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-stderr.txt
  13. 2 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator.cmake
  14. 1 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt
  15. 8 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-stderr.txt
  16. 2 0
      Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex.cmake
  17. 20 0
      Tests/RunCMake/GenEx-LIST/FIND.cmake.in
  18. 1 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt
  19. 8 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index1-stderr.txt
  20. 2 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake
  21. 1 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt
  22. 8 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index2-stderr.txt
  23. 2 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake
  24. 1 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt
  25. 8 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index3-stderr.txt
  26. 2 0
      Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake
  27. 20 0
      Tests/RunCMake/GenEx-LIST/GET.cmake.in
  28. 1 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt
  29. 8 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-stderr.txt
  30. 2 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1.cmake
  31. 1 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt
  32. 8 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-stderr.txt
  33. 2 0
      Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2.cmake
  34. 50 0
      Tests/RunCMake/GenEx-LIST/INSERT.cmake.in
  35. 35 0
      Tests/RunCMake/GenEx-LIST/JOIN.cmake.in
  36. 30 0
      Tests/RunCMake/GenEx-LIST/LENGTH.cmake.in
  37. 18 0
      Tests/RunCMake/GenEx-LIST/POP_BACK.cmake.in
  38. 18 0
      Tests/RunCMake/GenEx-LIST/POP_FRONT.cmake.in
  39. 34 0
      Tests/RunCMake/GenEx-LIST/PREPEND.cmake.in
  40. 1 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt
  41. 8 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-stderr.txt
  42. 2 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake
  43. 1 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt
  44. 8 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-stderr.txt
  45. 2 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2.cmake
  46. 1 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt
  47. 8 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-stderr.txt
  48. 2 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3.cmake
  49. 25 0
      Tests/RunCMake/GenEx-LIST/REMOVE_AT.cmake.in
  50. 20 0
      Tests/RunCMake/GenEx-LIST/REMOVE_DUPLICATES.cmake.in
  51. 32 0
      Tests/RunCMake/GenEx-LIST/REMOVE_ITEM.cmake.in
  52. 19 0
      Tests/RunCMake/GenEx-LIST/REVERSE.cmake.in
  53. 130 0
      Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake
  54. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt
  55. 8 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-stderr.txt
  56. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option.cmake
  57. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt
  58. 8 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-stderr.txt
  59. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option.cmake
  60. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt
  61. 8 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-stderr.txt
  62. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option.cmake
  63. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt
  64. 9 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-stderr.txt
  65. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option.cmake
  66. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt
  67. 9 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-stderr.txt
  68. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option.cmake
  69. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt
  70. 9 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-stderr.txt
  71. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option.cmake
  72. 1 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt
  73. 8 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-option-stderr.txt
  74. 2 0
      Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake
  75. 92 0
      Tests/RunCMake/GenEx-LIST/SORT.cmake.in
  76. 1 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt
  77. 8 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-stderr.txt
  78. 2 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1.cmake
  79. 1 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt
  80. 8 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-stderr.txt
  81. 2 0
      Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2.cmake
  82. 32 0
      Tests/RunCMake/GenEx-LIST/SUBLIST.cmake.in
  83. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-APPEND.cmake.in
  84. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-PREPEND.cmake.in
  85. 1 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt
  86. 9 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-stderr.txt
  87. 2 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1.cmake
  88. 1 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt
  89. 9 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-stderr.txt
  90. 2 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2.cmake
  91. 1 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt
  92. 8 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-stderr.txt
  93. 2 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex.cmake
  94. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE.cmake.in
  95. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-STRIP.cmake.in
  96. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-TOLOWER.cmake.in
  97. 50 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-TOUPPER.cmake.in
  98. 8 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-missing-arg-stderr.txt
  99. 1 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt
  100. 8 0
      Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-stderr.txt

+ 15 - 15
Help/command/list.rst

@@ -188,7 +188,7 @@ For more information on regular expressions look under
 
   .. versionadded:: 3.12
 
-  Transforms the list by applying an action to all or, by specifying a
+  Transforms the list by applying an ``<ACTION>`` to all or, by specifying a
   ``<SELECTOR>``, to the selected elements of the list, storing the result
   in-place or in the specified output variable.
 
@@ -205,42 +205,42 @@ For more information on regular expressions look under
     :command:`APPEND <string(APPEND)>`, :command:`PREPEND <string(PREPEND)>`
       Append, prepend specified value to each element of the list.
 
-      .. code-block:: cmake
-
-        list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
+      .. signature::
+        list(TRANSFORM <list> (APPEND|PREPEND) <value> ...)
+        :target: TRANSFORM_APPEND
 
-    :command:`TOUPPER <string(TOUPPER)>`, :command:`TOLOWER <string(TOLOWER)>`
-      Convert each element of the list to upper, lower characters.
+    :command:`TOLOWER <string(TOLOWER)>`, :command:`TOUPPER <string(TOUPPER)>`
+      Convert each element of the list to lower, upper characters.
 
-      .. code-block:: cmake
-
-        list(TRANSFORM <list> <TOLOWER|TOUPPER> ...)
+      .. signature::
+        list(TRANSFORM <list> (TOLOWER|TOUPPER) ...)
+        :target: TRANSFORM_TOLOWER
 
     :command:`STRIP <string(STRIP)>`
       Remove leading and trailing spaces from each element of the list.
 
-      .. code-block:: cmake
-
+      .. signature::
         list(TRANSFORM <list> STRIP ...)
+        :target: TRANSFORM_STRIP
 
     :command:`GENEX_STRIP <string(GENEX_STRIP)>`
       Strip any
       :manual:`generator expressions <cmake-generator-expressions(7)>`
       from each element of the list.
 
-      .. code-block:: cmake
-
+      .. signature::
         list(TRANSFORM <list> GENEX_STRIP ...)
+        :target: TRANSFORM_GENEX_STRIP
 
     :command:`REPLACE <string(REGEX 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 :command:`string(REGEX REPLACE)`).
 
-      .. code-block:: cmake
-
+      .. signature::
         list(TRANSFORM <list> REPLACE <regular_expression>
                                       <replace_expression> ...)
+        :target: TRANSFORM_REPLACE
 
   ``<SELECTOR>`` determines which elements of the list will be transformed.
   Only one type of selector can be specified at a time.

+ 262 - 2
Help/manual/cmake-generator-expressions.7.rst

@@ -104,6 +104,17 @@ improved further like so:
     VERBATIM
   )
 
+Finally, the above example can be expressed in a more simple and robust way
+using an alternate generator expression:
+
+.. code-block:: cmake
+
+  add_custom_target(run_some_tool
+    COMMAND some_tool "$<LIST:TRANSFORM,$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>,PREPEND,-I>"
+    COMMAND_EXPAND_LISTS
+    VERBATIM
+  )
+
 A common mistake is to try to split a generator expression across multiple
 lines with indenting:
 
@@ -318,6 +329,15 @@ String Transformations
 List Expressions
 ----------------
 
+Most of the expressions in this section are closely associated with the
+:command:`list` command, providing the same capabilities, but in
+the form of a generator expression.
+
+.. _GenEx List Comparisons:
+
+List Comparisons
+^^^^^^^^^^^^^^^^
+
 .. genex:: $<IN_LIST:string,list>
 
   .. versionadded:: 3.12
@@ -325,9 +345,186 @@ List Expressions
   ``1`` if ``string`` is an item in the semicolon-separated ``list``, else ``0``.
   It uses case-sensitive comparisons.
 
-.. genex:: $<JOIN:list,string>
+.. _GenEx List Queries:
+
+List Queries
+^^^^^^^^^^^^
+
+.. genex:: $<LIST:LENGTH,list>
+
+  .. versionadded:: 3.27
+
+  Returns the list's length.
+
+.. genex:: $<LIST:GET,list,index,...>
+
+  .. versionadded:: 3.27
+
+  Returns the list of elements specified by indices from the list.
+
+.. genex:: $<LIST:SUBLIST,list,begin,length>
+
+  .. versionadded:: 3.27
+
+  Returns a sublist of the given list. If <length> is 0, an empty list will be
+  returned. If <length> is -1 or the list is smaller than <begin>+<length> then
+  the remaining elements of the list starting at <begin> will be returned.
+
+.. genex:: $<LIST:FIND,list,value>
+
+  .. versionadded:: 3.27
+
+  Returns the index of the element specified in the list or -1 if it wasn't
+  found.
+
+.. _GenEx List Transformations:
+
+List Transformations
+^^^^^^^^^^^^^^^^^^^^
+
+.. genex:: $<LIST:JOIN,list,glue>
+
+  .. versionadded:: 3.27
+
+  Returns a string which joins the list with the content of the ``glue`` string
+  inserted between each item.
+
+.. genex:: $<LIST:APPEND,list,element,...>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the elements appended.
+
+.. genex:: $<LIST:PREPEND,list,element,...>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the elements inserted at the beginning of the list.
+
+.. genex:: $<LIST:INSERT,list,index,element,...>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the elements inserted at the specified index. It is an
+  error to specify an out-of-range index. Valid indexes are 0 to N where N is
+  the length of the list, inclusive. An empty list has length 0.
+
+.. genex:: $<LIST:POP_BACK,list>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the last element was removed.
+
+.. genex:: $<LIST:POP_FRONT,list>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the first element was removed.
+
+.. genex:: $<LIST:REMOVE_ITEM,list,value,...>
+
+  .. versionadded:: 3.27
+
+  Returns a list with all instances of the given values were removed.
+
+.. genex:: $<LIST:REMOVE_AT,list,index,...>
+
+  .. versionadded:: 3.27
 
-  Joins the list with the content of ``string`` inserted between each item.
+  Returns a list with all values at given indices were removed.
+
+.. genex:: $<LIST:REMOVE_DUPLICATES,list>
+
+  .. versionadded:: 3.27
+
+  Returns a list where duplicated items were removed. The relative order of
+  items is preserved, but if duplicates are encountered, only the first
+  instance is preserved.
+
+.. genex:: $<LIST:FILTER,list,INCLUDE|EXCLUDE,regex>
+
+  .. versionadded:: 3.27
+
+  Returns a list with the items that match the regular expression ``regex``
+  were included or removed.
+
+.. genex:: $<LIST:TRANSFORM,list,ACTION[,SELECTOR]>
+
+  .. versionadded:: 3.27
+
+  Returns the list transformed by applying an ``ACTION`` to all or, by
+  specifying a ``SELECTOR``, to the selected elements of the list.
+
+  .. note::
+
+    The ``TRANSFORM`` sub-command does not change the number of elements in the
+    list. If a ``SELECTOR`` is specified, only some elements will be changed,
+    the other ones will remain the same as before the transformation.
+
+  ``ACTION`` specifies the action to apply to the elements of the list.
+  The actions have exactly the same semantics as of the
+  :command:`list(TRANSFORM)` command.  ``ACTION`` must be one of the following:
+
+    :command:`APPEND <list(TRANSFORM_APPEND)>`, :command:`PREPEND <list(TRANSFORM_APPEND)>`
+      Append, prepend specified value to each element of the list.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,(APPEND|PREPEND),value[,SELECTOR]>
+
+    :command:`TOLOWER <list(TRANSFORM_TOLOWER)>`, :command:`TOUPPER <list(TRANSFORM_TOLOWER)>`
+      Convert each element of the list to lower, upper characters.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,(TOLOWER|TOUPPER)[,SELECTOR]>
+
+    :command:`STRIP <list(TRANSFORM_STRIP)>`
+      Remove leading and trailing spaces from each element of the list.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,STRIP[,SELECTOR]>
+
+    :command:`REPLACE <list(TRANSFORM_REPLACE)>`:
+      Match the regular expression as many times as possible and substitute
+      the replacement expression for the match for each element of the list.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,REPLACE,regular_expression,replace_expression[,SELECTOR]>
+
+  ``SELECTOR`` determines which elements of the list will be transformed.
+  Only one type of selector can be specified at a time. When given,
+  ``SELECTOR`` must be one of the following:
+
+    ``AT``
+      Specify a list of indexes.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,ACTION,AT,index[,index...]>
+
+    ``FOR``
+      Specify a range with, optionally, an increment used to iterate over the
+      range.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,ACTION,FOR,start,stop[,step]>
+
+    ``REGEX``
+      Specify a regular expression.
+      Only elements matching the regular expression will be transformed.
+
+      .. code-block:: cmake
+
+        $<LIST:TRANSFORM,list,ACTION,REGEX,regular_expression>
+
+.. genex:: $<JOIN:list,glue>
+
+  Joins the list with the content of the ``glue`` string inserted between each
+  item.
 
 .. genex:: $<REMOVE_DUPLICATES:list>
 
@@ -344,6 +541,69 @@ List Expressions
   Includes or removes items from ``list`` that match the regular expression
   ``regex``.
 
+.. _GenEx List Ordering:
+
+List Ordering
+^^^^^^^^^^^^^
+
+.. genex:: $<LIST:REVERSE,list>
+
+  .. versionadded:: 3.27
+
+  Returns the list with the elements in reverse order.
+
+.. genex:: $<LIST:SORT,list[,(COMPARE:option|CASE:option|ORDER:option)]...>
+
+  .. versionadded:: 3.27
+
+  Returns the list sorted according the specified options.
+
+  Use one of the ``COMPARE`` options to select the comparison method
+  for sorting:
+
+    ``STRING``
+      Sorts a list of strings alphabetically.
+      This is the default behavior if the ``COMPARE`` option is not given.
+
+    ``FILE_BASENAME``
+      Sorts a list of pathnames of files by their basenames.
+
+    ``NATURAL``
+      Sorts a list of strings using natural order
+      (see ``strverscmp(3)`` manual), i.e. such that contiguous digits
+      are compared as whole numbers.
+      For example: the following list `10.0 1.1 2.1 8.0 2.0 3.1`
+      will be sorted as `1.1 2.0 2.1 3.1 8.0 10.0` if the ``NATURAL``
+      comparison is selected where it will be sorted as
+      `1.1 10.0 2.0 2.1 3.1 8.0` with the ``STRING`` comparison.
+
+  Use one of the ``CASE`` options to select a case sensitive or case
+  insensitive sort mode:
+
+    ``SENSITIVE``
+      List items are sorted in a case-sensitive manner.
+      This is the default behavior if the ``CASE`` option is not given.
+
+    ``INSENSITIVE``
+      List items are sorted case insensitively.  The order of
+      items which differ only by upper/lowercase is not specified.
+
+  To control the sort order, one of the ``ORDER`` options can be given:
+
+    ``ASCENDING``
+      Sorts the list in ascending order.
+      This is the default behavior when the ``ORDER`` option is not given.
+
+    ``DESCENDING``
+      Sorts the list in descending order.
+
+  This is an error to specify multiple times the same option. Various options
+  can be specified in any order:
+
+  .. code-block:: cmake
+
+    $<LIST:SORT,list,CASE:SENSITIVE,COMPARE:STRING,ORDER:DESCENDING>
+
 Path Expressions
 ----------------
 

+ 4 - 0
Help/release/dev/GenEx-LIST.rst

@@ -0,0 +1,4 @@
+GenEx-LIST
+----------
+
+* The :genex:`LIST` generator expression was added to manage lists.

+ 705 - 10
Source/cmGeneratorExpressionNode.cxx

@@ -635,22 +635,48 @@ public:
 
 using Arguments = Range<std::vector<std::string>>;
 
-bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
-                           const GeneratorExpressionContent* cnt,
-                           cm::string_view option, std::size_t count,
-                           int required = 1, bool exactly = true)
+bool CheckGenExParameters(cmGeneratorExpressionContext* ctx,
+                          const GeneratorExpressionContent* cnt,
+                          cm::string_view genex, cm::string_view option,
+                          std::size_t count, int required = 1,
+                          bool exactly = true)
 {
   if (static_cast<int>(count) < required ||
       (exactly && static_cast<int>(count) > required)) {
+    std::string nbParameters;
+    switch (required) {
+      case 1:
+        nbParameters = "one parameter";
+        break;
+      case 2:
+        nbParameters = "two parameters";
+        break;
+      case 3:
+        nbParameters = "three parameters";
+        break;
+      case 4:
+        nbParameters = "four parameters";
+        break;
+      default:
+        nbParameters = cmStrCat(std::to_string(required), " parameters");
+    }
     reportError(ctx, cnt->GetOriginalExpression(),
-                cmStrCat("$<PATH:", option, "> expression requires ",
-                         (exactly ? "exactly" : "at least"), ' ',
-                         (required == 1 ? "one parameter" : "two parameters"),
+                cmStrCat("$<", genex, ':', option, "> expression requires ",
+                         (exactly ? "exactly" : "at least"), ' ', nbParameters,
                          '.'));
     return false;
   }
   return true;
 };
+
+bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
+                           const GeneratorExpressionContent* cnt,
+                           cm::string_view option, std::size_t count,
+                           int required = 1, bool exactly = true)
+{
+  return CheckGenExParameters(ctx, cnt, "PATH"_s, option, count, required,
+                              exactly);
+}
 bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
                          const GeneratorExpressionContent* cnt,
                          cm::string_view option, const Arguments& args,
@@ -658,6 +684,7 @@ bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
 {
   return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
 };
+
 std::string ToString(bool isTrue)
 {
   return isTrue ? "1" : "0";
@@ -1108,6 +1135,670 @@ static const struct PathEqualNode : public cmGeneratorExpressionNode
   }
 } pathEqualNode;
 
+namespace {
+inline bool CheckListParametersEx(cmGeneratorExpressionContext* ctx,
+                                  const GeneratorExpressionContent* cnt,
+                                  cm::string_view option, std::size_t count,
+                                  int required = 1, bool exactly = true)
+{
+  return CheckGenExParameters(ctx, cnt, "LIST"_s, option, count, required,
+                              exactly);
+}
+inline bool CheckListParameters(cmGeneratorExpressionContext* ctx,
+                                const GeneratorExpressionContent* cnt,
+                                cm::string_view option, const Arguments& args,
+                                int required = 1)
+{
+  return CheckListParametersEx(ctx, cnt, option, args.size(), required);
+};
+
+inline cmList GetList(std::string const& list)
+{
+  return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes };
+}
+
+bool GetNumericArgument(const std::string& arg, int& value)
+{
+  try {
+    std::size_t pos;
+
+    value = std::stoi(arg, &pos);
+    if (pos != arg.length()) {
+      // this is not a number
+      return false;
+    }
+  } catch (const std::invalid_argument&) {
+    return false;
+  }
+
+  return true;
+}
+
+bool GetNumericArguments(
+  cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt,
+  Arguments const& args, std::vector<int>& indexes,
+  cmList::ExpandElements expandElements = cmList::ExpandElements::No)
+{
+  using IndexRange = cmRange<Arguments::const_iterator>;
+  IndexRange arguments(args.begin(), args.end());
+  cmList list;
+  if (expandElements == cmList::ExpandElements::Yes) {
+    list = cmList{ args.begin(), args.end(), expandElements };
+    arguments = IndexRange{ list.begin(), list.end() };
+  }
+
+  for (auto const& value : arguments) {
+    int index;
+    if (!GetNumericArgument(value, index)) {
+      reportError(ctx, cnt->GetOriginalExpression(),
+                  cmStrCat("index: \"", value, "\" is not a valid index"));
+      return false;
+    }
+    indexes.push_back(index);
+  }
+  return true;
+}
+}
+
+static const struct ListNode : public cmGeneratorExpressionNode
+{
+  ListNode() {} // NOLINT(modernize-use-equals-default)
+
+  int NumExpectedParameters() const override { return TwoOrMoreParameters; }
+
+  bool AcceptsArbitraryContentParameter() const override { return true; }
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+  {
+    static std::unordered_map<
+      cm::string_view,
+      std::function<std::string(cmGeneratorExpressionContext*,
+                                const GeneratorExpressionContent*,
+                                Arguments&)>>
+      listCommands{
+        { "LENGTH"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "LENGTH"_s, args)) {
+              return std::to_string(GetList(args.front()).size());
+            }
+            return std::string{};
+          } },
+        { "GET"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "GET"_s, args.size(), 2,
+                                      false)) {
+              auto list = GetList(args.front());
+              if (list.empty()) {
+                reportError(ctx, cnt->GetOriginalExpression(),
+                            "given empty list");
+                return std::string{};
+              }
+
+              std::vector<int> indexes;
+              if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
+                                       cmList::ExpandElements::Yes)) {
+                return std::string{};
+              }
+              try {
+                return list.get_items(indexes.begin(), indexes.end())
+                  .to_string();
+              } catch (std::out_of_range& e) {
+                reportError(ctx, cnt->GetOriginalExpression(), e.what());
+                return std::string{};
+              }
+            }
+            return std::string{};
+          } },
+        { "JOIN"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "JOIN"_s, args, 2)) {
+              return GetList(args.front()).join(args[1]);
+            }
+            return std::string{};
+          } },
+        { "SUBLIST"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "SUBLIST"_s, args, 3)) {
+              auto list = GetList(args.front());
+              if (!list.empty()) {
+                std::vector<int> indexes;
+                if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes)) {
+                  return std::string{};
+                }
+                if (indexes[0] < 0) {
+                  reportError(ctx, cnt->GetOriginalExpression(),
+                              cmStrCat("begin index: ", indexes[0],
+                                       " is out of range 0 - ",
+                                       list.size() - 1));
+                  return std::string{};
+                }
+                if (indexes[1] < -1) {
+                  reportError(ctx, cnt->GetOriginalExpression(),
+                              cmStrCat("length: ", indexes[1],
+                                       " should be -1 or greater"));
+                  return std::string{};
+                }
+                try {
+                  return list
+                    .sublist(static_cast<cmList::size_type>(indexes[0]),
+                             static_cast<cmList::size_type>(indexes[1]))
+                    .to_string();
+                } catch (std::out_of_range& e) {
+                  reportError(ctx, cnt->GetOriginalExpression(), e.what());
+                  return std::string{};
+                }
+              }
+            }
+            return std::string{};
+          } },
+        { "FIND"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "FIND"_s, args, 2)) {
+              auto list = GetList(args.front());
+              auto index = list.find(args[1]);
+              return index == cmList::npos ? "-1" : std::to_string(index);
+            }
+            return std::string{};
+          } },
+        { "APPEND"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "APPEND"_s, args.size(), 2,
+                                      false)) {
+              auto list = args.front();
+              args.advance(1);
+              return cmList::append(args.begin(), args.end(), list);
+            }
+            return std::string{};
+          } },
+        { "PREPEND"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "PREPEND"_s, args.size(), 2,
+                                      false)) {
+              auto list = args.front();
+              args.advance(1);
+              return cmList::prepend(args.begin(), args.end(), list);
+            }
+            return std::string{};
+          } },
+        { "INSERT"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "INSERT"_s, args.size(), 3,
+                                      false)) {
+              int index;
+              if (!GetNumericArgument(args[1], index)) {
+                reportError(
+                  ctx, cnt->GetOriginalExpression(),
+                  cmStrCat("index: \"", args[1], "\" is not a valid index"));
+                return std::string{};
+              }
+              try {
+                auto list = GetList(args.front());
+                args.advance(2);
+                list.insert_items(index, args.begin(), args.end());
+                return list.to_string();
+              } catch (std::out_of_range& e) {
+                reportError(ctx, cnt->GetOriginalExpression(), e.what());
+                return std::string{};
+              }
+            }
+            return std::string{};
+          } },
+        { "POP_BACK"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "POP_BACK"_s, args)) {
+              auto list = GetList(args.front());
+              if (!list.empty()) {
+                list.pop_back();
+                return list.to_string();
+              }
+            }
+            return std::string{};
+          } },
+        { "POP_FRONT"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "POP_FRONT"_s, args)) {
+              auto list = GetList(args.front());
+              if (!list.empty()) {
+                list.pop_front();
+                return list.to_string();
+              }
+            }
+            return std::string{};
+          } },
+        { "REMOVE_DUPLICATES"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "REMOVE_DUPLICATES"_s, args)) {
+              return GetList(args.front()).remove_duplicates().to_string();
+            }
+            return std::string{};
+          } },
+        { "REMOVE_ITEM"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "REMOVE_ITEM"_s, args.size(),
+                                      2, false)) {
+              auto list = GetList(args.front());
+              args.advance(1);
+              cmList items{ args.begin(), args.end(),
+                            cmList::ExpandElements::Yes };
+              return list.remove_items(items.begin(), items.end()).to_string();
+            }
+            return std::string{};
+          } },
+        { "REMOVE_AT"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "REMOVE_AT"_s, args.size(), 2,
+                                      false)) {
+              auto list = GetList(args.front());
+              std::vector<int> indexes;
+              if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
+                                       cmList::ExpandElements::Yes)) {
+                return std::string{};
+              }
+              try {
+                return list.remove_items(indexes.begin(), indexes.end())
+                  .to_string();
+              } catch (std::out_of_range& e) {
+                reportError(ctx, cnt->GetOriginalExpression(), e.what());
+                return std::string{};
+              }
+            }
+            return std::string{};
+          } },
+        { "FILTER"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "FILTER"_s, args, 3)) {
+              auto const& op = args[1];
+              if (op != "INCLUDE"_s && op != "EXCLUDE"_s) {
+                reportError(
+                  ctx, cnt->GetOriginalExpression(),
+                  cmStrCat("sub-command FILTER does not recognize operator \"",
+                           op, "\". It must be either INCLUDE or EXCLUDE."));
+                return std::string{};
+              }
+              try {
+                return GetList(args.front())
+                  .filter(args[2],
+                          op == "INCLUDE"_s ? cmList::FilterMode::INCLUDE
+                                            : cmList::FilterMode::EXCLUDE)
+                  .to_string();
+              } catch (std::invalid_argument&) {
+                reportError(
+                  ctx, cnt->GetOriginalExpression(),
+                  cmStrCat("sub-command FILTER, failed to compile regex \"",
+                           args[2], "\"."));
+                return std::string{};
+              }
+            }
+            return std::string{};
+          } },
+        { "TRANSFORM"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "TRANSFORM"_s, args.size(), 2,
+                                      false)) {
+              auto list = GetList(args.front());
+              if (!list.empty()) {
+                struct ActionDescriptor
+                {
+                  ActionDescriptor(std::string name)
+                    : Name(std::move(name))
+                  {
+                  }
+                  ActionDescriptor(std::string name,
+                                   cmList::TransformAction action, int arity)
+                    : Name(std::move(name))
+                    , Action(action)
+                    , Arity(arity)
+                  {
+                  }
+
+                  operator const std::string&() const { return this->Name; }
+
+                  std::string Name;
+                  cmList::TransformAction Action;
+                  int Arity = 0;
+                };
+
+                static std::set<
+                  ActionDescriptor,
+                  std::function<bool(const std::string&, const std::string&)>>
+                  descriptors{
+                    { { "APPEND", cmList::TransformAction::APPEND, 1 },
+                      { "PREPEND", cmList::TransformAction::PREPEND, 1 },
+                      { "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
+                      { "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
+                      { "STRIP", cmList::TransformAction::STRIP, 0 },
+                      { "REPLACE", cmList::TransformAction::REPLACE, 2 } },
+                    [](const std::string& x, const std::string& y) {
+                      return x < y;
+                    }
+                  };
+
+                auto descriptor = descriptors.find(args.advance(1).front());
+                if (descriptor == descriptors.end()) {
+                  reportError(ctx, cnt->GetOriginalExpression(),
+                              cmStrCat(" sub-command TRANSFORM, ",
+                                       args.front(), " invalid action."));
+                  return std::string{};
+                }
+
+                // Action arguments
+                args.advance(1);
+                if (args.size() < descriptor->Arity) {
+                  reportError(ctx, cnt->GetOriginalExpression(),
+                              cmStrCat("sub-command TRANSFORM, action ",
+                                       descriptor->Name, " expects ",
+                                       descriptor->Arity, " argument(s)."));
+                  return std::string{};
+                }
+                std::vector<std::string> arguments;
+                if (descriptor->Arity > 0) {
+                  arguments = std::vector<std::string>(
+                    args.begin(), args.begin() + descriptor->Arity);
+                  args.advance(descriptor->Arity);
+                }
+
+                const std::string REGEX{ "REGEX" };
+                const std::string AT{ "AT" };
+                const std::string FOR{ "FOR" };
+                std::unique_ptr<cmList::TransformSelector> selector;
+
+                try {
+                  // handle optional arguments
+                  while (!args.empty()) {
+                    if ((args.front() == REGEX || args.front() == AT ||
+                         args.front() == FOR) &&
+                        selector) {
+                      reportError(ctx, cnt->GetOriginalExpression(),
+                                  cmStrCat("sub-command TRANSFORM, selector "
+                                           "already specified (",
+                                           selector->GetTag(), ")."));
+
+                      return std::string{};
+                    }
+
+                    // REGEX selector
+                    if (args.front() == REGEX) {
+                      if (args.advance(1).empty()) {
+                        reportError(
+                          ctx, cnt->GetOriginalExpression(),
+                          "sub-command TRANSFORM, selector REGEX expects "
+                          "'regular expression' argument.");
+                        return std::string{};
+                      }
+
+                      selector = cmList::TransformSelector::New<
+                        cmList::TransformSelector::REGEX>(args.front());
+
+                      args.advance(1);
+                      continue;
+                    }
+
+                    // AT selector
+                    if (args.front() == AT) {
+                      args.advance(1);
+                      // get all specified indexes
+                      std::vector<cmList::index_type> indexes;
+                      while (!args.empty()) {
+                        cmList indexList{ args.front() };
+                        for (auto const& index : indexList) {
+                          int value;
+
+                          if (!GetNumericArgument(index, value)) {
+                            // this is not a number, stop processing
+                            reportError(
+                              ctx, cnt->GetOriginalExpression(),
+                              cmStrCat("sub-command TRANSFORM, selector AT: '",
+                                       index, "': unexpected argument."));
+                            return std::string{};
+                          }
+                          indexes.push_back(value);
+                        }
+                        args.advance(1);
+                      }
+
+                      if (indexes.empty()) {
+                        reportError(ctx, cnt->GetOriginalExpression(),
+                                    "sub-command TRANSFORM, selector AT "
+                                    "expects at least one "
+                                    "numeric value.");
+                        return std::string{};
+                      }
+
+                      selector = cmList::TransformSelector::New<
+                        cmList::TransformSelector::AT>(std::move(indexes));
+
+                      continue;
+                    }
+
+                    // FOR selector
+                    if (args.front() == FOR) {
+                      if (args.advance(1).size() < 2) {
+                        reportError(ctx, cnt->GetOriginalExpression(),
+                                    "sub-command TRANSFORM, selector FOR "
+                                    "expects, at least,"
+                                    " two arguments.");
+                        return std::string{};
+                      }
+
+                      cmList::index_type start = 0;
+                      cmList::index_type stop = 0;
+                      cmList::index_type step = 1;
+                      bool valid = false;
+
+                      if (GetNumericArgument(args.front(), start) &&
+                          GetNumericArgument(args.advance(1).front(), stop)) {
+                        valid = true;
+                      }
+
+                      if (!valid) {
+                        reportError(
+                          ctx, cnt->GetOriginalExpression(),
+                          "sub-command TRANSFORM, selector FOR expects, "
+                          "at least, two numeric values.");
+                        return std::string{};
+                      }
+                      // try to read a third numeric value for step
+                      if (!args.advance(1).empty()) {
+                        if (!GetNumericArgument(args.front(), step)) {
+                          // this is not a number
+                          step = -1;
+                        }
+                        args.advance(1);
+                      }
+
+                      if (step <= 0) {
+                        reportError(
+                          ctx, cnt->GetOriginalExpression(),
+                          "sub-command TRANSFORM, selector FOR expects "
+                          "positive numeric value for <step>.");
+                        return std::string{};
+                      }
+
+                      selector = cmList::TransformSelector::New<
+                        cmList::TransformSelector::FOR>({ start, stop, step });
+                      continue;
+                    }
+
+                    reportError(ctx, cnt->GetOriginalExpression(),
+                                cmStrCat("sub-command TRANSFORM, '",
+                                         cmJoin(args, ", "),
+                                         "': unexpected argument(s)."));
+                    return std::string{};
+                  }
+
+                  return list
+                    .transform(descriptor->Action, arguments,
+                               std::move(selector))
+                    .to_string();
+                } catch (cmList::transform_error& e) {
+                  reportError(ctx, cnt->GetOriginalExpression(), e.what());
+                  return std::string{};
+                }
+              }
+            }
+            return std::string{};
+          } },
+        { "REVERSE"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParameters(ctx, cnt, "REVERSE"_s, args)) {
+              return GetList(args.front()).reverse().to_string();
+            }
+            return std::string{};
+          } },
+        { "SORT"_s,
+          [](cmGeneratorExpressionContext* ctx,
+             const GeneratorExpressionContent* cnt,
+             Arguments& args) -> std::string {
+            if (CheckListParametersEx(ctx, cnt, "SORT"_s, args.size(), 1,
+                                      false)) {
+              auto list = GetList(args.front());
+              args.advance(1);
+              const auto COMPARE = "COMPARE:"_s;
+              const auto CASE = "CASE:"_s;
+              const auto ORDER = "ORDER:"_s;
+              using SortConfig = cmList::SortConfiguration;
+              SortConfig sortConfig;
+              for (auto const& arg : args) {
+                if (cmHasPrefix(arg, COMPARE)) {
+                  if (sortConfig.Compare !=
+                      SortConfig::CompareMethod::DEFAULT) {
+                    reportError(ctx, cnt->GetOriginalExpression(),
+                                "sub-command SORT, COMPARE option has been "
+                                "specified multiple times.");
+                    return std::string{};
+                  }
+                  auto option =
+                    cm::string_view{ arg.c_str() + COMPARE.length() };
+                  if (option == "STRING"_s) {
+                    sortConfig.Compare = SortConfig::CompareMethod::STRING;
+                    continue;
+                  }
+                  if (option == "FILE_BASENAME"_s) {
+                    sortConfig.Compare =
+                      SortConfig::CompareMethod::FILE_BASENAME;
+                    continue;
+                  }
+                  if (option == "NATURAL"_s) {
+                    sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
+                    continue;
+                  }
+                  reportError(
+                    ctx, cnt->GetOriginalExpression(),
+                    cmStrCat(
+                      "sub-command SORT, an invalid COMPARE option has been "
+                      "specified: \"",
+                      option, "\"."));
+                  return std::string{};
+                }
+                if (cmHasPrefix(arg, CASE)) {
+                  if (sortConfig.Case !=
+                      SortConfig::CaseSensitivity::DEFAULT) {
+                    reportError(ctx, cnt->GetOriginalExpression(),
+                                "sub-command SORT, CASE option has been "
+                                "specified multiple times.");
+                    return std::string{};
+                  }
+                  auto option = cm::string_view{ arg.c_str() + CASE.length() };
+                  if (option == "SENSITIVE"_s) {
+                    sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
+                    continue;
+                  }
+                  if (option == "INSENSITIVE"_s) {
+                    sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
+                    continue;
+                  }
+                  reportError(
+                    ctx, cnt->GetOriginalExpression(),
+                    cmStrCat(
+                      "sub-command SORT, an invalid CASE option has been "
+                      "specified: \"",
+                      option, "\"."));
+                  return std::string{};
+                }
+                if (cmHasPrefix(arg, ORDER)) {
+                  if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
+                    reportError(ctx, cnt->GetOriginalExpression(),
+                                "sub-command SORT, ORDER option has been "
+                                "specified multiple times.");
+                    return std::string{};
+                  }
+                  auto option =
+                    cm::string_view{ arg.c_str() + ORDER.length() };
+                  if (option == "ASCENDING"_s) {
+                    sortConfig.Order = SortConfig::OrderMode::ASCENDING;
+                    continue;
+                  }
+                  if (option == "DESCENDING"_s) {
+                    sortConfig.Order = SortConfig::OrderMode::DESCENDING;
+                    continue;
+                  }
+                  reportError(
+                    ctx, cnt->GetOriginalExpression(),
+                    cmStrCat(
+                      "sub-command SORT, an invalid ORDER option has been "
+                      "specified: \"",
+                      option, "\"."));
+                  return std::string{};
+                }
+                reportError(ctx, cnt->GetOriginalExpression(),
+                            cmStrCat("sub-command SORT, option \"", arg,
+                                     "\" is invalid."));
+                return std::string{};
+              }
+
+              return list.sort(sortConfig).to_string();
+            }
+            return std::string{};
+          } }
+      };
+
+    if (cm::contains(listCommands, parameters.front())) {
+      auto args = Arguments{ parameters }.advance(1);
+      return listCommands[parameters.front()](context, content, args);
+    }
+
+    reportError(context, content->GetOriginalExpression(),
+                cmStrCat(parameters.front(), ": invalid option."));
+    return std::string{};
+  }
+} listNode;
+
 static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
 {
   MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
@@ -1559,7 +2250,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
       // reportError(context, content->GetOriginalExpression(), "");
       reportError(
         context, content->GetOriginalExpression(),
-        "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets "
+        "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary "
+        "targets "
         "to specify include directories, compile definitions, and compile "
         "options.  It may not be used with the add_custom_command, "
         "add_custom_target, or file(GENERATE) commands.");
@@ -1704,7 +2396,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
       reportError(
         context, content->GetOriginalExpression(),
         "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
-        "to specify link libraries, link directories, link options, and link "
+        "to specify link libraries, link directories, link options, and "
+        "link "
         "depends.");
       return std::string();
     }
@@ -2086,7 +2779,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
         reportError(
           context, content->GetOriginalExpression(),
           "$<TARGET_PROPERTY:prop>  may only be used with binary targets.  "
-          "It may not be used with add_custom_command or add_custom_target.  "
+          "It may not be used with add_custom_command or add_custom_target. "
+          " "
           " "
           "Specify the target to read a property from using the "
           "$<TARGET_PROPERTY:tgt,prop> signature instead.");
@@ -3780,6 +4474,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "IN_LIST", &inListNode },
     { "FILTER", &filterNode },
     { "REMOVE_DUPLICATES", &removeDuplicatesNode },
+    { "LIST", &listNode },
     { "LOWER_CASE", &lowerCaseNode },
     { "UPPER_CASE", &upperCaseNode },
     { "PATH", &pathNode },

+ 10 - 8
Source/cmList.cxx

@@ -835,18 +835,19 @@ cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
         cmStrCat("index: ", pos, " out of range (0, 0)"));
     }
 
+    auto index = pos;
     if (!this->Values.empty()) {
       auto length = this->Values.size();
-      if (pos < 0) {
-        pos = static_cast<index_type>(length) + pos;
+      if (index < 0) {
+        index = static_cast<index_type>(length) + index;
       }
-      if (pos < 0 || length <= static_cast<size_type>(pos)) {
+      if (index < 0 || length <= static_cast<size_type>(index)) {
         throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
                                          this->Values.size(), ", ",
                                          this->Values.size() - 1, ")"));
       }
     }
-    return pos;
+    return index;
   }
 
   return pos < 0 ? this->Values.size() + pos : pos;
@@ -860,18 +861,19 @@ cmList::size_type cmList::ComputeInsertIndex(index_type pos,
         cmStrCat("index: ", pos, " out of range (0, 0)"));
     }
 
+    auto index = pos;
     if (!this->Values.empty()) {
       auto length = this->Values.size();
-      if (pos < 0) {
-        pos = static_cast<index_type>(length) + pos;
+      if (index < 0) {
+        index = static_cast<index_type>(length) + index;
       }
-      if (pos < 0 || length < static_cast<size_type>(pos)) {
+      if (index < 0 || length < static_cast<size_type>(index)) {
         throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
                                          this->Values.size(), ", ",
                                          this->Values.size(), ")"));
       }
     }
-    return pos;
+    return index;
   }
 
   return pos < 0 ? this->Values.size() + pos : pos;

+ 1 - 1
Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt

@@ -1,5 +1,5 @@
 CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\):
-  list index: (-2147483643|2147483647) out of range \(-5, 4\)
+  list index: (-2147483648|2147483647) out of range \(-5, 4\)
 Call Stack \(most recent call first\):
   CMP0121-ERANGE-OLD.cmake:2 \(include\)
   CMakeLists.txt:3 \(include\)

+ 1 - 1
Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt

@@ -9,7 +9,7 @@ Call Stack \(most recent call first\):
 This warning is for project developers.  Use -Wno-dev to suppress it.
 .*
 CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\):
-  list index: (-2147483643|2147483647) out of range \(-5, 4\)
+  list index: (-2147483648|2147483647) out of range \(-5, 4\)
 Call Stack \(most recent call first\):
   CMP0121-ERANGE-WARN.cmake:2 \(include\)
   CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -370,6 +370,7 @@ add_RunCMake_test(GenEx-TARGET_PROPERTY)
 add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
 add_RunCMake_test(GenEx-PATH)
 add_RunCMake_test(GenEx-PATH_EQUAL)
+add_RunCMake_test(GenEx-LIST)
 add_RunCMake_test(GeneratorExpression)
 add_RunCMake_test(GeneratorInstance)
 add_RunCMake_test(GeneratorPlatform)

+ 34 - 0
Tests/RunCMake/GenEx-LIST/APPEND.cmake.in

@@ -0,0 +1,34 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(APPEND listvar e)
+set (output "$<LIST:APPEND,a;b;c;d,e>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(APPEND listvar e f)
+set (output "$<LIST:APPEND,a;b;c;d,e,f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(APPEND listvar e f)
+set (output "$<LIST:APPEND,a;b;c;d,e;f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+unset(listvar)
+list(APPEND listvar e f)
+set (output "$<LIST:APPEND,,e,f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:APPEND..." ${errors})

+ 5 - 0
Tests/RunCMake/GenEx-LIST/CMakeLists.txt

@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.18...3.25)
+
+project(${RunCMake_TEST} NONE)
+
+include(${RunCMake_TEST}.cmake)

+ 1 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at FILTER-wrong-operator.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:FILTER,a;b;c,WRONG_OPERATOR,\^a>
+
+  sub-command FILTER does not recognize operator "WRONG_OPERATOR".  It must
+  be either INCLUDE or EXCLUDE.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:FILTER,a;b;c,WRONG_OPERATOR,^a>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at FILTER-wrong-regex.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:FILTER,a;b;c,INCLUDE,\^\(a>
+
+  sub-command FILTER, failed to compile regex "\^\(a".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:FILTER,a;b;c,INCLUDE,^(a>")

+ 20 - 0
Tests/RunCMake/GenEx-LIST/FIND.cmake.in

@@ -0,0 +1,20 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(FIND listvar "c" reference)
+set (output "$<LIST:FIND,a;b;c;d,c>")
+if (NOT output EQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(FIND listvar "e" reference)
+set (output "$<LIST:FIND,a;b;c;d,e>")
+if (NOT output EQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:FIND..." ${errors})

+ 1 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index1-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at GET-wrong-index1.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:GET,,0>
+
+  given empty list
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,,0>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index2-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at GET-wrong-index2.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:GET,a;b,2>
+
+  index: 2 out of range \(-2, 1\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,a;b,2>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index3-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at GET-wrong-index3.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:GET,a;b,1,-3>
+
+  index: -3 out of range \(-2, 1\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,a;b,1,-3>")

+ 20 - 0
Tests/RunCMake/GenEx-LIST/GET.cmake.in

@@ -0,0 +1,20 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(GET listvar 0 2 reference)
+set (output "$<LIST:GET,a;b;c;d,0,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(GET listvar 0 -3 2 reference)
+set (output "$<LIST:GET,a;b;c;d,0,-3,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:GET..." ${errors})

+ 1 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at INSERT-wrong-index1.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:INSERT,a;b;c,4,d>
+
+  index: 4 out of range \(-3, 3\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:INSERT,a;b;c,4,d>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at INSERT-wrong-index2.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:INSERT,a;b;c,-4,d>
+
+  index: -4 out of range \(-3, 3\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:INSERT,a;b;c,-4,d>")

+ 50 - 0
Tests/RunCMake/GenEx-LIST/INSERT.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(INSERT listvar 1 e)
+set (output "$<LIST:INSERT,a;b;c;d,1,e>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+
+list(INSERT listvar 2 e f)
+set (output "$<LIST:INSERT,a;b;c;d,2,e,f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(INSERT listvar 0 e f)
+set (output "$<LIST:INSERT,a;b;c;d,0,e;f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(INSERT listvar -2 e f)
+set (output "$<LIST:INSERT,a;b;c;d,-2,e;f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(INSERT listvar 3 e f)
+set (output "$<LIST:INSERT,a;b;c;d,3,e;f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+unset(listvar)
+list(INSERT listvar 0 e f)
+set (output "$<LIST:INSERT,,0,e,f>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:INSERT..." ${errors})

+ 35 - 0
Tests/RunCMake/GenEx-LIST/JOIN.cmake.in

@@ -0,0 +1,35 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(JOIN listvar ":" reference)
+set (output "$<LIST:JOIN,a;b;c;d,:>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(JOIN listvar "" reference)
+set (output "$<LIST:JOIN,a;b;c;d,>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a)
+
+list(JOIN listvar ":" reference)
+set (output "$<LIST:JOIN,a,:>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+unset(listvar)
+
+list(JOIN listvar ":" reference)
+set (output "$<LIST:JOIN,,:>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+check_errors("LIST:JOIN..." ${errors})

+ 30 - 0
Tests/RunCMake/GenEx-LIST/LENGTH.cmake.in

@@ -0,0 +1,30 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(LENGTH listvar reference)
+set (output "$<LIST:LENGTH,a;b;c;d>")
+if (NOT output EQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar "")
+
+list(LENGTH listvar reference)
+set (output "$<LIST:LENGTH,>")
+if (NOT output EQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+unset(listvar)
+
+list(LENGTH listvar reference)
+set (output "$<LIST:LENGTH,>")
+if (NOT output EQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:LENGTH..." ${errors})

+ 18 - 0
Tests/RunCMake/GenEx-LIST/POP_BACK.cmake.in

@@ -0,0 +1,18 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(POP_BACK listvar)
+set (output "$<LIST:POP_BACK,a;b;c;d>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set (output "$<LIST:POP_BACK,>")
+if (NOT output STREQUAL "")
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:POP_BACK..." ${errors})

+ 18 - 0
Tests/RunCMake/GenEx-LIST/POP_FRONT.cmake.in

@@ -0,0 +1,18 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(POP_FRONT listvar)
+set (output "$<LIST:POP_FRONT,a;b;c;d>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set (output "$<LIST:POP_FRONT,>")
+if (NOT output STREQUAL "")
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:POP_FRONT..." ${errors})

+ 34 - 0
Tests/RunCMake/GenEx-LIST/PREPEND.cmake.in

@@ -0,0 +1,34 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(PREPEND listvar e)
+set (output "$<LIST:PREPEND,a;b;c;d,e>")
+if (NOT output STREQUAL listvar)
+  list (PREPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(PREPEND listvar e f)
+set (output "$<LIST:PREPEND,a;b;c;d,e,f>")
+if (NOT output STREQUAL listvar)
+  list (PREPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(PREPEND listvar e f)
+set (output "$<LIST:PREPEND,a;b;c;d,e;f>")
+if (NOT output STREQUAL listvar)
+  list (PREPEND errors "returns bad value: ${output}")
+endif()
+
+unset(listvar)
+list(PREPEND listvar e f)
+set (output "$<LIST:PREPEND,,e,f>")
+if (NOT output STREQUAL listvar)
+  list (PREPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:PREPEND..." ${errors})

+ 1 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at REMOVE_AT-wrong-index1.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:REMOVE_AT,,0>
+
+  index: 0 out of range \(0, 0\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:REMOVE_AT,,0>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at REMOVE_AT-wrong-index2.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:REMOVE_AT,a;b;c,3>
+
+  index: 3 out of range \(-3, 2\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:REMOVE_AT,a;b;c,3>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at REMOVE_AT-wrong-index3.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:REMOVE_AT,a;b;c,-4>
+
+  index: -4 out of range \(-3, 2\)
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:REMOVE_AT,a;b;c,-4>")

+ 25 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_AT.cmake.in

@@ -0,0 +1,25 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(REMOVE_AT listvar 1 3)
+set (output "$<LIST:REMOVE_AT,a;b;c;d,1,3>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(REMOVE_AT listvar 1 -2)
+set (output "$<LIST:REMOVE_AT,a;b;c;d,1;-2>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set (output "$<LIST:REMOVE_AT,a;b;c;d,1,0,3;2>")
+if (NOT output STREQUAL "")
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:REMOVE_AT..." ${errors})

+ 20 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_DUPLICATES.cmake.in

@@ -0,0 +1,20 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(REMOVE_DUPLICATES listvar)
+set (output "$<LIST:REMOVE_DUPLICATES,a;b;c;d>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar "b;c;b;a;a;c;b;a;c;b")
+list(REMOVE_DUPLICATES listvar)
+set (output "$<LIST:REMOVE_DUPLICATES,b;c;b;a;a;c;b;a;c;b>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:REMOVE_DUPLICATES..." ${errors})

+ 32 - 0
Tests/RunCMake/GenEx-LIST/REMOVE_ITEM.cmake.in

@@ -0,0 +1,32 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+list(REMOVE_ITEM listvar b d)
+set (output "$<LIST:REMOVE_ITEM,a;b;c;d,b,d>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(REMOVE_ITEM listvar b e)
+set (output "$<LIST:REMOVE_ITEM,a;b;c;d,b,e>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar a b c d)
+list(REMOVE_ITEM listvar b a d c)
+set (output "$<LIST:REMOVE_ITEM,a;b;c;d,b;a;d;c>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set (output "$<LIST:REMOVE_ITEM,,b;a;d;c>")
+if (NOT output STREQUAL "")
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:REMOVE_ITEM..." ${errors})

+ 19 - 0
Tests/RunCMake/GenEx-LIST/REVERSE.cmake.in

@@ -0,0 +1,19 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(REVERSE listvar)
+set (output "$<LIST:REVERSE,a;b;c;d>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set (output "$<LIST:REVERSE,>")
+if (NOT output STREQUAL "")
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:REVERSE..." ${errors})

+ 130 - 0
Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake

@@ -0,0 +1,130 @@
+
+include(RunCMake)
+
+run_cmake(no-arguments)
+run_cmake(bad-option)
+
+function(check_list_syntax name test)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-${test}-build)
+  set(RunCMake_TEST_VARIANT_DESCRIPTION " - ${name}")
+  run_cmake_with_options(${test} ${ARGN})
+endfunction()
+
+## Unexpected arguments
+### sub-commands with one argument
+foreach (subcommand IN ITEMS LENGTH POP_BACK POP_FRONT REMOVE_DUPLICATES REVERSE)
+  check_list_syntax (${subcommand} unexpected-arg "-DLIST_ARGUMENTS=${subcommand},ARG1,ARG2")
+endforeach()
+
+### sub-commands with two arguments
+foreach (subcommand IN ITEMS FIND JOIN)
+  check_list_syntax (${subcommand} unexpected-arg "-DLIST_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3")
+endforeach()
+
+### sub-commands with three arguments
+foreach (subcommand IN ITEMS SUBLIST FILTER)
+  check_list_syntax (${subcommand} unexpected-arg "-DLIST_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3,ARG4")
+endforeach()
+
+# TRANSFORM sub-commands
+  set(RunCMake-stderr-file "TRANSFORM-unexpected-arg-stderr.txt")
+foreach (action IN ITEMS TOLOWER TOUPPER STRIP)
+  check_list_syntax (TRANSFORM-${action} unexpected-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,${action},ARG2")
+endforeach()
+foreach (action IN ITEMS APPEND PREPEND)
+  check_list_syntax (TRANSFORM-${action} unexpected-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,${action},ARG2,ARG3")
+endforeach()
+foreach (action IN ITEMS REPLACE)
+  check_list_syntax (TRANSFORM-${action} unexpected-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,${action},ARG2,ARG3,ARG4")
+endforeach()
+check_list_syntax (TRANSFORM-SELECTOR-REGEX unexpected-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,STRIP,REGEX,ARG2,ARG3")
+check_list_syntax (TRANSFORM-SELECTOR-FOR unexpected-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,STRIP,FOR,1,2,3,4")
+unset(RunCMake-stderr-file)
+
+## Missing arguments
+### sub-command with, at least, two arguments
+foreach (subcommand IN ITEMS GET APPEND PREPEND REMOVE_ITEM REMOVE_AT TRANSFORM)
+  check_list_syntax (${subcommand} missing-arg "-DLIST_ARGUMENTS=${subcommand},ARG1")
+endforeach()
+
+### sub-command with, at least, three arguments
+foreach (subcommand IN ITEMS INSERT)
+  check_list_syntax (${subcommand} missing-arg "-DLIST_ARGUMENTS=${subcommand},ARG1,ARG2")
+endforeach()
+
+# TRANSFORM sub-commands
+set(RunCMake-stderr-file "TRANSFORM-missing-arg-stderr.txt")
+foreach (action IN ITEMS APPEND PREPEND)
+  check_list_syntax (TRANSFORM-${action} missing-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,${action}")
+endforeach()
+check_list_syntax (TRANSFORM-REPLACE-1 missing-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,REPLACE,ARG2")
+check_list_syntax (TRANSFORM-REPLACE-2 missing-arg "-DLIST_ARGUMENTS=TRANSFORM,ARG1,REPLACE")
+unset(RunCMake-stderr-file)
+
+
+run_cmake(GET-wrong-index1)
+run_cmake(GET-wrong-index2)
+run_cmake(GET-wrong-index3)
+run_cmake(SUBLIST-wrong-argument1)
+run_cmake(SUBLIST-wrong-argument2)
+run_cmake(INSERT-wrong-index1)
+run_cmake(INSERT-wrong-index2)
+run_cmake(REMOVE_AT-wrong-index1)
+run_cmake(REMOVE_AT-wrong-index2)
+run_cmake(REMOVE_AT-wrong-index3)
+run_cmake(FILTER-wrong-operator)
+run_cmake(FILTER-wrong-regex)
+run_cmake(TRANSFORM-wrong-action)
+run_cmake(TRANSFORM-REPLACE-wrong-regex)
+run_cmake(TRANSFORM-REPLACE-invalid-replace1)
+run_cmake(TRANSFORM-REPLACE-invalid-replace2)
+run_cmake(TRANSFORM-selector-REGEX-no-arguments)
+run_cmake(TRANSFORM-selector-REGEX-wrong-regex)
+run_cmake(TRANSFORM-selector-AT-no-arguments)
+run_cmake(TRANSFORM-selector-AT-wrong-argument)
+run_cmake(TRANSFORM-selector-AT-wrong-index)
+run_cmake(TRANSFORM-selector-FOR-no-arguments)
+run_cmake(TRANSFORM-selector-FOR-missing-arguments)
+run_cmake(TRANSFORM-selector-FOR-wrong-argument)
+run_cmake(TRANSFORM-selector-FOR-wrong-index)
+run_cmake(TRANSFORM-selector-FOR-zero-step)
+run_cmake(TRANSFORM-selector-FOR-negative-step)
+run_cmake(TRANSFORM-selector-FOR-backwards-range)
+run_cmake(SORT-wrong-option)
+run_cmake(SORT-wrong-COMPARE-option)
+run_cmake(SORT-wrong-CASE-option)
+run_cmake(SORT-wrong-ORDER-option)
+run_cmake(SORT-duplicate-COMPARE-option)
+run_cmake(SORT-duplicate-CASE-option)
+run_cmake(SORT-duplicate-ORDER-option)
+
+
+function(check_list_execution name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_VARIANT_DESCRIPTION " - ${name}")
+  run_cmake_with_options(generate -DLIST_TEST=${name})
+  run_cmake_command(check "${CMAKE_COMMAND}" "-DRunCMake_SOURCE_DIR=${RunCMake_SOURCE_DIR}" -P "${RunCMake_TEST_BINARY_DIR}/${name}.cmake")
+endfunction()
+
+check_list_execution (LENGTH)
+check_list_execution (GET)
+check_list_execution (JOIN)
+check_list_execution (SUBLIST)
+check_list_execution (FIND)
+check_list_execution (APPEND)
+check_list_execution (PREPEND)
+check_list_execution (INSERT)
+check_list_execution (POP_BACK)
+check_list_execution (POP_FRONT)
+check_list_execution (REMOVE_ITEM)
+check_list_execution (REMOVE_AT)
+check_list_execution (REMOVE_DUPLICATES)
+check_list_execution (TRANSFORM-TOUPPER)
+check_list_execution (TRANSFORM-TOLOWER)
+check_list_execution (TRANSFORM-STRIP)
+check_list_execution (TRANSFORM-APPEND)
+check_list_execution (TRANSFORM-PREPEND)
+check_list_execution (TRANSFORM-REPLACE)
+check_list_execution (REVERSE)
+check_list_execution (SORT)

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SORT-duplicate-CASE-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,COMPARE:STRING,CASE:SENSITIVE,CASE:SENSITIVE>
+
+  sub-command SORT, CASE option has been specified multiple times.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,COMPARE:STRING,CASE:SENSITIVE,CASE:SENSITIVE>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SORT-duplicate-COMPARE-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,COMPARE:STRING,COMPARE:NATURAL>
+
+  sub-command SORT, COMPARE option has been specified multiple times.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,COMPARE:STRING,COMPARE:NATURAL>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SORT-duplicate-ORDER-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,ORDER:ASCENDING,COMPARE:STRING,ORDER:DESCENDING,CASE:SENSITIVE>
+
+  sub-command SORT, ORDER option has been specified multiple times.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,ORDER:ASCENDING,COMPARE:STRING,ORDER:DESCENDING,CASE:SENSITIVE>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at SORT-wrong-CASE-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,COMPARE:STRING,CASE:WRONG_OPTION>
+
+  sub-command SORT, an invalid CASE option has been specified:
+  "WRONG_OPTION".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,COMPARE:STRING,CASE:WRONG_OPTION>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at SORT-wrong-COMPARE-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,COMPARE:WRONG_OPTION>
+
+  sub-command SORT, an invalid COMPARE option has been specified:
+  "WRONG_OPTION".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,COMPARE:WRONG_OPTION>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at SORT-wrong-ORDER-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,COMPARE:STRING,CASE:SENSITIVE,ORDER:WRONG_OPTION>
+
+  sub-command SORT, an invalid ORDER option has been specified:
+  "WRONG_OPTION".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,COMPARE:STRING,CASE:SENSITIVE,ORDER:WRONG_OPTION>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-option-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SORT-wrong-option.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SORT,a;b,WRONG_OPTION>
+
+  sub-command SORT, option "WRONG_OPTION" is invalid.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,WRONG_OPTION>")

+ 92 - 0
Tests/RunCMake/GenEx-LIST/SORT.cmake.in

@@ -0,0 +1,92 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(source_unsorted c/B.h a/c.h B/a.h)
+
+set(listvar ${source_unsorted})
+list(SORT listvar)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE INSENSITIVE ORDER ASCENDING COMPARE STRING)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:INSENSITIVE,ORDER:ASCENDING,COMPARE:STRING>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE INSENSITIVE ORDER DESCENDING COMPARE STRING)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:INSENSITIVE,ORDER:DESCENDING,COMPARE:STRING>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE SENSITIVE ORDER ASCENDING COMPARE STRING)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:SENSITIVE,ORDER:ASCENDING,COMPARE:STRING>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE SENSITIVE ORDER DESCENDING COMPARE STRING)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:SENSITIVE,ORDER:DESCENDING,COMPARE:STRING>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE INSENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:INSENSITIVE,ORDER:ASCENDING,COMPARE:FILE_BASENAME>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE INSENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:INSENSITIVE,ORDER:DESCENDING,COMPARE:FILE_BASENAME>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE SENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:SENSITIVE,ORDER:ASCENDING,COMPARE:FILE_BASENAME>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar ${source_unsorted})
+list(SORT listvar CASE SENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)
+set (output "$<LIST:SORT,c/B.h;a/c.h;B/a.h,CASE:SENSITIVE,ORDER:DESCENDING,COMPARE:FILE_BASENAME>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar 10.0 1.1 2.1 8.0 2.0 3.1)
+list(SORT listvar CASE SENSITIVE ORDER DESCENDING COMPARE STRING)
+set (output "$<LIST:SORT,10.0;1.1;2.1;8.0;2.0;3.1,CASE:SENSITIVE,ORDER:DESCENDING,COMPARE:STRING>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar 10.0 1.1 2.1 8.0 2.0 3.1)
+list(SORT listvar CASE SENSITIVE ORDER DESCENDING COMPARE NATURAL)
+set (output "$<LIST:SORT,10.0;1.1;2.1;8.0;2.0;3.1,CASE:SENSITIVE,ORDER:DESCENDING,COMPARE:NATURAL>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+set(listvar 10.0 1.1 2.1 8.0 2.0 3.1)
+list(SORT listvar CASE SENSITIVE ORDER ASCENDING COMPARE NATURAL)
+set (output "$<LIST:SORT,10.0;1.1;2.1;8.0;2.0;3.1,CASE:SENSITIVE,ORDER:ASCENDING,COMPARE:NATURAL>")
+if (NOT output STREQUAL listvar)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:SORT..." ${errors})

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SUBLIST-wrong-argument1.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SUBLIST,a;b;c,3,-1>
+
+  begin index: 3 is out of range 0 - 2
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SUBLIST,a;b;c,3,-1>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at SUBLIST-wrong-argument2.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:SUBLIST,a;b;c,1,-2>
+
+  length: -2 should be -1 or greater
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SUBLIST,a;b;c,1,-2>")

+ 32 - 0
Tests/RunCMake/GenEx-LIST/SUBLIST.cmake.in

@@ -0,0 +1,32 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar a b c d)
+
+list(SUBLIST listvar 1 2 reference)
+set (output "$<LIST:SUBLIST,a;b;c;d,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(SUBLIST listvar 1 -1 reference)
+set (output "$<LIST:SUBLIST,a;b;c;d,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(SUBLIST listvar 1 0 reference)
+set (output "$<LIST:SUBLIST,a;b;c;d,1,0>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(SUBLIST listvar 1 5 reference)
+set (output "$<LIST:SUBLIST,a;b;c;d,1,5>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:SUBLIST..." ${errors})

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-APPEND.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar alpha bravo charlie delta)
+
+list(TRANSFORM listvar APPEND "_A" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar APPEND "_A" REGEX "(r|t)a" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,APPEND,_A,REGEX,(r|t)a>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,APPEND..." ${errors})

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-PREPEND.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar alpha bravo charlie delta)
+
+list(TRANSFORM listvar PREPEND "P_" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar PREPEND "P_" REGEX "(r|t)a" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,PREPEND,P_,REGEX,(r|t)a>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,PREPEND..." ${errors})

+ 1 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-REPLACE-invalid-replace1.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:TRANSFORM,a;b,REPLACE,\^a,b\\>
+
+  sub-command TRANSFORM, action REPLACE: replace-expression ends in a
+  backslash.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,REPLACE,^a,b\\>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt

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

+ 9 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-REPLACE-invalid-replace2.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:TRANSFORM,a;b,REPLACE,\^a,\\b>
+
+  sub-command TRANSFORM, action REPLACE: Unknown escape "\\b" in
+  replace-expression.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,REPLACE,^a,\\b>")

+ 1 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-REPLACE-wrong-regex.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:TRANSFORM,a;b,REPLACE,\(a,b>
+
+  sub-command TRANSFORM, action REPLACE: Failed to compile regex "\(a".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex.cmake

@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,REPLACE,(a,b>")

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar alpha bravo charlie delta)
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar REPLACE "(.+a)$" "\\1_\\1" REGEX "(r|t)a" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,REPLACE,(.+a)$,\1_\1,REGEX,(r|t)a>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,REPLACE..." ${errors})

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-STRIP.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar " alpha" "bravo " " charlie " delta)
+
+list(TRANSFORM listvar STRIP OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar STRIP REGEX "(r|t)a" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM, alpha;bravo ; charlie ;delta,STRIP,REGEX,(r|t)a>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,STRIP..." ${errors})

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-TOLOWER.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar ALPHA BRAVO CHARLIE DELTA)
+
+list(TRANSFORM listvar TOLOWER OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOLOWER REGEX "(R|T)A" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,ALPHA;BRAVO;CHARLIE;DELTA,TOLOWER,REGEX,(R|T)A>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,TOLOWER..." ${errors})

+ 50 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-TOUPPER.cmake.in

@@ -0,0 +1,50 @@
+
+include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
+unset (errors)
+
+set(listvar alpha bravo charlie delta)
+
+list(TRANSFORM listvar TOUPPER OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER AT 1 3 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,AT,1,3>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER AT 1 -2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,AT,1,-2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER FOR 1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,FOR,1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER FOR 1 -1 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,FOR,1,-1>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER FOR 1 -1 2 OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,FOR,1,-1,2>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+list(TRANSFORM listvar TOUPPER REGEX "(r|t)a" OUTPUT_VARIABLE reference)
+set (output "$<LIST:TRANSFORM,alpha;bravo;charlie;delta,TOUPPER,REGEX,(r|t)a>")
+if (NOT output STREQUAL reference)
+  list (APPEND errors "returns bad value: ${output}")
+endif()
+
+
+check_errors("LIST:TRANSFORM,TOUPPER..." ${errors})

+ 8 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-missing-arg-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at missing-arg.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:TRANSFORM,ARG1,[A-Z]+(,ARG[0-9])?>
+
+  sub-command TRANSFORM, action [A-Z]+ expects (1|2) argument\(s\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt

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

+ 8 - 0
Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-AT-no-arguments.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<LIST:TRANSFORM,a;b,TOUPPER,AT>
+
+  sub-command TRANSFORM, selector AT expects at least one numeric value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

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