cmListCommand.cxx 39 KB

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