cmListCommand.cxx 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491
  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 <algorithm>
  5. #include <cassert>
  6. #include <cstddef>
  7. #include <cstdio>
  8. #include <cstdlib> // required for atoi
  9. #include <functional>
  10. #include <iterator>
  11. #include <set>
  12. #include <sstream>
  13. #include <stdexcept>
  14. #include <utility>
  15. #include <vector>
  16. #include <cm/memory>
  17. #include <cmext/algorithm>
  18. #include <cmext/string_view>
  19. #include "cmsys/RegularExpression.hxx"
  20. #include "cmAlgorithms.h"
  21. #include "cmExecutionStatus.h"
  22. #include "cmGeneratorExpression.h"
  23. #include "cmMakefile.h"
  24. #include "cmMessageType.h"
  25. #include "cmPolicies.h"
  26. #include "cmProperty.h"
  27. #include "cmRange.h"
  28. #include "cmStringAlgorithms.h"
  29. #include "cmStringReplaceHelper.h"
  30. #include "cmSubcommandTable.h"
  31. #include "cmSystemTools.h"
  32. namespace {
  33. bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
  34. std::string const& listName,
  35. std::vector<std::string>& varArgsExpanded,
  36. cmExecutionStatus& status);
  37. bool GetListString(std::string& listString, const std::string& var,
  38. const cmMakefile& makefile)
  39. {
  40. // get the old value
  41. cmProp cacheValue = makefile.GetDefinition(var);
  42. if (!cacheValue) {
  43. return false;
  44. }
  45. listString = *cacheValue;
  46. return true;
  47. }
  48. bool GetList(std::vector<std::string>& list, const std::string& var,
  49. const cmMakefile& makefile)
  50. {
  51. std::string listString;
  52. if (!GetListString(listString, var, makefile)) {
  53. return false;
  54. }
  55. // if the size of the list
  56. if (listString.empty()) {
  57. return true;
  58. }
  59. // expand the variable into a list
  60. cmExpandList(listString, list, true);
  61. // if no empty elements then just return
  62. if (!cm::contains(list, std::string())) {
  63. return true;
  64. }
  65. // if we have empty elements we need to check policy CMP0007
  66. switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) {
  67. case cmPolicies::WARN: {
  68. // Default is to warn and use old behavior
  69. // OLD behavior is to allow compatibility, so recall
  70. // ExpandListArgument without the true which will remove
  71. // empty values
  72. list.clear();
  73. cmExpandList(listString, list);
  74. std::string warn =
  75. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007),
  76. " List has value = [", listString, "].");
  77. makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn);
  78. return true;
  79. }
  80. case cmPolicies::OLD:
  81. // OLD behavior is to allow compatibility, so recall
  82. // ExpandListArgument without the true which will remove
  83. // empty values
  84. list.clear();
  85. cmExpandList(listString, list);
  86. return true;
  87. case cmPolicies::NEW:
  88. return true;
  89. case cmPolicies::REQUIRED_IF_USED:
  90. case cmPolicies::REQUIRED_ALWAYS:
  91. makefile.IssueMessage(
  92. MessageType::FATAL_ERROR,
  93. cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007));
  94. return false;
  95. }
  96. return true;
  97. }
  98. bool HandleLengthCommand(std::vector<std::string> const& args,
  99. cmExecutionStatus& status)
  100. {
  101. if (args.size() != 3) {
  102. status.SetError("sub-command LENGTH requires two arguments.");
  103. return false;
  104. }
  105. const std::string& listName = args[1];
  106. const std::string& variableName = args.back();
  107. std::vector<std::string> varArgsExpanded;
  108. // do not check the return value here
  109. // if the list var is not found varArgsExpanded will have size 0
  110. // and we will return 0
  111. GetList(varArgsExpanded, listName, status.GetMakefile());
  112. size_t length = varArgsExpanded.size();
  113. char buffer[1024];
  114. sprintf(buffer, "%d", static_cast<int>(length));
  115. status.GetMakefile().AddDefinition(variableName, buffer);
  116. return true;
  117. }
  118. bool HandleGetCommand(std::vector<std::string> const& args,
  119. cmExecutionStatus& status)
  120. {
  121. if (args.size() < 4) {
  122. status.SetError("sub-command GET requires at least three arguments.");
  123. return false;
  124. }
  125. const std::string& listName = args[1];
  126. const std::string& variableName = args.back();
  127. // expand the variable
  128. std::vector<std::string> varArgsExpanded;
  129. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  130. status.GetMakefile().AddDefinition(variableName, "NOTFOUND");
  131. return true;
  132. }
  133. // FIXME: Add policy to make non-existing lists an error like empty lists.
  134. if (varArgsExpanded.empty()) {
  135. status.SetError("GET given empty list");
  136. return false;
  137. }
  138. std::string value;
  139. size_t cc;
  140. const char* sep = "";
  141. size_t nitem = varArgsExpanded.size();
  142. for (cc = 2; cc < args.size() - 1; cc++) {
  143. int item = atoi(args[cc].c_str());
  144. value += sep;
  145. sep = ";";
  146. if (item < 0) {
  147. item = static_cast<int>(nitem) + item;
  148. }
  149. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  150. status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
  151. ", ", nitem - 1, ")"));
  152. return false;
  153. }
  154. value += varArgsExpanded[item];
  155. }
  156. status.GetMakefile().AddDefinition(variableName, value);
  157. return true;
  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. // If `listString` or `args` is empty, no need to append `;`,
  173. // then index is going to be `1` and points to the end-of-string ";"
  174. auto const offset =
  175. std::string::size_type(listString.empty() || args.empty());
  176. listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";");
  177. makefile.AddDefinition(listName, listString);
  178. return true;
  179. }
  180. bool HandlePrependCommand(std::vector<std::string> const& args,
  181. cmExecutionStatus& status)
  182. {
  183. assert(args.size() >= 2);
  184. // Skip if nothing to prepend.
  185. if (args.size() < 3) {
  186. return true;
  187. }
  188. cmMakefile& makefile = status.GetMakefile();
  189. std::string const& listName = args[1];
  190. // expand the variable
  191. std::string listString;
  192. GetListString(listString, listName, makefile);
  193. // If `listString` or `args` is empty, no need to append `;`,
  194. // then `offset` is going to be `1` and points to the end-of-string ";"
  195. auto const offset =
  196. std::string::size_type(listString.empty() || args.empty());
  197. listString.insert(0,
  198. cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]);
  199. makefile.AddDefinition(listName, listString);
  200. return true;
  201. }
  202. bool HandlePopBackCommand(std::vector<std::string> const& args,
  203. cmExecutionStatus& status)
  204. {
  205. assert(args.size() >= 2);
  206. cmMakefile& makefile = status.GetMakefile();
  207. auto ai = args.cbegin();
  208. ++ai; // Skip subcommand name
  209. std::string const& listName = *ai++;
  210. std::vector<std::string> varArgsExpanded;
  211. if (!GetList(varArgsExpanded, listName, makefile)) {
  212. // Can't get the list definition... undefine any vars given after.
  213. for (; ai != args.cend(); ++ai) {
  214. makefile.RemoveDefinition(*ai);
  215. }
  216. return true;
  217. }
  218. if (!varArgsExpanded.empty()) {
  219. if (ai == args.cend()) {
  220. // No variables are given... Just remove one element.
  221. varArgsExpanded.pop_back();
  222. } else {
  223. // Ok, assign elements to be removed to the given variables
  224. for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) {
  225. assert(!ai->empty());
  226. makefile.AddDefinition(*ai, varArgsExpanded.back());
  227. varArgsExpanded.pop_back();
  228. }
  229. // Undefine the rest variables if the list gets empty earlier...
  230. for (; ai != args.cend(); ++ai) {
  231. makefile.RemoveDefinition(*ai);
  232. }
  233. }
  234. makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
  235. } else if (ai !=
  236. 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. for (; ai != args.cend(); ++ai) {
  239. makefile.RemoveDefinition(*ai);
  240. }
  241. }
  242. return true;
  243. }
  244. bool HandlePopFrontCommand(std::vector<std::string> const& args,
  245. cmExecutionStatus& status)
  246. {
  247. assert(args.size() >= 2);
  248. cmMakefile& makefile = status.GetMakefile();
  249. auto ai = args.cbegin();
  250. ++ai; // Skip subcommand name
  251. std::string const& listName = *ai++;
  252. std::vector<std::string> varArgsExpanded;
  253. if (!GetList(varArgsExpanded, listName, makefile)) {
  254. // Can't get the list definition... undefine any vars given after.
  255. for (; ai != args.cend(); ++ai) {
  256. makefile.RemoveDefinition(*ai);
  257. }
  258. return true;
  259. }
  260. if (!varArgsExpanded.empty()) {
  261. if (ai == args.cend()) {
  262. // No variables are given... Just remove one element.
  263. varArgsExpanded.erase(varArgsExpanded.begin());
  264. } else {
  265. // Ok, assign elements to be removed to the given variables
  266. auto vi = varArgsExpanded.begin();
  267. for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) {
  268. assert(!ai->empty());
  269. makefile.AddDefinition(*ai, *vi);
  270. }
  271. varArgsExpanded.erase(varArgsExpanded.begin(), vi);
  272. // Undefine the rest variables if the list gets empty earlier...
  273. for (; ai != args.cend(); ++ai) {
  274. makefile.RemoveDefinition(*ai);
  275. }
  276. }
  277. makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
  278. } else if (ai !=
  279. args.cend()) { // The list is empty, but some args were given
  280. // Need to *undefine* 'em all, cuz there are no items to assign...
  281. for (; ai != args.cend(); ++ai) {
  282. makefile.RemoveDefinition(*ai);
  283. }
  284. }
  285. return true;
  286. }
  287. bool HandleFindCommand(std::vector<std::string> const& args,
  288. cmExecutionStatus& status)
  289. {
  290. if (args.size() != 4) {
  291. status.SetError("sub-command FIND requires three arguments.");
  292. return false;
  293. }
  294. const std::string& listName = args[1];
  295. const std::string& variableName = args.back();
  296. // expand the variable
  297. std::vector<std::string> varArgsExpanded;
  298. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  299. status.GetMakefile().AddDefinition(variableName, "-1");
  300. return true;
  301. }
  302. auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
  303. if (it != varArgsExpanded.end()) {
  304. status.GetMakefile().AddDefinition(
  305. variableName,
  306. std::to_string(std::distance(varArgsExpanded.begin(), it)));
  307. return true;
  308. }
  309. status.GetMakefile().AddDefinition(variableName, "-1");
  310. return true;
  311. }
  312. bool HandleInsertCommand(std::vector<std::string> const& args,
  313. cmExecutionStatus& status)
  314. {
  315. if (args.size() < 4) {
  316. status.SetError("sub-command INSERT requires at least three arguments.");
  317. return false;
  318. }
  319. const std::string& listName = args[1];
  320. // expand the variable
  321. int item = atoi(args[2].c_str());
  322. std::vector<std::string> varArgsExpanded;
  323. if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
  324. varArgsExpanded.empty()) &&
  325. item != 0) {
  326. status.SetError(cmStrCat("index: ", item, " out of range (0, 0)"));
  327. return false;
  328. }
  329. if (!varArgsExpanded.empty()) {
  330. size_t nitem = varArgsExpanded.size();
  331. if (item < 0) {
  332. item = static_cast<int>(nitem) + item;
  333. }
  334. if (item < 0 || nitem < static_cast<size_t>(item)) {
  335. status.SetError(cmStrCat("index: ", item, " out of range (-",
  336. varArgsExpanded.size(), ", ",
  337. varArgsExpanded.size(), ")"));
  338. return false;
  339. }
  340. }
  341. varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3,
  342. args.end());
  343. std::string value = cmJoin(varArgsExpanded, ";");
  344. status.GetMakefile().AddDefinition(listName, value);
  345. return true;
  346. }
  347. bool HandleJoinCommand(std::vector<std::string> const& args,
  348. cmExecutionStatus& status)
  349. {
  350. if (args.size() != 4) {
  351. status.SetError(cmStrCat("sub-command JOIN requires three arguments (",
  352. args.size() - 1, " found)."));
  353. return false;
  354. }
  355. const std::string& listName = args[1];
  356. const std::string& glue = args[2];
  357. const std::string& variableName = args[3];
  358. // expand the variable
  359. std::vector<std::string> varArgsExpanded;
  360. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  361. status.GetMakefile().AddDefinition(variableName, "");
  362. return true;
  363. }
  364. std::string value =
  365. cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue);
  366. status.GetMakefile().AddDefinition(variableName, value);
  367. return true;
  368. }
  369. bool HandleRemoveItemCommand(std::vector<std::string> const& args,
  370. cmExecutionStatus& status)
  371. {
  372. if (args.size() < 3) {
  373. status.SetError("sub-command REMOVE_ITEM requires two or more arguments.");
  374. return false;
  375. }
  376. const std::string& listName = args[1];
  377. // expand the variable
  378. std::vector<std::string> varArgsExpanded;
  379. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  380. return true;
  381. }
  382. std::vector<std::string> remove(args.begin() + 2, args.end());
  383. std::sort(remove.begin(), remove.end());
  384. auto remEnd = std::unique(remove.begin(), remove.end());
  385. auto remBegin = remove.begin();
  386. auto argsEnd =
  387. cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd));
  388. auto argsBegin = varArgsExpanded.cbegin();
  389. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  390. status.GetMakefile().AddDefinition(listName, value);
  391. return true;
  392. }
  393. bool HandleReverseCommand(std::vector<std::string> const& args,
  394. cmExecutionStatus& status)
  395. {
  396. assert(args.size() >= 2);
  397. if (args.size() > 2) {
  398. status.SetError("sub-command REVERSE only takes one argument.");
  399. return false;
  400. }
  401. const std::string& listName = args[1];
  402. // expand the variable
  403. std::vector<std::string> varArgsExpanded;
  404. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  405. return true;
  406. }
  407. std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";");
  408. status.GetMakefile().AddDefinition(listName, value);
  409. return true;
  410. }
  411. bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args,
  412. cmExecutionStatus& status)
  413. {
  414. assert(args.size() >= 2);
  415. if (args.size() > 2) {
  416. status.SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
  417. return false;
  418. }
  419. const std::string& listName = args[1];
  420. // expand the variable
  421. std::vector<std::string> varArgsExpanded;
  422. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  423. return true;
  424. }
  425. auto argsEnd = cmRemoveDuplicates(varArgsExpanded);
  426. auto argsBegin = varArgsExpanded.cbegin();
  427. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  428. status.GetMakefile().AddDefinition(listName, value);
  429. return true;
  430. }
  431. // Helpers for list(TRANSFORM <list> ...)
  432. using transform_type = std::function<std::string(const std::string&)>;
  433. class transform_error : public std::runtime_error
  434. {
  435. public:
  436. transform_error(const std::string& error)
  437. : std::runtime_error(error)
  438. {
  439. }
  440. };
  441. class TransformSelector
  442. {
  443. public:
  444. virtual ~TransformSelector() = default;
  445. std::string Tag;
  446. virtual bool Validate(std::size_t count = 0) = 0;
  447. virtual bool InSelection(const std::string&) = 0;
  448. virtual void Transform(std::vector<std::string>& list,
  449. const transform_type& transform)
  450. {
  451. std::transform(list.begin(), list.end(), list.begin(), transform);
  452. }
  453. protected:
  454. TransformSelector(std::string&& tag)
  455. : Tag(std::move(tag))
  456. {
  457. }
  458. };
  459. class TransformNoSelector : public TransformSelector
  460. {
  461. public:
  462. TransformNoSelector()
  463. : TransformSelector("NO SELECTOR")
  464. {
  465. }
  466. bool Validate(std::size_t) override { return true; }
  467. bool InSelection(const std::string&) override { return true; }
  468. };
  469. class TransformSelectorRegex : public TransformSelector
  470. {
  471. public:
  472. TransformSelectorRegex(const std::string& regex)
  473. : TransformSelector("REGEX")
  474. , Regex(regex)
  475. {
  476. }
  477. bool Validate(std::size_t) override { return this->Regex.is_valid(); }
  478. bool InSelection(const std::string& value) override
  479. {
  480. return this->Regex.find(value);
  481. }
  482. cmsys::RegularExpression Regex;
  483. };
  484. class TransformSelectorIndexes : public TransformSelector
  485. {
  486. public:
  487. std::vector<int> Indexes;
  488. bool InSelection(const std::string&) override { return true; }
  489. void Transform(std::vector<std::string>& list,
  490. const transform_type& transform) override
  491. {
  492. this->Validate(list.size());
  493. for (auto index : this->Indexes) {
  494. list[index] = transform(list[index]);
  495. }
  496. }
  497. protected:
  498. TransformSelectorIndexes(std::string&& tag)
  499. : TransformSelector(std::move(tag))
  500. {
  501. }
  502. TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
  503. : TransformSelector(std::move(tag))
  504. , Indexes(indexes)
  505. {
  506. }
  507. int NormalizeIndex(int index, std::size_t count)
  508. {
  509. if (index < 0) {
  510. index = static_cast<int>(count) + index;
  511. }
  512. if (index < 0 || count <= static_cast<std::size_t>(index)) {
  513. throw transform_error(cmStrCat(
  514. "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
  515. " out of range (-", count, ", ", count - 1, ")."));
  516. }
  517. return index;
  518. }
  519. };
  520. class TransformSelectorAt : public TransformSelectorIndexes
  521. {
  522. public:
  523. TransformSelectorAt(std::vector<int>&& indexes)
  524. : TransformSelectorIndexes("AT", std::move(indexes))
  525. {
  526. }
  527. bool Validate(std::size_t count) override
  528. {
  529. decltype(Indexes) indexes;
  530. for (auto index : Indexes) {
  531. indexes.push_back(this->NormalizeIndex(index, count));
  532. }
  533. this->Indexes = std::move(indexes);
  534. return true;
  535. }
  536. };
  537. class TransformSelectorFor : public TransformSelectorIndexes
  538. {
  539. public:
  540. TransformSelectorFor(int start, int stop, int step)
  541. : TransformSelectorIndexes("FOR")
  542. , Start(start)
  543. , Stop(stop)
  544. , Step(step)
  545. {
  546. }
  547. bool Validate(std::size_t count) override
  548. {
  549. this->Start = this->NormalizeIndex(this->Start, count);
  550. this->Stop = this->NormalizeIndex(this->Stop, count);
  551. // compute indexes
  552. auto size = (this->Stop - this->Start + 1) / this->Step;
  553. if ((this->Stop - this->Start + 1) % this->Step != 0) {
  554. size += 1;
  555. }
  556. this->Indexes.resize(size);
  557. auto start = this->Start;
  558. auto step = this->Step;
  559. std::generate(this->Indexes.begin(), this->Indexes.end(),
  560. [&start, step]() -> int {
  561. auto r = start;
  562. start += step;
  563. return r;
  564. });
  565. return true;
  566. }
  567. private:
  568. int Start, Stop, Step;
  569. };
  570. class TransformAction
  571. {
  572. public:
  573. virtual ~TransformAction() = default;
  574. virtual std::string Transform(const std::string& input) = 0;
  575. };
  576. class TransformReplace : public TransformAction
  577. {
  578. public:
  579. TransformReplace(const std::vector<std::string>& arguments,
  580. cmMakefile* makefile)
  581. : ReplaceHelper(arguments[0], arguments[1], makefile)
  582. {
  583. makefile->ClearMatches();
  584. if (!this->ReplaceHelper.IsRegularExpressionValid()) {
  585. throw transform_error(
  586. cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
  587. "regex \"",
  588. arguments[0], "\"."));
  589. }
  590. if (!this->ReplaceHelper.IsReplaceExpressionValid()) {
  591. throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
  592. this->ReplaceHelper.GetError(), "."));
  593. }
  594. }
  595. std::string Transform(const std::string& input) override
  596. {
  597. // Scan through the input for all matches.
  598. std::string output;
  599. if (!this->ReplaceHelper.Replace(input, output)) {
  600. throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
  601. this->ReplaceHelper.GetError(), "."));
  602. }
  603. return output;
  604. }
  605. private:
  606. cmStringReplaceHelper ReplaceHelper;
  607. };
  608. bool HandleTransformCommand(std::vector<std::string> const& args,
  609. cmExecutionStatus& status)
  610. {
  611. if (args.size() < 3) {
  612. status.SetError(
  613. "sub-command TRANSFORM requires an action to be specified.");
  614. return false;
  615. }
  616. // Structure collecting all elements of the command
  617. struct Command
  618. {
  619. Command(const std::string& listName)
  620. : ListName(listName)
  621. , OutputName(listName)
  622. {
  623. }
  624. std::string Name;
  625. std::string ListName;
  626. std::vector<std::string> Arguments;
  627. std::unique_ptr<TransformAction> Action;
  628. std::unique_ptr<TransformSelector> Selector;
  629. std::string OutputName;
  630. } command(args[1]);
  631. // Descriptor of action
  632. // Arity: number of arguments required for the action
  633. // Transform: lambda function implementing the action
  634. struct ActionDescriptor
  635. {
  636. ActionDescriptor(std::string name)
  637. : Name(std::move(name))
  638. {
  639. }
  640. ActionDescriptor(std::string name, int arity, transform_type transform)
  641. : Name(std::move(name))
  642. , Arity(arity)
  643. #if defined(__GNUC__) && __GNUC__ == 6 && defined(__aarch64__)
  644. // std::function move constructor miscompiles on this architecture
  645. , Transform(transform)
  646. #else
  647. , Transform(std::move(transform))
  648. #endif
  649. {
  650. }
  651. operator const std::string&() const { return Name; }
  652. std::string Name;
  653. int Arity = 0;
  654. transform_type Transform;
  655. };
  656. // Build a set of supported actions.
  657. std::set<ActionDescriptor,
  658. std::function<bool(const std::string&, const std::string&)>>
  659. descriptors(
  660. [](const std::string& x, const std::string& y) { return x < y; });
  661. descriptors = { { "APPEND", 1,
  662. [&command](const std::string& s) -> std::string {
  663. if (command.Selector->InSelection(s)) {
  664. return s + command.Arguments[0];
  665. }
  666. return s;
  667. } },
  668. { "PREPEND", 1,
  669. [&command](const std::string& s) -> std::string {
  670. if (command.Selector->InSelection(s)) {
  671. return command.Arguments[0] + s;
  672. }
  673. return s;
  674. } },
  675. { "TOUPPER", 0,
  676. [&command](const std::string& s) -> std::string {
  677. if (command.Selector->InSelection(s)) {
  678. return cmSystemTools::UpperCase(s);
  679. }
  680. return s;
  681. } },
  682. { "TOLOWER", 0,
  683. [&command](const std::string& s) -> std::string {
  684. if (command.Selector->InSelection(s)) {
  685. return cmSystemTools::LowerCase(s);
  686. }
  687. return s;
  688. } },
  689. { "STRIP", 0,
  690. [&command](const std::string& s) -> std::string {
  691. if (command.Selector->InSelection(s)) {
  692. return cmTrimWhitespace(s);
  693. }
  694. return s;
  695. } },
  696. { "GENEX_STRIP", 0,
  697. [&command](const std::string& s) -> std::string {
  698. if (command.Selector->InSelection(s)) {
  699. return cmGeneratorExpression::Preprocess(
  700. s,
  701. cmGeneratorExpression::StripAllGeneratorExpressions);
  702. }
  703. return s;
  704. } },
  705. { "REPLACE", 2,
  706. [&command](const std::string& s) -> std::string {
  707. if (command.Selector->InSelection(s)) {
  708. return command.Action->Transform(s);
  709. }
  710. return s;
  711. } } };
  712. using size_type = std::vector<std::string>::size_type;
  713. size_type index = 2;
  714. // Parse all possible function parameters
  715. auto descriptor = descriptors.find(args[index]);
  716. if (descriptor == descriptors.end()) {
  717. status.SetError(
  718. cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action."));
  719. return false;
  720. }
  721. // Action arguments
  722. index += 1;
  723. if (args.size() < index + descriptor->Arity) {
  724. status.SetError(cmStrCat("sub-command TRANSFORM, action ",
  725. descriptor->Name, " expects ", descriptor->Arity,
  726. " argument(s)."));
  727. return false;
  728. }
  729. command.Name = descriptor->Name;
  730. index += descriptor->Arity;
  731. if (descriptor->Arity > 0) {
  732. command.Arguments =
  733. std::vector<std::string>(args.begin() + 3, args.begin() + index);
  734. }
  735. if (command.Name == "REPLACE") {
  736. try {
  737. command.Action = cm::make_unique<TransformReplace>(
  738. command.Arguments, &status.GetMakefile());
  739. } catch (const transform_error& e) {
  740. status.SetError(e.what());
  741. return false;
  742. }
  743. }
  744. const std::string REGEX{ "REGEX" };
  745. const std::string AT{ "AT" };
  746. const std::string FOR{ "FOR" };
  747. const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
  748. // handle optional arguments
  749. while (args.size() > index) {
  750. if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
  751. command.Selector) {
  752. status.SetError(
  753. cmStrCat("sub-command TRANSFORM, selector already specified (",
  754. command.Selector->Tag, ")."));
  755. return false;
  756. }
  757. // REGEX selector
  758. if (args[index] == REGEX) {
  759. if (args.size() == ++index) {
  760. status.SetError("sub-command TRANSFORM, selector REGEX expects "
  761. "'regular expression' argument.");
  762. return false;
  763. }
  764. command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]);
  765. if (!command.Selector->Validate()) {
  766. status.SetError(
  767. cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
  768. "regex \"",
  769. args[index], "\"."));
  770. return false;
  771. }
  772. index += 1;
  773. continue;
  774. }
  775. // AT selector
  776. if (args[index] == AT) {
  777. // get all specified indexes
  778. std::vector<int> indexes;
  779. while (args.size() > ++index) {
  780. std::size_t pos;
  781. int value;
  782. try {
  783. value = std::stoi(args[index], &pos);
  784. if (pos != args[index].length()) {
  785. // this is not a number, stop processing
  786. break;
  787. }
  788. indexes.push_back(value);
  789. } catch (const std::invalid_argument&) {
  790. // this is not a number, stop processing
  791. break;
  792. }
  793. }
  794. if (indexes.empty()) {
  795. status.SetError(
  796. "sub-command TRANSFORM, selector AT expects at least one "
  797. "numeric value.");
  798. return false;
  799. }
  800. command.Selector =
  801. cm::make_unique<TransformSelectorAt>(std::move(indexes));
  802. continue;
  803. }
  804. // FOR selector
  805. if (args[index] == FOR) {
  806. if (args.size() <= ++index + 1) {
  807. status.SetError(
  808. "sub-command TRANSFORM, selector FOR expects, at least,"
  809. " two arguments.");
  810. return false;
  811. }
  812. int start = 0;
  813. int stop = 0;
  814. int step = 1;
  815. bool valid = true;
  816. try {
  817. std::size_t pos;
  818. start = std::stoi(args[index], &pos);
  819. if (pos != args[index].length()) {
  820. // this is not a number
  821. valid = false;
  822. } else {
  823. stop = std::stoi(args[++index], &pos);
  824. if (pos != args[index].length()) {
  825. // this is not a number
  826. valid = false;
  827. }
  828. }
  829. } catch (const std::invalid_argument&) {
  830. // this is not numbers
  831. valid = false;
  832. }
  833. if (!valid) {
  834. status.SetError("sub-command TRANSFORM, selector FOR expects, "
  835. "at least, two numeric values.");
  836. return false;
  837. }
  838. // try to read a third numeric value for step
  839. if (args.size() > ++index) {
  840. try {
  841. std::size_t pos;
  842. step = std::stoi(args[index], &pos);
  843. if (pos != args[index].length()) {
  844. // this is not a number
  845. step = 1;
  846. } else {
  847. index += 1;
  848. }
  849. } catch (const std::invalid_argument&) {
  850. // this is not number, ignore exception
  851. }
  852. }
  853. if (step < 0) {
  854. status.SetError("sub-command TRANSFORM, selector FOR expects "
  855. "non negative numeric value for <step>.");
  856. }
  857. command.Selector =
  858. cm::make_unique<TransformSelectorFor>(start, stop, step);
  859. continue;
  860. }
  861. // output variable
  862. if (args[index] == OUTPUT_VARIABLE) {
  863. if (args.size() == ++index) {
  864. status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
  865. "expects variable name argument.");
  866. return false;
  867. }
  868. command.OutputName = args[index++];
  869. continue;
  870. }
  871. status.SetError(cmStrCat("sub-command TRANSFORM, '",
  872. cmJoin(cmMakeRange(args).advance(index), " "),
  873. "': unexpected argument(s)."));
  874. return false;
  875. }
  876. // expand the list variable
  877. std::vector<std::string> varArgsExpanded;
  878. if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) {
  879. status.GetMakefile().AddDefinition(command.OutputName, "");
  880. return true;
  881. }
  882. if (!command.Selector) {
  883. // no selector specified, apply transformation to all elements
  884. command.Selector = cm::make_unique<TransformNoSelector>();
  885. }
  886. try {
  887. command.Selector->Transform(varArgsExpanded, descriptor->Transform);
  888. } catch (const transform_error& e) {
  889. status.SetError(e.what());
  890. return false;
  891. }
  892. status.GetMakefile().AddDefinition(command.OutputName,
  893. cmJoin(varArgsExpanded, ";"));
  894. return true;
  895. }
  896. class cmStringSorter
  897. {
  898. public:
  899. enum class Order
  900. {
  901. UNINITIALIZED,
  902. ASCENDING,
  903. DESCENDING,
  904. };
  905. enum class Compare
  906. {
  907. UNINITIALIZED,
  908. STRING,
  909. FILE_BASENAME,
  910. NATURAL,
  911. };
  912. enum class CaseSensitivity
  913. {
  914. UNINITIALIZED,
  915. SENSITIVE,
  916. INSENSITIVE,
  917. };
  918. protected:
  919. using StringFilter = std::string (*)(const std::string&);
  920. StringFilter GetCompareFilter(Compare compare)
  921. {
  922. return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName
  923. : nullptr;
  924. }
  925. StringFilter GetCaseFilter(CaseSensitivity sensitivity)
  926. {
  927. return (sensitivity == CaseSensitivity::INSENSITIVE)
  928. ? cmSystemTools::LowerCase
  929. : nullptr;
  930. }
  931. using ComparisonFunction =
  932. std::function<bool(const std::string&, const std::string&)>;
  933. ComparisonFunction GetComparisonFunction(Compare compare)
  934. {
  935. if (compare == Compare::NATURAL) {
  936. return std::function<bool(const std::string&, const std::string&)>(
  937. [](const std::string& x, const std::string& y) {
  938. return cmSystemTools::strverscmp(x, y) < 0;
  939. });
  940. }
  941. return std::function<bool(const std::string&, const std::string&)>(
  942. [](const std::string& x, const std::string& y) { return x < y; });
  943. }
  944. public:
  945. cmStringSorter(Compare compare, CaseSensitivity caseSensitivity,
  946. Order desc = Order::ASCENDING)
  947. : filters{ GetCompareFilter(compare), GetCaseFilter(caseSensitivity) }
  948. , sortMethod(GetComparisonFunction(compare))
  949. , descending(desc == Order::DESCENDING)
  950. {
  951. }
  952. std::string ApplyFilter(const std::string& argument)
  953. {
  954. std::string result = argument;
  955. for (auto filter : filters) {
  956. if (filter != nullptr) {
  957. result = filter(result);
  958. }
  959. }
  960. return result;
  961. }
  962. bool operator()(const std::string& a, const std::string& b)
  963. {
  964. std::string af = ApplyFilter(a);
  965. std::string bf = ApplyFilter(b);
  966. bool result;
  967. if (descending) {
  968. result = sortMethod(bf, af);
  969. } else {
  970. result = sortMethod(af, bf);
  971. }
  972. return result;
  973. }
  974. protected:
  975. StringFilter filters[2] = { nullptr, nullptr };
  976. ComparisonFunction sortMethod;
  977. bool descending;
  978. };
  979. bool HandleSortCommand(std::vector<std::string> const& args,
  980. cmExecutionStatus& status)
  981. {
  982. assert(args.size() >= 2);
  983. if (args.size() > 8) {
  984. status.SetError("sub-command SORT only takes up to six arguments.");
  985. return false;
  986. }
  987. auto sortCompare = cmStringSorter::Compare::UNINITIALIZED;
  988. auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED;
  989. auto sortOrder = cmStringSorter::Order::UNINITIALIZED;
  990. size_t argumentIndex = 2;
  991. const std::string messageHint = "sub-command SORT ";
  992. while (argumentIndex < args.size()) {
  993. const std::string option = args[argumentIndex++];
  994. if (option == "COMPARE") {
  995. if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
  996. std::string error = cmStrCat(messageHint, "option \"", option,
  997. "\" has been specified multiple times.");
  998. status.SetError(error);
  999. return false;
  1000. }
  1001. if (argumentIndex < args.size()) {
  1002. const std::string argument = args[argumentIndex++];
  1003. if (argument == "STRING") {
  1004. sortCompare = cmStringSorter::Compare::STRING;
  1005. } else if (argument == "FILE_BASENAME") {
  1006. sortCompare = cmStringSorter::Compare::FILE_BASENAME;
  1007. } else if (argument == "NATURAL") {
  1008. sortCompare = cmStringSorter::Compare::NATURAL;
  1009. } else {
  1010. std::string error =
  1011. cmStrCat(messageHint, "value \"", argument, "\" for option \"",
  1012. option, "\" is invalid.");
  1013. status.SetError(error);
  1014. return false;
  1015. }
  1016. } else {
  1017. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1018. option, "\"."));
  1019. return false;
  1020. }
  1021. } else if (option == "CASE") {
  1022. if (sortCaseSensitivity !=
  1023. cmStringSorter::CaseSensitivity::UNINITIALIZED) {
  1024. status.SetError(cmStrCat(messageHint, "option \"", option,
  1025. "\" has been specified multiple times."));
  1026. return false;
  1027. }
  1028. if (argumentIndex < args.size()) {
  1029. const std::string argument = args[argumentIndex++];
  1030. if (argument == "SENSITIVE") {
  1031. sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
  1032. } else if (argument == "INSENSITIVE") {
  1033. sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
  1034. } else {
  1035. status.SetError(cmStrCat(messageHint, "value \"", argument,
  1036. "\" for option \"", option,
  1037. "\" is invalid."));
  1038. return false;
  1039. }
  1040. } else {
  1041. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1042. option, "\"."));
  1043. return false;
  1044. }
  1045. } else if (option == "ORDER") {
  1046. if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
  1047. status.SetError(cmStrCat(messageHint, "option \"", option,
  1048. "\" has been specified multiple times."));
  1049. return false;
  1050. }
  1051. if (argumentIndex < args.size()) {
  1052. const std::string argument = args[argumentIndex++];
  1053. if (argument == "ASCENDING") {
  1054. sortOrder = cmStringSorter::Order::ASCENDING;
  1055. } else if (argument == "DESCENDING") {
  1056. sortOrder = cmStringSorter::Order::DESCENDING;
  1057. } else {
  1058. status.SetError(cmStrCat(messageHint, "value \"", argument,
  1059. "\" for option \"", option,
  1060. "\" is invalid."));
  1061. return false;
  1062. }
  1063. } else {
  1064. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1065. option, "\"."));
  1066. return false;
  1067. }
  1068. } else {
  1069. status.SetError(
  1070. cmStrCat(messageHint, "option \"", option, "\" is unknown."));
  1071. return false;
  1072. }
  1073. }
  1074. // set Default Values if Option is not given
  1075. if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) {
  1076. sortCompare = cmStringSorter::Compare::STRING;
  1077. }
  1078. if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) {
  1079. sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
  1080. }
  1081. if (sortOrder == cmStringSorter::Order::UNINITIALIZED) {
  1082. sortOrder = cmStringSorter::Order::ASCENDING;
  1083. }
  1084. const std::string& listName = args[1];
  1085. // expand the variable
  1086. std::vector<std::string> varArgsExpanded;
  1087. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  1088. return true;
  1089. }
  1090. if ((sortCompare == cmStringSorter::Compare::STRING) &&
  1091. (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) &&
  1092. (sortOrder == cmStringSorter::Order::ASCENDING)) {
  1093. std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
  1094. } else {
  1095. cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder);
  1096. std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter);
  1097. }
  1098. std::string value = cmJoin(varArgsExpanded, ";");
  1099. status.GetMakefile().AddDefinition(listName, value);
  1100. return true;
  1101. }
  1102. bool HandleSublistCommand(std::vector<std::string> const& args,
  1103. cmExecutionStatus& status)
  1104. {
  1105. if (args.size() != 5) {
  1106. status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
  1107. args.size() - 1, " found)."));
  1108. return false;
  1109. }
  1110. const std::string& listName = args[1];
  1111. const std::string& variableName = args.back();
  1112. // expand the variable
  1113. std::vector<std::string> varArgsExpanded;
  1114. if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
  1115. varArgsExpanded.empty()) {
  1116. status.GetMakefile().AddDefinition(variableName, "");
  1117. return true;
  1118. }
  1119. const int start = atoi(args[2].c_str());
  1120. const int length = atoi(args[3].c_str());
  1121. using size_type = decltype(varArgsExpanded)::size_type;
  1122. if (start < 0 || size_type(start) >= varArgsExpanded.size()) {
  1123. status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
  1124. varArgsExpanded.size() - 1));
  1125. return false;
  1126. }
  1127. if (length < -1) {
  1128. status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
  1129. return false;
  1130. }
  1131. const size_type end =
  1132. (length == -1 || size_type(start + length) > varArgsExpanded.size())
  1133. ? varArgsExpanded.size()
  1134. : size_type(start + length);
  1135. std::vector<std::string> sublist(varArgsExpanded.begin() + start,
  1136. varArgsExpanded.begin() + end);
  1137. status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";"));
  1138. return true;
  1139. }
  1140. bool HandleRemoveAtCommand(std::vector<std::string> const& args,
  1141. cmExecutionStatus& status)
  1142. {
  1143. if (args.size() < 3) {
  1144. status.SetError("sub-command REMOVE_AT requires at least "
  1145. "two arguments.");
  1146. return false;
  1147. }
  1148. const std::string& listName = args[1];
  1149. // expand the variable
  1150. std::vector<std::string> varArgsExpanded;
  1151. if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
  1152. varArgsExpanded.empty()) {
  1153. std::ostringstream str;
  1154. str << "index: ";
  1155. for (size_t i = 1; i < args.size(); ++i) {
  1156. str << args[i];
  1157. if (i != args.size() - 1) {
  1158. str << ", ";
  1159. }
  1160. }
  1161. str << " out of range (0, 0)";
  1162. status.SetError(str.str());
  1163. return false;
  1164. }
  1165. size_t cc;
  1166. std::vector<size_t> removed;
  1167. size_t nitem = varArgsExpanded.size();
  1168. for (cc = 2; cc < args.size(); ++cc) {
  1169. int item = atoi(args[cc].c_str());
  1170. if (item < 0) {
  1171. item = static_cast<int>(nitem) + item;
  1172. }
  1173. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  1174. status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
  1175. ", ", nitem - 1, ")"));
  1176. return false;
  1177. }
  1178. removed.push_back(static_cast<size_t>(item));
  1179. }
  1180. std::sort(removed.begin(), removed.end());
  1181. auto remEnd = std::unique(removed.begin(), removed.end());
  1182. auto remBegin = removed.begin();
  1183. auto argsEnd =
  1184. cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
  1185. auto argsBegin = varArgsExpanded.cbegin();
  1186. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  1187. status.GetMakefile().AddDefinition(listName, value);
  1188. return true;
  1189. }
  1190. bool HandleFilterCommand(std::vector<std::string> const& args,
  1191. cmExecutionStatus& status)
  1192. {
  1193. if (args.size() < 2) {
  1194. status.SetError("sub-command FILTER requires a list to be specified.");
  1195. return false;
  1196. }
  1197. if (args.size() < 3) {
  1198. status.SetError(
  1199. "sub-command FILTER requires an operator to be specified.");
  1200. return false;
  1201. }
  1202. if (args.size() < 4) {
  1203. status.SetError("sub-command FILTER requires a mode to be specified.");
  1204. return false;
  1205. }
  1206. const std::string& op = args[2];
  1207. bool includeMatches;
  1208. if (op == "INCLUDE") {
  1209. includeMatches = true;
  1210. } else if (op == "EXCLUDE") {
  1211. includeMatches = false;
  1212. } else {
  1213. status.SetError("sub-command FILTER does not recognize operator " + op);
  1214. return false;
  1215. }
  1216. const std::string& listName = args[1];
  1217. // expand the variable
  1218. std::vector<std::string> varArgsExpanded;
  1219. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  1220. return true;
  1221. }
  1222. const std::string& mode = args[3];
  1223. if (mode == "REGEX") {
  1224. if (args.size() != 5) {
  1225. status.SetError("sub-command FILTER, mode REGEX "
  1226. "requires five arguments.");
  1227. return false;
  1228. }
  1229. return FilterRegex(args, includeMatches, listName, varArgsExpanded,
  1230. status);
  1231. }
  1232. status.SetError("sub-command FILTER does not recognize mode " + mode);
  1233. return false;
  1234. }
  1235. class MatchesRegex
  1236. {
  1237. public:
  1238. MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
  1239. : regex(in_regex)
  1240. , includeMatches(in_includeMatches)
  1241. {
  1242. }
  1243. bool operator()(const std::string& target)
  1244. {
  1245. return regex.find(target) ^ includeMatches;
  1246. }
  1247. private:
  1248. cmsys::RegularExpression& regex;
  1249. const bool includeMatches;
  1250. };
  1251. bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
  1252. std::string const& listName,
  1253. std::vector<std::string>& varArgsExpanded,
  1254. cmExecutionStatus& status)
  1255. {
  1256. const std::string& pattern = args[4];
  1257. cmsys::RegularExpression regex(pattern);
  1258. if (!regex.is_valid()) {
  1259. std::string error =
  1260. cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
  1261. pattern, "\".");
  1262. status.SetError(error);
  1263. return false;
  1264. }
  1265. auto argsBegin = varArgsExpanded.begin();
  1266. auto argsEnd = varArgsExpanded.end();
  1267. auto newArgsEnd =
  1268. std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
  1269. std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
  1270. status.GetMakefile().AddDefinition(listName, value);
  1271. return true;
  1272. }
  1273. } // namespace
  1274. bool cmListCommand(std::vector<std::string> const& args,
  1275. cmExecutionStatus& status)
  1276. {
  1277. if (args.size() < 2) {
  1278. status.SetError("must be called with at least two arguments.");
  1279. return false;
  1280. }
  1281. static cmSubcommandTable const subcommand{
  1282. { "LENGTH"_s, HandleLengthCommand },
  1283. { "GET"_s, HandleGetCommand },
  1284. { "APPEND"_s, HandleAppendCommand },
  1285. { "PREPEND"_s, HandlePrependCommand },
  1286. { "POP_BACK"_s, HandlePopBackCommand },
  1287. { "POP_FRONT"_s, HandlePopFrontCommand },
  1288. { "FIND"_s, HandleFindCommand },
  1289. { "INSERT"_s, HandleInsertCommand },
  1290. { "JOIN"_s, HandleJoinCommand },
  1291. { "REMOVE_AT"_s, HandleRemoveAtCommand },
  1292. { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
  1293. { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
  1294. { "TRANSFORM"_s, HandleTransformCommand },
  1295. { "SORT"_s, HandleSortCommand },
  1296. { "SUBLIST"_s, HandleSublistCommand },
  1297. { "REVERSE"_s, HandleReverseCommand },
  1298. { "FILTER"_s, HandleFilterCommand },
  1299. };
  1300. return subcommand(args[0], args, status);
  1301. }