cmListCommand.cxx 16 KB

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