cmListCommand.cxx 16 KB

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