cmWhileCommand.cxx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 "cmWhileCommand.h"
  4. #include "cm_memory.hxx"
  5. #include "cmConditionEvaluator.h"
  6. #include "cmExecutionStatus.h"
  7. #include "cmExpandedCommandArgument.h"
  8. #include "cmFunctionBlocker.h"
  9. #include "cmListFileCache.h"
  10. #include "cmMakefile.h"
  11. #include "cmMessageType.h"
  12. #include "cmSystemTools.h"
  13. #include <string>
  14. #include <utility>
  15. class cmWhileFunctionBlocker : public cmFunctionBlocker
  16. {
  17. public:
  18. cmWhileFunctionBlocker(cmMakefile* mf);
  19. ~cmWhileFunctionBlocker() override;
  20. bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
  21. cmExecutionStatus&) override;
  22. bool ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) override;
  23. bool Replay(std::vector<cmListFileFunction> const& functions,
  24. cmExecutionStatus& inStatus);
  25. std::vector<cmListFileArgument> Args;
  26. std::vector<cmListFileFunction> Functions;
  27. private:
  28. cmMakefile* Makefile;
  29. int Depth;
  30. };
  31. cmWhileFunctionBlocker::cmWhileFunctionBlocker(cmMakefile* mf)
  32. : Makefile(mf)
  33. , Depth(0)
  34. {
  35. this->Makefile->PushLoopBlock();
  36. }
  37. cmWhileFunctionBlocker::~cmWhileFunctionBlocker()
  38. {
  39. this->Makefile->PopLoopBlock();
  40. }
  41. bool cmWhileFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
  42. cmMakefile& mf,
  43. cmExecutionStatus& inStatus)
  44. {
  45. // at end of for each execute recorded commands
  46. if (lff.Name.Lower == "while") {
  47. // record the number of while commands past this one
  48. this->Depth++;
  49. } else if (lff.Name.Lower == "endwhile") {
  50. // if this is the endwhile for this while loop then execute
  51. if (!this->Depth) {
  52. // Remove the function blocker for this scope or bail.
  53. std::unique_ptr<cmFunctionBlocker> fb(
  54. mf.RemoveFunctionBlocker(this, lff));
  55. if (!fb) {
  56. return false;
  57. }
  58. return this->Replay(this->Functions, inStatus);
  59. }
  60. // decrement for each nested while that ends
  61. this->Depth--;
  62. }
  63. // record the command
  64. this->Functions.push_back(lff);
  65. // always return true
  66. return true;
  67. }
  68. bool cmWhileFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
  69. cmMakefile&)
  70. {
  71. if (lff.Name.Lower == "endwhile") {
  72. // if the endwhile has arguments, then make sure
  73. // they match the arguments of the matching while
  74. if (lff.Arguments.empty() || lff.Arguments == this->Args) {
  75. return true;
  76. }
  77. }
  78. return false;
  79. }
  80. bool cmWhileFunctionBlocker::Replay(
  81. std::vector<cmListFileFunction> const& functions,
  82. cmExecutionStatus& inStatus)
  83. {
  84. cmMakefile& mf = inStatus.GetMakefile();
  85. std::string errorString;
  86. std::vector<cmExpandedCommandArgument> expandedArguments;
  87. mf.ExpandArguments(this->Args, expandedArguments);
  88. MessageType messageType;
  89. cmListFileContext execContext = this->GetStartingContext();
  90. cmCommandContext commandContext;
  91. commandContext.Line = execContext.Line;
  92. commandContext.Name = execContext.Name;
  93. cmConditionEvaluator conditionEvaluator(mf, this->GetStartingContext(),
  94. mf.GetBacktrace(commandContext));
  95. bool isTrue =
  96. conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
  97. while (isTrue) {
  98. if (!errorString.empty()) {
  99. std::string err = "had incorrect arguments: ";
  100. for (cmListFileArgument const& arg : this->Args) {
  101. err += (arg.Delim ? "\"" : "");
  102. err += arg.Value;
  103. err += (arg.Delim ? "\"" : "");
  104. err += " ";
  105. }
  106. err += "(";
  107. err += errorString;
  108. err += ").";
  109. mf.IssueMessage(messageType, err);
  110. if (messageType == MessageType::FATAL_ERROR) {
  111. cmSystemTools::SetFatalErrorOccured();
  112. return true;
  113. }
  114. }
  115. // Invoke all the functions that were collected in the block.
  116. for (cmListFileFunction const& fn : functions) {
  117. cmExecutionStatus status(mf);
  118. mf.ExecuteCommand(fn, status);
  119. if (status.GetReturnInvoked()) {
  120. inStatus.SetReturnInvoked();
  121. return true;
  122. }
  123. if (status.GetBreakInvoked()) {
  124. return true;
  125. }
  126. if (status.GetContinueInvoked()) {
  127. break;
  128. }
  129. if (cmSystemTools::GetFatalErrorOccured()) {
  130. return true;
  131. }
  132. }
  133. expandedArguments.clear();
  134. mf.ExpandArguments(this->Args, expandedArguments);
  135. isTrue =
  136. conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
  137. }
  138. return true;
  139. }
  140. bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
  141. cmExecutionStatus& status)
  142. {
  143. if (args.empty()) {
  144. status.SetError("called with incorrect number of arguments");
  145. return false;
  146. }
  147. // create a function blocker
  148. {
  149. cmMakefile& makefile = status.GetMakefile();
  150. auto fb = cm::make_unique<cmWhileFunctionBlocker>(&makefile);
  151. fb->Args = args;
  152. makefile.AddFunctionBlocker(std::move(fb));
  153. }
  154. return true;
  155. }