resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmListCommand.cxx
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"
4
5 #include <algorithm>
6 #include <cassert>
7 #include <cstdio>
8 #include <functional>
9 #include <iterator>
10 #include <set>
11 #include <sstream>
12 #include <stdexcept>
13 #include <utility>
14 #include <vector>
15
16 #include <cm/memory>
17 #include <cmext/algorithm>
18 #include <cmext/string_view>
19
20 #include "cmsys/RegularExpression.hxx"
21
22 #include "cmAlgorithms.h"
23 #include "cmExecutionStatus.h"
24 #include "cmGeneratorExpression.h"
25 #include "cmMakefile.h"
26 #include "cmMessageType.h"
27 #include "cmPolicies.h"
28 #include "cmRange.h"
29 #include "cmStringAlgorithms.h"
30 #include "cmStringReplaceHelper.h"
31 #include "cmSubcommandTable.h"
32 #include "cmSystemTools.h"
33 #include "cmValue.h"
34
35 namespace {
36
37 bool GetIndexArg(const std::string& arg, int* idx, cmMakefile& mf)
38 {
39   long value;
40   if (!cmStrToLong(arg, &value)) {
41     switch (mf.GetPolicyStatus(cmPolicies::CMP0121)) {
42       case cmPolicies::WARN: {
43         // Default is to warn and use old behavior OLD behavior is to allow
44         // compatibility, so issue a warning and use the previous behavior.
45         std::string warn =
46           cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0121),
47                    " Invalid list index \"", arg, "\".");
48         mf.IssueMessage(MessageType::AUTHOR_WARNING, warn);
49         CM_FALLTHROUGH;
50       }
51       case cmPolicies::OLD:
52         // OLD behavior is to allow compatibility, so just ignore the
53         // situation.
54         break;
55       case cmPolicies::NEW:
56         return false;
57       case cmPolicies::REQUIRED_IF_USED:
58       case cmPolicies::REQUIRED_ALWAYS:
59         std::string msg =
60           cmStrCat(cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0121),
61                    " Invalid list index \"", arg, "\".");
62         mf.IssueMessage(MessageType::FATAL_ERROR, msg);
63         break;
64     }
65   }
66
67   // Truncation is happening here, but it had always been happening here.
68   *idx = static_cast<int>(value);
69
70   return true;
71 }
72
73 bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
74                  std::string const& listName,
75                  std::vector<std::string>& varArgsExpanded,
76                  cmExecutionStatus& status);
77
78 bool GetListString(std::string& listString, const std::string& var,
79                    const cmMakefile& makefile)
80 {
81   // get the old value
82   cmValue cacheValue = makefile.GetDefinition(var);
83   if (!cacheValue) {
84     return false;
85   }
86   listString = *cacheValue;
87   return true;
88 }
89
90 bool GetList(std::vector<std::string>& list, const std::string& var,
91              const cmMakefile& makefile)
92 {
93   std::string listString;
94   if (!GetListString(listString, var, makefile)) {
95     return false;
96   }
97   // if the size of the list
98   if (listString.empty()) {
99     return true;
100   }
101   // expand the variable into a list
102   cmExpandList(listString, list, true);
103   // if no empty elements then just return
104   if (!cm::contains(list, std::string())) {
105     return true;
106   }
107   // if we have empty elements we need to check policy CMP0007
108   switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) {
109     case cmPolicies::WARN: {
110       // Default is to warn and use old behavior
111       // OLD behavior is to allow compatibility, so recall
112       // ExpandListArgument without the true which will remove
113       // empty values
114       list.clear();
115       cmExpandList(listString, list);
116       std::string warn =
117         cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007),
118                  " List has value = [", listString, "].");
119       makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn);
120       return true;
121     }
122     case cmPolicies::OLD:
123       // OLD behavior is to allow compatibility, so recall
124       // ExpandListArgument without the true which will remove
125       // empty values
126       list.clear();
127       cmExpandList(listString, list);
128       return true;
129     case cmPolicies::NEW:
130       return true;
131     case cmPolicies::REQUIRED_IF_USED:
132     case cmPolicies::REQUIRED_ALWAYS:
133       makefile.IssueMessage(
134         MessageType::FATAL_ERROR,
135         cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007));
136       return false;
137   }
138   return true;
139 }
140
141 bool HandleLengthCommand(std::vector<std::string> const& args,
142                          cmExecutionStatus& status)
143 {
144   if (args.size() != 3) {
145     status.SetError("sub-command LENGTH requires two arguments.");
146     return false;
147   }
148
149   const std::string& listName = args[1];
150   const std::string& variableName = args.back();
151   std::vector<std::string> varArgsExpanded;
152   // do not check the return value here
153   // if the list var is not found varArgsExpanded will have size 0
154   // and we will return 0
155   GetList(varArgsExpanded, listName, status.GetMakefile());
156   size_t length = varArgsExpanded.size();
157   char buffer[1024];
158   snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length));
159
160   status.GetMakefile().AddDefinition(variableName, buffer);
161   return true;
162 }
163
164 bool HandleGetCommand(std::vector<std::string> const& args,
165                       cmExecutionStatus& status)
166 {
167   if (args.size() < 4) {
168     status.SetError("sub-command GET requires at least three arguments.");
169     return false;
170   }
171
172   const std::string& listName = args[1];
173   const std::string& variableName = args.back();
174   // expand the variable
175   std::vector<std::string> varArgsExpanded;
176   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
177     status.GetMakefile().AddDefinition(variableName, "NOTFOUND");
178     return true;
179   }
180   // FIXME: Add policy to make non-existing lists an error like empty lists.
181   if (varArgsExpanded.empty()) {
182     status.SetError("GET given empty list");
183     return false;
184   }
185
186   std::string value;
187   size_t cc;
188   const char* sep = "";
189   size_t nitem = varArgsExpanded.size();
190   for (cc = 2; cc < args.size() - 1; cc++) {
191     int item;
192     if (!GetIndexArg(args[cc], &item, status.GetMakefile())) {
193       status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
194       return false;
195     }
196     value += sep;
197     sep = ";";
198     if (item < 0) {
199       item = static_cast<int>(nitem) + item;
200     }
201     if (item < 0 || nitem <= static_cast<size_t>(item)) {
202       status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
203                                ", ", nitem - 1, ")"));
204       return false;
205     }
206     value += varArgsExpanded[item];
207   }
208
209   status.GetMakefile().AddDefinition(variableName, value);
210   return true;
211 }
212
213 bool HandleAppendCommand(std::vector<std::string> const& args,
214                          cmExecutionStatus& status)
215 {
216   assert(args.size() >= 2);
217
218   // Skip if nothing to append.
219   if (args.size() < 3) {
220     return true;
221   }
222
223   cmMakefile& makefile = status.GetMakefile();
224   std::string const& listName = args[1];
225   // expand the variable
226   std::string listString;
227   GetListString(listString, listName, makefile);
228
229   // If `listString` or `args` is empty, no need to append `;`,
230   // then index is going to be `1` and points to the end-of-string ";"
231   auto const offset =
232     static_cast<std::string::size_type>(listString.empty() || args.empty());
233   listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";");
234
235   makefile.AddDefinition(listName, listString);
236   return true;
237 }
238
239 bool HandlePrependCommand(std::vector<std::string> const& args,
240                           cmExecutionStatus& status)
241 {
242   assert(args.size() >= 2);
243
244   // Skip if nothing to prepend.
245   if (args.size() < 3) {
246     return true;
247   }
248
249   cmMakefile& makefile = status.GetMakefile();
250   std::string const& listName = args[1];
251   // expand the variable
252   std::string listString;
253   GetListString(listString, listName, makefile);
254
255   // If `listString` or `args` is empty, no need to append `;`,
256   // then `offset` is going to be `1` and points to the end-of-string ";"
257   auto const offset =
258     static_cast<std::string::size_type>(listString.empty() || args.empty());
259   listString.insert(0,
260                     cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]);
261
262   makefile.AddDefinition(listName, listString);
263   return true;
264 }
265
266 bool HandlePopBackCommand(std::vector<std::string> const& args,
267                           cmExecutionStatus& status)
268 {
269   assert(args.size() >= 2);
270
271   cmMakefile& makefile = status.GetMakefile();
272   auto ai = args.cbegin();
273   ++ai; // Skip subcommand name
274   std::string const& listName = *ai++;
275   std::vector<std::string> varArgsExpanded;
276   if (!GetList(varArgsExpanded, listName, makefile)) {
277     // Can't get the list definition... undefine any vars given after.
278     for (; ai != args.cend(); ++ai) {
279       makefile.RemoveDefinition(*ai);
280     }
281     return true;
282   }
283
284   if (!varArgsExpanded.empty()) {
285     if (ai == args.cend()) {
286       // No variables are given... Just remove one element.
287       varArgsExpanded.pop_back();
288     } else {
289       // Ok, assign elements to be removed to the given variables
290       for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) {
291         assert(!ai->empty());
292         makefile.AddDefinition(*ai, varArgsExpanded.back());
293         varArgsExpanded.pop_back();
294       }
295       // Undefine the rest variables if the list gets empty earlier...
296       for (; ai != args.cend(); ++ai) {
297         makefile.RemoveDefinition(*ai);
298       }
299     }
300
301     makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
302
303   } else if (ai !=
304              args.cend()) { // The list is empty, but some args were given
305     // Need to *undefine* 'em all, cuz there are no items to assign...
306     for (; ai != args.cend(); ++ai) {
307       makefile.RemoveDefinition(*ai);
308     }
309   }
310
311   return true;
312 }
313
314 bool HandlePopFrontCommand(std::vector<std::string> const& args,
315                            cmExecutionStatus& status)
316 {
317   assert(args.size() >= 2);
318
319   cmMakefile& makefile = status.GetMakefile();
320   auto ai = args.cbegin();
321   ++ai; // Skip subcommand name
322   std::string const& listName = *ai++;
323   std::vector<std::string> varArgsExpanded;
324   if (!GetList(varArgsExpanded, listName, makefile)) {
325     // Can't get the list definition... undefine any vars given after.
326     for (; ai != args.cend(); ++ai) {
327       makefile.RemoveDefinition(*ai);
328     }
329     return true;
330   }
331
332   if (!varArgsExpanded.empty()) {
333     if (ai == args.cend()) {
334       // No variables are given... Just remove one element.
335       varArgsExpanded.erase(varArgsExpanded.begin());
336     } else {
337       // Ok, assign elements to be removed to the given variables
338       auto vi = varArgsExpanded.begin();
339       for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) {
340         assert(!ai->empty());
341         makefile.AddDefinition(*ai, *vi);
342       }
343       varArgsExpanded.erase(varArgsExpanded.begin(), vi);
344       // Undefine the rest variables if the list gets empty earlier...
345       for (; ai != args.cend(); ++ai) {
346         makefile.RemoveDefinition(*ai);
347       }
348     }
349
350     makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
351
352   } else if (ai !=
353              args.cend()) { // The list is empty, but some args were given
354     // Need to *undefine* 'em all, cuz there are no items to assign...
355     for (; ai != args.cend(); ++ai) {
356       makefile.RemoveDefinition(*ai);
357     }
358   }
359
360   return true;
361 }
362
363 bool HandleFindCommand(std::vector<std::string> const& args,
364                        cmExecutionStatus& status)
365 {
366   if (args.size() != 4) {
367     status.SetError("sub-command FIND requires three arguments.");
368     return false;
369   }
370
371   const std::string& listName = args[1];
372   const std::string& variableName = args.back();
373   // expand the variable
374   std::vector<std::string> varArgsExpanded;
375   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
376     status.GetMakefile().AddDefinition(variableName, "-1");
377     return true;
378   }
379
380   auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
381   if (it != varArgsExpanded.end()) {
382     status.GetMakefile().AddDefinition(
383       variableName,
384       std::to_string(std::distance(varArgsExpanded.begin(), it)));
385     return true;
386   }
387
388   status.GetMakefile().AddDefinition(variableName, "-1");
389   return true;
390 }
391
392 bool HandleInsertCommand(std::vector<std::string> const& args,
393                          cmExecutionStatus& status)
394 {
395   if (args.size() < 4) {
396     status.SetError("sub-command INSERT requires at least three arguments.");
397     return false;
398   }
399
400   const std::string& listName = args[1];
401
402   // expand the variable
403   int item;
404   if (!GetIndexArg(args[2], &item, status.GetMakefile())) {
405     status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
406     return false;
407   }
408   std::vector<std::string> varArgsExpanded;
409   if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
410        varArgsExpanded.empty()) &&
411       item != 0) {
412     status.SetError(cmStrCat("index: ", item, " out of range (0, 0)"));
413     return false;
414   }
415
416   if (!varArgsExpanded.empty()) {
417     size_t nitem = varArgsExpanded.size();
418     if (item < 0) {
419       item = static_cast<int>(nitem) + item;
420     }
421     if (item < 0 || nitem < static_cast<size_t>(item)) {
422       status.SetError(cmStrCat("index: ", item, " out of range (-",
423                                varArgsExpanded.size(), ", ",
424                                varArgsExpanded.size(), ")"));
425       return false;
426     }
427   }
428
429   varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3,
430                          args.end());
431
432   std::string value = cmJoin(varArgsExpanded, ";");
433   status.GetMakefile().AddDefinition(listName, value);
434   return true;
435 }
436
437 bool HandleJoinCommand(std::vector<std::string> const& args,
438                        cmExecutionStatus& status)
439 {
440   if (args.size() != 4) {
441     status.SetError(cmStrCat("sub-command JOIN requires three arguments (",
442                              args.size() - 1, " found)."));
443     return false;
444   }
445
446   const std::string& listName = args[1];
447   const std::string& glue = args[2];
448   const std::string& variableName = args[3];
449
450   // expand the variable
451   std::vector<std::string> varArgsExpanded;
452   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
453     status.GetMakefile().AddDefinition(variableName, "");
454     return true;
455   }
456
457   std::string value =
458     cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue);
459
460   status.GetMakefile().AddDefinition(variableName, value);
461   return true;
462 }
463
464 bool HandleRemoveItemCommand(std::vector<std::string> const& args,
465                              cmExecutionStatus& status)
466 {
467   assert(args.size() >= 2);
468
469   if (args.size() == 2) {
470     return true;
471   }
472
473   const std::string& listName = args[1];
474   // expand the variable
475   std::vector<std::string> varArgsExpanded;
476   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
477     return true;
478   }
479
480   std::vector<std::string> remove(args.begin() + 2, args.end());
481   std::sort(remove.begin(), remove.end());
482   auto remEnd = std::unique(remove.begin(), remove.end());
483   auto remBegin = remove.begin();
484
485   auto argsEnd =
486     cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd));
487   auto argsBegin = varArgsExpanded.cbegin();
488   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
489   status.GetMakefile().AddDefinition(listName, value);
490   return true;
491 }
492
493 bool HandleReverseCommand(std::vector<std::string> const& args,
494                           cmExecutionStatus& status)
495 {
496   assert(args.size() >= 2);
497   if (args.size() > 2) {
498     status.SetError("sub-command REVERSE only takes one argument.");
499     return false;
500   }
501
502   const std::string& listName = args[1];
503   // expand the variable
504   std::vector<std::string> varArgsExpanded;
505   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
506     return true;
507   }
508
509   std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";");
510
511   status.GetMakefile().AddDefinition(listName, value);
512   return true;
513 }
514
515 bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args,
516                                    cmExecutionStatus& status)
517 {
518   assert(args.size() >= 2);
519   if (args.size() > 2) {
520     status.SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
521     return false;
522   }
523
524   const std::string& listName = args[1];
525   // expand the variable
526   std::vector<std::string> varArgsExpanded;
527   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
528     return true;
529   }
530
531   auto argsEnd = cmRemoveDuplicates(varArgsExpanded);
532   auto argsBegin = varArgsExpanded.cbegin();
533   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
534
535   status.GetMakefile().AddDefinition(listName, value);
536   return true;
537 }
538
539 // Helpers for list(TRANSFORM <list> ...)
540 using transform_type = std::function<std::string(const std::string&)>;
541
542 class transform_error : public std::runtime_error
543 {
544 public:
545   transform_error(const std::string& error)
546     : std::runtime_error(error)
547   {
548   }
549 };
550
551 class TransformSelector
552 {
553 public:
554   virtual ~TransformSelector() = default;
555
556   std::string Tag;
557
558   virtual bool Validate(std::size_t count = 0) = 0;
559
560   virtual bool InSelection(const std::string&) = 0;
561
562   virtual void Transform(std::vector<std::string>& list,
563                          const transform_type& transform)
564   {
565     std::transform(list.begin(), list.end(), list.begin(), transform);
566   }
567
568 protected:
569   TransformSelector(std::string&& tag)
570     : Tag(std::move(tag))
571   {
572   }
573 };
574 class TransformNoSelector : public TransformSelector
575 {
576 public:
577   TransformNoSelector()
578     : TransformSelector("NO SELECTOR")
579   {
580   }
581
582   bool Validate(std::size_t) override { return true; }
583
584   bool InSelection(const std::string&) override { return true; }
585 };
586 class TransformSelectorRegex : public TransformSelector
587 {
588 public:
589   TransformSelectorRegex(const std::string& regex)
590     : TransformSelector("REGEX")
591     , Regex(regex)
592   {
593   }
594
595   bool Validate(std::size_t) override { return this->Regex.is_valid(); }
596
597   bool InSelection(const std::string& value) override
598   {
599     return this->Regex.find(value);
600   }
601
602   cmsys::RegularExpression Regex;
603 };
604 class TransformSelectorIndexes : public TransformSelector
605 {
606 public:
607   std::vector<int> Indexes;
608
609   bool InSelection(const std::string&) override { return true; }
610
611   void Transform(std::vector<std::string>& list,
612                  const transform_type& transform) override
613   {
614     this->Validate(list.size());
615
616     for (auto index : this->Indexes) {
617       list[index] = transform(list[index]);
618     }
619   }
620
621 protected:
622   TransformSelectorIndexes(std::string&& tag)
623     : TransformSelector(std::move(tag))
624   {
625   }
626   TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
627     : TransformSelector(std::move(tag))
628     , Indexes(indexes)
629   {
630   }
631
632   int NormalizeIndex(int index, std::size_t count)
633   {
634     if (index < 0) {
635       index = static_cast<int>(count) + index;
636     }
637     if (index < 0 || count <= static_cast<std::size_t>(index)) {
638       throw transform_error(cmStrCat(
639         "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
640         " out of range (-", count, ", ", count - 1, ")."));
641     }
642     return index;
643   }
644 };
645 class TransformSelectorAt : public TransformSelectorIndexes
646 {
647 public:
648   TransformSelectorAt(std::vector<int>&& indexes)
649     : TransformSelectorIndexes("AT", std::move(indexes))
650   {
651   }
652
653   bool Validate(std::size_t count) override
654   {
655     decltype(this->Indexes) indexes;
656
657     for (auto index : this->Indexes) {
658       indexes.push_back(this->NormalizeIndex(index, count));
659     }
660     this->Indexes = std::move(indexes);
661
662     return true;
663   }
664 };
665 class TransformSelectorFor : public TransformSelectorIndexes
666 {
667 public:
668   TransformSelectorFor(int start, int stop, int step)
669     : TransformSelectorIndexes("FOR")
670     , Start(start)
671     , Stop(stop)
672     , Step(step)
673   {
674   }
675
676   bool Validate(std::size_t count) override
677   {
678     this->Start = this->NormalizeIndex(this->Start, count);
679     this->Stop = this->NormalizeIndex(this->Stop, count);
680
681     // Does stepping move us further from the end?
682     if (this->Start > this->Stop) {
683       throw transform_error(
684         cmStrCat("sub-command TRANSFORM, selector FOR "
685                  "expects <start> to be no greater than <stop> (",
686                  this->Start, " > ", this->Stop, ")"));
687     }
688
689     // compute indexes
690     auto size = (this->Stop - this->Start + 1) / this->Step;
691     if ((this->Stop - this->Start + 1) % this->Step != 0) {
692       size += 1;
693     }
694
695     this->Indexes.resize(size);
696     auto start = this->Start;
697     auto step = this->Step;
698     std::generate(this->Indexes.begin(), this->Indexes.end(),
699                   [&start, step]() -> int {
700                     auto r = start;
701                     start += step;
702                     return r;
703                   });
704
705     return true;
706   }
707
708 private:
709   int Start, Stop, Step;
710 };
711
712 class TransformAction
713 {
714 public:
715   virtual ~TransformAction() = default;
716
717   virtual std::string Transform(const std::string& input) = 0;
718 };
719 class TransformReplace : public TransformAction
720 {
721 public:
722   TransformReplace(const std::vector<std::string>& arguments,
723                    cmMakefile* makefile)
724     : ReplaceHelper(arguments[0], arguments[1], makefile)
725   {
726     makefile->ClearMatches();
727
728     if (!this->ReplaceHelper.IsRegularExpressionValid()) {
729       throw transform_error(
730         cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
731                  "regex \"",
732                  arguments[0], "\"."));
733     }
734     if (!this->ReplaceHelper.IsReplaceExpressionValid()) {
735       throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
736                                      this->ReplaceHelper.GetError(), "."));
737     }
738   }
739
740   std::string Transform(const std::string& input) override
741   {
742     // Scan through the input for all matches.
743     std::string output;
744
745     if (!this->ReplaceHelper.Replace(input, output)) {
746       throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
747                                      this->ReplaceHelper.GetError(), "."));
748     }
749
750     return output;
751   }
752
753 private:
754   cmStringReplaceHelper ReplaceHelper;
755 };
756
757 bool HandleTransformCommand(std::vector<std::string> const& args,
758                             cmExecutionStatus& status)
759 {
760   if (args.size() < 3) {
761     status.SetError(
762       "sub-command TRANSFORM requires an action to be specified.");
763     return false;
764   }
765
766   // Structure collecting all elements of the command
767   struct Command
768   {
769     Command(const std::string& listName)
770       : ListName(listName)
771       , OutputName(listName)
772     {
773     }
774
775     std::string Name;
776     std::string ListName;
777     std::vector<std::string> Arguments;
778     std::unique_ptr<TransformAction> Action;
779     std::unique_ptr<TransformSelector> Selector;
780     std::string OutputName;
781   } command(args[1]);
782
783   // Descriptor of action
784   // Arity: number of arguments required for the action
785   // Transform: lambda function implementing the action
786   struct ActionDescriptor
787   {
788     ActionDescriptor(std::string name)
789       : Name(std::move(name))
790     {
791     }
792     ActionDescriptor(std::string name, int arity, transform_type transform)
793       : Name(std::move(name))
794       , Arity(arity)
795 #if defined(__GNUC__) && __GNUC__ == 6 && defined(__aarch64__)
796       // std::function move constructor miscompiles on this architecture
797       , Transform(transform)
798 #else
799       , Transform(std::move(transform))
800 #endif
801     {
802     }
803
804     operator const std::string&() const { return this->Name; }
805
806     std::string Name;
807     int Arity = 0;
808     transform_type Transform;
809   };
810
811   // Build a set of supported actions.
812   std::set<ActionDescriptor,
813            std::function<bool(const std::string&, const std::string&)>>
814     descriptors(
815       [](const std::string& x, const std::string& y) { return x < y; });
816   descriptors = { { "APPEND", 1,
817                     [&command](const std::string& s) -> std::string {
818                       if (command.Selector->InSelection(s)) {
819                         return s + command.Arguments[0];
820                       }
821
822                       return s;
823                     } },
824                   { "PREPEND", 1,
825                     [&command](const std::string& s) -> std::string {
826                       if (command.Selector->InSelection(s)) {
827                         return command.Arguments[0] + s;
828                       }
829
830                       return s;
831                     } },
832                   { "TOUPPER", 0,
833                     [&command](const std::string& s) -> std::string {
834                       if (command.Selector->InSelection(s)) {
835                         return cmSystemTools::UpperCase(s);
836                       }
837
838                       return s;
839                     } },
840                   { "TOLOWER", 0,
841                     [&command](const std::string& s) -> std::string {
842                       if (command.Selector->InSelection(s)) {
843                         return cmSystemTools::LowerCase(s);
844                       }
845
846                       return s;
847                     } },
848                   { "STRIP", 0,
849                     [&command](const std::string& s) -> std::string {
850                       if (command.Selector->InSelection(s)) {
851                         return cmTrimWhitespace(s);
852                       }
853
854                       return s;
855                     } },
856                   { "GENEX_STRIP", 0,
857                     [&command](const std::string& s) -> std::string {
858                       if (command.Selector->InSelection(s)) {
859                         return cmGeneratorExpression::Preprocess(
860                           s,
861                           cmGeneratorExpression::StripAllGeneratorExpressions);
862                       }
863
864                       return s;
865                     } },
866                   { "REPLACE", 2,
867                     [&command](const std::string& s) -> std::string {
868                       if (command.Selector->InSelection(s)) {
869                         return command.Action->Transform(s);
870                       }
871
872                       return s;
873                     } } };
874
875   using size_type = std::vector<std::string>::size_type;
876   size_type index = 2;
877
878   // Parse all possible function parameters
879   auto descriptor = descriptors.find(args[index]);
880
881   if (descriptor == descriptors.end()) {
882     status.SetError(
883       cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action."));
884     return false;
885   }
886
887   // Action arguments
888   index += 1;
889   if (args.size() < index + descriptor->Arity) {
890     status.SetError(cmStrCat("sub-command TRANSFORM, action ",
891                              descriptor->Name, " expects ", descriptor->Arity,
892                              " argument(s)."));
893     return false;
894   }
895
896   command.Name = descriptor->Name;
897   index += descriptor->Arity;
898   if (descriptor->Arity > 0) {
899     command.Arguments =
900       std::vector<std::string>(args.begin() + 3, args.begin() + index);
901   }
902
903   if (command.Name == "REPLACE") {
904     try {
905       command.Action = cm::make_unique<TransformReplace>(
906         command.Arguments, &status.GetMakefile());
907     } catch (const transform_error& e) {
908       status.SetError(e.what());
909       return false;
910     }
911   }
912
913   const std::string REGEX{ "REGEX" };
914   const std::string AT{ "AT" };
915   const std::string FOR{ "FOR" };
916   const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
917
918   // handle optional arguments
919   while (args.size() > index) {
920     if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
921         command.Selector) {
922       status.SetError(
923         cmStrCat("sub-command TRANSFORM, selector already specified (",
924                  command.Selector->Tag, ")."));
925
926       return false;
927     }
928
929     // REGEX selector
930     if (args[index] == REGEX) {
931       if (args.size() == ++index) {
932         status.SetError("sub-command TRANSFORM, selector REGEX expects "
933                         "'regular expression' argument.");
934         return false;
935       }
936
937       command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]);
938       if (!command.Selector->Validate()) {
939         status.SetError(
940           cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
941                    "regex \"",
942                    args[index], "\"."));
943         return false;
944       }
945
946       index += 1;
947       continue;
948     }
949
950     // AT selector
951     if (args[index] == AT) {
952       // get all specified indexes
953       std::vector<int> indexes;
954       while (args.size() > ++index) {
955         std::size_t pos;
956         int value;
957
958         try {
959           value = std::stoi(args[index], &pos);
960           if (pos != args[index].length()) {
961             // this is not a number, stop processing
962             break;
963           }
964           indexes.push_back(value);
965         } catch (const std::invalid_argument&) {
966           // this is not a number, stop processing
967           break;
968         }
969       }
970
971       if (indexes.empty()) {
972         status.SetError(
973           "sub-command TRANSFORM, selector AT expects at least one "
974           "numeric value.");
975         return false;
976       }
977
978       command.Selector =
979         cm::make_unique<TransformSelectorAt>(std::move(indexes));
980
981       continue;
982     }
983
984     // FOR selector
985     if (args[index] == FOR) {
986       if (args.size() <= ++index + 1) {
987         status.SetError(
988           "sub-command TRANSFORM, selector FOR expects, at least,"
989           " two arguments.");
990         return false;
991       }
992
993       int start = 0;
994       int stop = 0;
995       int step = 1;
996       bool valid = true;
997       try {
998         std::size_t pos;
999
1000         start = std::stoi(args[index], &pos);
1001         if (pos != args[index].length()) {
1002           // this is not a number
1003           valid = false;
1004         } else {
1005           stop = std::stoi(args[++index], &pos);
1006           if (pos != args[index].length()) {
1007             // this is not a number
1008             valid = false;
1009           }
1010         }
1011       } catch (const std::invalid_argument&) {
1012         // this is not numbers
1013         valid = false;
1014       }
1015       if (!valid) {
1016         status.SetError("sub-command TRANSFORM, selector FOR expects, "
1017                         "at least, two numeric values.");
1018         return false;
1019       }
1020       // try to read a third numeric value for step
1021       if (args.size() > ++index) {
1022         try {
1023           std::size_t pos;
1024
1025           step = std::stoi(args[index], &pos);
1026           if (pos != args[index].length()) {
1027             // this is not a number
1028             step = 1;
1029           } else {
1030             index += 1;
1031           }
1032         } catch (const std::invalid_argument&) {
1033           // this is not number, ignore exception
1034         }
1035       }
1036
1037       if (step <= 0) {
1038         status.SetError("sub-command TRANSFORM, selector FOR expects "
1039                         "positive numeric value for <step>.");
1040         return false;
1041       }
1042
1043       command.Selector =
1044         cm::make_unique<TransformSelectorFor>(start, stop, step);
1045
1046       continue;
1047     }
1048
1049     // output variable
1050     if (args[index] == OUTPUT_VARIABLE) {
1051       if (args.size() == ++index) {
1052         status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
1053                         "expects variable name argument.");
1054         return false;
1055       }
1056
1057       command.OutputName = args[index++];
1058       continue;
1059     }
1060
1061     status.SetError(cmStrCat("sub-command TRANSFORM, '",
1062                              cmJoin(cmMakeRange(args).advance(index), " "),
1063                              "': unexpected argument(s)."));
1064     return false;
1065   }
1066
1067   // expand the list variable
1068   std::vector<std::string> varArgsExpanded;
1069   if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) {
1070     status.GetMakefile().AddDefinition(command.OutputName, "");
1071     return true;
1072   }
1073
1074   if (!command.Selector) {
1075     // no selector specified, apply transformation to all elements
1076     command.Selector = cm::make_unique<TransformNoSelector>();
1077   }
1078
1079   try {
1080     command.Selector->Transform(varArgsExpanded, descriptor->Transform);
1081   } catch (const transform_error& e) {
1082     status.SetError(e.what());
1083     return false;
1084   }
1085
1086   status.GetMakefile().AddDefinition(command.OutputName,
1087                                      cmJoin(varArgsExpanded, ";"));
1088
1089   return true;
1090 }
1091
1092 class cmStringSorter
1093 {
1094 public:
1095   enum class Order
1096   {
1097     UNINITIALIZED,
1098     ASCENDING,
1099     DESCENDING,
1100   };
1101
1102   enum class Compare
1103   {
1104     UNINITIALIZED,
1105     STRING,
1106     FILE_BASENAME,
1107     NATURAL,
1108   };
1109   enum class CaseSensitivity
1110   {
1111     UNINITIALIZED,
1112     SENSITIVE,
1113     INSENSITIVE,
1114   };
1115
1116 protected:
1117   using StringFilter = std::string (*)(const std::string&);
1118   StringFilter GetCompareFilter(Compare compare)
1119   {
1120     return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName
1121                                                : nullptr;
1122   }
1123
1124   StringFilter GetCaseFilter(CaseSensitivity sensitivity)
1125   {
1126     return (sensitivity == CaseSensitivity::INSENSITIVE)
1127       ? cmSystemTools::LowerCase
1128       : nullptr;
1129   }
1130
1131   using ComparisonFunction =
1132     std::function<bool(const std::string&, const std::string&)>;
1133   ComparisonFunction GetComparisonFunction(Compare compare)
1134   {
1135     if (compare == Compare::NATURAL) {
1136       return std::function<bool(const std::string&, const std::string&)>(
1137         [](const std::string& x, const std::string& y) {
1138           return cmSystemTools::strverscmp(x, y) < 0;
1139         });
1140     }
1141     return std::function<bool(const std::string&, const std::string&)>(
1142       [](const std::string& x, const std::string& y) { return x < y; });
1143   }
1144
1145 public:
1146   cmStringSorter(Compare compare, CaseSensitivity caseSensitivity,
1147                  Order desc = Order::ASCENDING)
1148     : filters{ this->GetCompareFilter(compare),
1149                this->GetCaseFilter(caseSensitivity) }
1150     , sortMethod(this->GetComparisonFunction(compare))
1151     , descending(desc == Order::DESCENDING)
1152   {
1153   }
1154
1155   std::string ApplyFilter(const std::string& argument)
1156   {
1157     std::string result = argument;
1158     for (auto filter : this->filters) {
1159       if (filter != nullptr) {
1160         result = filter(result);
1161       }
1162     }
1163     return result;
1164   }
1165
1166   bool operator()(const std::string& a, const std::string& b)
1167   {
1168     std::string af = this->ApplyFilter(a);
1169     std::string bf = this->ApplyFilter(b);
1170     bool result;
1171     if (this->descending) {
1172       result = this->sortMethod(bf, af);
1173     } else {
1174       result = this->sortMethod(af, bf);
1175     }
1176     return result;
1177   }
1178
1179 protected:
1180   StringFilter filters[2] = { nullptr, nullptr };
1181   ComparisonFunction sortMethod;
1182   bool descending;
1183 };
1184
1185 bool HandleSortCommand(std::vector<std::string> const& args,
1186                        cmExecutionStatus& status)
1187 {
1188   assert(args.size() >= 2);
1189   if (args.size() > 8) {
1190     status.SetError("sub-command SORT only takes up to six arguments.");
1191     return false;
1192   }
1193
1194   auto sortCompare = cmStringSorter::Compare::UNINITIALIZED;
1195   auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED;
1196   auto sortOrder = cmStringSorter::Order::UNINITIALIZED;
1197
1198   size_t argumentIndex = 2;
1199   const std::string messageHint = "sub-command SORT ";
1200
1201   while (argumentIndex < args.size()) {
1202     std::string const& option = args[argumentIndex++];
1203     if (option == "COMPARE") {
1204       if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
1205         std::string error = cmStrCat(messageHint, "option \"", option,
1206                                      "\" has been specified multiple times.");
1207         status.SetError(error);
1208         return false;
1209       }
1210       if (argumentIndex < args.size()) {
1211         std::string const& argument = args[argumentIndex++];
1212         if (argument == "STRING") {
1213           sortCompare = cmStringSorter::Compare::STRING;
1214         } else if (argument == "FILE_BASENAME") {
1215           sortCompare = cmStringSorter::Compare::FILE_BASENAME;
1216         } else if (argument == "NATURAL") {
1217           sortCompare = cmStringSorter::Compare::NATURAL;
1218         } else {
1219           std::string error =
1220             cmStrCat(messageHint, "value \"", argument, "\" for option \"",
1221                      option, "\" is invalid.");
1222           status.SetError(error);
1223           return false;
1224         }
1225       } else {
1226         status.SetError(cmStrCat(messageHint, "missing argument for option \"",
1227                                  option, "\"."));
1228         return false;
1229       }
1230     } else if (option == "CASE") {
1231       if (sortCaseSensitivity !=
1232           cmStringSorter::CaseSensitivity::UNINITIALIZED) {
1233         status.SetError(cmStrCat(messageHint, "option \"", option,
1234                                  "\" has been specified multiple times."));
1235         return false;
1236       }
1237       if (argumentIndex < args.size()) {
1238         std::string const& argument = args[argumentIndex++];
1239         if (argument == "SENSITIVE") {
1240           sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
1241         } else if (argument == "INSENSITIVE") {
1242           sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
1243         } else {
1244           status.SetError(cmStrCat(messageHint, "value \"", argument,
1245                                    "\" for option \"", option,
1246                                    "\" is invalid."));
1247           return false;
1248         }
1249       } else {
1250         status.SetError(cmStrCat(messageHint, "missing argument for option \"",
1251                                  option, "\"."));
1252         return false;
1253       }
1254     } else if (option == "ORDER") {
1255
1256       if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
1257         status.SetError(cmStrCat(messageHint, "option \"", option,
1258                                  "\" has been specified multiple times."));
1259         return false;
1260       }
1261       if (argumentIndex < args.size()) {
1262         std::string const& argument = args[argumentIndex++];
1263         if (argument == "ASCENDING") {
1264           sortOrder = cmStringSorter::Order::ASCENDING;
1265         } else if (argument == "DESCENDING") {
1266           sortOrder = cmStringSorter::Order::DESCENDING;
1267         } else {
1268           status.SetError(cmStrCat(messageHint, "value \"", argument,
1269                                    "\" for option \"", option,
1270                                    "\" is invalid."));
1271           return false;
1272         }
1273       } else {
1274         status.SetError(cmStrCat(messageHint, "missing argument for option \"",
1275                                  option, "\"."));
1276         return false;
1277       }
1278     } else {
1279       status.SetError(
1280         cmStrCat(messageHint, "option \"", option, "\" is unknown."));
1281       return false;
1282     }
1283   }
1284   // set Default Values if Option is not given
1285   if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) {
1286     sortCompare = cmStringSorter::Compare::STRING;
1287   }
1288   if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) {
1289     sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
1290   }
1291   if (sortOrder == cmStringSorter::Order::UNINITIALIZED) {
1292     sortOrder = cmStringSorter::Order::ASCENDING;
1293   }
1294
1295   const std::string& listName = args[1];
1296   // expand the variable
1297   std::vector<std::string> varArgsExpanded;
1298   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
1299     return true;
1300   }
1301
1302   if ((sortCompare == cmStringSorter::Compare::STRING) &&
1303       (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) &&
1304       (sortOrder == cmStringSorter::Order::ASCENDING)) {
1305     std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
1306   } else {
1307     cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder);
1308     std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter);
1309   }
1310
1311   std::string value = cmJoin(varArgsExpanded, ";");
1312   status.GetMakefile().AddDefinition(listName, value);
1313   return true;
1314 }
1315
1316 bool HandleSublistCommand(std::vector<std::string> const& args,
1317                           cmExecutionStatus& status)
1318 {
1319   if (args.size() != 5) {
1320     status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
1321                              args.size() - 1, " found)."));
1322     return false;
1323   }
1324
1325   const std::string& listName = args[1];
1326   const std::string& variableName = args.back();
1327
1328   // expand the variable
1329   std::vector<std::string> varArgsExpanded;
1330   if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
1331       varArgsExpanded.empty()) {
1332     status.GetMakefile().AddDefinition(variableName, "");
1333     return true;
1334   }
1335
1336   int start;
1337   int length;
1338   if (!GetIndexArg(args[2], &start, status.GetMakefile())) {
1339     status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
1340     return false;
1341   }
1342   if (!GetIndexArg(args[3], &length, status.GetMakefile())) {
1343     status.SetError(cmStrCat("index: ", args[3], " is not a valid index"));
1344     return false;
1345   }
1346
1347   using size_type = decltype(varArgsExpanded)::size_type;
1348
1349   if (start < 0 || static_cast<size_type>(start) >= varArgsExpanded.size()) {
1350     status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
1351                              varArgsExpanded.size() - 1));
1352     return false;
1353   }
1354   if (length < -1) {
1355     status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
1356     return false;
1357   }
1358
1359   const size_type end =
1360     (length == -1 ||
1361      static_cast<size_type>(start + length) > varArgsExpanded.size())
1362     ? varArgsExpanded.size()
1363     : static_cast<size_type>(start + length);
1364   std::vector<std::string> sublist(varArgsExpanded.begin() + start,
1365                                    varArgsExpanded.begin() + end);
1366   status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";"));
1367   return true;
1368 }
1369
1370 bool HandleRemoveAtCommand(std::vector<std::string> const& args,
1371                            cmExecutionStatus& status)
1372 {
1373   if (args.size() < 3) {
1374     status.SetError("sub-command REMOVE_AT requires at least "
1375                     "two arguments.");
1376     return false;
1377   }
1378
1379   const std::string& listName = args[1];
1380   // expand the variable
1381   std::vector<std::string> varArgsExpanded;
1382   if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
1383       varArgsExpanded.empty()) {
1384     std::ostringstream str;
1385     str << "index: ";
1386     for (size_t i = 1; i < args.size(); ++i) {
1387       str << args[i];
1388       if (i != args.size() - 1) {
1389         str << ", ";
1390       }
1391     }
1392     str << " out of range (0, 0)";
1393     status.SetError(str.str());
1394     return false;
1395   }
1396
1397   size_t cc;
1398   std::vector<size_t> removed;
1399   size_t nitem = varArgsExpanded.size();
1400   for (cc = 2; cc < args.size(); ++cc) {
1401     int item;
1402     if (!GetIndexArg(args[cc], &item, status.GetMakefile())) {
1403       status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
1404       return false;
1405     }
1406     if (item < 0) {
1407       item = static_cast<int>(nitem) + item;
1408     }
1409     if (item < 0 || nitem <= static_cast<size_t>(item)) {
1410       status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
1411                                ", ", nitem - 1, ")"));
1412       return false;
1413     }
1414     removed.push_back(static_cast<size_t>(item));
1415   }
1416
1417   std::sort(removed.begin(), removed.end());
1418   auto remEnd = std::unique(removed.begin(), removed.end());
1419   auto remBegin = removed.begin();
1420
1421   auto argsEnd =
1422     cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
1423   auto argsBegin = varArgsExpanded.cbegin();
1424   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
1425
1426   status.GetMakefile().AddDefinition(listName, value);
1427   return true;
1428 }
1429
1430 bool HandleFilterCommand(std::vector<std::string> const& args,
1431                          cmExecutionStatus& status)
1432 {
1433   if (args.size() < 2) {
1434     status.SetError("sub-command FILTER requires a list to be specified.");
1435     return false;
1436   }
1437
1438   if (args.size() < 3) {
1439     status.SetError(
1440       "sub-command FILTER requires an operator to be specified.");
1441     return false;
1442   }
1443
1444   if (args.size() < 4) {
1445     status.SetError("sub-command FILTER requires a mode to be specified.");
1446     return false;
1447   }
1448
1449   const std::string& op = args[2];
1450   bool includeMatches;
1451   if (op == "INCLUDE") {
1452     includeMatches = true;
1453   } else if (op == "EXCLUDE") {
1454     includeMatches = false;
1455   } else {
1456     status.SetError("sub-command FILTER does not recognize operator " + op);
1457     return false;
1458   }
1459
1460   const std::string& listName = args[1];
1461   // expand the variable
1462   std::vector<std::string> varArgsExpanded;
1463   if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
1464     return true;
1465   }
1466
1467   const std::string& mode = args[3];
1468   if (mode == "REGEX") {
1469     if (args.size() != 5) {
1470       status.SetError("sub-command FILTER, mode REGEX "
1471                       "requires five arguments.");
1472       return false;
1473     }
1474     return FilterRegex(args, includeMatches, listName, varArgsExpanded,
1475                        status);
1476   }
1477
1478   status.SetError("sub-command FILTER does not recognize mode " + mode);
1479   return false;
1480 }
1481
1482 class MatchesRegex
1483 {
1484 public:
1485   MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
1486     : regex(in_regex)
1487     , includeMatches(in_includeMatches)
1488   {
1489   }
1490
1491   bool operator()(const std::string& target)
1492   {
1493     return this->regex.find(target) ^ this->includeMatches;
1494   }
1495
1496 private:
1497   cmsys::RegularExpression& regex;
1498   const bool includeMatches;
1499 };
1500
1501 bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
1502                  std::string const& listName,
1503                  std::vector<std::string>& varArgsExpanded,
1504                  cmExecutionStatus& status)
1505 {
1506   const std::string& pattern = args[4];
1507   cmsys::RegularExpression regex(pattern);
1508   if (!regex.is_valid()) {
1509     std::string error =
1510       cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
1511                pattern, "\".");
1512     status.SetError(error);
1513     return false;
1514   }
1515
1516   auto argsBegin = varArgsExpanded.begin();
1517   auto argsEnd = varArgsExpanded.end();
1518   auto newArgsEnd =
1519     std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
1520
1521   std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
1522   status.GetMakefile().AddDefinition(listName, value);
1523   return true;
1524 }
1525
1526 } // namespace
1527
1528 bool cmListCommand(std::vector<std::string> const& args,
1529                    cmExecutionStatus& status)
1530 {
1531   if (args.size() < 2) {
1532     status.SetError("must be called with at least two arguments.");
1533     return false;
1534   }
1535
1536   static cmSubcommandTable const subcommand{
1537     { "LENGTH"_s, HandleLengthCommand },
1538     { "GET"_s, HandleGetCommand },
1539     { "APPEND"_s, HandleAppendCommand },
1540     { "PREPEND"_s, HandlePrependCommand },
1541     { "POP_BACK"_s, HandlePopBackCommand },
1542     { "POP_FRONT"_s, HandlePopFrontCommand },
1543     { "FIND"_s, HandleFindCommand },
1544     { "INSERT"_s, HandleInsertCommand },
1545     { "JOIN"_s, HandleJoinCommand },
1546     { "REMOVE_AT"_s, HandleRemoveAtCommand },
1547     { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
1548     { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
1549     { "TRANSFORM"_s, HandleTransformCommand },
1550     { "SORT"_s, HandleSortCommand },
1551     { "SUBLIST"_s, HandleSublistCommand },
1552     { "REVERSE"_s, HandleReverseCommand },
1553     { "FILTER"_s, HandleFilterCommand },
1554   };
1555
1556   return subcommand(args[0], args, status);
1557 }