/src/CMake/Source/cmListCommand.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmListCommand.h" |
4 | | |
5 | | #include <cassert> |
6 | | #include <cstdio> |
7 | | #include <functional> |
8 | | #include <set> |
9 | | #include <sstream> |
10 | | #include <stdexcept> |
11 | | #include <utility> |
12 | | #include <vector> |
13 | | |
14 | | #include <cm/memory> |
15 | | #include <cm/optional> |
16 | | #include <cmext/algorithm> |
17 | | #include <cmext/string_view> |
18 | | |
19 | | #include "cmExecutionStatus.h" |
20 | | #include "cmList.h" |
21 | | #include "cmMakefile.h" |
22 | | #include "cmPolicies.h" |
23 | | #include "cmRange.h" |
24 | | #include "cmStringAlgorithms.h" |
25 | | #include "cmSubcommandTable.h" |
26 | | #include "cmValue.h" |
27 | | |
28 | | namespace { |
29 | | |
30 | | bool GetIndexArg(std::string const& arg, int* idx, cmMakefile& mf) |
31 | 0 | { |
32 | 0 | long value; |
33 | 0 | if (!cmStrToLong(arg, &value)) { |
34 | 0 | switch (mf.GetPolicyStatus(cmPolicies::CMP0121)) { |
35 | 0 | case cmPolicies::WARN: { |
36 | | // Default is to warn and use old behavior OLD behavior is to allow |
37 | | // compatibility, so issue a warning and use the previous behavior. |
38 | 0 | mf.IssuePolicyWarning( |
39 | 0 | cmPolicies::CMP0121, {}, |
40 | 0 | cmStrCat("Invalid list index \""_s, arg, "\"."_s)); |
41 | 0 | CM_FALLTHROUGH; |
42 | 0 | } |
43 | 0 | case cmPolicies::OLD: |
44 | | // OLD behavior is to allow compatibility, so just ignore the |
45 | | // situation. |
46 | 0 | break; |
47 | 0 | case cmPolicies::NEW: |
48 | 0 | return false; |
49 | 0 | } |
50 | 0 | } |
51 | | |
52 | | // Truncation is happening here, but it had always been happening here. |
53 | 0 | *idx = static_cast<int>(value); |
54 | |
|
55 | 0 | return true; |
56 | 0 | } |
57 | | |
58 | | bool GetListString(std::string& listString, std::string const& var, |
59 | | cmMakefile const& makefile) |
60 | 0 | { |
61 | | // get the old value |
62 | 0 | cmValue cacheValue = makefile.GetDefinition(var); |
63 | 0 | if (!cacheValue) { |
64 | 0 | return false; |
65 | 0 | } |
66 | 0 | listString = *cacheValue; |
67 | 0 | return true; |
68 | 0 | } |
69 | | |
70 | | cm::optional<cmList> GetList(std::string const& var, |
71 | | cmMakefile const& makefile) |
72 | 0 | { |
73 | 0 | cm::optional<cmList> list; |
74 | |
|
75 | 0 | std::string listString; |
76 | 0 | if (!GetListString(listString, var, makefile)) { |
77 | 0 | return list; |
78 | 0 | } |
79 | | // if the size of the list |
80 | 0 | if (listString.empty()) { |
81 | 0 | list.emplace(); |
82 | 0 | return list; |
83 | 0 | } |
84 | | // expand the variable into a list |
85 | 0 | list.emplace(listString, cmList::EmptyElements::Yes); |
86 | | // if no empty elements then just return |
87 | 0 | if (!cm::contains(*list, std::string())) { |
88 | 0 | return list; |
89 | 0 | } |
90 | 0 | return list; |
91 | 0 | } |
92 | | |
93 | | bool HandleLengthCommand(std::vector<std::string> const& args, |
94 | | cmExecutionStatus& status) |
95 | 0 | { |
96 | 0 | if (args.size() != 3) { |
97 | 0 | status.SetError("sub-command LENGTH requires two arguments."); |
98 | 0 | return false; |
99 | 0 | } |
100 | | |
101 | 0 | std::string const& listName = args[1]; |
102 | 0 | std::string const& variableName = args.back(); |
103 | |
|
104 | 0 | auto list = GetList(listName, status.GetMakefile()); |
105 | 0 | status.GetMakefile().AddDefinition(variableName, |
106 | 0 | std::to_string(list ? list->size() : 0)); |
107 | |
|
108 | 0 | return true; |
109 | 0 | } |
110 | | |
111 | | bool HandleGetCommand(std::vector<std::string> const& args, |
112 | | cmExecutionStatus& status) |
113 | 0 | { |
114 | 0 | if (args.size() < 4) { |
115 | 0 | status.SetError("sub-command GET requires at least three arguments."); |
116 | 0 | return false; |
117 | 0 | } |
118 | | |
119 | 0 | std::string const& listName = args[1]; |
120 | 0 | std::string const& variableName = args.back(); |
121 | | // expand the variable |
122 | 0 | auto list = GetList(listName, status.GetMakefile()); |
123 | 0 | if (!list) { |
124 | 0 | status.GetMakefile().AddDefinition(variableName, "NOTFOUND"); |
125 | 0 | return true; |
126 | 0 | } |
127 | | // FIXME: Add policy to make non-existing lists an error like empty lists. |
128 | 0 | if (list->empty()) { |
129 | 0 | status.SetError("GET given empty list"); |
130 | 0 | return false; |
131 | 0 | } |
132 | | |
133 | 0 | std::vector<int> indexes; |
134 | 0 | for (std::size_t cc = 2; cc < args.size() - 1; cc++) { |
135 | 0 | int index; |
136 | 0 | if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { |
137 | 0 | status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); |
138 | 0 | return false; |
139 | 0 | } |
140 | 0 | indexes.push_back(index); |
141 | 0 | } |
142 | | |
143 | 0 | try { |
144 | 0 | auto values = list->get_items(indexes.begin(), indexes.end()); |
145 | 0 | status.GetMakefile().AddDefinition(variableName, values.to_string()); |
146 | 0 | return true; |
147 | 0 | } catch (std::out_of_range& e) { |
148 | 0 | status.SetError(e.what()); |
149 | 0 | return false; |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | bool HandleAppendCommand(std::vector<std::string> const& args, |
154 | | cmExecutionStatus& status) |
155 | 0 | { |
156 | 0 | assert(args.size() >= 2); |
157 | | |
158 | | // Skip if nothing to append. |
159 | 0 | if (args.size() < 3) { |
160 | 0 | return true; |
161 | 0 | } |
162 | | |
163 | 0 | cmMakefile& makefile = status.GetMakefile(); |
164 | 0 | std::string const& listName = args[1]; |
165 | | // expand the variable |
166 | 0 | std::string listString; |
167 | 0 | GetListString(listString, listName, makefile); |
168 | |
|
169 | 0 | makefile.AddDefinition( |
170 | 0 | listName, cmList::append(listString, args.begin() + 2, args.end())); |
171 | 0 | return true; |
172 | 0 | } |
173 | | |
174 | | bool HandlePrependCommand(std::vector<std::string> const& args, |
175 | | cmExecutionStatus& status) |
176 | 0 | { |
177 | 0 | assert(args.size() >= 2); |
178 | | |
179 | | // Skip if nothing to prepend. |
180 | 0 | if (args.size() < 3) { |
181 | 0 | return true; |
182 | 0 | } |
183 | | |
184 | 0 | cmMakefile& makefile = status.GetMakefile(); |
185 | 0 | std::string const& listName = args[1]; |
186 | | // expand the variable |
187 | 0 | std::string listString; |
188 | 0 | GetListString(listString, listName, makefile); |
189 | |
|
190 | 0 | makefile.AddDefinition( |
191 | 0 | listName, cmList::prepend(listString, args.begin() + 2, args.end())); |
192 | 0 | return true; |
193 | 0 | } |
194 | | |
195 | | bool HandlePopBackCommand(std::vector<std::string> const& args, |
196 | | cmExecutionStatus& status) |
197 | 0 | { |
198 | 0 | assert(args.size() >= 2); |
199 | |
|
200 | 0 | cmMakefile& makefile = status.GetMakefile(); |
201 | 0 | auto ai = args.cbegin(); |
202 | 0 | ++ai; // Skip subcommand name |
203 | 0 | std::string const& listName = *ai++; |
204 | 0 | auto list = GetList(listName, makefile); |
205 | |
|
206 | 0 | if (!list) { |
207 | | // Can't get the list definition... undefine any vars given after. |
208 | 0 | for (; ai != args.cend(); ++ai) { |
209 | 0 | makefile.RemoveDefinition(*ai); |
210 | 0 | } |
211 | 0 | return true; |
212 | 0 | } |
213 | | |
214 | 0 | if (!list->empty()) { |
215 | 0 | if (ai == args.cend()) { |
216 | | // No variables are given... Just remove one element. |
217 | 0 | list->pop_back(); |
218 | 0 | } else { |
219 | | // Ok, assign elements to be removed to the given variables |
220 | 0 | for (; !list->empty() && ai != args.cend(); ++ai) { |
221 | 0 | assert(!ai->empty()); |
222 | 0 | makefile.AddDefinition(*ai, list->back()); |
223 | 0 | list->pop_back(); |
224 | 0 | } |
225 | | // Undefine the rest variables if the list gets empty earlier... |
226 | 0 | for (; ai != args.cend(); ++ai) { |
227 | 0 | makefile.RemoveDefinition(*ai); |
228 | 0 | } |
229 | 0 | } |
230 | |
|
231 | 0 | makefile.AddDefinition(listName, list->to_string()); |
232 | |
|
233 | 0 | } else if (ai != |
234 | 0 | args.cend()) { // The list is empty, but some args were given |
235 | | // Need to *undefine* 'em all, cuz there are no items to assign... |
236 | 0 | for (; ai != args.cend(); ++ai) { |
237 | 0 | makefile.RemoveDefinition(*ai); |
238 | 0 | } |
239 | 0 | } |
240 | |
|
241 | 0 | return true; |
242 | 0 | } |
243 | | |
244 | | bool HandlePopFrontCommand(std::vector<std::string> const& args, |
245 | | cmExecutionStatus& status) |
246 | 0 | { |
247 | 0 | assert(args.size() >= 2); |
248 | |
|
249 | 0 | cmMakefile& makefile = status.GetMakefile(); |
250 | 0 | auto ai = args.cbegin(); |
251 | 0 | ++ai; // Skip subcommand name |
252 | 0 | std::string const& listName = *ai++; |
253 | 0 | auto list = GetList(listName, makefile); |
254 | |
|
255 | 0 | if (!list) { |
256 | | // Can't get the list definition... undefine any vars given after. |
257 | 0 | for (; ai != args.cend(); ++ai) { |
258 | 0 | makefile.RemoveDefinition(*ai); |
259 | 0 | } |
260 | 0 | return true; |
261 | 0 | } |
262 | | |
263 | 0 | if (!list->empty()) { |
264 | 0 | if (ai == args.cend()) { |
265 | | // No variables are given... Just remove one element. |
266 | 0 | list->pop_front(); |
267 | 0 | } else { |
268 | | // Ok, assign elements to be removed to the given variables |
269 | 0 | auto vi = list->begin(); |
270 | 0 | for (; vi != list->end() && ai != args.cend(); ++ai, ++vi) { |
271 | 0 | assert(!ai->empty()); |
272 | 0 | makefile.AddDefinition(*ai, *vi); |
273 | 0 | } |
274 | 0 | list->erase(list->begin(), vi); |
275 | | // Undefine the rest variables if the list gets empty earlier... |
276 | 0 | for (; ai != args.cend(); ++ai) { |
277 | 0 | makefile.RemoveDefinition(*ai); |
278 | 0 | } |
279 | 0 | } |
280 | |
|
281 | 0 | makefile.AddDefinition(listName, list->to_string()); |
282 | |
|
283 | 0 | } else if (ai != |
284 | 0 | args.cend()) { // The list is empty, but some args were given |
285 | | // Need to *undefine* 'em all, cuz there are no items to assign... |
286 | 0 | for (; ai != args.cend(); ++ai) { |
287 | 0 | makefile.RemoveDefinition(*ai); |
288 | 0 | } |
289 | 0 | } |
290 | |
|
291 | 0 | return true; |
292 | 0 | } |
293 | | |
294 | | bool HandleFindCommand(std::vector<std::string> const& args, |
295 | | cmExecutionStatus& status) |
296 | 0 | { |
297 | 0 | if (args.size() != 4) { |
298 | 0 | status.SetError("sub-command FIND requires three arguments."); |
299 | 0 | return false; |
300 | 0 | } |
301 | | |
302 | 0 | std::string const& listName = args[1]; |
303 | 0 | std::string const& variableName = args.back(); |
304 | | // expand the variable |
305 | 0 | auto list = GetList(listName, status.GetMakefile()); |
306 | |
|
307 | 0 | if (!list) { |
308 | 0 | status.GetMakefile().AddDefinition(variableName, "-1"); |
309 | 0 | return true; |
310 | 0 | } |
311 | | |
312 | 0 | auto index = list->find(args[2]); |
313 | 0 | status.GetMakefile().AddDefinition( |
314 | 0 | variableName, index == cmList::npos ? "-1" : std::to_string(index)); |
315 | 0 | return true; |
316 | 0 | } |
317 | | |
318 | | bool HandleInsertCommand(std::vector<std::string> const& args, |
319 | | cmExecutionStatus& status) |
320 | 0 | { |
321 | 0 | if (args.size() < 4) { |
322 | 0 | status.SetError("sub-command INSERT requires at least three arguments."); |
323 | 0 | return false; |
324 | 0 | } |
325 | | |
326 | 0 | std::string const& listName = args[1]; |
327 | | |
328 | | // expand the variable |
329 | 0 | int index; |
330 | 0 | if (!GetIndexArg(args[2], &index, status.GetMakefile())) { |
331 | 0 | status.SetError(cmStrCat("index: ", args[2], " is not a valid index")); |
332 | 0 | return false; |
333 | 0 | } |
334 | 0 | auto list = GetList(listName, status.GetMakefile()); |
335 | 0 | if (!list) { |
336 | 0 | list = cmList{}; |
337 | 0 | } |
338 | |
|
339 | 0 | try { |
340 | 0 | list->insert_items(index, args.begin() + 3, args.end(), |
341 | 0 | cmList::ExpandElements::No, cmList::EmptyElements::Yes); |
342 | 0 | status.GetMakefile().AddDefinition(listName, list->to_string()); |
343 | 0 | return true; |
344 | 0 | } catch (std::out_of_range& e) { |
345 | 0 | status.SetError(e.what()); |
346 | 0 | return false; |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | bool HandleJoinCommand(std::vector<std::string> const& args, |
351 | | cmExecutionStatus& status) |
352 | 0 | { |
353 | 0 | if (args.size() != 4) { |
354 | 0 | status.SetError(cmStrCat("sub-command JOIN requires three arguments (", |
355 | 0 | args.size() - 1, " found).")); |
356 | 0 | return false; |
357 | 0 | } |
358 | | |
359 | 0 | std::string const& listName = args[1]; |
360 | 0 | std::string const& glue = args[2]; |
361 | 0 | std::string const& variableName = args[3]; |
362 | | |
363 | | // expand the variable |
364 | 0 | auto list = GetList(listName, status.GetMakefile()); |
365 | |
|
366 | 0 | if (!list) { |
367 | 0 | status.GetMakefile().AddDefinition(variableName, ""); |
368 | 0 | return true; |
369 | 0 | } |
370 | | |
371 | 0 | status.GetMakefile().AddDefinition(variableName, list->join(glue)); |
372 | 0 | return true; |
373 | 0 | } |
374 | | |
375 | | bool HandleRemoveItemCommand(std::vector<std::string> const& args, |
376 | | cmExecutionStatus& status) |
377 | 0 | { |
378 | 0 | assert(args.size() >= 2); |
379 | |
|
380 | 0 | if (args.size() == 2) { |
381 | 0 | return true; |
382 | 0 | } |
383 | | |
384 | 0 | std::string const& listName = args[1]; |
385 | | // expand the variable |
386 | 0 | auto list = GetList(listName, status.GetMakefile()); |
387 | |
|
388 | 0 | if (!list) { |
389 | 0 | return true; |
390 | 0 | } |
391 | | |
392 | 0 | status.GetMakefile().AddDefinition( |
393 | 0 | listName, list->remove_items(args.begin() + 2, args.end()).to_string()); |
394 | 0 | return true; |
395 | 0 | } |
396 | | |
397 | | bool HandleReverseCommand(std::vector<std::string> const& args, |
398 | | cmExecutionStatus& status) |
399 | 0 | { |
400 | 0 | assert(args.size() >= 2); |
401 | 0 | if (args.size() > 2) { |
402 | 0 | status.SetError("sub-command REVERSE only takes one argument."); |
403 | 0 | return false; |
404 | 0 | } |
405 | | |
406 | 0 | std::string const& listName = args[1]; |
407 | | // expand the variable |
408 | 0 | auto list = GetList(listName, status.GetMakefile()); |
409 | |
|
410 | 0 | if (!list) { |
411 | 0 | return true; |
412 | 0 | } |
413 | | |
414 | 0 | status.GetMakefile().AddDefinition(listName, list->reverse().to_string()); |
415 | 0 | return true; |
416 | 0 | } |
417 | | |
418 | | bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args, |
419 | | cmExecutionStatus& status) |
420 | 0 | { |
421 | 0 | assert(args.size() >= 2); |
422 | 0 | if (args.size() > 2) { |
423 | 0 | status.SetError("sub-command REMOVE_DUPLICATES only takes one argument."); |
424 | 0 | return false; |
425 | 0 | } |
426 | | |
427 | 0 | std::string const& listName = args[1]; |
428 | | // expand the variable |
429 | 0 | auto list = GetList(listName, status.GetMakefile()); |
430 | |
|
431 | 0 | if (!list) { |
432 | 0 | return true; |
433 | 0 | } |
434 | | |
435 | 0 | status.GetMakefile().AddDefinition(listName, |
436 | 0 | list->remove_duplicates().to_string()); |
437 | 0 | return true; |
438 | 0 | } |
439 | | |
440 | | bool HandleTransformCommand(std::vector<std::string> const& args, |
441 | | cmExecutionStatus& status) |
442 | 0 | { |
443 | 0 | if (args.size() < 3) { |
444 | 0 | status.SetError( |
445 | 0 | "sub-command TRANSFORM requires an action to be specified."); |
446 | 0 | return false; |
447 | 0 | } |
448 | | |
449 | | // Descriptor of action |
450 | | // Action: enum value identifying action |
451 | | // Arity: number of arguments required for the action |
452 | 0 | struct ActionDescriptor |
453 | 0 | { |
454 | 0 | ActionDescriptor(std::string name) |
455 | 0 | : Name(std::move(name)) |
456 | 0 | { |
457 | 0 | } |
458 | 0 | ActionDescriptor(std::string name, cmList::TransformAction action, |
459 | 0 | int arity) |
460 | 0 | : Name(std::move(name)) |
461 | 0 | , Action(action) |
462 | 0 | , Arity(arity) |
463 | 0 | { |
464 | 0 | } |
465 | |
|
466 | 0 | operator std::string const&() const { return this->Name; } |
467 | |
|
468 | 0 | std::string Name; |
469 | 0 | cmList::TransformAction Action; |
470 | 0 | int Arity = 0; |
471 | 0 | }; |
472 | | |
473 | | // Build a set of supported actions. |
474 | 0 | std::set<ActionDescriptor, |
475 | 0 | std::function<bool(std::string const&, std::string const&)>> |
476 | 0 | descriptors{ { { "APPEND", cmList::TransformAction::APPEND, 1 }, |
477 | 0 | { "PREPEND", cmList::TransformAction::PREPEND, 1 }, |
478 | 0 | { "TOUPPER", cmList::TransformAction::TOUPPER, 0 }, |
479 | 0 | { "TOLOWER", cmList::TransformAction::TOLOWER, 0 }, |
480 | 0 | { "STRIP", cmList::TransformAction::STRIP, 0 }, |
481 | 0 | { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 }, |
482 | 0 | { "REPLACE", cmList::TransformAction::REPLACE, 2 }, |
483 | 0 | { "APPLY", cmList::TransformAction::APPLY, 1 } }, |
484 | 0 | [](std::string const& x, std::string const& y) { |
485 | 0 | return x < y; |
486 | 0 | } }; |
487 | |
|
488 | 0 | std::string const& listName = args[1]; |
489 | | |
490 | | // Parse all possible function parameters |
491 | 0 | using size_type = std::vector<std::string>::size_type; |
492 | 0 | size_type index = 2; |
493 | |
|
494 | 0 | auto descriptor = descriptors.find(args[index]); |
495 | |
|
496 | 0 | if (descriptor == descriptors.end()) { |
497 | 0 | status.SetError( |
498 | 0 | cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action.")); |
499 | 0 | return false; |
500 | 0 | } |
501 | | |
502 | | // Action arguments |
503 | 0 | index += 1; |
504 | 0 | if (args.size() < index + descriptor->Arity) { |
505 | 0 | status.SetError(cmStrCat("sub-command TRANSFORM, action ", |
506 | 0 | descriptor->Name, " expects ", descriptor->Arity, |
507 | 0 | " argument(s).")); |
508 | 0 | return false; |
509 | 0 | } |
510 | | |
511 | 0 | std::vector<std::string> arguments; |
512 | 0 | index += descriptor->Arity; |
513 | 0 | if (descriptor->Arity > 0) { |
514 | 0 | arguments = |
515 | 0 | std::vector<std::string>(args.begin() + 3, args.begin() + index); |
516 | 0 | } |
517 | |
|
518 | 0 | std::string const REGEX{ "REGEX" }; |
519 | 0 | std::string const AT{ "AT" }; |
520 | 0 | std::string const FOR{ "FOR" }; |
521 | 0 | std::string const PREDICATE{ "PREDICATE" }; |
522 | 0 | std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; |
523 | 0 | std::unique_ptr<cmList::TransformSelector> selector; |
524 | 0 | std::string outputName = listName; |
525 | |
|
526 | 0 | try { |
527 | | // handle optional arguments |
528 | 0 | while (args.size() > index) { |
529 | 0 | if ((args[index] == REGEX || args[index] == AT || args[index] == FOR || |
530 | 0 | args[index] == PREDICATE) && |
531 | 0 | selector) { |
532 | 0 | status.SetError( |
533 | 0 | cmStrCat("sub-command TRANSFORM, selector already specified (", |
534 | 0 | selector->GetTag(), ").")); |
535 | |
|
536 | 0 | return false; |
537 | 0 | } |
538 | | |
539 | | // REGEX selector |
540 | 0 | if (args[index] == REGEX) { |
541 | 0 | if (args.size() == ++index) { |
542 | 0 | status.SetError("sub-command TRANSFORM, selector REGEX expects " |
543 | 0 | "'regular expression' argument."); |
544 | 0 | return false; |
545 | 0 | } |
546 | | |
547 | 0 | selector = |
548 | 0 | cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( |
549 | 0 | args[index]); |
550 | |
|
551 | 0 | index += 1; |
552 | 0 | continue; |
553 | 0 | } |
554 | | |
555 | | // AT selector |
556 | 0 | if (args[index] == AT) { |
557 | | // get all specified indexes |
558 | 0 | std::vector<cmList::index_type> indexes; |
559 | 0 | while (args.size() > ++index) { |
560 | 0 | std::size_t pos; |
561 | 0 | int value; |
562 | |
|
563 | 0 | try { |
564 | 0 | value = std::stoi(args[index], &pos); |
565 | 0 | if (pos != args[index].length()) { |
566 | | // this is not a number, stop processing |
567 | 0 | break; |
568 | 0 | } |
569 | 0 | indexes.push_back(value); |
570 | 0 | } catch (std::invalid_argument const&) { |
571 | | // this is not a number, stop processing |
572 | 0 | break; |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | 0 | if (indexes.empty()) { |
577 | 0 | status.SetError( |
578 | 0 | "sub-command TRANSFORM, selector AT expects at least one " |
579 | 0 | "numeric value."); |
580 | 0 | return false; |
581 | 0 | } |
582 | | |
583 | 0 | selector = |
584 | 0 | cmList::TransformSelector::New<cmList::TransformSelector::AT>( |
585 | 0 | std::move(indexes)); |
586 | |
|
587 | 0 | continue; |
588 | 0 | } |
589 | | |
590 | | // FOR selector |
591 | 0 | if (args[index] == FOR) { |
592 | 0 | if (args.size() <= ++index + 1) { |
593 | 0 | status.SetError( |
594 | 0 | "sub-command TRANSFORM, selector FOR expects, at least," |
595 | 0 | " two arguments."); |
596 | 0 | return false; |
597 | 0 | } |
598 | | |
599 | 0 | cmList::index_type start = 0; |
600 | 0 | cmList::index_type stop = 0; |
601 | 0 | cmList::index_type step = 1; |
602 | 0 | bool valid = true; |
603 | 0 | try { |
604 | 0 | std::size_t pos; |
605 | |
|
606 | 0 | start = std::stoi(args[index], &pos); |
607 | 0 | if (pos != args[index].length()) { |
608 | | // this is not a number |
609 | 0 | valid = false; |
610 | 0 | } else { |
611 | 0 | stop = std::stoi(args[++index], &pos); |
612 | 0 | if (pos != args[index].length()) { |
613 | | // this is not a number |
614 | 0 | valid = false; |
615 | 0 | } |
616 | 0 | } |
617 | 0 | } catch (std::invalid_argument const&) { |
618 | | // this is not numbers |
619 | 0 | valid = false; |
620 | 0 | } |
621 | 0 | if (!valid) { |
622 | 0 | status.SetError("sub-command TRANSFORM, selector FOR expects, " |
623 | 0 | "at least, two numeric values."); |
624 | 0 | return false; |
625 | 0 | } |
626 | | // try to read a third numeric value for step |
627 | 0 | if (args.size() > ++index) { |
628 | 0 | try { |
629 | 0 | std::size_t pos; |
630 | |
|
631 | 0 | step = std::stoi(args[index], &pos); |
632 | 0 | if (pos != args[index].length()) { |
633 | | // this is not a number |
634 | 0 | step = 1; |
635 | 0 | } else { |
636 | 0 | index += 1; |
637 | 0 | } |
638 | 0 | } catch (std::invalid_argument const&) { |
639 | | // this is not number, ignore exception |
640 | 0 | } |
641 | 0 | } |
642 | |
|
643 | 0 | if (step <= 0) { |
644 | 0 | status.SetError("sub-command TRANSFORM, selector FOR expects " |
645 | 0 | "positive numeric value for <step>."); |
646 | 0 | return false; |
647 | 0 | } |
648 | | |
649 | 0 | selector = |
650 | 0 | cmList::TransformSelector::New<cmList::TransformSelector::FOR>( |
651 | 0 | { start, stop, step }); |
652 | |
|
653 | 0 | continue; |
654 | 0 | } |
655 | | |
656 | | // PREDICATE selector |
657 | 0 | if (args[index] == PREDICATE) { |
658 | 0 | if (args.size() == ++index) { |
659 | 0 | status.SetError("sub-command TRANSFORM, selector PREDICATE expects " |
660 | 0 | "'function name' argument."); |
661 | 0 | return false; |
662 | 0 | } |
663 | | |
664 | 0 | selector = cmList::TransformSelector::NewPREDICATE( |
665 | 0 | args[index], status.GetMakefile()); |
666 | |
|
667 | 0 | index += 1; |
668 | 0 | continue; |
669 | 0 | } |
670 | | |
671 | | // output variable |
672 | 0 | if (args[index] == OUTPUT_VARIABLE) { |
673 | 0 | if (args.size() == ++index) { |
674 | 0 | status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " |
675 | 0 | "expects variable name argument."); |
676 | 0 | return false; |
677 | 0 | } |
678 | | |
679 | 0 | outputName = args[index++]; |
680 | 0 | continue; |
681 | 0 | } |
682 | | |
683 | 0 | status.SetError(cmStrCat("sub-command TRANSFORM, '", |
684 | 0 | cmJoin(cmMakeRange(args).advance(index), " "), |
685 | 0 | "': unexpected argument(s).")); |
686 | 0 | return false; |
687 | 0 | } |
688 | | |
689 | | // expand the list variable |
690 | 0 | auto list = GetList(listName, status.GetMakefile()); |
691 | |
|
692 | 0 | if (!list) { |
693 | 0 | status.GetMakefile().AddDefinition(outputName, ""); |
694 | 0 | return true; |
695 | 0 | } |
696 | | |
697 | 0 | if (!selector) { |
698 | 0 | selector = cmList::TransformSelector::New(); |
699 | 0 | } |
700 | 0 | selector->Makefile = &status.GetMakefile(); |
701 | |
|
702 | 0 | if (descriptor->Action == cmList::TransformAction::APPLY) { |
703 | 0 | list->transform(descriptor->Action, arguments.front(), |
704 | 0 | status.GetMakefile(), std::move(selector)); |
705 | 0 | } else { |
706 | 0 | list->transform(descriptor->Action, arguments, std::move(selector)); |
707 | 0 | } |
708 | 0 | status.GetMakefile().AddDefinition(outputName, list->to_string()); |
709 | 0 | return true; |
710 | 0 | } catch (cmList::transform_error& e) { |
711 | 0 | status.SetError(e.what()); |
712 | 0 | return false; |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | bool HandleSortCommand(std::vector<std::string> const& args, |
717 | | cmExecutionStatus& status) |
718 | 0 | { |
719 | 0 | assert(args.size() >= 2); |
720 | 0 | if (args.size() > 8) { |
721 | 0 | status.SetError("sub-command SORT only takes up to six arguments."); |
722 | 0 | return false; |
723 | 0 | } |
724 | | |
725 | 0 | using SortConfig = cmList::SortConfiguration; |
726 | 0 | SortConfig sortConfig; |
727 | 0 | bool hasComparator = false; |
728 | |
|
729 | 0 | size_t argumentIndex = 2; |
730 | 0 | std::string const messageHint = "sub-command SORT "; |
731 | |
|
732 | 0 | while (argumentIndex < args.size()) { |
733 | 0 | std::string const& option = args[argumentIndex++]; |
734 | 0 | if (option == "COMPARE") { |
735 | 0 | if (hasComparator) { |
736 | 0 | status.SetError(cmStrCat(messageHint, |
737 | 0 | "option \"COMPARE\" is incompatible " |
738 | 0 | "with \"COMPARATOR\".")); |
739 | 0 | return false; |
740 | 0 | } |
741 | 0 | if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { |
742 | 0 | std::string error = cmStrCat(messageHint, "option \"", option, |
743 | 0 | "\" has been specified multiple times."); |
744 | 0 | status.SetError(error); |
745 | 0 | return false; |
746 | 0 | } |
747 | 0 | if (argumentIndex < args.size()) { |
748 | 0 | std::string const& argument = args[argumentIndex++]; |
749 | 0 | if (argument == "STRING") { |
750 | 0 | sortConfig.Compare = SortConfig::CompareMethod::STRING; |
751 | 0 | } else if (argument == "FILE_BASENAME") { |
752 | 0 | sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; |
753 | 0 | } else if (argument == "NATURAL") { |
754 | 0 | sortConfig.Compare = SortConfig::CompareMethod::NATURAL; |
755 | 0 | } else { |
756 | 0 | std::string error = |
757 | 0 | cmStrCat(messageHint, "value \"", argument, "\" for option \"", |
758 | 0 | option, "\" is invalid."); |
759 | 0 | status.SetError(error); |
760 | 0 | return false; |
761 | 0 | } |
762 | 0 | } else { |
763 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
764 | 0 | option, "\".")); |
765 | 0 | return false; |
766 | 0 | } |
767 | 0 | } else if (option == "CASE") { |
768 | 0 | if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { |
769 | 0 | status.SetError(cmStrCat(messageHint, "option \"", option, |
770 | 0 | "\" has been specified multiple times.")); |
771 | 0 | return false; |
772 | 0 | } |
773 | 0 | if (argumentIndex < args.size()) { |
774 | 0 | std::string const& argument = args[argumentIndex++]; |
775 | 0 | if (argument == "SENSITIVE") { |
776 | 0 | sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; |
777 | 0 | } else if (argument == "INSENSITIVE") { |
778 | 0 | sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; |
779 | 0 | } else { |
780 | 0 | status.SetError(cmStrCat(messageHint, "value \"", argument, |
781 | 0 | "\" for option \"", option, |
782 | 0 | "\" is invalid.")); |
783 | 0 | return false; |
784 | 0 | } |
785 | 0 | } else { |
786 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
787 | 0 | option, "\".")); |
788 | 0 | return false; |
789 | 0 | } |
790 | 0 | } else if (option == "ORDER") { |
791 | |
|
792 | 0 | if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { |
793 | 0 | status.SetError(cmStrCat(messageHint, "option \"", option, |
794 | 0 | "\" has been specified multiple times.")); |
795 | 0 | return false; |
796 | 0 | } |
797 | 0 | if (argumentIndex < args.size()) { |
798 | 0 | std::string const& argument = args[argumentIndex++]; |
799 | 0 | if (argument == "ASCENDING") { |
800 | 0 | sortConfig.Order = SortConfig::OrderMode::ASCENDING; |
801 | 0 | } else if (argument == "DESCENDING") { |
802 | 0 | sortConfig.Order = SortConfig::OrderMode::DESCENDING; |
803 | 0 | } else { |
804 | 0 | status.SetError(cmStrCat(messageHint, "value \"", argument, |
805 | 0 | "\" for option \"", option, |
806 | 0 | "\" is invalid.")); |
807 | 0 | return false; |
808 | 0 | } |
809 | 0 | } else { |
810 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
811 | 0 | option, "\".")); |
812 | 0 | return false; |
813 | 0 | } |
814 | 0 | } else if (option == "COMPARATOR") { |
815 | 0 | if (hasComparator) { |
816 | 0 | status.SetError(cmStrCat(messageHint, "option \"", option, |
817 | 0 | "\" has been specified multiple times.")); |
818 | 0 | return false; |
819 | 0 | } |
820 | 0 | if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { |
821 | 0 | status.SetError(cmStrCat(messageHint, |
822 | 0 | "option \"COMPARATOR\" is incompatible " |
823 | 0 | "with \"COMPARE\".")); |
824 | 0 | return false; |
825 | 0 | } |
826 | 0 | if (argumentIndex < args.size()) { |
827 | 0 | sortConfig.ComparatorFunction = args[argumentIndex++]; |
828 | 0 | sortConfig.Compare = SortConfig::CompareMethod::COMPARATOR; |
829 | 0 | hasComparator = true; |
830 | 0 | } else { |
831 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
832 | 0 | option, "\".")); |
833 | 0 | return false; |
834 | 0 | } |
835 | 0 | } else { |
836 | 0 | status.SetError( |
837 | 0 | cmStrCat(messageHint, "option \"", option, "\" is unknown.")); |
838 | 0 | return false; |
839 | 0 | } |
840 | 0 | } |
841 | | |
842 | 0 | std::string const& listName = args[1]; |
843 | | // expand the variable |
844 | 0 | auto list = GetList(listName, status.GetMakefile()); |
845 | |
|
846 | 0 | if (!list) { |
847 | 0 | return true; |
848 | 0 | } |
849 | | |
850 | 0 | if (hasComparator) { |
851 | 0 | try { |
852 | 0 | status.GetMakefile().AddDefinition( |
853 | 0 | listName, list->sort(sortConfig, status.GetMakefile()).to_string()); |
854 | 0 | return true; |
855 | 0 | } catch (std::invalid_argument& e) { |
856 | 0 | status.SetError(e.what()); |
857 | 0 | return false; |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | 0 | status.GetMakefile().AddDefinition(listName, |
862 | 0 | list->sort(sortConfig).to_string()); |
863 | 0 | return true; |
864 | 0 | } |
865 | | |
866 | | bool HandleSublistCommand(std::vector<std::string> const& args, |
867 | | cmExecutionStatus& status) |
868 | 0 | { |
869 | 0 | if (args.size() != 5) { |
870 | 0 | status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (", |
871 | 0 | args.size() - 1, " found).")); |
872 | 0 | return false; |
873 | 0 | } |
874 | | |
875 | 0 | std::string const& listName = args[1]; |
876 | 0 | std::string const& variableName = args.back(); |
877 | | |
878 | | // expand the variable |
879 | 0 | auto list = GetList(listName, status.GetMakefile()); |
880 | |
|
881 | 0 | if (!list || list->empty()) { |
882 | 0 | status.GetMakefile().AddDefinition(variableName, ""); |
883 | 0 | return true; |
884 | 0 | } |
885 | | |
886 | 0 | int start; |
887 | 0 | int length; |
888 | 0 | if (!GetIndexArg(args[2], &start, status.GetMakefile())) { |
889 | 0 | status.SetError(cmStrCat("index: ", args[2], " is not a valid index")); |
890 | 0 | return false; |
891 | 0 | } |
892 | 0 | if (!GetIndexArg(args[3], &length, status.GetMakefile())) { |
893 | 0 | status.SetError(cmStrCat("index: ", args[3], " is not a valid index")); |
894 | 0 | return false; |
895 | 0 | } |
896 | | |
897 | 0 | if (start < 0) { |
898 | 0 | status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ", |
899 | 0 | list->size() - 1)); |
900 | 0 | return false; |
901 | 0 | } |
902 | 0 | if (length < -1) { |
903 | 0 | status.SetError(cmStrCat("length: ", length, " should be -1 or greater")); |
904 | 0 | return false; |
905 | 0 | } |
906 | | |
907 | 0 | using size_type = cmList::size_type; |
908 | |
|
909 | 0 | try { |
910 | 0 | auto sublist = list->sublist(static_cast<size_type>(start), |
911 | 0 | static_cast<size_type>(length)); |
912 | 0 | status.GetMakefile().AddDefinition(variableName, sublist.to_string()); |
913 | 0 | return true; |
914 | 0 | } catch (std::out_of_range& e) { |
915 | 0 | status.SetError(e.what()); |
916 | 0 | return false; |
917 | 0 | } |
918 | 0 | } |
919 | | |
920 | | bool HandleRemoveAtCommand(std::vector<std::string> const& args, |
921 | | cmExecutionStatus& status) |
922 | 0 | { |
923 | 0 | if (args.size() < 3) { |
924 | 0 | status.SetError("sub-command REMOVE_AT requires at least " |
925 | 0 | "two arguments."); |
926 | 0 | return false; |
927 | 0 | } |
928 | | |
929 | 0 | std::string const& listName = args[1]; |
930 | | // expand the variable |
931 | 0 | auto list = GetList(listName, status.GetMakefile()); |
932 | |
|
933 | 0 | if (!list || list->empty()) { |
934 | 0 | std::ostringstream str; |
935 | 0 | str << "index: "; |
936 | 0 | for (size_t i = 1; i < args.size(); ++i) { |
937 | 0 | str << args[i]; |
938 | 0 | if (i != args.size() - 1) { |
939 | 0 | str << ", "; |
940 | 0 | } |
941 | 0 | } |
942 | 0 | str << " out of range (0, 0)"; |
943 | 0 | status.SetError(str.str()); |
944 | 0 | return false; |
945 | 0 | } |
946 | | |
947 | 0 | size_t cc; |
948 | 0 | std::vector<cmList::index_type> removed; |
949 | 0 | for (cc = 2; cc < args.size(); ++cc) { |
950 | 0 | int index; |
951 | 0 | if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { |
952 | 0 | status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); |
953 | 0 | return false; |
954 | 0 | } |
955 | 0 | removed.push_back(index); |
956 | 0 | } |
957 | | |
958 | 0 | try { |
959 | 0 | status.GetMakefile().AddDefinition( |
960 | 0 | listName, |
961 | 0 | list->remove_items(removed.begin(), removed.end()).to_string()); |
962 | 0 | return true; |
963 | 0 | } catch (std::out_of_range& e) { |
964 | 0 | status.SetError(e.what()); |
965 | 0 | return false; |
966 | 0 | } |
967 | 0 | } |
968 | | |
969 | | bool HandleFilterCommand(std::vector<std::string> const& args, |
970 | | cmExecutionStatus& status) |
971 | 0 | { |
972 | 0 | if (args.size() < 2) { |
973 | 0 | status.SetError("sub-command FILTER requires a list to be specified."); |
974 | 0 | return false; |
975 | 0 | } |
976 | | |
977 | 0 | if (args.size() < 3) { |
978 | 0 | status.SetError( |
979 | 0 | "sub-command FILTER requires an operator to be specified."); |
980 | 0 | return false; |
981 | 0 | } |
982 | | |
983 | 0 | if (args.size() < 4) { |
984 | 0 | status.SetError("sub-command FILTER requires a mode to be specified."); |
985 | 0 | return false; |
986 | 0 | } |
987 | | |
988 | 0 | std::string const& op = args[2]; |
989 | 0 | cmList::FilterMode filterMode; |
990 | 0 | if (op == "INCLUDE") { |
991 | 0 | filterMode = cmList::FilterMode::INCLUDE; |
992 | 0 | } else if (op == "EXCLUDE") { |
993 | 0 | filterMode = cmList::FilterMode::EXCLUDE; |
994 | 0 | } else { |
995 | 0 | status.SetError("sub-command FILTER does not recognize operator " + op); |
996 | 0 | return false; |
997 | 0 | } |
998 | | |
999 | 0 | std::string const& listName = args[1]; |
1000 | | // expand the variable |
1001 | 0 | auto list = GetList(listName, status.GetMakefile()); |
1002 | |
|
1003 | 0 | if (!list) { |
1004 | 0 | return true; |
1005 | 0 | } |
1006 | | |
1007 | 0 | std::string const& mode = args[3]; |
1008 | 0 | if (mode == "REGEX") { |
1009 | 0 | if (args.size() != 5) { |
1010 | 0 | status.SetError("sub-command FILTER, mode REGEX " |
1011 | 0 | "requires five arguments."); |
1012 | 0 | return false; |
1013 | 0 | } |
1014 | 0 | std::string const& pattern = args[4]; |
1015 | |
|
1016 | 0 | try { |
1017 | 0 | status.GetMakefile().AddDefinition( |
1018 | 0 | listName, list->filter(pattern, filterMode).to_string()); |
1019 | 0 | return true; |
1020 | 0 | } catch (std::invalid_argument& e) { |
1021 | 0 | status.SetError(e.what()); |
1022 | 0 | return false; |
1023 | 0 | } |
1024 | 0 | } |
1025 | | |
1026 | 0 | if (mode == "PREDICATE") { |
1027 | 0 | if (args.size() != 5) { |
1028 | 0 | status.SetError("sub-command FILTER, mode PREDICATE " |
1029 | 0 | "requires five arguments."); |
1030 | 0 | return false; |
1031 | 0 | } |
1032 | 0 | std::string const& functionName = args[4]; |
1033 | |
|
1034 | 0 | try { |
1035 | 0 | status.GetMakefile().AddDefinition( |
1036 | 0 | listName, |
1037 | 0 | list->filter(functionName, filterMode, status.GetMakefile()) |
1038 | 0 | .to_string()); |
1039 | 0 | return true; |
1040 | 0 | } catch (std::invalid_argument& e) { |
1041 | 0 | status.SetError(e.what()); |
1042 | 0 | return false; |
1043 | 0 | } |
1044 | 0 | } |
1045 | | |
1046 | 0 | status.SetError("sub-command FILTER does not recognize mode " + mode); |
1047 | 0 | return false; |
1048 | 0 | } |
1049 | | } // namespace |
1050 | | |
1051 | | bool cmListCommand(std::vector<std::string> const& args, |
1052 | | cmExecutionStatus& status) |
1053 | 0 | { |
1054 | 0 | if (args.size() < 2) { |
1055 | 0 | status.SetError("must be called with at least two arguments."); |
1056 | 0 | return false; |
1057 | 0 | } |
1058 | | |
1059 | 0 | static cmSubcommandTable const subcommand{ |
1060 | 0 | { "LENGTH"_s, HandleLengthCommand }, |
1061 | 0 | { "GET"_s, HandleGetCommand }, |
1062 | 0 | { "APPEND"_s, HandleAppendCommand }, |
1063 | 0 | { "PREPEND"_s, HandlePrependCommand }, |
1064 | 0 | { "POP_BACK"_s, HandlePopBackCommand }, |
1065 | 0 | { "POP_FRONT"_s, HandlePopFrontCommand }, |
1066 | 0 | { "FIND"_s, HandleFindCommand }, |
1067 | 0 | { "INSERT"_s, HandleInsertCommand }, |
1068 | 0 | { "JOIN"_s, HandleJoinCommand }, |
1069 | 0 | { "REMOVE_AT"_s, HandleRemoveAtCommand }, |
1070 | 0 | { "REMOVE_ITEM"_s, HandleRemoveItemCommand }, |
1071 | 0 | { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand }, |
1072 | 0 | { "TRANSFORM"_s, HandleTransformCommand }, |
1073 | 0 | { "SORT"_s, HandleSortCommand }, |
1074 | 0 | { "SUBLIST"_s, HandleSublistCommand }, |
1075 | 0 | { "REVERSE"_s, HandleReverseCommand }, |
1076 | 0 | { "FILTER"_s, HandleFilterCommand }, |
1077 | 0 | }; |
1078 | |
|
1079 | 0 | return subcommand(args[0], args, status); |
1080 | 0 | } |