cmWhileCommand.cxx 4.9 KB

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