cmIfCommand.cxx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmIfCommand.h"
  4. #include "cmConditionEvaluator.h"
  5. #include "cmExecutionStatus.h"
  6. #include "cmExpandedCommandArgument.h"
  7. #include "cmMakefile.h"
  8. #include "cmOutputConverter.h"
  9. #include "cmSystemTools.h"
  10. #include "cmake.h"
  11. #include <memory> // IWYU pragma: keep
  12. static std::string cmIfCommandError(
  13. std::vector<cmExpandedCommandArgument> const& args)
  14. {
  15. std::string err = "given arguments:\n ";
  16. for (cmExpandedCommandArgument const& i : args) {
  17. err += " ";
  18. err += cmOutputConverter::EscapeForCMake(i.GetValue());
  19. }
  20. err += "\n";
  21. return err;
  22. }
  23. //=========================================================================
  24. bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
  25. cmMakefile& mf,
  26. cmExecutionStatus& inStatus)
  27. {
  28. // we start by recording all the functions
  29. if (!cmSystemTools::Strucmp(lff.Name.c_str(), "if")) {
  30. this->ScopeDepth++;
  31. } else if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endif")) {
  32. this->ScopeDepth--;
  33. // if this is the endif for this if statement, then start executing
  34. if (!this->ScopeDepth) {
  35. // Remove the function blocker for this scope or bail.
  36. std::unique_ptr<cmFunctionBlocker> fb(
  37. mf.RemoveFunctionBlocker(this, lff));
  38. if (!fb.get()) {
  39. return false;
  40. }
  41. // execute the functions for the true parts of the if statement
  42. cmExecutionStatus status;
  43. int scopeDepth = 0;
  44. for (cmListFileFunction const& func : this->Functions) {
  45. // keep track of scope depth
  46. if (!cmSystemTools::Strucmp(func.Name.c_str(), "if")) {
  47. scopeDepth++;
  48. }
  49. if (!cmSystemTools::Strucmp(func.Name.c_str(), "endif")) {
  50. scopeDepth--;
  51. }
  52. // watch for our state change
  53. if (scopeDepth == 0 &&
  54. !cmSystemTools::Strucmp(func.Name.c_str(), "else")) {
  55. if (this->ElseSeen) {
  56. cmListFileBacktrace bt = mf.GetBacktrace(func);
  57. mf.GetCMakeInstance()->IssueMessage(
  58. cmake::FATAL_ERROR,
  59. "A duplicate ELSE command was found inside an IF block.", bt);
  60. cmSystemTools::SetFatalErrorOccured();
  61. return true;
  62. }
  63. this->IsBlocking = this->HasRun;
  64. this->HasRun = true;
  65. this->ElseSeen = true;
  66. // if trace is enabled, print a (trivially) evaluated "else"
  67. // statement
  68. if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) {
  69. mf.PrintCommandTrace(func);
  70. }
  71. } else if (scopeDepth == 0 &&
  72. !cmSystemTools::Strucmp(func.Name.c_str(), "elseif")) {
  73. if (this->ElseSeen) {
  74. cmListFileBacktrace bt = mf.GetBacktrace(func);
  75. mf.GetCMakeInstance()->IssueMessage(
  76. cmake::FATAL_ERROR,
  77. "An ELSEIF command was found after an ELSE command.", bt);
  78. cmSystemTools::SetFatalErrorOccured();
  79. return true;
  80. }
  81. if (this->HasRun) {
  82. this->IsBlocking = true;
  83. } else {
  84. // if trace is enabled, print the evaluated "elseif" statement
  85. if (mf.GetCMakeInstance()->GetTrace()) {
  86. mf.PrintCommandTrace(func);
  87. }
  88. std::string errorString;
  89. std::vector<cmExpandedCommandArgument> expandedArguments;
  90. mf.ExpandArguments(func.Arguments, expandedArguments);
  91. cmake::MessageType messType;
  92. cmListFileContext conditionContext =
  93. cmListFileContext::FromCommandContext(
  94. func, this->GetStartingContext().FilePath);
  95. cmConditionEvaluator conditionEvaluator(mf, conditionContext,
  96. mf.GetBacktrace(func));
  97. bool isTrue = conditionEvaluator.IsTrue(expandedArguments,
  98. errorString, messType);
  99. if (!errorString.empty()) {
  100. std::string err = cmIfCommandError(expandedArguments);
  101. err += errorString;
  102. cmListFileBacktrace bt = mf.GetBacktrace(func);
  103. mf.GetCMakeInstance()->IssueMessage(messType, err, bt);
  104. if (messType == cmake::FATAL_ERROR) {
  105. cmSystemTools::SetFatalErrorOccured();
  106. return true;
  107. }
  108. }
  109. if (isTrue) {
  110. this->IsBlocking = false;
  111. this->HasRun = true;
  112. }
  113. }
  114. }
  115. // should we execute?
  116. else if (!this->IsBlocking) {
  117. status.Clear();
  118. mf.ExecuteCommand(func, status);
  119. if (status.GetReturnInvoked()) {
  120. inStatus.SetReturnInvoked();
  121. return true;
  122. }
  123. if (status.GetBreakInvoked()) {
  124. inStatus.SetBreakInvoked();
  125. return true;
  126. }
  127. if (status.GetContinueInvoked()) {
  128. inStatus.SetContinueInvoked();
  129. return true;
  130. }
  131. }
  132. }
  133. return true;
  134. }
  135. }
  136. // record the command
  137. this->Functions.push_back(lff);
  138. // always return true
  139. return true;
  140. }
  141. //=========================================================================
  142. bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
  143. cmMakefile&)
  144. {
  145. if (!cmSystemTools::Strucmp(lff.Name.c_str(), "endif")) {
  146. // if the endif has arguments, then make sure
  147. // they match the arguments of the matching if
  148. if (lff.Arguments.empty() || lff.Arguments == this->Args) {
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. //=========================================================================
  155. bool cmIfCommand::InvokeInitialPass(
  156. const std::vector<cmListFileArgument>& args, cmExecutionStatus&)
  157. {
  158. std::string errorString;
  159. std::vector<cmExpandedCommandArgument> expandedArguments;
  160. this->Makefile->ExpandArguments(args, expandedArguments);
  161. cmake::MessageType status;
  162. cmConditionEvaluator conditionEvaluator(
  163. *(this->Makefile), this->Makefile->GetExecutionContext(),
  164. this->Makefile->GetBacktrace());
  165. bool isTrue =
  166. conditionEvaluator.IsTrue(expandedArguments, errorString, status);
  167. if (!errorString.empty()) {
  168. std::string err = "if " + cmIfCommandError(expandedArguments);
  169. err += errorString;
  170. if (status == cmake::FATAL_ERROR) {
  171. this->Makefile->IssueMessage(cmake::FATAL_ERROR, err);
  172. cmSystemTools::SetFatalErrorOccured();
  173. return true;
  174. }
  175. this->Makefile->IssueMessage(status, err);
  176. }
  177. cmIfFunctionBlocker* f = new cmIfFunctionBlocker();
  178. // if is isn't true block the commands
  179. f->ScopeDepth = 1;
  180. f->IsBlocking = !isTrue;
  181. if (isTrue) {
  182. f->HasRun = true;
  183. }
  184. f->Args = args;
  185. this->Makefile->AddFunctionBlocker(f);
  186. return true;
  187. }