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