cmListCommand.cxx 28 KB

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