/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 "cmDiagnostics.h" |
20 | | #include "cmExecutionStatus.h" |
21 | | #include "cmList.h" |
22 | | #include "cmMakefile.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.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, 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 | { "APPLY", cmList::TransformAction::APPLY, 1 } }, |
486 | 0 | [](std::string const& x, std::string const& y) { |
487 | 0 | return x < y; |
488 | 0 | } }; |
489 | |
|
490 | 0 | std::string const& listName = args[1]; |
491 | | |
492 | | // Parse all possible function parameters |
493 | 0 | using size_type = std::vector<std::string>::size_type; |
494 | 0 | size_type index = 2; |
495 | |
|
496 | 0 | auto descriptor = descriptors.find(args[index]); |
497 | |
|
498 | 0 | if (descriptor == descriptors.end()) { |
499 | 0 | status.SetError( |
500 | 0 | cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action.")); |
501 | 0 | return false; |
502 | 0 | } |
503 | | |
504 | | // Action arguments |
505 | 0 | index += 1; |
506 | 0 | if (args.size() < index + descriptor->Arity) { |
507 | 0 | status.SetError(cmStrCat("sub-command TRANSFORM, action ", |
508 | 0 | descriptor->Name, " expects ", descriptor->Arity, |
509 | 0 | " argument(s).")); |
510 | 0 | return false; |
511 | 0 | } |
512 | | |
513 | 0 | std::vector<std::string> arguments; |
514 | 0 | index += descriptor->Arity; |
515 | 0 | if (descriptor->Arity > 0) { |
516 | 0 | arguments = |
517 | 0 | std::vector<std::string>(args.begin() + 3, args.begin() + index); |
518 | 0 | } |
519 | |
|
520 | 0 | std::string const REGEX{ "REGEX" }; |
521 | 0 | std::string const AT{ "AT" }; |
522 | 0 | std::string const FOR{ "FOR" }; |
523 | 0 | std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; |
524 | 0 | std::unique_ptr<cmList::TransformSelector> selector; |
525 | 0 | std::string outputName = listName; |
526 | |
|
527 | 0 | try { |
528 | | // handle optional arguments |
529 | 0 | while (args.size() > index) { |
530 | 0 | if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && |
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 | | // output variable |
657 | 0 | if (args[index] == OUTPUT_VARIABLE) { |
658 | 0 | if (args.size() == ++index) { |
659 | 0 | status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " |
660 | 0 | "expects variable name argument."); |
661 | 0 | return false; |
662 | 0 | } |
663 | | |
664 | 0 | outputName = args[index++]; |
665 | 0 | continue; |
666 | 0 | } |
667 | | |
668 | 0 | status.SetError(cmStrCat("sub-command TRANSFORM, '", |
669 | 0 | cmJoin(cmMakeRange(args).advance(index), " "), |
670 | 0 | "': unexpected argument(s).")); |
671 | 0 | return false; |
672 | 0 | } |
673 | | |
674 | | // expand the list variable |
675 | 0 | auto list = GetList(listName, status.GetMakefile()); |
676 | |
|
677 | 0 | if (!list) { |
678 | 0 | status.GetMakefile().AddDefinition(outputName, ""); |
679 | 0 | return true; |
680 | 0 | } |
681 | | |
682 | 0 | if (!selector) { |
683 | 0 | selector = cmList::TransformSelector::New(); |
684 | 0 | } |
685 | 0 | selector->Makefile = &status.GetMakefile(); |
686 | |
|
687 | 0 | if (descriptor->Action == cmList::TransformAction::APPLY) { |
688 | 0 | list->transform(descriptor->Action, arguments.front(), |
689 | 0 | status.GetMakefile(), std::move(selector)); |
690 | 0 | } else { |
691 | 0 | list->transform(descriptor->Action, arguments, std::move(selector)); |
692 | 0 | } |
693 | 0 | status.GetMakefile().AddDefinition(outputName, list->to_string()); |
694 | 0 | return true; |
695 | 0 | } catch (cmList::transform_error& e) { |
696 | 0 | status.SetError(e.what()); |
697 | 0 | return false; |
698 | 0 | } |
699 | 0 | } |
700 | | |
701 | | bool HandleSortCommand(std::vector<std::string> const& args, |
702 | | cmExecutionStatus& status) |
703 | 0 | { |
704 | 0 | assert(args.size() >= 2); |
705 | 0 | if (args.size() > 8) { |
706 | 0 | status.SetError("sub-command SORT only takes up to six arguments."); |
707 | 0 | return false; |
708 | 0 | } |
709 | | |
710 | 0 | using SortConfig = cmList::SortConfiguration; |
711 | 0 | SortConfig sortConfig; |
712 | |
|
713 | 0 | size_t argumentIndex = 2; |
714 | 0 | std::string const messageHint = "sub-command SORT "; |
715 | |
|
716 | 0 | while (argumentIndex < args.size()) { |
717 | 0 | std::string const& option = args[argumentIndex++]; |
718 | 0 | if (option == "COMPARE") { |
719 | 0 | if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { |
720 | 0 | std::string error = cmStrCat(messageHint, "option \"", option, |
721 | 0 | "\" has been specified multiple times."); |
722 | 0 | status.SetError(error); |
723 | 0 | return false; |
724 | 0 | } |
725 | 0 | if (argumentIndex < args.size()) { |
726 | 0 | std::string const& argument = args[argumentIndex++]; |
727 | 0 | if (argument == "STRING") { |
728 | 0 | sortConfig.Compare = SortConfig::CompareMethod::STRING; |
729 | 0 | } else if (argument == "FILE_BASENAME") { |
730 | 0 | sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; |
731 | 0 | } else if (argument == "NATURAL") { |
732 | 0 | sortConfig.Compare = SortConfig::CompareMethod::NATURAL; |
733 | 0 | } else { |
734 | 0 | std::string error = |
735 | 0 | cmStrCat(messageHint, "value \"", argument, "\" for option \"", |
736 | 0 | option, "\" is invalid."); |
737 | 0 | status.SetError(error); |
738 | 0 | return false; |
739 | 0 | } |
740 | 0 | } else { |
741 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
742 | 0 | option, "\".")); |
743 | 0 | return false; |
744 | 0 | } |
745 | 0 | } else if (option == "CASE") { |
746 | 0 | if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { |
747 | 0 | status.SetError(cmStrCat(messageHint, "option \"", option, |
748 | 0 | "\" has been specified multiple times.")); |
749 | 0 | return false; |
750 | 0 | } |
751 | 0 | if (argumentIndex < args.size()) { |
752 | 0 | std::string const& argument = args[argumentIndex++]; |
753 | 0 | if (argument == "SENSITIVE") { |
754 | 0 | sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; |
755 | 0 | } else if (argument == "INSENSITIVE") { |
756 | 0 | sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; |
757 | 0 | } else { |
758 | 0 | status.SetError(cmStrCat(messageHint, "value \"", argument, |
759 | 0 | "\" for option \"", option, |
760 | 0 | "\" is invalid.")); |
761 | 0 | return false; |
762 | 0 | } |
763 | 0 | } else { |
764 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
765 | 0 | option, "\".")); |
766 | 0 | return false; |
767 | 0 | } |
768 | 0 | } else if (option == "ORDER") { |
769 | |
|
770 | 0 | if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { |
771 | 0 | status.SetError(cmStrCat(messageHint, "option \"", option, |
772 | 0 | "\" has been specified multiple times.")); |
773 | 0 | return false; |
774 | 0 | } |
775 | 0 | if (argumentIndex < args.size()) { |
776 | 0 | std::string const& argument = args[argumentIndex++]; |
777 | 0 | if (argument == "ASCENDING") { |
778 | 0 | sortConfig.Order = SortConfig::OrderMode::ASCENDING; |
779 | 0 | } else if (argument == "DESCENDING") { |
780 | 0 | sortConfig.Order = SortConfig::OrderMode::DESCENDING; |
781 | 0 | } else { |
782 | 0 | status.SetError(cmStrCat(messageHint, "value \"", argument, |
783 | 0 | "\" for option \"", option, |
784 | 0 | "\" is invalid.")); |
785 | 0 | return false; |
786 | 0 | } |
787 | 0 | } else { |
788 | 0 | status.SetError(cmStrCat(messageHint, "missing argument for option \"", |
789 | 0 | option, "\".")); |
790 | 0 | return false; |
791 | 0 | } |
792 | 0 | } else { |
793 | 0 | status.SetError( |
794 | 0 | cmStrCat(messageHint, "option \"", option, "\" is unknown.")); |
795 | 0 | return false; |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | 0 | std::string const& listName = args[1]; |
800 | | // expand the variable |
801 | 0 | auto list = GetList(listName, status.GetMakefile()); |
802 | |
|
803 | 0 | if (!list) { |
804 | 0 | return true; |
805 | 0 | } |
806 | | |
807 | 0 | status.GetMakefile().AddDefinition(listName, |
808 | 0 | list->sort(sortConfig).to_string()); |
809 | 0 | return true; |
810 | 0 | } |
811 | | |
812 | | bool HandleSublistCommand(std::vector<std::string> const& args, |
813 | | cmExecutionStatus& status) |
814 | 0 | { |
815 | 0 | if (args.size() != 5) { |
816 | 0 | status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (", |
817 | 0 | args.size() - 1, " found).")); |
818 | 0 | return false; |
819 | 0 | } |
820 | | |
821 | 0 | std::string const& listName = args[1]; |
822 | 0 | std::string const& variableName = args.back(); |
823 | | |
824 | | // expand the variable |
825 | 0 | auto list = GetList(listName, status.GetMakefile()); |
826 | |
|
827 | 0 | if (!list || list->empty()) { |
828 | 0 | status.GetMakefile().AddDefinition(variableName, ""); |
829 | 0 | return true; |
830 | 0 | } |
831 | | |
832 | 0 | int start; |
833 | 0 | int length; |
834 | 0 | if (!GetIndexArg(args[2], &start, status.GetMakefile())) { |
835 | 0 | status.SetError(cmStrCat("index: ", args[2], " is not a valid index")); |
836 | 0 | return false; |
837 | 0 | } |
838 | 0 | if (!GetIndexArg(args[3], &length, status.GetMakefile())) { |
839 | 0 | status.SetError(cmStrCat("index: ", args[3], " is not a valid index")); |
840 | 0 | return false; |
841 | 0 | } |
842 | | |
843 | 0 | if (start < 0) { |
844 | 0 | status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ", |
845 | 0 | list->size() - 1)); |
846 | 0 | return false; |
847 | 0 | } |
848 | 0 | if (length < -1) { |
849 | 0 | status.SetError(cmStrCat("length: ", length, " should be -1 or greater")); |
850 | 0 | return false; |
851 | 0 | } |
852 | | |
853 | 0 | using size_type = cmList::size_type; |
854 | |
|
855 | 0 | try { |
856 | 0 | auto sublist = list->sublist(static_cast<size_type>(start), |
857 | 0 | static_cast<size_type>(length)); |
858 | 0 | status.GetMakefile().AddDefinition(variableName, sublist.to_string()); |
859 | 0 | return true; |
860 | 0 | } catch (std::out_of_range& e) { |
861 | 0 | status.SetError(e.what()); |
862 | 0 | return false; |
863 | 0 | } |
864 | 0 | } |
865 | | |
866 | | bool HandleRemoveAtCommand(std::vector<std::string> const& args, |
867 | | cmExecutionStatus& status) |
868 | 0 | { |
869 | 0 | if (args.size() < 3) { |
870 | 0 | status.SetError("sub-command REMOVE_AT requires at least " |
871 | 0 | "two arguments."); |
872 | 0 | return false; |
873 | 0 | } |
874 | | |
875 | 0 | std::string const& listName = args[1]; |
876 | | // expand the variable |
877 | 0 | auto list = GetList(listName, status.GetMakefile()); |
878 | |
|
879 | 0 | if (!list || list->empty()) { |
880 | 0 | std::ostringstream str; |
881 | 0 | str << "index: "; |
882 | 0 | for (size_t i = 1; i < args.size(); ++i) { |
883 | 0 | str << args[i]; |
884 | 0 | if (i != args.size() - 1) { |
885 | 0 | str << ", "; |
886 | 0 | } |
887 | 0 | } |
888 | 0 | str << " out of range (0, 0)"; |
889 | 0 | status.SetError(str.str()); |
890 | 0 | return false; |
891 | 0 | } |
892 | | |
893 | 0 | size_t cc; |
894 | 0 | std::vector<cmList::index_type> removed; |
895 | 0 | for (cc = 2; cc < args.size(); ++cc) { |
896 | 0 | int index; |
897 | 0 | if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { |
898 | 0 | status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); |
899 | 0 | return false; |
900 | 0 | } |
901 | 0 | removed.push_back(index); |
902 | 0 | } |
903 | | |
904 | 0 | try { |
905 | 0 | status.GetMakefile().AddDefinition( |
906 | 0 | listName, |
907 | 0 | list->remove_items(removed.begin(), removed.end()).to_string()); |
908 | 0 | return true; |
909 | 0 | } catch (std::out_of_range& e) { |
910 | 0 | status.SetError(e.what()); |
911 | 0 | return false; |
912 | 0 | } |
913 | 0 | } |
914 | | |
915 | | bool HandleFilterCommand(std::vector<std::string> const& args, |
916 | | cmExecutionStatus& status) |
917 | 0 | { |
918 | 0 | if (args.size() < 2) { |
919 | 0 | status.SetError("sub-command FILTER requires a list to be specified."); |
920 | 0 | return false; |
921 | 0 | } |
922 | | |
923 | 0 | if (args.size() < 3) { |
924 | 0 | status.SetError( |
925 | 0 | "sub-command FILTER requires an operator to be specified."); |
926 | 0 | return false; |
927 | 0 | } |
928 | | |
929 | 0 | if (args.size() < 4) { |
930 | 0 | status.SetError("sub-command FILTER requires a mode to be specified."); |
931 | 0 | return false; |
932 | 0 | } |
933 | | |
934 | 0 | std::string const& op = args[2]; |
935 | 0 | cmList::FilterMode filterMode; |
936 | 0 | if (op == "INCLUDE") { |
937 | 0 | filterMode = cmList::FilterMode::INCLUDE; |
938 | 0 | } else if (op == "EXCLUDE") { |
939 | 0 | filterMode = cmList::FilterMode::EXCLUDE; |
940 | 0 | } else { |
941 | 0 | status.SetError("sub-command FILTER does not recognize operator " + op); |
942 | 0 | return false; |
943 | 0 | } |
944 | | |
945 | 0 | std::string const& listName = args[1]; |
946 | | // expand the variable |
947 | 0 | auto list = GetList(listName, status.GetMakefile()); |
948 | |
|
949 | 0 | if (!list) { |
950 | 0 | return true; |
951 | 0 | } |
952 | | |
953 | 0 | std::string const& mode = args[3]; |
954 | 0 | if (mode != "REGEX") { |
955 | 0 | status.SetError("sub-command FILTER does not recognize mode " + mode); |
956 | 0 | return false; |
957 | 0 | } |
958 | 0 | if (args.size() != 5) { |
959 | 0 | status.SetError("sub-command FILTER, mode REGEX " |
960 | 0 | "requires five arguments."); |
961 | 0 | return false; |
962 | 0 | } |
963 | 0 | std::string const& pattern = args[4]; |
964 | |
|
965 | 0 | try { |
966 | 0 | status.GetMakefile().AddDefinition( |
967 | 0 | listName, list->filter(pattern, filterMode).to_string()); |
968 | 0 | return true; |
969 | 0 | } catch (std::invalid_argument& e) { |
970 | 0 | status.SetError(e.what()); |
971 | 0 | return false; |
972 | 0 | } |
973 | 0 | } |
974 | | } // namespace |
975 | | |
976 | | bool cmListCommand(std::vector<std::string> const& args, |
977 | | cmExecutionStatus& status) |
978 | 0 | { |
979 | 0 | if (args.size() < 2) { |
980 | 0 | status.SetError("must be called with at least two arguments."); |
981 | 0 | return false; |
982 | 0 | } |
983 | | |
984 | 0 | static cmSubcommandTable const subcommand{ |
985 | 0 | { "LENGTH"_s, HandleLengthCommand }, |
986 | 0 | { "GET"_s, HandleGetCommand }, |
987 | 0 | { "APPEND"_s, HandleAppendCommand }, |
988 | 0 | { "PREPEND"_s, HandlePrependCommand }, |
989 | 0 | { "POP_BACK"_s, HandlePopBackCommand }, |
990 | 0 | { "POP_FRONT"_s, HandlePopFrontCommand }, |
991 | 0 | { "FIND"_s, HandleFindCommand }, |
992 | 0 | { "INSERT"_s, HandleInsertCommand }, |
993 | 0 | { "JOIN"_s, HandleJoinCommand }, |
994 | 0 | { "REMOVE_AT"_s, HandleRemoveAtCommand }, |
995 | 0 | { "REMOVE_ITEM"_s, HandleRemoveItemCommand }, |
996 | 0 | { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand }, |
997 | 0 | { "TRANSFORM"_s, HandleTransformCommand }, |
998 | 0 | { "SORT"_s, HandleSortCommand }, |
999 | 0 | { "SUBLIST"_s, HandleSublistCommand }, |
1000 | 0 | { "REVERSE"_s, HandleReverseCommand }, |
1001 | 0 | { "FILTER"_s, HandleFilterCommand }, |
1002 | 0 | }; |
1003 | |
|
1004 | 0 | return subcommand(args[0], args, status); |
1005 | 0 | } |