You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

660 lines
17 KiB

19 years ago
19 years ago
19 years ago
19 years ago
25 years ago
25 years ago
23 years ago
25 years ago
19 years ago
21 years ago
22 years ago
22 years ago
21 years ago
21 years ago
21 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmIfCommand.h"
  14. #include "cmStringCommand.h"
  15. #include <stdlib.h> // required for atof
  16. #include <list>
  17. #include <cmsys/RegularExpression.hxx>
  18. bool cmIfFunctionBlocker::
  19. IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf)
  20. {
  21. // if we are blocking then all we need to do is keep track of
  22. // scope depth of nested if statements
  23. if (this->IsBlocking)
  24. {
  25. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
  26. {
  27. this->ScopeDepth++;
  28. return true;
  29. }
  30. }
  31. if (this->IsBlocking && this->ScopeDepth)
  32. {
  33. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
  34. {
  35. this->ScopeDepth--;
  36. }
  37. return true;
  38. }
  39. // watch for our ELSE or ENDIF
  40. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else") ||
  41. !cmSystemTools::Strucmp(lff.Name.c_str(),"elseif") ||
  42. !cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
  43. {
  44. // if it was an else statement then we should change state
  45. // and block this Else Command
  46. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else"))
  47. {
  48. this->IsBlocking = this->HasRun;
  49. return true;
  50. }
  51. // if it was an elseif statement then we should check state
  52. // and possibly block this Else Command
  53. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"elseif"))
  54. {
  55. if (!this->HasRun)
  56. {
  57. char* errorString = 0;
  58. std::vector<std::string> expandedArguments;
  59. mf.ExpandArguments(lff.Arguments, expandedArguments);
  60. bool isTrue =
  61. cmIfCommand::IsTrue(expandedArguments,&errorString,&mf);
  62. if (errorString)
  63. {
  64. std::string err = "had incorrect arguments: ";
  65. unsigned int i;
  66. for(i =0; i < lff.Arguments.size(); ++i)
  67. {
  68. err += (lff.Arguments[i].Quoted?"\"":"");
  69. err += lff.Arguments[i].Value;
  70. err += (lff.Arguments[i].Quoted?"\"":"");
  71. err += " ";
  72. }
  73. err += "(";
  74. err += errorString;
  75. err += ").";
  76. cmSystemTools::Error(err.c_str());
  77. delete [] errorString;
  78. return false;
  79. }
  80. if (isTrue)
  81. {
  82. this->IsBlocking = false;
  83. this->HasRun = true;
  84. return true;
  85. }
  86. }
  87. this->IsBlocking = true;
  88. return true;
  89. }
  90. // otherwise it must be an ENDIF statement, in that case remove the
  91. // function blocker
  92. mf.RemoveFunctionBlocker(lff);
  93. return true;
  94. }
  95. return this->IsBlocking;
  96. }
  97. bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
  98. cmMakefile& mf)
  99. {
  100. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
  101. {
  102. if (cmSystemTools::IsOn
  103. (mf.GetPropertyOrDefinition("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS"))
  104. || lff.Arguments == this->Args)
  105. {
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. void cmIfFunctionBlocker::
  112. ScopeEnded(cmMakefile &mf)
  113. {
  114. std::string errmsg = "The end of a CMakeLists file was reached with an "
  115. "IF statement that was not closed properly.\nWithin the directory: ";
  116. errmsg += mf.GetCurrentDirectory();
  117. errmsg += "\nThe arguments are: ";
  118. for(std::vector<cmListFileArgument>::const_iterator j = this->Args.begin();
  119. j != this->Args.end(); ++j)
  120. {
  121. errmsg += (j->Quoted?"\"":"");
  122. errmsg += j->Value;
  123. errmsg += (j->Quoted?"\"":"");
  124. errmsg += " ";
  125. }
  126. cmSystemTools::Message(errmsg.c_str(), "Warning");
  127. }
  128. bool cmIfCommand
  129. ::InvokeInitialPass(const std::vector<cmListFileArgument>& args)
  130. {
  131. char* errorString = 0;
  132. std::vector<std::string> expandedArguments;
  133. this->Makefile->ExpandArguments(args, expandedArguments);
  134. bool isTrue =
  135. cmIfCommand::IsTrue(expandedArguments,&errorString,this->Makefile);
  136. if (errorString)
  137. {
  138. std::string err = "had incorrect arguments: ";
  139. unsigned int i;
  140. for(i =0; i < args.size(); ++i)
  141. {
  142. err += (args[i].Quoted?"\"":"");
  143. err += args[i].Value;
  144. err += (args[i].Quoted?"\"":"");
  145. err += " ";
  146. }
  147. err += "(";
  148. err += errorString;
  149. err += ").";
  150. this->SetError(err.c_str());
  151. delete [] errorString;
  152. return false;
  153. }
  154. cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
  155. // if is isn't true block the commands
  156. f->IsBlocking = !isTrue;
  157. if (isTrue)
  158. {
  159. f->HasRun = true;
  160. }
  161. f->Args = args;
  162. this->Makefile->AddFunctionBlocker(f);
  163. return true;
  164. }
  165. namespace
  166. {
  167. void IncrementArguments(std::list<std::string> &newArgs,
  168. std::list<std::string>::iterator &argP1,
  169. std::list<std::string>::iterator &argP2)
  170. {
  171. if (argP1 != newArgs.end())
  172. {
  173. argP1++;
  174. argP2 = argP1;
  175. if (argP1 != newArgs.end())
  176. {
  177. argP2++;
  178. }
  179. }
  180. }
  181. }
  182. // order of operations,
  183. // IS_DIRECTORY EXISTS COMMAND DEFINED
  184. // MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL
  185. // AND OR
  186. //
  187. // There is an issue on whether the arguments should be values of references,
  188. // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
  189. // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
  190. // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
  191. // take numeric values or variable names. STRLESS and STRGREATER take
  192. // variable names but if the variable name is not found it will use the name
  193. // directly. AND OR take variables or the values 0 or 1.
  194. bool cmIfCommand::IsTrue(const std::vector<std::string> &args,
  195. char **errorString, cmMakefile *makefile)
  196. {
  197. // check for the different signatures
  198. const char *def;
  199. const char *def2;
  200. const char* msg = "Unknown arguments specified";
  201. *errorString = new char[strlen(msg) + 1];
  202. strcpy(*errorString, msg);
  203. // handle empty invocation
  204. if (args.size() < 1)
  205. {
  206. delete [] *errorString;
  207. *errorString = 0;
  208. return false;
  209. }
  210. // store the reduced args in this vector
  211. std::list<std::string> newArgs;
  212. int reducible;
  213. unsigned int i;
  214. // copy to the list structure
  215. for(i = 0; i < args.size(); ++i)
  216. {
  217. newArgs.push_back(args[i]);
  218. }
  219. std::list<std::string>::iterator argP1;
  220. std::list<std::string>::iterator argP2;
  221. // now loop through the arguments and see if we can reduce any of them
  222. // we do this multiple times. Once for each level of precedence
  223. do
  224. {
  225. reducible = 0;
  226. std::list<std::string>::iterator arg = newArgs.begin();
  227. while (arg != newArgs.end())
  228. {
  229. argP1 = arg;
  230. IncrementArguments(newArgs,argP1,argP2);
  231. // does a file exist
  232. if (*arg == "EXISTS" && argP1 != newArgs.end())
  233. {
  234. if(cmSystemTools::FileExists((argP1)->c_str()))
  235. {
  236. *arg = "1";
  237. }
  238. else
  239. {
  240. *arg = "0";
  241. }
  242. newArgs.erase(argP1);
  243. argP1 = arg;
  244. IncrementArguments(newArgs,argP1,argP2);
  245. reducible = 1;
  246. }
  247. // does a directory with this name exist
  248. if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end())
  249. {
  250. if(cmSystemTools::FileIsDirectory((argP1)->c_str()))
  251. {
  252. *arg = "1";
  253. }
  254. else
  255. {
  256. *arg = "0";
  257. }
  258. newArgs.erase(argP1);
  259. argP1 = arg;
  260. IncrementArguments(newArgs,argP1,argP2);
  261. reducible = 1;
  262. }
  263. // is the given path an absolute path ?
  264. if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end())
  265. {
  266. if(cmSystemTools::FileIsFullPath((argP1)->c_str()))
  267. {
  268. *arg = "1";
  269. }
  270. else
  271. {
  272. *arg = "0";
  273. }
  274. newArgs.erase(argP1);
  275. argP1 = arg;
  276. IncrementArguments(newArgs,argP1,argP2);
  277. reducible = 1;
  278. }
  279. // does a command exist
  280. if (*arg == "COMMAND" && argP1 != newArgs.end())
  281. {
  282. if(makefile->CommandExists((argP1)->c_str()))
  283. {
  284. *arg = "1";
  285. }
  286. else
  287. {
  288. *arg = "0";
  289. }
  290. newArgs.erase(argP1);
  291. argP1 = arg;
  292. IncrementArguments(newArgs,argP1,argP2);
  293. reducible = 1;
  294. }
  295. // is a variable defined
  296. if (*arg == "DEFINED" && argP1 != newArgs.end())
  297. {
  298. size_t argP1len = argP1->size();
  299. bool bdef = false;
  300. if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
  301. argP1->operator[](argP1len-1) == '}')
  302. {
  303. std::string env = argP1->substr(4, argP1len-5);
  304. bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
  305. }
  306. else
  307. {
  308. bdef = makefile->IsDefinitionSet((argP1)->c_str());
  309. }
  310. if(bdef)
  311. {
  312. *arg = "1";
  313. }
  314. else
  315. {
  316. *arg = "0";
  317. }
  318. newArgs.erase(argP1);
  319. argP1 = arg;
  320. IncrementArguments(newArgs,argP1,argP2);
  321. reducible = 1;
  322. }
  323. ++arg;
  324. }
  325. }
  326. while (reducible);
  327. // now loop through the arguments and see if we can reduce any of them
  328. // we do this multiple times. Once for each level of precedence
  329. do
  330. {
  331. reducible = 0;
  332. std::list<std::string>::iterator arg = newArgs.begin();
  333. while (arg != newArgs.end())
  334. {
  335. argP1 = arg;
  336. IncrementArguments(newArgs,argP1,argP2);
  337. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  338. *(argP1) == "MATCHES")
  339. {
  340. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  341. const char* rex = (argP2)->c_str();
  342. cmStringCommand::ClearMatches(makefile);
  343. cmsys::RegularExpression regEntry;
  344. if ( !regEntry.compile(rex) )
  345. {
  346. cmOStringStream error;
  347. error << "Regular expression \"" << rex << "\" cannot compile";
  348. delete [] *errorString;
  349. *errorString = new char[error.str().size() + 1];
  350. strcpy(*errorString, error.str().c_str());
  351. return false;
  352. }
  353. if (regEntry.find(def))
  354. {
  355. cmStringCommand::StoreMatches(makefile, regEntry);
  356. *arg = "1";
  357. }
  358. else
  359. {
  360. *arg = "0";
  361. }
  362. newArgs.erase(argP2);
  363. newArgs.erase(argP1);
  364. argP1 = arg;
  365. IncrementArguments(newArgs,argP1,argP2);
  366. reducible = 1;
  367. }
  368. if (argP1 != newArgs.end() && *arg == "MATCHES")
  369. {
  370. *arg = "0";
  371. newArgs.erase(argP1);
  372. argP1 = arg;
  373. IncrementArguments(newArgs,argP1,argP2);
  374. reducible = 1;
  375. }
  376. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  377. (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
  378. *(argP1) == "EQUAL"))
  379. {
  380. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  381. def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
  382. double lhs;
  383. double rhs;
  384. if(sscanf(def, "%lg", &lhs) != 1 ||
  385. sscanf(def2, "%lg", &rhs) != 1)
  386. {
  387. *arg = "0";
  388. }
  389. else if (*(argP1) == "LESS")
  390. {
  391. if(lhs < rhs)
  392. {
  393. *arg = "1";
  394. }
  395. else
  396. {
  397. *arg = "0";
  398. }
  399. }
  400. else if (*(argP1) == "GREATER")
  401. {
  402. if(lhs > rhs)
  403. {
  404. *arg = "1";
  405. }
  406. else
  407. {
  408. *arg = "0";
  409. }
  410. }
  411. else
  412. {
  413. if(lhs == rhs)
  414. {
  415. *arg = "1";
  416. }
  417. else
  418. {
  419. *arg = "0";
  420. }
  421. }
  422. newArgs.erase(argP2);
  423. newArgs.erase(argP1);
  424. argP1 = arg;
  425. IncrementArguments(newArgs,argP1,argP2);
  426. reducible = 1;
  427. }
  428. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  429. (*(argP1) == "STRLESS" ||
  430. *(argP1) == "STREQUAL" ||
  431. *(argP1) == "STRGREATER"))
  432. {
  433. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  434. def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
  435. int val = strcmp(def,def2);
  436. int result;
  437. if (*(argP1) == "STRLESS")
  438. {
  439. result = (val < 0);
  440. }
  441. else if (*(argP1) == "STRGREATER")
  442. {
  443. result = (val > 0);
  444. }
  445. else // strequal
  446. {
  447. result = (val == 0);
  448. }
  449. if(result)
  450. {
  451. *arg = "1";
  452. }
  453. else
  454. {
  455. *arg = "0";
  456. }
  457. newArgs.erase(argP2);
  458. newArgs.erase(argP1);
  459. argP1 = arg;
  460. IncrementArguments(newArgs,argP1,argP2);
  461. reducible = 1;
  462. }
  463. // is file A newer than file B
  464. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  465. *(argP1) == "IS_NEWER_THAN")
  466. {
  467. int fileIsNewer=0;
  468. bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
  469. (argP2)->c_str(),
  470. &fileIsNewer);
  471. if(success==false || fileIsNewer==1 || fileIsNewer==0)
  472. {
  473. *arg = "1";
  474. }
  475. else
  476. {
  477. *arg = "0";
  478. }
  479. newArgs.erase(argP2);
  480. newArgs.erase(argP1);
  481. argP1 = arg;
  482. IncrementArguments(newArgs,argP1,argP2);
  483. reducible = 1;
  484. }
  485. ++arg;
  486. }
  487. }
  488. while (reducible);
  489. // now loop through the arguments and see if we can reduce any of them
  490. // we do this multiple times. Once for each level of precedence
  491. do
  492. {
  493. reducible = 0;
  494. std::list<std::string>::iterator arg = newArgs.begin();
  495. while (arg != newArgs.end())
  496. {
  497. argP1 = arg;
  498. IncrementArguments(newArgs,argP1,argP2);
  499. if (argP1 != newArgs.end() && *arg == "NOT")
  500. {
  501. def = cmIfCommand::GetVariableOrNumber((argP1)->c_str(), makefile);
  502. if(!cmSystemTools::IsOff(def))
  503. {
  504. *arg = "0";
  505. }
  506. else
  507. {
  508. *arg = "1";
  509. }
  510. newArgs.erase(argP1);
  511. argP1 = arg;
  512. IncrementArguments(newArgs,argP1,argP2);
  513. reducible = 1;
  514. }
  515. ++arg;
  516. }
  517. }
  518. while (reducible);
  519. // now loop through the arguments and see if we can reduce any of them
  520. // we do this multiple times. Once for each level of precedence
  521. do
  522. {
  523. reducible = 0;
  524. std::list<std::string>::iterator arg = newArgs.begin();
  525. while (arg != newArgs.end())
  526. {
  527. argP1 = arg;
  528. IncrementArguments(newArgs,argP1,argP2);
  529. if (argP1 != newArgs.end() && *(argP1) == "AND" &&
  530. argP2 != newArgs.end())
  531. {
  532. def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile);
  533. def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile);
  534. if(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2))
  535. {
  536. *arg = "0";
  537. }
  538. else
  539. {
  540. *arg = "1";
  541. }
  542. newArgs.erase(argP2);
  543. newArgs.erase(argP1);
  544. argP1 = arg;
  545. IncrementArguments(newArgs,argP1,argP2);
  546. reducible = 1;
  547. }
  548. if (argP1 != newArgs.end() && *(argP1) == "OR" &&
  549. argP2 != newArgs.end())
  550. {
  551. def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile);
  552. def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile);
  553. if(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2))
  554. {
  555. *arg = "0";
  556. }
  557. else
  558. {
  559. *arg = "1";
  560. }
  561. newArgs.erase(argP2);
  562. newArgs.erase(argP1);
  563. argP1 = arg;
  564. IncrementArguments(newArgs,argP1,argP2);
  565. reducible = 1;
  566. }
  567. ++arg;
  568. }
  569. }
  570. while (reducible);
  571. // now at the end there should only be one argument left
  572. if (newArgs.size() == 1)
  573. {
  574. delete [] *errorString;
  575. *errorString = 0;
  576. if (*newArgs.begin() == "0")
  577. {
  578. return false;
  579. }
  580. if (*newArgs.begin() == "1")
  581. {
  582. return true;
  583. }
  584. def = makefile->GetDefinition(args[0].c_str());
  585. if(cmSystemTools::IsOff(def))
  586. {
  587. return false;
  588. }
  589. }
  590. return true;
  591. }
  592. const char* cmIfCommand::GetVariableOrString(const char* str,
  593. const cmMakefile* mf)
  594. {
  595. const char* def = mf->GetDefinition(str);
  596. if(!def)
  597. {
  598. def = str;
  599. }
  600. return def;
  601. }
  602. const char* cmIfCommand::GetVariableOrNumber(const char* str,
  603. const cmMakefile* mf)
  604. {
  605. const char* def = mf->GetDefinition(str);
  606. if(!def)
  607. {
  608. if (atoi(str))
  609. {
  610. def = str;
  611. }
  612. }
  613. return def;
  614. }