summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Chevrier <marc.chevrier@gmail.com>2023-04-04 14:42:58 (GMT)
committerMarc Chevrier <marc.chevrier@gmail.com>2023-04-16 10:00:20 (GMT)
commit31675964e7ba37f1412aaa95af4199e9b62e8c08 (patch)
tree7d53c8bdc2dbed748efa86750fe88358a92f325c
parente256e35daa79732a200883cef398fcd0f8227a3d (diff)
downloadCMake-31675964e7ba37f1412aaa95af4199e9b62e8c08.zip
CMake-31675964e7ba37f1412aaa95af4199e9b62e8c08.tar.gz
CMake-31675964e7ba37f1412aaa95af4199e9b62e8c08.tar.bz2
GenEx LIST: list operations
Fixes: #24550, #24547
-rw-r--r--Help/command/list.rst30
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst264
-rw-r--r--Help/release/dev/GenEx-LIST.rst4
-rw-r--r--Source/cmGeneratorExpressionNode.cxx715
-rw-r--r--Source/cmList.cxx18
-rw-r--r--Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt2
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/APPEND.cmake.in34
-rw-r--r--Tests/RunCMake/GenEx-LIST/CMakeLists.txt5
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/FIND.cmake.in20
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index1-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index2-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index3-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/GET.cmake.in20
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/INSERT.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/JOIN.cmake.in35
-rw-r--r--Tests/RunCMake/GenEx-LIST/LENGTH.cmake.in30
-rw-r--r--Tests/RunCMake/GenEx-LIST/POP_BACK.cmake.in18
-rw-r--r--Tests/RunCMake/GenEx-LIST/POP_FRONT.cmake.in18
-rw-r--r--Tests/RunCMake/GenEx-LIST/PREPEND.cmake.in34
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_AT.cmake.in25
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_DUPLICATES.cmake.in20
-rw-r--r--Tests/RunCMake/GenEx-LIST/REMOVE_ITEM.cmake.in32
-rw-r--r--Tests/RunCMake/GenEx-LIST/REVERSE.cmake.in19
-rw-r--r--Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake130
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-option-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SORT.cmake.in92
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/SUBLIST.cmake.in32
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-APPEND.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-PREPEND.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-STRIP.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-TOLOWER.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-TOUPPER.cmake.in50
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-missing-arg-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-unexpected-arg-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-stderr.txt7
-rw-r--r--Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/bad-option-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/bad-option-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/bad-option.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/check_errors.cmake13
-rw-r--r--Tests/RunCMake/GenEx-LIST/generate.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/missing-arg-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/missing-arg-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/missing-arg.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/no-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/no-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/no-arguments.cmake2
-rw-r--r--Tests/RunCMake/GenEx-LIST/unexpected-arg-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LIST/unexpected-arg-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LIST/unexpected-arg.cmake2
152 files changed, 2382 insertions, 37 deletions
diff --git a/Help/command/list.rst b/Help/command/list.rst
index 191003a..0c7a562 100644
--- a/Help/command/list.rst
+++ b/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.
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 9da3799..9d29dc3 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/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
----------------
diff --git a/Help/release/dev/GenEx-LIST.rst b/Help/release/dev/GenEx-LIST.rst
new file mode 100644
index 0000000..f65a092
--- /dev/null
+++ b/Help/release/dev/GenEx-LIST.rst
@@ -0,0 +1,4 @@
+GenEx-LIST
+----------
+
+* The :genex:`LIST` generator expression was added to manage lists.
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index a47366b..727931c 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/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 },
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
index bf5a654..2064afb 100644
--- a/Source/cmList.cxx
+++ b/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;
diff --git a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt
index 5a03559..ac01c80 100644
--- a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-OLD-stderr.txt
+++ b/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\)
diff --git a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
index cb4ea46..c644dd3 100644
--- a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
+++ b/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\)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index f05f784..90feeb6 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/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)
diff --git a/Tests/RunCMake/GenEx-LIST/APPEND.cmake.in b/Tests/RunCMake/GenEx-LIST/APPEND.cmake.in
new file mode 100644
index 0000000..19e1d12
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/CMakeLists.txt b/Tests/RunCMake/GenEx-LIST/CMakeLists.txt
new file mode 100644
index 0000000..5161b99
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-stderr.txt b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator-stderr.txt
new file mode 100644
index 0000000..3064c00
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator.cmake b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-operator.cmake
new file mode 100644
index 0000000..e01b4fe
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-stderr.txt b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex-stderr.txt
new file mode 100644
index 0000000..b2f9b8a
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex.cmake b/Tests/RunCMake/GenEx-LIST/FILTER-wrong-regex.cmake
new file mode 100644
index 0000000..1311f31
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/FIND.cmake.in b/Tests/RunCMake/GenEx-LIST/FIND.cmake.in
new file mode 100644
index 0000000..e2242c3
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index1-stderr.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index1-stderr.txt
new file mode 100644
index 0000000..dcd1f2f
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake b/Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake
new file mode 100644
index 0000000..4c395b3
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index1.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,,0>")
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index2-stderr.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index2-stderr.txt
new file mode 100644
index 0000000..20f1af4
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake b/Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake
new file mode 100644
index 0000000..28c97e2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index2.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,a;b,2>")
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index3-stderr.txt b/Tests/RunCMake/GenEx-LIST/GET-wrong-index3-stderr.txt
new file mode 100644
index 0000000..a5dccbe
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake b/Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake
new file mode 100644
index 0000000..fd7be17
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/GET-wrong-index3.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:GET,a;b,1,-3>")
diff --git a/Tests/RunCMake/GenEx-LIST/GET.cmake.in b/Tests/RunCMake/GenEx-LIST/GET.cmake.in
new file mode 100644
index 0000000..102eb42
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-stderr.txt b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1-stderr.txt
new file mode 100644
index 0000000..ce36fb1
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1.cmake b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index1.cmake
new file mode 100644
index 0000000..ead5832
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-stderr.txt b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2-stderr.txt
new file mode 100644
index 0000000..f0ea1d7
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2.cmake b/Tests/RunCMake/GenEx-LIST/INSERT-wrong-index2.cmake
new file mode 100644
index 0000000..c057b87
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/INSERT.cmake.in b/Tests/RunCMake/GenEx-LIST/INSERT.cmake.in
new file mode 100644
index 0000000..d3bb9b9
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/JOIN.cmake.in b/Tests/RunCMake/GenEx-LIST/JOIN.cmake.in
new file mode 100644
index 0000000..0a56450
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/LENGTH.cmake.in b/Tests/RunCMake/GenEx-LIST/LENGTH.cmake.in
new file mode 100644
index 0000000..0840e11
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/POP_BACK.cmake.in b/Tests/RunCMake/GenEx-LIST/POP_BACK.cmake.in
new file mode 100644
index 0000000..ba95c83
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/POP_FRONT.cmake.in b/Tests/RunCMake/GenEx-LIST/POP_FRONT.cmake.in
new file mode 100644
index 0000000..0d676af
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/PREPEND.cmake.in b/Tests/RunCMake/GenEx-LIST/PREPEND.cmake.in
new file mode 100644
index 0000000..49b8f98
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-stderr.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1-stderr.txt
new file mode 100644
index 0000000..b874f1d
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake
new file mode 100644
index 0000000..d87559e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index1.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:REMOVE_AT,,0>")
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-stderr.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2-stderr.txt
new file mode 100644
index 0000000..509eed4
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2.cmake b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index2.cmake
new file mode 100644
index 0000000..7ecfad2
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-stderr.txt b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3-stderr.txt
new file mode 100644
index 0000000..c8fc465
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3.cmake b/Tests/RunCMake/GenEx-LIST/REMOVE_AT-wrong-index3.cmake
new file mode 100644
index 0000000..f8f9a3e
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_AT.cmake.in b/Tests/RunCMake/GenEx-LIST/REMOVE_AT.cmake.in
new file mode 100644
index 0000000..42a9476
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_DUPLICATES.cmake.in b/Tests/RunCMake/GenEx-LIST/REMOVE_DUPLICATES.cmake.in
new file mode 100644
index 0000000..8785ae5
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/REMOVE_ITEM.cmake.in b/Tests/RunCMake/GenEx-LIST/REMOVE_ITEM.cmake.in
new file mode 100644
index 0000000..5434a5d
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/REVERSE.cmake.in b/Tests/RunCMake/GenEx-LIST/REVERSE.cmake.in
new file mode 100644
index 0000000..295e1a3
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake
new file mode 100644
index 0000000..1946e84
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option-stderr.txt
new file mode 100644
index 0000000..4f3121a
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-CASE-option.cmake
new file mode 100644
index 0000000..e09dc6c
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option-stderr.txt
new file mode 100644
index 0000000..fbb96bb
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-COMPARE-option.cmake
new file mode 100644
index 0000000..cf8da0d
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option-stderr.txt
new file mode 100644
index 0000000..b45034a
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-duplicate-ORDER-option.cmake
new file mode 100644
index 0000000..3826072
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option-stderr.txt
new file mode 100644
index 0000000..d36e63b
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-wrong-CASE-option.cmake
new file mode 100644
index 0000000..58df9ea
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option-stderr.txt
new file mode 100644
index 0000000..70a99c6
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-wrong-COMPARE-option.cmake
new file mode 100644
index 0000000..73727bb
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option-stderr.txt
new file mode 100644
index 0000000..2e23d8c
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-wrong-ORDER-option.cmake
new file mode 100644
index 0000000..135c935
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-wrong-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/SORT-wrong-option-stderr.txt
new file mode 100644
index 0000000..3c2d492
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake b/Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake
new file mode 100644
index 0000000..fca268b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SORT-wrong-option.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:SORT,a;b,WRONG_OPTION>")
diff --git a/Tests/RunCMake/GenEx-LIST/SORT.cmake.in b/Tests/RunCMake/GenEx-LIST/SORT.cmake.in
new file mode 100644
index 0000000..aca6691
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-stderr.txt b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1-stderr.txt
new file mode 100644
index 0000000..078f345
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1.cmake b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument1.cmake
new file mode 100644
index 0000000..293c2ae
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-stderr.txt b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2-stderr.txt
new file mode 100644
index 0000000..7271b30
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2.cmake b/Tests/RunCMake/GenEx-LIST/SUBLIST-wrong-argument2.cmake
new file mode 100644
index 0000000..9d1db53
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/SUBLIST.cmake.in b/Tests/RunCMake/GenEx-LIST/SUBLIST.cmake.in
new file mode 100644
index 0000000..9fcb6d5
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-APPEND.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-APPEND.cmake.in
new file mode 100644
index 0000000..a6529ec
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-PREPEND.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-PREPEND.cmake.in
new file mode 100644
index 0000000..5ec6acd
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1-stderr.txt
new file mode 100644
index 0000000..6674b30
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace1.cmake
new file mode 100644
index 0000000..d45e1fd
--- /dev/null
+++ b/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\\>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2-stderr.txt
new file mode 100644
index 0000000..2351b72
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-invalid-replace2.cmake
new file mode 100644
index 0000000..b800f24
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex-stderr.txt
new file mode 100644
index 0000000..617c3d6
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE-wrong-regex.cmake
new file mode 100644
index 0000000..941e3e7
--- /dev/null
+++ b/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>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-REPLACE.cmake.in
new file mode 100644
index 0000000..5fba231
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-STRIP.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-STRIP.cmake.in
new file mode 100644
index 0000000..860faec
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-TOLOWER.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-TOLOWER.cmake.in
new file mode 100644
index 0000000..43e9955
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-TOUPPER.cmake.in b/Tests/RunCMake/GenEx-LIST/TRANSFORM-TOUPPER.cmake.in
new file mode 100644
index 0000000..be2bc30
--- /dev/null
+++ b/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})
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-missing-arg-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-missing-arg-stderr.txt
new file mode 100644
index 0000000..46e36c3
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments-stderr.txt
new file mode 100644
index 0000000..69b4915
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments.cmake
new file mode 100644
index 0000000..2447511
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-no-arguments.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,AT>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-stderr.txt
new file mode 100644
index 0000000..40007a1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-AT-wrong-argument.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b,TOUPPER,AT,0,1x,2>
+
+ sub-command TRANSFORM, selector AT: '1x': unexpected argument.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument.cmake
new file mode 100644
index 0000000..49f4e90
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-argument.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,AT,0,1x,2>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-stderr.txt
new file mode 100644
index 0000000..66085ab
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-AT-wrong-index.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,AT,0,3,2>
+
+ sub-command TRANSFORM, selector AT, index: 3 out of range \(-3, 2\).
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index.cmake
new file mode 100644
index 0000000..fd6c3ed
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-AT-wrong-index.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,AT,0,3,2>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-stderr.txt
new file mode 100644
index 0000000..77cffdc
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-selector-FOR-backwards-range.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,2,0>
+
+ sub-command TRANSFORM, selector FOR expects <start> to be no greater than
+ <stop> \(2 > 0\)
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range.cmake
new file mode 100644
index 0000000..17f57f5
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-backwards-range.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,2,0>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-stderr.txt
new file mode 100644
index 0000000..a9de7f8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-FOR-missing-arguments.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b,TOUPPER,FOR,0>
+
+ sub-command TRANSFORM, selector FOR expects, at least, two arguments.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments.cmake
new file mode 100644
index 0000000..5ae481a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-missing-arguments.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,FOR,0>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-stderr.txt
new file mode 100644
index 0000000..9c81de4
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-selector-FOR-negative-step.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,2,-1>
+
+ sub-command TRANSFORM, selector FOR expects positive numeric value for
+ <step>.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step.cmake
new file mode 100644
index 0000000..b8e6433
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-negative-step.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,2,-1>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-stderr.txt
new file mode 100644
index 0000000..d31f171
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-FOR-no-arguments.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b,TOUPPER,FOR>
+
+ sub-command TRANSFORM, selector FOR expects, at least, two arguments.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments.cmake
new file mode 100644
index 0000000..6545ff9
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-no-arguments.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,FOR>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-stderr.txt
new file mode 100644
index 0000000..727be10
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-FOR-wrong-argument.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,1x,2>
+
+ sub-command TRANSFORM, selector FOR expects, at least, two numeric values.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument.cmake
new file mode 100644
index 0000000..a50b62c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-argument.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,1x,2>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-stderr.txt
new file mode 100644
index 0000000..664e35a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-FOR-wrong-index.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,4,2>
+
+ sub-command TRANSFORM, selector FOR, index: 4 out of range \(-3, 2\).
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index.cmake
new file mode 100644
index 0000000..f35bfbf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-wrong-index.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,4,2>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-stderr.txt
new file mode 100644
index 0000000..e542fbf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-selector-FOR-zero-step.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,2,0>
+
+ sub-command TRANSFORM, selector FOR expects positive numeric value for
+ <step>.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step.cmake
new file mode 100644
index 0000000..2bfaab0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-FOR-zero-step.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,TOUPPER,FOR,0,2,0>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-stderr.txt
new file mode 100644
index 0000000..079172b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TRANSFORM-selector-REGEX-no-arguments.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b,TOUPPER,REGEX>
+
+ sub-command TRANSFORM, selector REGEX expects 'regular expression'
+ argument.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments.cmake
new file mode 100644
index 0000000..1f802b7
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-no-arguments.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,REGEX>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-stderr.txt
new file mode 100644
index 0000000..3fe2b01
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TRANSFORM-selector-REGEX-wrong-regex.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b,TOUPPER,REGEX,\(a>
+
+ sub-command TRANSFORM, selector REGEX failed to compile regex "\(a".
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex.cmake
new file mode 100644
index 0000000..1648092
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-selector-REGEX-wrong-regex.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b,TOUPPER,REGEX,(a>")
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-unexpected-arg-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-unexpected-arg-stderr.txt
new file mode 100644
index 0000000..04e9df1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-unexpected-arg-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at unexpected-arg.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,ARG1,[A-Z_]+(,[A-Z0-9]+)+>
+
+ sub-command TRANSFORM, '[A-Z0-9]+': unexpected argument\(s\).
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-result.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-stderr.txt b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-stderr.txt
new file mode 100644
index 0000000..1c30c4f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at TRANSFORM-wrong-action.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:TRANSFORM,a;b;c,WRONG_ACTION>
+ sub-command TRANSFORM, WRONG_ACTION invalid action.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action.cmake b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action.cmake
new file mode 100644
index 0000000..fba6c90
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/TRANSFORM-wrong-action.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:TRANSFORM,a;b;c,WRONG_ACTION>")
diff --git a/Tests/RunCMake/GenEx-LIST/bad-option-result.txt b/Tests/RunCMake/GenEx-LIST/bad-option-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/bad-option-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/bad-option-stderr.txt b/Tests/RunCMake/GenEx-LIST/bad-option-stderr.txt
new file mode 100644
index 0000000..37048c5
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/bad-option-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at bad-option.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:BAD_OPTION,ARG>
+
+ BAD_OPTION: invalid option.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/bad-option.cmake b/Tests/RunCMake/GenEx-LIST/bad-option.cmake
new file mode 100644
index 0000000..d1dc85f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/bad-option.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:BAD_OPTION,ARG>")
diff --git a/Tests/RunCMake/GenEx-LIST/check_errors.cmake b/Tests/RunCMake/GenEx-LIST/check_errors.cmake
new file mode 100644
index 0000000..7e60fc7
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/check_errors.cmake
@@ -0,0 +1,13 @@
+
+function (CHECK_ERRORS command)
+ set (errors ${ARGN})
+ set (command "$<${command}>")
+ if (errors)
+ string (LENGTH "${command}" length)
+ math (EXPR count "${length} + 2")
+ string (REPEAT " " ${count} shift)
+ list (TRANSFORM errors PREPEND "${shift}")
+ list (JOIN errors "\n" msg)
+ message (FATAL_ERROR "${command}: ${msg}")
+ endif()
+endfunction()
diff --git a/Tests/RunCMake/GenEx-LIST/generate.cmake b/Tests/RunCMake/GenEx-LIST/generate.cmake
new file mode 100644
index 0000000..92cdca2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/generate.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT "${LIST_TEST}.cmake" INPUT "${LIST_TEST}.cmake.in")
diff --git a/Tests/RunCMake/GenEx-LIST/missing-arg-result.txt b/Tests/RunCMake/GenEx-LIST/missing-arg-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/missing-arg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/missing-arg-stderr.txt b/Tests/RunCMake/GenEx-LIST/missing-arg-stderr.txt
new file mode 100644
index 0000000..c7cbe4b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/missing-arg-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at missing-arg.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:[A-Z_]+,.+>
+
+ \$<LIST:[A-Z_]+> expression requires at least (two|three) parameters.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/missing-arg.cmake b/Tests/RunCMake/GenEx-LIST/missing-arg.cmake
new file mode 100644
index 0000000..48b26e1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/missing-arg.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:${LIST_ARGUMENTS}>")
diff --git a/Tests/RunCMake/GenEx-LIST/no-arguments-result.txt b/Tests/RunCMake/GenEx-LIST/no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LIST/no-arguments-stderr.txt
new file mode 100644
index 0000000..ee782ec
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/no-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at no-arguments.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:>
+
+ \$<LIST> expression requires at least two parameters.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/no-arguments.cmake b/Tests/RunCMake/GenEx-LIST/no-arguments.cmake
new file mode 100644
index 0000000..f704e2b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/no-arguments.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:>")
diff --git a/Tests/RunCMake/GenEx-LIST/unexpected-arg-result.txt b/Tests/RunCMake/GenEx-LIST/unexpected-arg-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/unexpected-arg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LIST/unexpected-arg-stderr.txt b/Tests/RunCMake/GenEx-LIST/unexpected-arg-stderr.txt
new file mode 100644
index 0000000..a7b1f25
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/unexpected-arg-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at unexpected-arg.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<LIST:[A-Z_]+,.+>
+
+ \$<LIST:[A-Z_]+(,[A-Z_]+)?> expression requires exactly (one|two|three) parameters?.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LIST/unexpected-arg.cmake b/Tests/RunCMake/GenEx-LIST/unexpected-arg.cmake
new file mode 100644
index 0000000..48b26e1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/unexpected-arg.cmake
@@ -0,0 +1,2 @@
+
+file(GENERATE OUTPUT result.txt CONTENT "$<LIST:${LIST_ARGUMENTS}>")