cmWhileCommand.cxx 4.4 KB

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