1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmListCommand.h"
15 #include <cm/optional>
16 #include <cmext/algorithm>
17 #include <cmext/string_view>
19 #include "cmExecutionStatus.h"
21 #include "cmMakefile.h"
22 #include "cmMessageType.h"
23 #include "cmPolicies.h"
25 #include "cmStringAlgorithms.h"
26 #include "cmSubcommandTable.h"
31 bool GetIndexArg(const std::string
& arg
, int* idx
, cmMakefile
& mf
)
34 if (!cmStrToLong(arg
, &value
)) {
35 switch (mf
.GetPolicyStatus(cmPolicies::CMP0121
)) {
36 case cmPolicies::WARN
: {
37 // Default is to warn and use old behavior OLD behavior is to allow
38 // compatibility, so issue a warning and use the previous behavior.
40 cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0121
),
41 " Invalid list index \"", arg
, "\".");
42 mf
.IssueMessage(MessageType::AUTHOR_WARNING
, warn
);
46 // OLD behavior is to allow compatibility, so just ignore the
51 case cmPolicies::REQUIRED_IF_USED
:
52 case cmPolicies::REQUIRED_ALWAYS
:
54 cmStrCat(cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0121
),
55 " Invalid list index \"", arg
, "\".");
56 mf
.IssueMessage(MessageType::FATAL_ERROR
, msg
);
61 // Truncation is happening here, but it had always been happening here.
62 *idx
= static_cast<int>(value
);
67 bool GetListString(std::string
& listString
, const std::string
& var
,
68 const cmMakefile
& makefile
)
71 cmValue cacheValue
= makefile
.GetDefinition(var
);
75 listString
= *cacheValue
;
79 cm::optional
<cmList
> GetList(const std::string
& var
,
80 const cmMakefile
& makefile
)
82 cm::optional
<cmList
> list
;
84 std::string listString
;
85 if (!GetListString(listString
, var
, makefile
)) {
88 // if the size of the list
89 if (listString
.empty()) {
93 // expand the variable into a list
94 list
.emplace(listString
, cmList::EmptyElements::Yes
);
95 // if no empty elements then just return
96 if (!cm::contains(*list
, std::string())) {
99 // if we have empty elements we need to check policy CMP0007
100 switch (makefile
.GetPolicyStatus(cmPolicies::CMP0007
)) {
101 case cmPolicies::WARN
: {
102 // Default is to warn and use old behavior
103 // OLD behavior is to allow compatibility, so recall
104 // ExpandListArgument without the true which will remove
106 list
->assign(listString
);
108 cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007
),
109 " List has value = [", listString
, "].");
110 makefile
.IssueMessage(MessageType::AUTHOR_WARNING
, warn
);
113 case cmPolicies::OLD
:
114 // OLD behavior is to allow compatibility, so recall
115 // ExpandListArgument without the true which will remove
117 list
->assign(listString
);
119 case cmPolicies::NEW
:
121 case cmPolicies::REQUIRED_IF_USED
:
122 case cmPolicies::REQUIRED_ALWAYS
:
123 makefile
.IssueMessage(
124 MessageType::FATAL_ERROR
,
125 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007
));
131 bool HandleLengthCommand(std::vector
<std::string
> const& args
,
132 cmExecutionStatus
& status
)
134 if (args
.size() != 3) {
135 status
.SetError("sub-command LENGTH requires two arguments.");
139 const std::string
& listName
= args
[1];
140 const std::string
& variableName
= args
.back();
142 auto list
= GetList(listName
, status
.GetMakefile());
143 status
.GetMakefile().AddDefinition(variableName
,
144 std::to_string(list
? list
->size() : 0));
149 bool HandleGetCommand(std::vector
<std::string
> const& args
,
150 cmExecutionStatus
& status
)
152 if (args
.size() < 4) {
153 status
.SetError("sub-command GET requires at least three arguments.");
157 const std::string
& listName
= args
[1];
158 const std::string
& variableName
= args
.back();
159 // expand the variable
160 auto list
= GetList(listName
, status
.GetMakefile());
162 status
.GetMakefile().AddDefinition(variableName
, "NOTFOUND");
165 // FIXME: Add policy to make non-existing lists an error like empty lists.
167 status
.SetError("GET given empty list");
171 std::vector
<int> indexes
;
172 for (std::size_t cc
= 2; cc
< args
.size() - 1; cc
++) {
174 if (!GetIndexArg(args
[cc
], &index
, status
.GetMakefile())) {
175 status
.SetError(cmStrCat("index: ", args
[cc
], " is not a valid index"));
178 indexes
.push_back(index
);
182 auto values
= list
->get_items(indexes
.begin(), indexes
.end());
183 status
.GetMakefile().AddDefinition(variableName
, values
.to_string());
185 } catch (std::out_of_range
& e
) {
186 status
.SetError(e
.what());
191 bool HandleAppendCommand(std::vector
<std::string
> const& args
,
192 cmExecutionStatus
& status
)
194 assert(args
.size() >= 2);
196 // Skip if nothing to append.
197 if (args
.size() < 3) {
201 cmMakefile
& makefile
= status
.GetMakefile();
202 std::string
const& listName
= args
[1];
203 // expand the variable
204 std::string listString
;
205 GetListString(listString
, listName
, makefile
);
207 makefile
.AddDefinition(
208 listName
, cmList::append(listString
, args
.begin() + 2, args
.end()));
212 bool HandlePrependCommand(std::vector
<std::string
> const& args
,
213 cmExecutionStatus
& status
)
215 assert(args
.size() >= 2);
217 // Skip if nothing to prepend.
218 if (args
.size() < 3) {
222 cmMakefile
& makefile
= status
.GetMakefile();
223 std::string
const& listName
= args
[1];
224 // expand the variable
225 std::string listString
;
226 GetListString(listString
, listName
, makefile
);
228 makefile
.AddDefinition(
229 listName
, cmList::prepend(listString
, args
.begin() + 2, args
.end()));
233 bool HandlePopBackCommand(std::vector
<std::string
> const& args
,
234 cmExecutionStatus
& status
)
236 assert(args
.size() >= 2);
238 cmMakefile
& makefile
= status
.GetMakefile();
239 auto ai
= args
.cbegin();
240 ++ai
; // Skip subcommand name
241 std::string
const& listName
= *ai
++;
242 auto list
= GetList(listName
, makefile
);
245 // Can't get the list definition... undefine any vars given after.
246 for (; ai
!= args
.cend(); ++ai
) {
247 makefile
.RemoveDefinition(*ai
);
252 if (!list
->empty()) {
253 if (ai
== args
.cend()) {
254 // No variables are given... Just remove one element.
257 // Ok, assign elements to be removed to the given variables
258 for (; !list
->empty() && ai
!= args
.cend(); ++ai
) {
259 assert(!ai
->empty());
260 makefile
.AddDefinition(*ai
, list
->back());
263 // Undefine the rest variables if the list gets empty earlier...
264 for (; ai
!= args
.cend(); ++ai
) {
265 makefile
.RemoveDefinition(*ai
);
269 makefile
.AddDefinition(listName
, list
->to_string());
272 args
.cend()) { // The list is empty, but some args were given
273 // Need to *undefine* 'em all, cuz there are no items to assign...
274 for (; ai
!= args
.cend(); ++ai
) {
275 makefile
.RemoveDefinition(*ai
);
282 bool HandlePopFrontCommand(std::vector
<std::string
> const& args
,
283 cmExecutionStatus
& status
)
285 assert(args
.size() >= 2);
287 cmMakefile
& makefile
= status
.GetMakefile();
288 auto ai
= args
.cbegin();
289 ++ai
; // Skip subcommand name
290 std::string
const& listName
= *ai
++;
291 auto list
= GetList(listName
, makefile
);
294 // Can't get the list definition... undefine any vars given after.
295 for (; ai
!= args
.cend(); ++ai
) {
296 makefile
.RemoveDefinition(*ai
);
301 if (!list
->empty()) {
302 if (ai
== args
.cend()) {
303 // No variables are given... Just remove one element.
306 // Ok, assign elements to be removed to the given variables
307 auto vi
= list
->begin();
308 for (; vi
!= list
->end() && ai
!= args
.cend(); ++ai
, ++vi
) {
309 assert(!ai
->empty());
310 makefile
.AddDefinition(*ai
, *vi
);
312 list
->erase(list
->begin(), vi
);
313 // Undefine the rest variables if the list gets empty earlier...
314 for (; ai
!= args
.cend(); ++ai
) {
315 makefile
.RemoveDefinition(*ai
);
319 makefile
.AddDefinition(listName
, list
->to_string());
322 args
.cend()) { // The list is empty, but some args were given
323 // Need to *undefine* 'em all, cuz there are no items to assign...
324 for (; ai
!= args
.cend(); ++ai
) {
325 makefile
.RemoveDefinition(*ai
);
332 bool HandleFindCommand(std::vector
<std::string
> const& args
,
333 cmExecutionStatus
& status
)
335 if (args
.size() != 4) {
336 status
.SetError("sub-command FIND requires three arguments.");
340 const std::string
& listName
= args
[1];
341 const std::string
& variableName
= args
.back();
342 // expand the variable
343 auto list
= GetList(listName
, status
.GetMakefile());
346 status
.GetMakefile().AddDefinition(variableName
, "-1");
350 auto index
= list
->find(args
[2]);
351 status
.GetMakefile().AddDefinition(
352 variableName
, index
== cmList::npos
? "-1" : std::to_string(index
));
356 bool HandleInsertCommand(std::vector
<std::string
> const& args
,
357 cmExecutionStatus
& status
)
359 if (args
.size() < 4) {
360 status
.SetError("sub-command INSERT requires at least three arguments.");
364 const std::string
& listName
= args
[1];
366 // expand the variable
368 if (!GetIndexArg(args
[2], &index
, status
.GetMakefile())) {
369 status
.SetError(cmStrCat("index: ", args
[2], " is not a valid index"));
372 auto list
= GetList(listName
, status
.GetMakefile());
378 list
->insert_items(index
, args
.begin() + 3, args
.end(),
379 cmList::ExpandElements::No
, cmList::EmptyElements::Yes
);
380 status
.GetMakefile().AddDefinition(listName
, list
->to_string());
382 } catch (std::out_of_range
& e
) {
383 status
.SetError(e
.what());
388 bool HandleJoinCommand(std::vector
<std::string
> const& args
,
389 cmExecutionStatus
& status
)
391 if (args
.size() != 4) {
392 status
.SetError(cmStrCat("sub-command JOIN requires three arguments (",
393 args
.size() - 1, " found)."));
397 const std::string
& listName
= args
[1];
398 const std::string
& glue
= args
[2];
399 const std::string
& variableName
= args
[3];
401 // expand the variable
402 auto list
= GetList(listName
, status
.GetMakefile());
405 status
.GetMakefile().AddDefinition(variableName
, "");
409 status
.GetMakefile().AddDefinition(variableName
, list
->join(glue
));
413 bool HandleRemoveItemCommand(std::vector
<std::string
> const& args
,
414 cmExecutionStatus
& status
)
416 assert(args
.size() >= 2);
418 if (args
.size() == 2) {
422 const std::string
& listName
= args
[1];
423 // expand the variable
424 auto list
= GetList(listName
, status
.GetMakefile());
430 status
.GetMakefile().AddDefinition(
431 listName
, list
->remove_items(args
.begin() + 2, args
.end()).to_string());
435 bool HandleReverseCommand(std::vector
<std::string
> const& args
,
436 cmExecutionStatus
& status
)
438 assert(args
.size() >= 2);
439 if (args
.size() > 2) {
440 status
.SetError("sub-command REVERSE only takes one argument.");
444 const std::string
& listName
= args
[1];
445 // expand the variable
446 auto list
= GetList(listName
, status
.GetMakefile());
452 status
.GetMakefile().AddDefinition(listName
, list
->reverse().to_string());
456 bool HandleRemoveDuplicatesCommand(std::vector
<std::string
> const& args
,
457 cmExecutionStatus
& status
)
459 assert(args
.size() >= 2);
460 if (args
.size() > 2) {
461 status
.SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
465 const std::string
& listName
= args
[1];
466 // expand the variable
467 auto list
= GetList(listName
, status
.GetMakefile());
473 status
.GetMakefile().AddDefinition(listName
,
474 list
->remove_duplicates().to_string());
478 bool HandleTransformCommand(std::vector
<std::string
> const& args
,
479 cmExecutionStatus
& status
)
481 if (args
.size() < 3) {
483 "sub-command TRANSFORM requires an action to be specified.");
487 // Descriptor of action
488 // Action: enum value identifying action
489 // Arity: number of arguments required for the action
490 struct ActionDescriptor
492 ActionDescriptor(std::string name
)
493 : Name(std::move(name
))
496 ActionDescriptor(std::string name
, cmList::TransformAction action
,
498 : Name(std::move(name
))
504 operator const std::string
&() const { return this->Name
; }
507 cmList::TransformAction Action
;
511 // Build a set of supported actions.
512 std::set
<ActionDescriptor
,
513 std::function
<bool(const std::string
&, const std::string
&)>>
514 descriptors
{ { { "APPEND", cmList::TransformAction::APPEND
, 1 },
515 { "PREPEND", cmList::TransformAction::PREPEND
, 1 },
516 { "TOUPPER", cmList::TransformAction::TOUPPER
, 0 },
517 { "TOLOWER", cmList::TransformAction::TOLOWER
, 0 },
518 { "STRIP", cmList::TransformAction::STRIP
, 0 },
519 { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP
, 0 },
520 { "REPLACE", cmList::TransformAction::REPLACE
, 2 } },
521 [](const std::string
& x
, const std::string
& y
) {
525 const std::string
& listName
= args
[1];
527 // Parse all possible function parameters
528 using size_type
= std::vector
<std::string
>::size_type
;
531 auto descriptor
= descriptors
.find(args
[index
]);
533 if (descriptor
== descriptors
.end()) {
535 cmStrCat(" sub-command TRANSFORM, ", args
[index
], " invalid action."));
541 if (args
.size() < index
+ descriptor
->Arity
) {
542 status
.SetError(cmStrCat("sub-command TRANSFORM, action ",
543 descriptor
->Name
, " expects ", descriptor
->Arity
,
548 std::vector
<std::string
> arguments
;
549 index
+= descriptor
->Arity
;
550 if (descriptor
->Arity
> 0) {
552 std::vector
<std::string
>(args
.begin() + 3, args
.begin() + index
);
555 const std::string REGEX
{ "REGEX" };
556 const std::string AT
{ "AT" };
557 const std::string FOR
{ "FOR" };
558 const std::string OUTPUT_VARIABLE
{ "OUTPUT_VARIABLE" };
559 std::unique_ptr
<cmList::TransformSelector
> selector
;
560 std::string outputName
= listName
;
563 // handle optional arguments
564 while (args
.size() > index
) {
565 if ((args
[index
] == REGEX
|| args
[index
] == AT
|| args
[index
] == FOR
) &&
568 cmStrCat("sub-command TRANSFORM, selector already specified (",
569 selector
->GetTag(), ")."));
575 if (args
[index
] == REGEX
) {
576 if (args
.size() == ++index
) {
577 status
.SetError("sub-command TRANSFORM, selector REGEX expects "
578 "'regular expression' argument.");
583 cmList::TransformSelector::New
<cmList::TransformSelector::REGEX
>(
591 if (args
[index
] == AT
) {
592 // get all specified indexes
593 std::vector
<cmList::index_type
> indexes
;
594 while (args
.size() > ++index
) {
599 value
= std::stoi(args
[index
], &pos
);
600 if (pos
!= args
[index
].length()) {
601 // this is not a number, stop processing
604 indexes
.push_back(value
);
605 } catch (const std::invalid_argument
&) {
606 // this is not a number, stop processing
611 if (indexes
.empty()) {
613 "sub-command TRANSFORM, selector AT expects at least one "
619 cmList::TransformSelector::New
<cmList::TransformSelector::AT
>(
626 if (args
[index
] == FOR
) {
627 if (args
.size() <= ++index
+ 1) {
629 "sub-command TRANSFORM, selector FOR expects, at least,"
634 cmList::index_type start
= 0;
635 cmList::index_type stop
= 0;
636 cmList::index_type step
= 1;
641 start
= std::stoi(args
[index
], &pos
);
642 if (pos
!= args
[index
].length()) {
643 // this is not a number
646 stop
= std::stoi(args
[++index
], &pos
);
647 if (pos
!= args
[index
].length()) {
648 // this is not a number
652 } catch (const std::invalid_argument
&) {
653 // this is not numbers
657 status
.SetError("sub-command TRANSFORM, selector FOR expects, "
658 "at least, two numeric values.");
661 // try to read a third numeric value for step
662 if (args
.size() > ++index
) {
666 step
= std::stoi(args
[index
], &pos
);
667 if (pos
!= args
[index
].length()) {
668 // this is not a number
673 } catch (const std::invalid_argument
&) {
674 // this is not number, ignore exception
679 status
.SetError("sub-command TRANSFORM, selector FOR expects "
680 "positive numeric value for <step>.");
685 cmList::TransformSelector::New
<cmList::TransformSelector::FOR
>(
686 { start
, stop
, step
});
692 if (args
[index
] == OUTPUT_VARIABLE
) {
693 if (args
.size() == ++index
) {
694 status
.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
695 "expects variable name argument.");
699 outputName
= args
[index
++];
703 status
.SetError(cmStrCat("sub-command TRANSFORM, '",
704 cmJoin(cmMakeRange(args
).advance(index
), " "),
705 "': unexpected argument(s)."));
709 // expand the list variable
710 auto list
= GetList(listName
, status
.GetMakefile());
713 status
.GetMakefile().AddDefinition(outputName
, "");
717 list
->transform(descriptor
->Action
, arguments
, std::move(selector
));
718 status
.GetMakefile().AddDefinition(outputName
, list
->to_string());
720 } catch (cmList::transform_error
& e
) {
721 status
.SetError(e
.what());
726 bool HandleSortCommand(std::vector
<std::string
> const& args
,
727 cmExecutionStatus
& status
)
729 assert(args
.size() >= 2);
730 if (args
.size() > 8) {
731 status
.SetError("sub-command SORT only takes up to six arguments.");
735 using SortConfig
= cmList::SortConfiguration
;
736 SortConfig sortConfig
;
738 size_t argumentIndex
= 2;
739 const std::string messageHint
= "sub-command SORT ";
741 while (argumentIndex
< args
.size()) {
742 std::string
const& option
= args
[argumentIndex
++];
743 if (option
== "COMPARE") {
744 if (sortConfig
.Compare
!= SortConfig::CompareMethod::DEFAULT
) {
745 std::string error
= cmStrCat(messageHint
, "option \"", option
,
746 "\" has been specified multiple times.");
747 status
.SetError(error
);
750 if (argumentIndex
< args
.size()) {
751 std::string
const& argument
= args
[argumentIndex
++];
752 if (argument
== "STRING") {
753 sortConfig
.Compare
= SortConfig::CompareMethod::STRING
;
754 } else if (argument
== "FILE_BASENAME") {
755 sortConfig
.Compare
= SortConfig::CompareMethod::FILE_BASENAME
;
756 } else if (argument
== "NATURAL") {
757 sortConfig
.Compare
= SortConfig::CompareMethod::NATURAL
;
760 cmStrCat(messageHint
, "value \"", argument
, "\" for option \"",
761 option
, "\" is invalid.");
762 status
.SetError(error
);
766 status
.SetError(cmStrCat(messageHint
, "missing argument for option \"",
770 } else if (option
== "CASE") {
771 if (sortConfig
.Case
!= SortConfig::CaseSensitivity::DEFAULT
) {
772 status
.SetError(cmStrCat(messageHint
, "option \"", option
,
773 "\" has been specified multiple times."));
776 if (argumentIndex
< args
.size()) {
777 std::string
const& argument
= args
[argumentIndex
++];
778 if (argument
== "SENSITIVE") {
779 sortConfig
.Case
= SortConfig::CaseSensitivity::SENSITIVE
;
780 } else if (argument
== "INSENSITIVE") {
781 sortConfig
.Case
= SortConfig::CaseSensitivity::INSENSITIVE
;
783 status
.SetError(cmStrCat(messageHint
, "value \"", argument
,
784 "\" for option \"", option
,
789 status
.SetError(cmStrCat(messageHint
, "missing argument for option \"",
793 } else if (option
== "ORDER") {
795 if (sortConfig
.Order
!= SortConfig::OrderMode::DEFAULT
) {
796 status
.SetError(cmStrCat(messageHint
, "option \"", option
,
797 "\" has been specified multiple times."));
800 if (argumentIndex
< args
.size()) {
801 std::string
const& argument
= args
[argumentIndex
++];
802 if (argument
== "ASCENDING") {
803 sortConfig
.Order
= SortConfig::OrderMode::ASCENDING
;
804 } else if (argument
== "DESCENDING") {
805 sortConfig
.Order
= SortConfig::OrderMode::DESCENDING
;
807 status
.SetError(cmStrCat(messageHint
, "value \"", argument
,
808 "\" for option \"", option
,
813 status
.SetError(cmStrCat(messageHint
, "missing argument for option \"",
819 cmStrCat(messageHint
, "option \"", option
, "\" is unknown."));
824 const std::string
& listName
= args
[1];
825 // expand the variable
826 auto list
= GetList(listName
, status
.GetMakefile());
832 status
.GetMakefile().AddDefinition(listName
,
833 list
->sort(sortConfig
).to_string());
837 bool HandleSublistCommand(std::vector
<std::string
> const& args
,
838 cmExecutionStatus
& status
)
840 if (args
.size() != 5) {
841 status
.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
842 args
.size() - 1, " found)."));
846 const std::string
& listName
= args
[1];
847 const std::string
& variableName
= args
.back();
849 // expand the variable
850 auto list
= GetList(listName
, status
.GetMakefile());
852 if (!list
|| list
->empty()) {
853 status
.GetMakefile().AddDefinition(variableName
, "");
859 if (!GetIndexArg(args
[2], &start
, status
.GetMakefile())) {
860 status
.SetError(cmStrCat("index: ", args
[2], " is not a valid index"));
863 if (!GetIndexArg(args
[3], &length
, status
.GetMakefile())) {
864 status
.SetError(cmStrCat("index: ", args
[3], " is not a valid index"));
869 status
.SetError(cmStrCat("begin index: ", start
, " is out of range 0 - ",
874 status
.SetError(cmStrCat("length: ", length
, " should be -1 or greater"));
878 using size_type
= cmList::size_type
;
881 auto sublist
= list
->sublist(static_cast<size_type
>(start
),
882 static_cast<size_type
>(length
));
883 status
.GetMakefile().AddDefinition(variableName
, sublist
.to_string());
885 } catch (std::out_of_range
& e
) {
886 status
.SetError(e
.what());
891 bool HandleRemoveAtCommand(std::vector
<std::string
> const& args
,
892 cmExecutionStatus
& status
)
894 if (args
.size() < 3) {
895 status
.SetError("sub-command REMOVE_AT requires at least "
900 const std::string
& listName
= args
[1];
901 // expand the variable
902 auto list
= GetList(listName
, status
.GetMakefile());
904 if (!list
|| list
->empty()) {
905 std::ostringstream str
;
907 for (size_t i
= 1; i
< args
.size(); ++i
) {
909 if (i
!= args
.size() - 1) {
913 str
<< " out of range (0, 0)";
914 status
.SetError(str
.str());
919 std::vector
<cmList::index_type
> removed
;
920 for (cc
= 2; cc
< args
.size(); ++cc
) {
922 if (!GetIndexArg(args
[cc
], &index
, status
.GetMakefile())) {
923 status
.SetError(cmStrCat("index: ", args
[cc
], " is not a valid index"));
926 removed
.push_back(index
);
930 status
.GetMakefile().AddDefinition(
932 list
->remove_items(removed
.begin(), removed
.end()).to_string());
934 } catch (std::out_of_range
& e
) {
935 status
.SetError(e
.what());
940 bool HandleFilterCommand(std::vector
<std::string
> const& args
,
941 cmExecutionStatus
& status
)
943 if (args
.size() < 2) {
944 status
.SetError("sub-command FILTER requires a list to be specified.");
948 if (args
.size() < 3) {
950 "sub-command FILTER requires an operator to be specified.");
954 if (args
.size() < 4) {
955 status
.SetError("sub-command FILTER requires a mode to be specified.");
959 const std::string
& op
= args
[2];
960 cmList::FilterMode filterMode
;
961 if (op
== "INCLUDE") {
962 filterMode
= cmList::FilterMode::INCLUDE
;
963 } else if (op
== "EXCLUDE") {
964 filterMode
= cmList::FilterMode::EXCLUDE
;
966 status
.SetError("sub-command FILTER does not recognize operator " + op
);
970 const std::string
& listName
= args
[1];
971 // expand the variable
972 auto list
= GetList(listName
, status
.GetMakefile());
978 const std::string
& mode
= args
[3];
979 if (mode
!= "REGEX") {
980 status
.SetError("sub-command FILTER does not recognize mode " + mode
);
983 if (args
.size() != 5) {
984 status
.SetError("sub-command FILTER, mode REGEX "
985 "requires five arguments.");
988 const std::string
& pattern
= args
[4];
991 status
.GetMakefile().AddDefinition(
992 listName
, list
->filter(pattern
, filterMode
).to_string());
994 } catch (std::invalid_argument
& e
) {
995 status
.SetError(e
.what());
1001 bool cmListCommand(std::vector
<std::string
> const& args
,
1002 cmExecutionStatus
& status
)
1004 if (args
.size() < 2) {
1005 status
.SetError("must be called with at least two arguments.");
1009 static cmSubcommandTable
const subcommand
{
1010 { "LENGTH"_s
, HandleLengthCommand
},
1011 { "GET"_s
, HandleGetCommand
},
1012 { "APPEND"_s
, HandleAppendCommand
},
1013 { "PREPEND"_s
, HandlePrependCommand
},
1014 { "POP_BACK"_s
, HandlePopBackCommand
},
1015 { "POP_FRONT"_s
, HandlePopFrontCommand
},
1016 { "FIND"_s
, HandleFindCommand
},
1017 { "INSERT"_s
, HandleInsertCommand
},
1018 { "JOIN"_s
, HandleJoinCommand
},
1019 { "REMOVE_AT"_s
, HandleRemoveAtCommand
},
1020 { "REMOVE_ITEM"_s
, HandleRemoveItemCommand
},
1021 { "REMOVE_DUPLICATES"_s
, HandleRemoveDuplicatesCommand
},
1022 { "TRANSFORM"_s
, HandleTransformCommand
},
1023 { "SORT"_s
, HandleSortCommand
},
1024 { "SUBLIST"_s
, HandleSublistCommand
},
1025 { "REVERSE"_s
, HandleReverseCommand
},
1026 { "FILTER"_s
, HandleFilterCommand
},
1029 return subcommand(args
[0], args
, status
);