cmListCommand.cxx 46 KB

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