cmListCommand.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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 <iterator>
  8. #include <sstream>
  9. #include <stdio.h>
  10. #include <stdlib.h> // required for atoi
  11. #include "cmAlgorithms.h"
  12. #include "cmMakefile.h"
  13. #include "cmPolicies.h"
  14. #include "cmSystemTools.h"
  15. #include "cmake.h"
  16. class cmExecutionStatus;
  17. bool cmListCommand::InitialPass(std::vector<std::string> const& args,
  18. cmExecutionStatus&)
  19. {
  20. if (args.size() < 2) {
  21. this->SetError("must be called with at least two arguments.");
  22. return false;
  23. }
  24. const std::string& subCommand = args[0];
  25. if (subCommand == "LENGTH") {
  26. return this->HandleLengthCommand(args);
  27. }
  28. if (subCommand == "GET") {
  29. return this->HandleGetCommand(args);
  30. }
  31. if (subCommand == "APPEND") {
  32. return this->HandleAppendCommand(args);
  33. }
  34. if (subCommand == "FIND") {
  35. return this->HandleFindCommand(args);
  36. }
  37. if (subCommand == "INSERT") {
  38. return this->HandleInsertCommand(args);
  39. }
  40. if (subCommand == "REMOVE_AT") {
  41. return this->HandleRemoveAtCommand(args);
  42. }
  43. if (subCommand == "REMOVE_ITEM") {
  44. return this->HandleRemoveItemCommand(args);
  45. }
  46. if (subCommand == "REMOVE_DUPLICATES") {
  47. return this->HandleRemoveDuplicatesCommand(args);
  48. }
  49. if (subCommand == "SORT") {
  50. return this->HandleSortCommand(args);
  51. }
  52. if (subCommand == "REVERSE") {
  53. return this->HandleReverseCommand(args);
  54. }
  55. if (subCommand == "FILTER") {
  56. return this->HandleFilterCommand(args);
  57. }
  58. std::string e = "does not recognize sub-command " + subCommand;
  59. this->SetError(e);
  60. return false;
  61. }
  62. bool cmListCommand::GetListString(std::string& listString,
  63. const std::string& var)
  64. {
  65. // get the old value
  66. const char* cacheValue = this->Makefile->GetDefinition(var);
  67. if (!cacheValue) {
  68. return false;
  69. }
  70. listString = cacheValue;
  71. return true;
  72. }
  73. bool cmListCommand::GetList(std::vector<std::string>& list,
  74. const std::string& var)
  75. {
  76. std::string listString;
  77. if (!this->GetListString(listString, var)) {
  78. return false;
  79. }
  80. // if the size of the list
  81. if (listString.empty()) {
  82. return true;
  83. }
  84. // expand the variable into a list
  85. cmSystemTools::ExpandListArgument(listString, list, true);
  86. // if no empty elements then just return
  87. if (std::find(list.begin(), list.end(), std::string()) == list.end()) {
  88. return true;
  89. }
  90. // if we have empty elements we need to check policy CMP0007
  91. switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0007)) {
  92. case cmPolicies::WARN: {
  93. // Default is to warn and use old behavior
  94. // OLD behavior is to allow compatibility, so recall
  95. // ExpandListArgument without the true which will remove
  96. // empty values
  97. list.clear();
  98. cmSystemTools::ExpandListArgument(listString, list);
  99. std::string warn = cmPolicies::GetPolicyWarning(cmPolicies::CMP0007);
  100. warn += " List has value = [";
  101. warn += listString;
  102. warn += "].";
  103. this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, warn);
  104. return true;
  105. }
  106. case cmPolicies::OLD:
  107. // OLD behavior is to allow compatibility, so recall
  108. // ExpandListArgument without the true which will remove
  109. // empty values
  110. list.clear();
  111. cmSystemTools::ExpandListArgument(listString, list);
  112. return true;
  113. case cmPolicies::NEW:
  114. return true;
  115. case cmPolicies::REQUIRED_IF_USED:
  116. case cmPolicies::REQUIRED_ALWAYS:
  117. this->Makefile->IssueMessage(
  118. cmake::FATAL_ERROR,
  119. cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007));
  120. return false;
  121. }
  122. return true;
  123. }
  124. bool cmListCommand::HandleLengthCommand(std::vector<std::string> const& args)
  125. {
  126. if (args.size() != 3) {
  127. this->SetError("sub-command LENGTH requires two arguments.");
  128. return false;
  129. }
  130. const std::string& listName = args[1];
  131. const std::string& variableName = args[args.size() - 1];
  132. std::vector<std::string> varArgsExpanded;
  133. // do not check the return value here
  134. // if the list var is not found varArgsExpanded will have size 0
  135. // and we will return 0
  136. this->GetList(varArgsExpanded, listName);
  137. size_t length = varArgsExpanded.size();
  138. char buffer[1024];
  139. sprintf(buffer, "%d", static_cast<int>(length));
  140. this->Makefile->AddDefinition(variableName, buffer);
  141. return true;
  142. }
  143. bool cmListCommand::HandleGetCommand(std::vector<std::string> const& args)
  144. {
  145. if (args.size() < 4) {
  146. this->SetError("sub-command GET requires at least three arguments.");
  147. return false;
  148. }
  149. const std::string& listName = args[1];
  150. const std::string& variableName = args[args.size() - 1];
  151. // expand the variable
  152. std::vector<std::string> varArgsExpanded;
  153. if (!this->GetList(varArgsExpanded, listName)) {
  154. this->Makefile->AddDefinition(variableName, "NOTFOUND");
  155. return true;
  156. }
  157. // FIXME: Add policy to make non-existing lists an error like empty lists.
  158. if (varArgsExpanded.empty()) {
  159. this->SetError("GET given empty list");
  160. return false;
  161. }
  162. std::string value;
  163. size_t cc;
  164. const char* sep = "";
  165. size_t nitem = varArgsExpanded.size();
  166. for (cc = 2; cc < args.size() - 1; cc++) {
  167. int item = atoi(args[cc].c_str());
  168. value += sep;
  169. sep = ";";
  170. if (item < 0) {
  171. item = static_cast<int>(nitem) + item;
  172. }
  173. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  174. std::ostringstream str;
  175. str << "index: " << item << " out of range (-" << nitem << ", "
  176. << nitem - 1 << ")";
  177. this->SetError(str.str());
  178. return false;
  179. }
  180. value += varArgsExpanded[item];
  181. }
  182. this->Makefile->AddDefinition(variableName, value.c_str());
  183. return true;
  184. }
  185. bool cmListCommand::HandleAppendCommand(std::vector<std::string> const& args)
  186. {
  187. assert(args.size() >= 2);
  188. // Skip if nothing to append.
  189. if (args.size() < 3) {
  190. return true;
  191. }
  192. const std::string& listName = args[1];
  193. // expand the variable
  194. std::string listString;
  195. this->GetListString(listString, listName);
  196. if (!listString.empty() && !args.empty()) {
  197. listString += ";";
  198. }
  199. listString += cmJoin(cmMakeRange(args).advance(2), ";");
  200. this->Makefile->AddDefinition(listName, listString.c_str());
  201. return true;
  202. }
  203. bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args)
  204. {
  205. if (args.size() != 4) {
  206. this->SetError("sub-command FIND requires three arguments.");
  207. return false;
  208. }
  209. const std::string& listName = args[1];
  210. const std::string& variableName = args[args.size() - 1];
  211. // expand the variable
  212. std::vector<std::string> varArgsExpanded;
  213. if (!this->GetList(varArgsExpanded, listName)) {
  214. this->Makefile->AddDefinition(variableName, "-1");
  215. return true;
  216. }
  217. std::vector<std::string>::iterator it =
  218. std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
  219. if (it != varArgsExpanded.end()) {
  220. std::ostringstream indexStream;
  221. indexStream << std::distance(varArgsExpanded.begin(), it);
  222. this->Makefile->AddDefinition(variableName, indexStream.str().c_str());
  223. return true;
  224. }
  225. this->Makefile->AddDefinition(variableName, "-1");
  226. return true;
  227. }
  228. bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args)
  229. {
  230. if (args.size() < 4) {
  231. this->SetError("sub-command INSERT requires at least three arguments.");
  232. return false;
  233. }
  234. const std::string& listName = args[1];
  235. // expand the variable
  236. int item = atoi(args[2].c_str());
  237. std::vector<std::string> varArgsExpanded;
  238. if ((!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) &&
  239. item != 0) {
  240. std::ostringstream str;
  241. str << "index: " << item << " out of range (0, 0)";
  242. this->SetError(str.str());
  243. return false;
  244. }
  245. if (!varArgsExpanded.empty()) {
  246. size_t nitem = varArgsExpanded.size();
  247. if (item < 0) {
  248. item = static_cast<int>(nitem) + item;
  249. }
  250. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  251. std::ostringstream str;
  252. str << "index: " << item << " out of range (-" << varArgsExpanded.size()
  253. << ", "
  254. << (varArgsExpanded.empty() ? 0 : (varArgsExpanded.size() - 1))
  255. << ")";
  256. this->SetError(str.str());
  257. return false;
  258. }
  259. }
  260. varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3,
  261. args.end());
  262. std::string value = cmJoin(varArgsExpanded, ";");
  263. this->Makefile->AddDefinition(listName, value.c_str());
  264. return true;
  265. }
  266. bool cmListCommand::HandleRemoveItemCommand(
  267. std::vector<std::string> const& args)
  268. {
  269. if (args.size() < 3) {
  270. this->SetError("sub-command REMOVE_ITEM requires two or more arguments.");
  271. return false;
  272. }
  273. const std::string& listName = args[1];
  274. // expand the variable
  275. std::vector<std::string> varArgsExpanded;
  276. if (!this->GetList(varArgsExpanded, listName)) {
  277. this->SetError("sub-command REMOVE_ITEM requires list to be present.");
  278. return false;
  279. }
  280. std::vector<std::string> remove(args.begin() + 2, args.end());
  281. std::sort(remove.begin(), remove.end());
  282. std::vector<std::string>::const_iterator remEnd =
  283. std::unique(remove.begin(), remove.end());
  284. std::vector<std::string>::const_iterator remBegin = remove.begin();
  285. std::vector<std::string>::const_iterator argsEnd =
  286. cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd));
  287. std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
  288. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  289. this->Makefile->AddDefinition(listName, value.c_str());
  290. return true;
  291. }
  292. bool cmListCommand::HandleReverseCommand(std::vector<std::string> const& args)
  293. {
  294. assert(args.size() >= 2);
  295. if (args.size() > 2) {
  296. this->SetError("sub-command REVERSE only takes one argument.");
  297. return false;
  298. }
  299. const std::string& listName = args[1];
  300. // expand the variable
  301. std::vector<std::string> varArgsExpanded;
  302. if (!this->GetList(varArgsExpanded, listName)) {
  303. this->SetError("sub-command REVERSE requires list to be present.");
  304. return false;
  305. }
  306. std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";");
  307. this->Makefile->AddDefinition(listName, value.c_str());
  308. return true;
  309. }
  310. bool cmListCommand::HandleRemoveDuplicatesCommand(
  311. std::vector<std::string> const& args)
  312. {
  313. assert(args.size() >= 2);
  314. if (args.size() > 2) {
  315. this->SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
  316. return false;
  317. }
  318. const std::string& listName = args[1];
  319. // expand the variable
  320. std::vector<std::string> varArgsExpanded;
  321. if (!this->GetList(varArgsExpanded, listName)) {
  322. this->SetError(
  323. "sub-command REMOVE_DUPLICATES requires list to be present.");
  324. return false;
  325. }
  326. std::vector<std::string>::const_iterator argsEnd =
  327. cmRemoveDuplicates(varArgsExpanded);
  328. std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
  329. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  330. this->Makefile->AddDefinition(listName, value.c_str());
  331. return true;
  332. }
  333. bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
  334. {
  335. assert(args.size() >= 2);
  336. if (args.size() > 2) {
  337. this->SetError("sub-command SORT only takes one argument.");
  338. return false;
  339. }
  340. const std::string& listName = args[1];
  341. // expand the variable
  342. std::vector<std::string> varArgsExpanded;
  343. if (!this->GetList(varArgsExpanded, listName)) {
  344. this->SetError("sub-command SORT requires list to be present.");
  345. return false;
  346. }
  347. std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
  348. std::string value = cmJoin(varArgsExpanded, ";");
  349. this->Makefile->AddDefinition(listName, value.c_str());
  350. return true;
  351. }
  352. bool cmListCommand::HandleRemoveAtCommand(std::vector<std::string> const& args)
  353. {
  354. if (args.size() < 3) {
  355. this->SetError("sub-command REMOVE_AT requires at least "
  356. "two arguments.");
  357. return false;
  358. }
  359. const std::string& listName = args[1];
  360. // expand the variable
  361. std::vector<std::string> varArgsExpanded;
  362. if (!this->GetList(varArgsExpanded, listName)) {
  363. this->SetError("sub-command REMOVE_AT requires list to be present.");
  364. return false;
  365. }
  366. // FIXME: Add policy to make non-existing lists an error like empty lists.
  367. if (varArgsExpanded.empty()) {
  368. this->SetError("REMOVE_AT given empty list");
  369. return false;
  370. }
  371. size_t cc;
  372. std::vector<size_t> removed;
  373. size_t nitem = varArgsExpanded.size();
  374. for (cc = 2; cc < args.size(); ++cc) {
  375. int item = atoi(args[cc].c_str());
  376. if (item < 0) {
  377. item = static_cast<int>(nitem) + item;
  378. }
  379. if (item < 0 || nitem <= static_cast<size_t>(item)) {
  380. std::ostringstream str;
  381. str << "index: " << item << " out of range (-" << nitem << ", "
  382. << nitem - 1 << ")";
  383. this->SetError(str.str());
  384. return false;
  385. }
  386. removed.push_back(static_cast<size_t>(item));
  387. }
  388. std::sort(removed.begin(), removed.end());
  389. std::vector<size_t>::const_iterator remEnd =
  390. std::unique(removed.begin(), removed.end());
  391. std::vector<size_t>::const_iterator remBegin = removed.begin();
  392. std::vector<std::string>::const_iterator argsEnd =
  393. cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
  394. std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
  395. std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
  396. this->Makefile->AddDefinition(listName, value.c_str());
  397. return true;
  398. }
  399. bool cmListCommand::HandleFilterCommand(std::vector<std::string> const& args)
  400. {
  401. if (args.size() < 2) {
  402. this->SetError("sub-command FILTER requires a list to be specified.");
  403. return false;
  404. }
  405. if (args.size() < 3) {
  406. this->SetError("sub-command FILTER requires an operator to be specified.");
  407. return false;
  408. }
  409. if (args.size() < 4) {
  410. this->SetError("sub-command FILTER requires a mode to be specified.");
  411. return false;
  412. }
  413. const std::string& listName = args[1];
  414. // expand the variable
  415. std::vector<std::string> varArgsExpanded;
  416. if (!this->GetList(varArgsExpanded, listName)) {
  417. this->SetError("sub-command FILTER requires list to be present.");
  418. return false;
  419. }
  420. const std::string& op = args[2];
  421. bool includeMatches;
  422. if (op == "INCLUDE") {
  423. includeMatches = true;
  424. } else if (op == "EXCLUDE") {
  425. includeMatches = false;
  426. } else {
  427. this->SetError("sub-command FILTER does not recognize operator " + op);
  428. return false;
  429. }
  430. const std::string& mode = args[3];
  431. if (mode == "REGEX") {
  432. if (args.size() != 5) {
  433. this->SetError("sub-command FILTER, mode REGEX "
  434. "requires five arguments.");
  435. return false;
  436. }
  437. return this->FilterRegex(args, includeMatches, listName, varArgsExpanded);
  438. }
  439. this->SetError("sub-command FILTER does not recognize mode " + mode);
  440. return false;
  441. }
  442. class MatchesRegex
  443. {
  444. public:
  445. MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
  446. : regex(in_regex)
  447. , includeMatches(in_includeMatches)
  448. {
  449. }
  450. bool operator()(const std::string& target)
  451. {
  452. return regex.find(target) ^ includeMatches;
  453. }
  454. private:
  455. cmsys::RegularExpression& regex;
  456. const bool includeMatches;
  457. };
  458. bool cmListCommand::FilterRegex(std::vector<std::string> const& args,
  459. bool includeMatches,
  460. std::string const& listName,
  461. std::vector<std::string>& varArgsExpanded)
  462. {
  463. const std::string& pattern = args[4];
  464. cmsys::RegularExpression regex(pattern);
  465. if (!regex.is_valid()) {
  466. std::string error = "sub-command FILTER, mode REGEX ";
  467. error += "failed to compile regex \"";
  468. error += pattern;
  469. error += "\".";
  470. this->SetError(error);
  471. return false;
  472. }
  473. std::vector<std::string>::iterator argsBegin = varArgsExpanded.begin();
  474. std::vector<std::string>::iterator argsEnd = varArgsExpanded.end();
  475. std::vector<std::string>::iterator newArgsEnd =
  476. std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
  477. std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
  478. this->Makefile->AddDefinition(listName, value.c_str());
  479. return true;
  480. }