GenEx LIST: list operations

Fixes: #24550, #24547
This commit is contained in:
Marc Chevrier
2023-04-04 16:42:58 +02:00
parent e256e35daa
commit 31675964e7
152 changed files with 2382 additions and 37 deletions

View File

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

View File

@@ -104,6 +104,17 @@ improved further like so:
VERBATIM 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 A common mistake is to try to split a generator expression across multiple
lines with indenting: lines with indenting:
@@ -318,6 +329,15 @@ String Transformations
List Expressions 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> .. genex:: $<IN_LIST:string,list>
.. versionadded:: 3.12 .. versionadded:: 3.12
@@ -325,9 +345,186 @@ List Expressions
``1`` if ``string`` is an item in the semicolon-separated ``list``, else ``0``. ``1`` if ``string`` is an item in the semicolon-separated ``list``, else ``0``.
It uses case-sensitive comparisons. It uses case-sensitive comparisons.
.. genex:: $<JOIN:list,string> .. _GenEx List Queries:
Joins the list with the content of ``string`` inserted between each item. 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
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> .. genex:: $<REMOVE_DUPLICATES:list>
@@ -344,6 +541,69 @@ List Expressions
Includes or removes items from ``list`` that match the regular expression Includes or removes items from ``list`` that match the regular expression
``regex``. ``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 Path Expressions
---------------- ----------------

View File

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

View File

@@ -635,22 +635,48 @@ public:
using Arguments = Range<std::vector<std::string>>; using Arguments = Range<std::vector<std::string>>;
bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx, bool CheckGenExParameters(cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt, const GeneratorExpressionContent* cnt,
cm::string_view option, std::size_t count, cm::string_view genex, cm::string_view option,
int required = 1, bool exactly = true) std::size_t count, int required = 1,
bool exactly = true)
{ {
if (static_cast<int>(count) < required || if (static_cast<int>(count) < required ||
(exactly && 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(), reportError(ctx, cnt->GetOriginalExpression(),
cmStrCat("$<PATH:", option, "> expression requires ", cmStrCat("$<", genex, ':', option, "> expression requires ",
(exactly ? "exactly" : "at least"), ' ', (exactly ? "exactly" : "at least"), ' ', nbParameters,
(required == 1 ? "one parameter" : "two parameters"),
'.')); '.'));
return false; return false;
} }
return true; 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, bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt, const GeneratorExpressionContent* cnt,
cm::string_view option, const Arguments& args, cm::string_view option, const Arguments& args,
@@ -658,6 +684,7 @@ bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
{ {
return CheckPathParametersEx(ctx, cnt, option, args.size(), required); return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
}; };
std::string ToString(bool isTrue) std::string ToString(bool isTrue)
{ {
return isTrue ? "1" : "0"; return isTrue ? "1" : "0";
@@ -1108,6 +1135,670 @@ static const struct PathEqualNode : public cmGeneratorExpressionNode
} }
} pathEqualNode; } 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 static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
{ {
MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default) MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
@@ -1559,7 +2250,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
// reportError(context, content->GetOriginalExpression(), ""); // reportError(context, content->GetOriginalExpression(), "");
reportError( reportError(
context, content->GetOriginalExpression(), 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 " "to specify include directories, compile definitions, and compile "
"options. It may not be used with the add_custom_command, " "options. It may not be used with the add_custom_command, "
"add_custom_target, or file(GENERATE) commands."); "add_custom_target, or file(GENERATE) commands.");
@@ -1704,7 +2396,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
reportError( reportError(
context, content->GetOriginalExpression(), context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets " "$<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."); "depends.");
return std::string(); return std::string();
} }
@@ -2086,7 +2779,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
reportError( reportError(
context, content->GetOriginalExpression(), context, content->GetOriginalExpression(),
"$<TARGET_PROPERTY:prop> may only be used with binary targets. " "$<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 " "Specify the target to read a property from using the "
"$<TARGET_PROPERTY:tgt,prop> signature instead."); "$<TARGET_PROPERTY:tgt,prop> signature instead.");
@@ -3780,6 +4474,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "IN_LIST", &inListNode }, { "IN_LIST", &inListNode },
{ "FILTER", &filterNode }, { "FILTER", &filterNode },
{ "REMOVE_DUPLICATES", &removeDuplicatesNode }, { "REMOVE_DUPLICATES", &removeDuplicatesNode },
{ "LIST", &listNode },
{ "LOWER_CASE", &lowerCaseNode }, { "LOWER_CASE", &lowerCaseNode },
{ "UPPER_CASE", &upperCaseNode }, { "UPPER_CASE", &upperCaseNode },
{ "PATH", &pathNode }, { "PATH", &pathNode },

View File

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

View File

@@ -1,5 +1,5 @@
CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\): 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\): Call Stack \(most recent call first\):
CMP0121-ERANGE-OLD.cmake:2 \(include\) CMP0121-ERANGE-OLD.cmake:2 \(include\)
CMakeLists.txt:3 \(include\) CMakeLists.txt:3 \(include\)

View File

@@ -9,7 +9,7 @@ Call Stack \(most recent call first\):
This warning is for project developers. Use -Wno-dev to suppress it. This warning is for project developers. Use -Wno-dev to suppress it.
.* .*
CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\): 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\): Call Stack \(most recent call first\):
CMP0121-ERANGE-WARN.cmake:2 \(include\) CMP0121-ERANGE-WARN.cmake:2 \(include\)
CMakeLists.txt:3 \(include\) CMakeLists.txt:3 \(include\)

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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