cmListCommand.cxx 15 KB

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