cmIfCommand.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  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 "cmIfCommand.h"
  11. #include "cmStringCommand.h"
  12. #include <stdlib.h> // required for atof
  13. #include <list>
  14. #include <cmsys/RegularExpression.hxx>
  15. //=========================================================================
  16. bool cmIfFunctionBlocker::
  17. IsFunctionBlocked(const cmListFileFunction& lff,
  18. cmMakefile &mf,
  19. cmExecutionStatus &inStatus)
  20. {
  21. // we start by recording all the functions
  22. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
  23. {
  24. this->ScopeDepth++;
  25. }
  26. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
  27. {
  28. this->ScopeDepth--;
  29. // if this is the endif for this if statement, then start executing
  30. if (!this->ScopeDepth)
  31. {
  32. // Remove the function blocker for this scope or bail.
  33. cmsys::auto_ptr<cmFunctionBlocker>
  34. fb(mf.RemoveFunctionBlocker(this, lff));
  35. if(!fb.get()) { return false; }
  36. // execute the functions for the true parts of the if statement
  37. cmExecutionStatus status;
  38. int scopeDepth = 0;
  39. for(unsigned int c = 0; c < this->Functions.size(); ++c)
  40. {
  41. // keep track of scope depth
  42. if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"if"))
  43. {
  44. scopeDepth++;
  45. }
  46. if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
  47. {
  48. scopeDepth--;
  49. }
  50. // watch for our state change
  51. if (scopeDepth == 0 &&
  52. !cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"else"))
  53. {
  54. this->IsBlocking = this->HasRun;
  55. this->HasRun = true;
  56. }
  57. else if (scopeDepth == 0 && !cmSystemTools::Strucmp
  58. (this->Functions[c].Name.c_str(),"elseif"))
  59. {
  60. if (this->HasRun)
  61. {
  62. this->IsBlocking = true;
  63. }
  64. else
  65. {
  66. // Place this call on the call stack.
  67. cmMakefileCall stack_manager(&mf, this->Functions[c], status);
  68. static_cast<void>(stack_manager);
  69. std::string errorString;
  70. std::vector<std::string> expandedArguments;
  71. mf.ExpandArguments(this->Functions[c].Arguments,
  72. expandedArguments);
  73. cmake::MessageType messType;
  74. bool isTrue =
  75. cmIfCommand::IsTrue(expandedArguments, errorString,
  76. &mf, messType);
  77. if (errorString.size())
  78. {
  79. std::string err = "given arguments\n ";
  80. unsigned int i;
  81. for(i =0; i < this->Functions[c].Arguments.size(); ++i)
  82. {
  83. err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
  84. err += this->Functions[c].Arguments[i].Value;
  85. err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
  86. err += " ";
  87. }
  88. err += "\n";
  89. err += errorString;
  90. mf.IssueMessage(messType, err);
  91. if (messType == cmake::FATAL_ERROR)
  92. {
  93. cmSystemTools::SetFatalErrorOccured();
  94. return true;
  95. }
  96. }
  97. if (isTrue)
  98. {
  99. this->IsBlocking = false;
  100. this->HasRun = true;
  101. }
  102. }
  103. }
  104. // should we execute?
  105. else if (!this->IsBlocking)
  106. {
  107. status.Clear();
  108. mf.ExecuteCommand(this->Functions[c],status);
  109. if (status.GetReturnInvoked())
  110. {
  111. inStatus.SetReturnInvoked(true);
  112. return true;
  113. }
  114. if (status.GetBreakInvoked())
  115. {
  116. inStatus.SetBreakInvoked(true);
  117. return true;
  118. }
  119. }
  120. }
  121. return true;
  122. }
  123. }
  124. // record the command
  125. this->Functions.push_back(lff);
  126. // always return true
  127. return true;
  128. }
  129. //=========================================================================
  130. bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
  131. cmMakefile&)
  132. {
  133. if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
  134. {
  135. // if the endif has arguments, then make sure
  136. // they match the arguments of the matching if
  137. if (lff.Arguments.size() == 0 ||
  138. lff.Arguments == this->Args)
  139. {
  140. return true;
  141. }
  142. }
  143. return false;
  144. }
  145. //=========================================================================
  146. bool cmIfCommand
  147. ::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
  148. cmExecutionStatus &)
  149. {
  150. std::string errorString;
  151. std::vector<std::string> expandedArguments;
  152. this->Makefile->ExpandArguments(args, expandedArguments);
  153. cmake::MessageType status;
  154. bool isTrue =
  155. cmIfCommand::IsTrue(expandedArguments,errorString,
  156. this->Makefile, status);
  157. if (errorString.size())
  158. {
  159. std::string err = "given arguments\n ";
  160. unsigned int i;
  161. for(i =0; i < args.size(); ++i)
  162. {
  163. err += (args[i].Quoted?"\"":"");
  164. err += args[i].Value;
  165. err += (args[i].Quoted?"\"":"");
  166. err += " ";
  167. }
  168. err += "\n";
  169. err += errorString;
  170. if (status == cmake::FATAL_ERROR)
  171. {
  172. this->SetError(err.c_str());
  173. cmSystemTools::SetFatalErrorOccured();
  174. return false;
  175. }
  176. else
  177. {
  178. this->Makefile->IssueMessage(status, err);
  179. }
  180. }
  181. cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
  182. // if is isn't true block the commands
  183. f->ScopeDepth = 1;
  184. f->IsBlocking = !isTrue;
  185. if (isTrue)
  186. {
  187. f->HasRun = true;
  188. }
  189. f->Args = args;
  190. this->Makefile->AddFunctionBlocker(f);
  191. return true;
  192. }
  193. namespace
  194. {
  195. //=========================================================================
  196. // returns true if succesfull, the resulting bool parsed is stored in result
  197. bool GetBooleanValue(std::string &newArg,
  198. cmMakefile *makefile,
  199. bool &result,
  200. std::string &errorString,
  201. cmPolicies::PolicyStatus Policy12Status,
  202. cmake::MessageType &status)
  203. {
  204. if (Policy12Status != cmPolicies::OLD &&
  205. Policy12Status != cmPolicies::WARN)
  206. {
  207. // please note IsOn(var) does not always equal !IsOff(var)
  208. // that is why each is called
  209. if (cmSystemTools::IsOn(newArg.c_str()))
  210. {
  211. result = true;
  212. return true;
  213. }
  214. if (cmSystemTools::IsOff(newArg.c_str()))
  215. {
  216. result = false;
  217. return true;
  218. }
  219. return false;
  220. }
  221. // Old policy is more complex...
  222. // 0 and 1 are very common, test for them first quickly
  223. if (newArg == "0")
  224. {
  225. result = false;
  226. return true;
  227. }
  228. if (newArg == "1")
  229. {
  230. result = true;
  231. return true;
  232. }
  233. // old behavior is to dereference the var
  234. if (Policy12Status == cmPolicies::OLD)
  235. {
  236. return false;
  237. }
  238. // now test for values that may be the name of a variable
  239. // warn if used
  240. if (cmSystemTools::IsOn(newArg.c_str()))
  241. {
  242. // only warn if the value would change
  243. const char *def = makefile->GetDefinition(newArg.c_str());
  244. if (cmSystemTools::IsOff(def))
  245. {
  246. cmPolicies* policies = makefile->GetPolicies();
  247. errorString = "A variable or argument named \""
  248. + newArg
  249. + "\" appears in a conditional statement. "
  250. + policies->GetPolicyWarning(cmPolicies::CMP0012);
  251. status = cmake::AUTHOR_WARNING;
  252. }
  253. return false;
  254. }
  255. if (cmSystemTools::IsOff(newArg.c_str()))
  256. {
  257. // only warn if the value would change
  258. const char *def = makefile->GetDefinition(newArg.c_str());
  259. if (!cmSystemTools::IsOff(def))
  260. {
  261. cmPolicies* policies = makefile->GetPolicies();
  262. errorString = "A variable or argument named \""
  263. + newArg
  264. + "\" appears in a conditional statement. "
  265. + policies->GetPolicyWarning(cmPolicies::CMP0012);
  266. status = cmake::AUTHOR_WARNING;
  267. }
  268. return false;
  269. }
  270. return false;
  271. }
  272. //=========================================================================
  273. // returns the resulting boolean value
  274. bool GetBooleanValueWithAutoDereference(
  275. std::string &newArg,
  276. cmMakefile *makefile,
  277. std::string &errorString,
  278. cmPolicies::PolicyStatus Policy12Status,
  279. cmake::MessageType &status)
  280. {
  281. bool result = false;
  282. if (GetBooleanValue(newArg, makefile, result,
  283. errorString, Policy12Status, status))
  284. {
  285. return result;
  286. }
  287. const char *def = makefile->GetDefinition(newArg.c_str());
  288. return !cmSystemTools::IsOff(def);
  289. }
  290. //=========================================================================
  291. void IncrementArguments(std::list<std::string> &newArgs,
  292. std::list<std::string>::iterator &argP1,
  293. std::list<std::string>::iterator &argP2)
  294. {
  295. if (argP1 != newArgs.end())
  296. {
  297. argP1++;
  298. argP2 = argP1;
  299. if (argP1 != newArgs.end())
  300. {
  301. argP2++;
  302. }
  303. }
  304. }
  305. //=========================================================================
  306. // helper function to reduce code duplication
  307. void HandlePredicate(bool value, int &reducible,
  308. std::list<std::string>::iterator &arg,
  309. std::list<std::string> &newArgs,
  310. std::list<std::string>::iterator &argP1,
  311. std::list<std::string>::iterator &argP2)
  312. {
  313. if(value)
  314. {
  315. *arg = "1";
  316. }
  317. else
  318. {
  319. *arg = "0";
  320. }
  321. newArgs.erase(argP1);
  322. argP1 = arg;
  323. IncrementArguments(newArgs,argP1,argP2);
  324. reducible = 1;
  325. }
  326. //=========================================================================
  327. // helper function to reduce code duplication
  328. void HandleBinaryOp(bool value, int &reducible,
  329. std::list<std::string>::iterator &arg,
  330. std::list<std::string> &newArgs,
  331. std::list<std::string>::iterator &argP1,
  332. std::list<std::string>::iterator &argP2)
  333. {
  334. if(value)
  335. {
  336. *arg = "1";
  337. }
  338. else
  339. {
  340. *arg = "0";
  341. }
  342. newArgs.erase(argP2);
  343. newArgs.erase(argP1);
  344. argP1 = arg;
  345. IncrementArguments(newArgs,argP1,argP2);
  346. reducible = 1;
  347. }
  348. //=========================================================================
  349. enum Op { OpLess, OpEqual, OpGreater };
  350. bool HandleVersionCompare(Op op, const char* lhs_str, const char* rhs_str)
  351. {
  352. // Parse out up to 4 components.
  353. unsigned int lhs[4] = {0,0,0,0};
  354. unsigned int rhs[4] = {0,0,0,0};
  355. sscanf(lhs_str, "%u.%u.%u.%u", &lhs[0], &lhs[1], &lhs[2], &lhs[3]);
  356. sscanf(rhs_str, "%u.%u.%u.%u", &rhs[0], &rhs[1], &rhs[2], &rhs[3]);
  357. // Do component-wise comparison.
  358. for(unsigned int i=0; i < 4; ++i)
  359. {
  360. if(lhs[i] < rhs[i])
  361. {
  362. // lhs < rhs, so true if operation is LESS
  363. return op == OpLess;
  364. }
  365. else if(lhs[i] > rhs[i])
  366. {
  367. // lhs > rhs, so true if operation is GREATER
  368. return op == OpGreater;
  369. }
  370. }
  371. // lhs == rhs, so true if operation is EQUAL
  372. return op == OpEqual;
  373. }
  374. //=========================================================================
  375. // level 0 processes parenthetical expressions
  376. bool HandleLevel0(std::list<std::string> &newArgs,
  377. cmMakefile *makefile,
  378. std::string &errorString,
  379. cmake::MessageType &status)
  380. {
  381. int reducible;
  382. do
  383. {
  384. reducible = 0;
  385. std::list<std::string>::iterator arg = newArgs.begin();
  386. while (arg != newArgs.end())
  387. {
  388. if (*arg == "(")
  389. {
  390. // search for the closing paren for this opening one
  391. std::list<std::string>::iterator argClose;
  392. argClose = arg;
  393. argClose++;
  394. unsigned int depth = 1;
  395. while (argClose != newArgs.end() && depth)
  396. {
  397. if (*argClose == "(")
  398. {
  399. depth++;
  400. }
  401. if (*argClose == ")")
  402. {
  403. depth--;
  404. }
  405. argClose++;
  406. }
  407. if (depth)
  408. {
  409. errorString = "mismatched parenthesis in condition";
  410. status = cmake::FATAL_ERROR;
  411. return false;
  412. }
  413. // store the reduced args in this vector
  414. std::vector<std::string> newArgs2;
  415. // copy to the list structure
  416. std::list<std::string>::iterator argP1 = arg;
  417. argP1++;
  418. for(; argP1 != argClose; argP1++)
  419. {
  420. newArgs2.push_back(*argP1);
  421. }
  422. newArgs2.pop_back();
  423. // now recursively invoke IsTrue to handle the values inside the
  424. // parenthetical expression
  425. bool value =
  426. cmIfCommand::IsTrue(newArgs2, errorString, makefile, status);
  427. if(value)
  428. {
  429. *arg = "1";
  430. }
  431. else
  432. {
  433. *arg = "0";
  434. }
  435. argP1 = arg;
  436. argP1++;
  437. // remove the now evaluated parenthetical expression
  438. newArgs.erase(argP1,argClose);
  439. }
  440. ++arg;
  441. }
  442. }
  443. while (reducible);
  444. return true;
  445. }
  446. //=========================================================================
  447. // level one handles most predicates except for NOT
  448. bool HandleLevel1(std::list<std::string> &newArgs,
  449. cmMakefile *makefile,
  450. std::string &, cmake::MessageType &)
  451. {
  452. int reducible;
  453. do
  454. {
  455. reducible = 0;
  456. std::list<std::string>::iterator arg = newArgs.begin();
  457. std::list<std::string>::iterator argP1;
  458. std::list<std::string>::iterator argP2;
  459. while (arg != newArgs.end())
  460. {
  461. argP1 = arg;
  462. IncrementArguments(newArgs,argP1,argP2);
  463. // does a file exist
  464. if (*arg == "EXISTS" && argP1 != newArgs.end())
  465. {
  466. HandlePredicate(
  467. cmSystemTools::FileExists((argP1)->c_str()),
  468. reducible, arg, newArgs, argP1, argP2);
  469. }
  470. // does a directory with this name exist
  471. if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end())
  472. {
  473. HandlePredicate(
  474. cmSystemTools::FileIsDirectory((argP1)->c_str()),
  475. reducible, arg, newArgs, argP1, argP2);
  476. }
  477. // is the given path an absolute path ?
  478. if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end())
  479. {
  480. HandlePredicate(
  481. cmSystemTools::FileIsFullPath((argP1)->c_str()),
  482. reducible, arg, newArgs, argP1, argP2);
  483. }
  484. // does a command exist
  485. if (*arg == "COMMAND" && argP1 != newArgs.end())
  486. {
  487. HandlePredicate(
  488. makefile->CommandExists((argP1)->c_str()),
  489. reducible, arg, newArgs, argP1, argP2);
  490. }
  491. // does a policy exist
  492. if (*arg == "POLICY" && argP1 != newArgs.end())
  493. {
  494. cmPolicies::PolicyID pid;
  495. HandlePredicate(
  496. makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid),
  497. reducible, arg, newArgs, argP1, argP2);
  498. }
  499. // does a target exist
  500. if (*arg == "TARGET" && argP1 != newArgs.end())
  501. {
  502. HandlePredicate(
  503. makefile->FindTargetToUse((argP1)->c_str())? true:false,
  504. reducible, arg, newArgs, argP1, argP2);
  505. }
  506. // is a variable defined
  507. if (*arg == "DEFINED" && argP1 != newArgs.end())
  508. {
  509. size_t argP1len = argP1->size();
  510. bool bdef = false;
  511. if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
  512. argP1->operator[](argP1len-1) == '}')
  513. {
  514. std::string env = argP1->substr(4, argP1len-5);
  515. bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
  516. }
  517. else
  518. {
  519. bdef = makefile->IsDefinitionSet((argP1)->c_str());
  520. }
  521. HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
  522. }
  523. ++arg;
  524. }
  525. }
  526. while (reducible);
  527. return true;
  528. }
  529. //=========================================================================
  530. // level two handles most binary operations except for AND OR
  531. bool HandleLevel2(std::list<std::string> &newArgs,
  532. cmMakefile *makefile,
  533. std::string &errorString,
  534. cmake::MessageType &status)
  535. {
  536. int reducible;
  537. const char *def;
  538. const char *def2;
  539. do
  540. {
  541. reducible = 0;
  542. std::list<std::string>::iterator arg = newArgs.begin();
  543. std::list<std::string>::iterator argP1;
  544. std::list<std::string>::iterator argP2;
  545. while (arg != newArgs.end())
  546. {
  547. argP1 = arg;
  548. IncrementArguments(newArgs,argP1,argP2);
  549. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  550. *(argP1) == "MATCHES")
  551. {
  552. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  553. const char* rex = (argP2)->c_str();
  554. cmStringCommand::ClearMatches(makefile);
  555. cmsys::RegularExpression regEntry;
  556. if ( !regEntry.compile(rex) )
  557. {
  558. cmOStringStream error;
  559. error << "Regular expression \"" << rex << "\" cannot compile";
  560. errorString = error.str();
  561. status = cmake::FATAL_ERROR;
  562. return false;
  563. }
  564. if (regEntry.find(def))
  565. {
  566. cmStringCommand::StoreMatches(makefile, regEntry);
  567. *arg = "1";
  568. }
  569. else
  570. {
  571. *arg = "0";
  572. }
  573. newArgs.erase(argP2);
  574. newArgs.erase(argP1);
  575. argP1 = arg;
  576. IncrementArguments(newArgs,argP1,argP2);
  577. reducible = 1;
  578. }
  579. if (argP1 != newArgs.end() && *arg == "MATCHES")
  580. {
  581. *arg = "0";
  582. newArgs.erase(argP1);
  583. argP1 = arg;
  584. IncrementArguments(newArgs,argP1,argP2);
  585. reducible = 1;
  586. }
  587. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  588. (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
  589. *(argP1) == "EQUAL"))
  590. {
  591. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  592. def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
  593. double lhs;
  594. double rhs;
  595. bool result;
  596. if(sscanf(def, "%lg", &lhs) != 1 ||
  597. sscanf(def2, "%lg", &rhs) != 1)
  598. {
  599. result = false;
  600. }
  601. else if (*(argP1) == "LESS")
  602. {
  603. result = (lhs < rhs);
  604. }
  605. else if (*(argP1) == "GREATER")
  606. {
  607. result = (lhs > rhs);
  608. }
  609. else
  610. {
  611. result = (lhs == rhs);
  612. }
  613. HandleBinaryOp(result,
  614. reducible, arg, newArgs, argP1, argP2);
  615. }
  616. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  617. (*(argP1) == "STRLESS" ||
  618. *(argP1) == "STREQUAL" ||
  619. *(argP1) == "STRGREATER"))
  620. {
  621. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  622. def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
  623. int val = strcmp(def,def2);
  624. bool result;
  625. if (*(argP1) == "STRLESS")
  626. {
  627. result = (val < 0);
  628. }
  629. else if (*(argP1) == "STRGREATER")
  630. {
  631. result = (val > 0);
  632. }
  633. else // strequal
  634. {
  635. result = (val == 0);
  636. }
  637. HandleBinaryOp(result,
  638. reducible, arg, newArgs, argP1, argP2);
  639. }
  640. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  641. (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" ||
  642. *(argP1) == "VERSION_EQUAL"))
  643. {
  644. def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
  645. def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
  646. Op op = OpEqual;
  647. if(*argP1 == "VERSION_LESS")
  648. {
  649. op = OpLess;
  650. }
  651. else if(*argP1 == "VERSION_GREATER")
  652. {
  653. op = OpGreater;
  654. }
  655. bool result = HandleVersionCompare(op, def, def2);
  656. HandleBinaryOp(result,
  657. reducible, arg, newArgs, argP1, argP2);
  658. }
  659. // is file A newer than file B
  660. if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
  661. *(argP1) == "IS_NEWER_THAN")
  662. {
  663. int fileIsNewer=0;
  664. bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
  665. (argP2)->c_str(),
  666. &fileIsNewer);
  667. HandleBinaryOp(
  668. (success==false || fileIsNewer==1 || fileIsNewer==0),
  669. reducible, arg, newArgs, argP1, argP2);
  670. }
  671. ++arg;
  672. }
  673. }
  674. while (reducible);
  675. return true;
  676. }
  677. //=========================================================================
  678. // level 3 handles NOT
  679. bool HandleLevel3(std::list<std::string> &newArgs,
  680. cmMakefile *makefile,
  681. std::string &errorString,
  682. cmPolicies::PolicyStatus Policy12Status,
  683. cmake::MessageType &status)
  684. {
  685. int reducible;
  686. do
  687. {
  688. reducible = 0;
  689. std::list<std::string>::iterator arg = newArgs.begin();
  690. std::list<std::string>::iterator argP1;
  691. std::list<std::string>::iterator argP2;
  692. while (arg != newArgs.end())
  693. {
  694. argP1 = arg;
  695. IncrementArguments(newArgs,argP1,argP2);
  696. if (argP1 != newArgs.end() && *arg == "NOT")
  697. {
  698. bool rhs = GetBooleanValueWithAutoDereference(*argP1, makefile,
  699. errorString,
  700. Policy12Status,
  701. status);
  702. HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
  703. }
  704. ++arg;
  705. }
  706. }
  707. while (reducible);
  708. return true;
  709. }
  710. //=========================================================================
  711. // level 4 handles AND OR
  712. bool HandleLevel4(std::list<std::string> &newArgs,
  713. cmMakefile *makefile,
  714. std::string &errorString,
  715. cmPolicies::PolicyStatus Policy12Status,
  716. cmake::MessageType &status)
  717. {
  718. int reducible;
  719. bool lhs;
  720. bool rhs;
  721. do
  722. {
  723. reducible = 0;
  724. std::list<std::string>::iterator arg = newArgs.begin();
  725. std::list<std::string>::iterator argP1;
  726. std::list<std::string>::iterator argP2;
  727. while (arg != newArgs.end())
  728. {
  729. argP1 = arg;
  730. IncrementArguments(newArgs,argP1,argP2);
  731. if (argP1 != newArgs.end() && *(argP1) == "AND" &&
  732. argP2 != newArgs.end())
  733. {
  734. lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
  735. errorString,
  736. Policy12Status,
  737. status);
  738. rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
  739. errorString,
  740. Policy12Status,
  741. status);
  742. HandleBinaryOp((lhs && rhs),
  743. reducible, arg, newArgs, argP1, argP2);
  744. }
  745. if (argP1 != newArgs.end() && *(argP1) == "OR" &&
  746. argP2 != newArgs.end())
  747. {
  748. lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
  749. errorString,
  750. Policy12Status,
  751. status);
  752. rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
  753. errorString,
  754. Policy12Status,
  755. status);
  756. HandleBinaryOp((lhs || rhs),
  757. reducible, arg, newArgs, argP1, argP2);
  758. }
  759. ++arg;
  760. }
  761. }
  762. while (reducible);
  763. return true;
  764. }
  765. }
  766. //=========================================================================
  767. // order of operations,
  768. // 1. ( ) -- parenthetical groups
  769. // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
  770. // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
  771. // 4. NOT
  772. // 5. AND OR
  773. //
  774. // There is an issue on whether the arguments should be values of references,
  775. // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
  776. // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
  777. // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
  778. // take numeric values or variable names. STRLESS and STRGREATER take
  779. // variable names but if the variable name is not found it will use the name
  780. // directly. AND OR take variables or the values 0 or 1.
  781. bool cmIfCommand::IsTrue(const std::vector<std::string> &args,
  782. std::string &errorString, cmMakefile *makefile,
  783. cmake::MessageType &status)
  784. {
  785. errorString = "";
  786. // handle empty invocation
  787. if (args.size() < 1)
  788. {
  789. return false;
  790. }
  791. // store the reduced args in this vector
  792. std::list<std::string> newArgs;
  793. // copy to the list structure
  794. for(unsigned int i = 0; i < args.size(); ++i)
  795. {
  796. newArgs.push_back(args[i]);
  797. }
  798. // now loop through the arguments and see if we can reduce any of them
  799. // we do this multiple times. Once for each level of precedence
  800. // parens
  801. if (!HandleLevel0(newArgs, makefile, errorString, status))
  802. {
  803. return false;
  804. }
  805. //predicates
  806. if (!HandleLevel1(newArgs, makefile, errorString, status))
  807. {
  808. return false;
  809. }
  810. // binary ops
  811. if (!HandleLevel2(newArgs, makefile, errorString, status))
  812. {
  813. return false;
  814. }
  815. // used to store the value of policy CMP0012 for performance
  816. cmPolicies::PolicyStatus Policy12Status =
  817. makefile->GetPolicyStatus(cmPolicies::CMP0012);
  818. // NOT
  819. if (!HandleLevel3(newArgs, makefile, errorString,
  820. Policy12Status, status))
  821. {
  822. return false;
  823. }
  824. // AND OR
  825. if (!HandleLevel4(newArgs, makefile, errorString,
  826. Policy12Status, status))
  827. {
  828. return false;
  829. }
  830. // now at the end there should only be one argument left
  831. if (newArgs.size() != 1)
  832. {
  833. errorString = "Unknown arguments specified";
  834. return false;
  835. }
  836. return GetBooleanValueWithAutoDereference(*(newArgs.begin()),
  837. makefile,
  838. errorString,
  839. Policy12Status,
  840. status);
  841. }
  842. //=========================================================================
  843. const char* cmIfCommand::GetVariableOrString(const char* str,
  844. const cmMakefile* mf)
  845. {
  846. const char* def = mf->GetDefinition(str);
  847. if(!def)
  848. {
  849. def = str;
  850. }
  851. return def;
  852. }