cmListCommand.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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. size_t cc;
  241. for ( cc = 2; cc < args.size(); ++ cc )
  242. {
  243. if(listString.size())
  244. {
  245. listString += ";";
  246. }
  247. listString += args[cc];
  248. }
  249. this->Makefile->AddDefinition(listName, listString.c_str());
  250. return true;
  251. }
  252. //----------------------------------------------------------------------------
  253. bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args)
  254. {
  255. if(args.size() != 4)
  256. {
  257. this->SetError("sub-command FIND requires three arguments.");
  258. return false;
  259. }
  260. const std::string& listName = args[1];
  261. const std::string& variableName = args[args.size() - 1];
  262. // expand the variable
  263. std::vector<std::string> varArgsExpanded;
  264. if ( !this->GetList(varArgsExpanded, listName) )
  265. {
  266. this->Makefile->AddDefinition(variableName, "-1");
  267. return true;
  268. }
  269. std::vector<std::string>::iterator it;
  270. unsigned int index = 0;
  271. for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it )
  272. {
  273. if ( *it == args[2] )
  274. {
  275. char indexString[32];
  276. sprintf(indexString, "%d", index);
  277. this->Makefile->AddDefinition(variableName, indexString);
  278. return true;
  279. }
  280. index++;
  281. }
  282. this->Makefile->AddDefinition(variableName, "-1");
  283. return true;
  284. }
  285. //----------------------------------------------------------------------------
  286. bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args)
  287. {
  288. if(args.size() < 4)
  289. {
  290. this->SetError("sub-command INSERT requires at least three arguments.");
  291. return false;
  292. }
  293. const std::string& listName = args[1];
  294. // expand the variable
  295. int item = atoi(args[2].c_str());
  296. std::vector<std::string> varArgsExpanded;
  297. if((!this->GetList(varArgsExpanded, listName)
  298. || varArgsExpanded.empty()) && item != 0)
  299. {
  300. std::ostringstream str;
  301. str << "index: " << item << " out of range (0, 0)";
  302. this->SetError(str.str());
  303. return false;
  304. }
  305. if ( varArgsExpanded.size() != 0 )
  306. {
  307. size_t nitem = varArgsExpanded.size();
  308. if ( item < 0 )
  309. {
  310. item = (int)nitem + item;
  311. }
  312. if ( item < 0 || nitem <= (size_t)item )
  313. {
  314. std::ostringstream str;
  315. str << "index: " << item << " out of range (-"
  316. << varArgsExpanded.size() << ", "
  317. << (varArgsExpanded.size() == 0?0:(varArgsExpanded.size()-1)) << ")";
  318. this->SetError(str.str());
  319. return false;
  320. }
  321. }
  322. size_t cc;
  323. size_t cnt = 0;
  324. for ( cc = 3; cc < args.size(); ++ cc )
  325. {
  326. varArgsExpanded.insert(varArgsExpanded.begin()+item+cnt, args[cc]);
  327. cnt ++;
  328. }
  329. std::string value = cmJoin(varArgsExpanded, ";");
  330. this->Makefile->AddDefinition(listName, value.c_str());
  331. return true;
  332. }
  333. //----------------------------------------------------------------------------
  334. bool cmListCommand
  335. ::HandleRemoveItemCommand(std::vector<std::string> const& args)
  336. {
  337. if(args.size() < 3)
  338. {
  339. this->SetError("sub-command REMOVE_ITEM requires two or more arguments.");
  340. return false;
  341. }
  342. const std::string& listName = args[1];
  343. // expand the variable
  344. std::vector<std::string> varArgsExpanded;
  345. if ( !this->GetList(varArgsExpanded, listName) )
  346. {
  347. this->SetError("sub-command REMOVE_ITEM requires list to be present.");
  348. return false;
  349. }
  350. size_t cc;
  351. for ( cc = 2; cc < args.size(); ++ cc )
  352. {
  353. size_t kk = 0;
  354. while ( kk < varArgsExpanded.size() )
  355. {
  356. if ( varArgsExpanded[kk] == args[cc] )
  357. {
  358. varArgsExpanded.erase(varArgsExpanded.begin()+kk);
  359. }
  360. else
  361. {
  362. kk ++;
  363. }
  364. }
  365. }
  366. std::string value = cmJoin(varArgsExpanded, ";");
  367. this->Makefile->AddDefinition(listName, value.c_str());
  368. return true;
  369. }
  370. //----------------------------------------------------------------------------
  371. bool cmListCommand
  372. ::HandleReverseCommand(std::vector<std::string> const& args)
  373. {
  374. assert(args.size() >= 2);
  375. if(args.size() > 2)
  376. {
  377. this->SetError(
  378. "sub-command REVERSE only takes one argument.");
  379. return false;
  380. }
  381. const std::string& listName = args[1];
  382. // expand the variable
  383. std::vector<std::string> varArgsExpanded;
  384. if ( !this->GetList(varArgsExpanded, listName) )
  385. {
  386. this->SetError("sub-command REVERSE requires list to be present.");
  387. return false;
  388. }
  389. std::string value;
  390. std::vector<std::string>::reverse_iterator it;
  391. const char* sep = "";
  392. for ( it = varArgsExpanded.rbegin(); it != varArgsExpanded.rend(); ++ it )
  393. {
  394. value += sep;
  395. value += it->c_str();
  396. sep = ";";
  397. }
  398. this->Makefile->AddDefinition(listName, value.c_str());
  399. return true;
  400. }
  401. //----------------------------------------------------------------------------
  402. bool cmListCommand
  403. ::HandleRemoveDuplicatesCommand(std::vector<std::string> const& args)
  404. {
  405. assert(args.size() >= 2);
  406. if(args.size() > 2)
  407. {
  408. this->SetError(
  409. "sub-command REMOVE_DUPLICATES only takes one argument.");
  410. return false;
  411. }
  412. const std::string& listName = args[1];
  413. // expand the variable
  414. std::vector<std::string> varArgsExpanded;
  415. if ( !this->GetList(varArgsExpanded, listName) )
  416. {
  417. this->SetError(
  418. "sub-command REMOVE_DUPLICATES requires list to be present.");
  419. return false;
  420. }
  421. std::string value;
  422. std::set<std::string> unique;
  423. std::vector<std::string>::iterator it;
  424. const char* sep = "";
  425. for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it )
  426. {
  427. if (unique.find(*it) != unique.end())
  428. {
  429. continue;
  430. }
  431. unique.insert(*it);
  432. value += sep;
  433. value += it->c_str();
  434. sep = ";";
  435. }
  436. this->Makefile->AddDefinition(listName, value.c_str());
  437. return true;
  438. }
  439. //----------------------------------------------------------------------------
  440. bool cmListCommand
  441. ::HandleSortCommand(std::vector<std::string> const& args)
  442. {
  443. assert(args.size() >= 2);
  444. if(args.size() > 2)
  445. {
  446. this->SetError(
  447. "sub-command SORT only takes one argument.");
  448. return false;
  449. }
  450. const std::string& listName = args[1];
  451. // expand the variable
  452. std::vector<std::string> varArgsExpanded;
  453. if ( !this->GetList(varArgsExpanded, listName) )
  454. {
  455. this->SetError("sub-command SORT requires list to be present.");
  456. return false;
  457. }
  458. std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
  459. std::string value = cmJoin(varArgsExpanded, ";");
  460. this->Makefile->AddDefinition(listName, value.c_str());
  461. return true;
  462. }
  463. //----------------------------------------------------------------------------
  464. bool cmListCommand::HandleRemoveAtCommand(
  465. std::vector<std::string> const& args)
  466. {
  467. if(args.size() < 3)
  468. {
  469. this->SetError("sub-command REMOVE_AT requires at least "
  470. "two arguments.");
  471. return false;
  472. }
  473. const std::string& listName = args[1];
  474. // expand the variable
  475. std::vector<std::string> varArgsExpanded;
  476. if ( !this->GetList(varArgsExpanded, listName) )
  477. {
  478. this->SetError("sub-command REMOVE_AT requires list to be present.");
  479. return false;
  480. }
  481. // FIXME: Add policy to make non-existing lists an error like empty lists.
  482. if(varArgsExpanded.empty())
  483. {
  484. this->SetError("REMOVE_AT given empty list");
  485. return false;
  486. }
  487. size_t cc;
  488. std::vector<size_t> removed;
  489. for ( cc = 2; cc < args.size(); ++ cc )
  490. {
  491. int item = atoi(args[cc].c_str());
  492. size_t nitem = varArgsExpanded.size();
  493. if ( item < 0 )
  494. {
  495. item = (int)nitem + item;
  496. }
  497. if ( item < 0 || nitem <= (size_t)item )
  498. {
  499. std::ostringstream str;
  500. str << "index: " << item << " out of range (-"
  501. << varArgsExpanded.size() << ", "
  502. << varArgsExpanded.size()-1 << ")";
  503. this->SetError(str.str());
  504. return false;
  505. }
  506. removed.push_back(static_cast<size_t>(item));
  507. }
  508. std::string value;
  509. const char* sep = "";
  510. for ( cc = 0; cc < varArgsExpanded.size(); ++ cc )
  511. {
  512. size_t kk;
  513. bool found = false;
  514. for ( kk = 0; kk < removed.size(); ++ kk )
  515. {
  516. if ( cc == removed[kk] )
  517. {
  518. found = true;
  519. }
  520. }
  521. if ( !found )
  522. {
  523. value += sep;
  524. value += varArgsExpanded[cc];
  525. sep = ";";
  526. }
  527. }
  528. this->Makefile->AddDefinition(listName, value.c_str());
  529. return true;
  530. }