cmListCommand.cxx 15 KB

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