cmListCommand.cxx 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  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(this->Indexes) indexes;
  530. for (auto index : this->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 this->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{ this->GetCompareFilter(compare),
  948. this->GetCaseFilter(caseSensitivity) }
  949. , sortMethod(this->GetComparisonFunction(compare))
  950. , descending(desc == Order::DESCENDING)
  951. {
  952. }
  953. std::string ApplyFilter(const std::string& argument)
  954. {
  955. std::string result = argument;
  956. for (auto filter : this->filters) {
  957. if (filter != nullptr) {
  958. result = filter(result);
  959. }
  960. }
  961. return result;
  962. }
  963. bool operator()(const std::string& a, const std::string& b)
  964. {
  965. std::string af = this->ApplyFilter(a);
  966. std::string bf = this->ApplyFilter(b);
  967. bool result;
  968. if (this->descending) {
  969. result = this->sortMethod(bf, af);
  970. } else {
  971. result = this->sortMethod(af, bf);
  972. }
  973. return result;
  974. }
  975. protected:
  976. StringFilter filters[2] = { nullptr, nullptr };
  977. ComparisonFunction sortMethod;
  978. bool descending;
  979. };
  980. bool HandleSortCommand(std::vector<std::string> const& args,
  981. cmExecutionStatus& status)
  982. {
  983. assert(args.size() >= 2);
  984. if (args.size() > 8) {
  985. status.SetError("sub-command SORT only takes up to six arguments.");
  986. return false;
  987. }
  988. auto sortCompare = cmStringSorter::Compare::UNINITIALIZED;
  989. auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED;
  990. auto sortOrder = cmStringSorter::Order::UNINITIALIZED;
  991. size_t argumentIndex = 2;
  992. const std::string messageHint = "sub-command SORT ";
  993. while (argumentIndex < args.size()) {
  994. const std::string option = args[argumentIndex++];
  995. if (option == "COMPARE") {
  996. if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
  997. std::string error = cmStrCat(messageHint, "option \"", option,
  998. "\" has been specified multiple times.");
  999. status.SetError(error);
  1000. return false;
  1001. }
  1002. if (argumentIndex < args.size()) {
  1003. const std::string argument = args[argumentIndex++];
  1004. if (argument == "STRING") {
  1005. sortCompare = cmStringSorter::Compare::STRING;
  1006. } else if (argument == "FILE_BASENAME") {
  1007. sortCompare = cmStringSorter::Compare::FILE_BASENAME;
  1008. } else if (argument == "NATURAL") {
  1009. sortCompare = cmStringSorter::Compare::NATURAL;
  1010. } else {
  1011. std::string error =
  1012. cmStrCat(messageHint, "value \"", argument, "\" for option \"",
  1013. option, "\" is invalid.");
  1014. status.SetError(error);
  1015. return false;
  1016. }
  1017. } else {
  1018. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1019. option, "\"."));
  1020. return false;
  1021. }
  1022. } else if (option == "CASE") {
  1023. if (sortCaseSensitivity !=
  1024. cmStringSorter::CaseSensitivity::UNINITIALIZED) {
  1025. status.SetError(cmStrCat(messageHint, "option \"", option,
  1026. "\" has been specified multiple times."));
  1027. return false;
  1028. }
  1029. if (argumentIndex < args.size()) {
  1030. const std::string argument = args[argumentIndex++];
  1031. if (argument == "SENSITIVE") {
  1032. sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
  1033. } else if (argument == "INSENSITIVE") {
  1034. sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
  1035. } else {
  1036. status.SetError(cmStrCat(messageHint, "value \"", argument,
  1037. "\" for option \"", option,
  1038. "\" is invalid."));
  1039. return false;
  1040. }
  1041. } else {
  1042. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1043. option, "\"."));
  1044. return false;
  1045. }
  1046. } else if (option == "ORDER") {
  1047. if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
  1048. status.SetError(cmStrCat(messageHint, "option \"", option,
  1049. "\" has been specified multiple times."));
  1050. return false;
  1051. }
  1052. if (argumentIndex < args.size()) {
  1053. const std::string argument = args[argumentIndex++];
  1054. if (argument == "ASCENDING") {
  1055. sortOrder = cmStringSorter::Order::ASCENDING;
  1056. } else if (argument == "DESCENDING") {
  1057. sortOrder = cmStringSorter::Order::DESCENDING;
  1058. } else {
  1059. status.SetError(cmStrCat(messageHint, "value \"", argument,
  1060. "\" for option \"", option,
  1061. "\" is invalid."));
  1062. return false;
  1063. }
  1064. } else {
  1065. status.SetError(cmStrCat(messageHint, "missing argument for option \"",
  1066. option, "\"."));
  1067. return false;
  1068. }
  1069. } else {
  1070. status.SetError(
  1071. cmStrCat(messageHint, "option \"", option, "\" is unknown."));
  1072. return false;
  1073. }
  1074. }
  1075. // set Default Values if Option is not given
  1076. if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) {
  1077. sortCompare = cmStringSorter::Compare::STRING;
  1078. }
  1079. if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) {
  1080. sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
  1081. }
  1082. if (sortOrder == cmStringSorter::Order::UNINITIALIZED) {
  1083. sortOrder = cmStringSorter::Order::ASCENDING;
  1084. }
  1085. const std::string& listName = args[1];
  1086. // expand the variable
  1087. std::vector<std::string> varArgsExpanded;
  1088. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  1089. return true;
  1090. }
  1091. if ((sortCompare == cmStringSorter::Compare::STRING) &&
  1092. (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) &&
  1093. (sortOrder == cmStringSorter::Order::ASCENDING)) {
  1094. std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
  1095. } else {
  1096. cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder);
  1097. std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter);
  1098. }
  1099. std::string value = cmJoin(varArgsExpanded, ";");
  1100. status.GetMakefile().AddDefinition(listName, value);
  1101. return true;
  1102. }
  1103. bool HandleSublistCommand(std::vector<std::string> const& args,
  1104. cmExecutionStatus& status)
  1105. {
  1106. if (args.size() != 5) {
  1107. status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
  1108. args.size() - 1, " found)."));
  1109. return false;
  1110. }
  1111. const std::string& listName = args[1];
  1112. const std::string& variableName = args.back();
  1113. // expand the variable
  1114. std::vector<std::string> varArgsExpanded;
  1115. if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
  1116. varArgsExpanded.empty()) {
  1117. status.GetMakefile().AddDefinition(variableName, "");
  1118. return true;
  1119. }
  1120. const int start = atoi(args[2].c_str());
  1121. const int length = atoi(args[3].c_str());
  1122. using size_type = decltype(varArgsExpanded)::size_type;
  1123. if (start < 0 || size_type(start) >= varArgsExpanded.size()) {
  1124. status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
  1125. varArgsExpanded.size() - 1));
  1126. return false;
  1127. }
  1128. if (length < -1) {
  1129. status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
  1130. return false;
  1131. }
  1132. const size_type end =
  1133. (length == -1 || size_type(start + length) > varArgsExpanded.size())
  1134. ? varArgsExpanded.size()
  1135. : size_type(start + length);
  1136. std::vector<std::string> sublist(varArgsExpanded.begin() + start,
  1137. varArgsExpanded.begin() + end);
  1138. status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";"));
  1139. return true;
  1140. }
  1141. bool HandleRemoveAtCommand(std::vector<std::string> const& args,
  1142. cmExecutionStatus& status)
  1143. {
  1144. if (args.size() < 3) {
  1145. status.SetError("sub-command REMOVE_AT requires at least "
  1146. "two arguments.");
  1147. return false;
  1148. }
  1149. const std::string& listName = args[1];
  1150. // expand the variable
  1151. std::vector<std::string> varArgsExpanded;
  1152. if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
  1153. varArgsExpanded.empty()) {
  1154. std::ostringstream str;
  1155. str << "index: ";
  1156. for (size_t i = 1; i < args.size(); ++i) {
  1157. str << args[i];
  1158. if (i != args.size() - 1) {
  1159. str << ", ";
  1160. }
  1161. }
  1162. str << " out of range (0, 0)";
  1163. status.SetError(str.str());
  1164. return false;
  1165. }
  1166. size_t cc;
  1167. std::vector<size_t> removed;
  1168. size_t nitem = varArgsExpanded.size();
  1169. for (cc = 2; cc < args.size(); ++cc) {
  1170. int item = atoi(args[cc].c_str());
  1171. if (item < 0) {
  1172. item = static_cast<int>(nitem) + item;
  1173. }
  1174. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  1175. status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
  1176. ", ", nitem - 1, ")"));
  1177. return false;
  1178. }
  1179. removed.push_back(static_cast<size_t>(item));
  1180. }
  1181. std::sort(removed.begin(), removed.end());
  1182. auto remEnd = std::unique(removed.begin(), removed.end());
  1183. auto remBegin = removed.begin();
  1184. auto argsEnd =
  1185. cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
  1186. auto argsBegin = varArgsExpanded.cbegin();
  1187. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  1188. status.GetMakefile().AddDefinition(listName, value);
  1189. return true;
  1190. }
  1191. bool HandleFilterCommand(std::vector<std::string> const& args,
  1192. cmExecutionStatus& status)
  1193. {
  1194. if (args.size() < 2) {
  1195. status.SetError("sub-command FILTER requires a list to be specified.");
  1196. return false;
  1197. }
  1198. if (args.size() < 3) {
  1199. status.SetError(
  1200. "sub-command FILTER requires an operator to be specified.");
  1201. return false;
  1202. }
  1203. if (args.size() < 4) {
  1204. status.SetError("sub-command FILTER requires a mode to be specified.");
  1205. return false;
  1206. }
  1207. const std::string& op = args[2];
  1208. bool includeMatches;
  1209. if (op == "INCLUDE") {
  1210. includeMatches = true;
  1211. } else if (op == "EXCLUDE") {
  1212. includeMatches = false;
  1213. } else {
  1214. status.SetError("sub-command FILTER does not recognize operator " + op);
  1215. return false;
  1216. }
  1217. const std::string& listName = args[1];
  1218. // expand the variable
  1219. std::vector<std::string> varArgsExpanded;
  1220. if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
  1221. return true;
  1222. }
  1223. const std::string& mode = args[3];
  1224. if (mode == "REGEX") {
  1225. if (args.size() != 5) {
  1226. status.SetError("sub-command FILTER, mode REGEX "
  1227. "requires five arguments.");
  1228. return false;
  1229. }
  1230. return FilterRegex(args, includeMatches, listName, varArgsExpanded,
  1231. status);
  1232. }
  1233. status.SetError("sub-command FILTER does not recognize mode " + mode);
  1234. return false;
  1235. }
  1236. class MatchesRegex
  1237. {
  1238. public:
  1239. MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
  1240. : regex(in_regex)
  1241. , includeMatches(in_includeMatches)
  1242. {
  1243. }
  1244. bool operator()(const std::string& target)
  1245. {
  1246. return this->regex.find(target) ^ this->includeMatches;
  1247. }
  1248. private:
  1249. cmsys::RegularExpression& regex;
  1250. const bool includeMatches;
  1251. };
  1252. bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
  1253. std::string const& listName,
  1254. std::vector<std::string>& varArgsExpanded,
  1255. cmExecutionStatus& status)
  1256. {
  1257. const std::string& pattern = args[4];
  1258. cmsys::RegularExpression regex(pattern);
  1259. if (!regex.is_valid()) {
  1260. std::string error =
  1261. cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
  1262. pattern, "\".");
  1263. status.SetError(error);
  1264. return false;
  1265. }
  1266. auto argsBegin = varArgsExpanded.begin();
  1267. auto argsEnd = varArgsExpanded.end();
  1268. auto newArgsEnd =
  1269. std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
  1270. std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
  1271. status.GetMakefile().AddDefinition(listName, value);
  1272. return true;
  1273. }
  1274. } // namespace
  1275. bool cmListCommand(std::vector<std::string> const& args,
  1276. cmExecutionStatus& status)
  1277. {
  1278. if (args.size() < 2) {
  1279. status.SetError("must be called with at least two arguments.");
  1280. return false;
  1281. }
  1282. static cmSubcommandTable const subcommand{
  1283. { "LENGTH"_s, HandleLengthCommand },
  1284. { "GET"_s, HandleGetCommand },
  1285. { "APPEND"_s, HandleAppendCommand },
  1286. { "PREPEND"_s, HandlePrependCommand },
  1287. { "POP_BACK"_s, HandlePopBackCommand },
  1288. { "POP_FRONT"_s, HandlePopFrontCommand },
  1289. { "FIND"_s, HandleFindCommand },
  1290. { "INSERT"_s, HandleInsertCommand },
  1291. { "JOIN"_s, HandleJoinCommand },
  1292. { "REMOVE_AT"_s, HandleRemoveAtCommand },
  1293. { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
  1294. { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
  1295. { "TRANSFORM"_s, HandleTransformCommand },
  1296. { "SORT"_s, HandleSortCommand },
  1297. { "SUBLIST"_s, HandleSublistCommand },
  1298. { "REVERSE"_s, HandleReverseCommand },
  1299. { "FILTER"_s, HandleFilterCommand },
  1300. };
  1301. return subcommand(args[0], args, status);
  1302. }