cmListCommand.cxx 29 KB

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