cmBlockCommand.cxx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 "cmBlockCommand.h"
  4. #include <cstdint> // IWYU pragma: keep
  5. #include <utility>
  6. #include <cm/memory>
  7. #include <cm/optional>
  8. #include <cm/string_view>
  9. #include <cmext/enum_set>
  10. #include <cmext/string_view>
  11. #include "cmArgumentParser.h"
  12. #include "cmArgumentParserTypes.h"
  13. #include "cmExecutionStatus.h"
  14. #include "cmFunctionBlocker.h"
  15. #include "cmListFileCache.h"
  16. #include "cmMakefile.h"
  17. #include "cmStringAlgorithms.h"
  18. #include "cmSystemTools.h"
  19. namespace {
  20. enum class ScopeType : std::uint8_t
  21. {
  22. VARIABLES,
  23. POLICIES
  24. };
  25. using ScopeSet = cm::enum_set<ScopeType>;
  26. class BlockScopePushPop
  27. {
  28. public:
  29. BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes);
  30. ~BlockScopePushPop() = default;
  31. BlockScopePushPop(const BlockScopePushPop&) = delete;
  32. BlockScopePushPop& operator=(const BlockScopePushPop&) = delete;
  33. private:
  34. std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
  35. std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
  36. };
  37. BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes)
  38. {
  39. if (scopes.contains(ScopeType::POLICIES)) {
  40. this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
  41. }
  42. if (scopes.contains(ScopeType::VARIABLES)) {
  43. this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
  44. }
  45. }
  46. class cmBlockFunctionBlocker : public cmFunctionBlocker
  47. {
  48. public:
  49. cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
  50. std::vector<std::string> variableNames);
  51. ~cmBlockFunctionBlocker() override;
  52. cm::string_view StartCommandName() const override { return "block"_s; }
  53. cm::string_view EndCommandName() const override { return "endblock"_s; }
  54. bool EndCommandSupportsArguments() const override { return false; }
  55. bool ArgumentsMatch(cmListFileFunction const& lff,
  56. cmMakefile& mf) const override;
  57. bool Replay(std::vector<cmListFileFunction> functions,
  58. cmExecutionStatus& inStatus) override;
  59. private:
  60. cmMakefile* Makefile;
  61. ScopeSet Scopes;
  62. BlockScopePushPop BlockScope;
  63. std::vector<std::string> VariableNames;
  64. };
  65. cmBlockFunctionBlocker::cmBlockFunctionBlocker(
  66. cmMakefile* const mf, const ScopeSet& scopes,
  67. std::vector<std::string> variableNames)
  68. : Makefile{ mf }
  69. , Scopes{ scopes }
  70. , BlockScope{ mf, scopes }
  71. , VariableNames{ std::move(variableNames) }
  72. {
  73. }
  74. cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
  75. {
  76. if (this->Scopes.contains(ScopeType::VARIABLES)) {
  77. this->Makefile->RaiseScope(this->VariableNames);
  78. }
  79. }
  80. bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
  81. cmMakefile&) const
  82. {
  83. // no arguments expected for endblock()
  84. // but this method should not be called because EndCommandHasArguments()
  85. // returns false.
  86. return lff.Arguments().empty();
  87. }
  88. bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
  89. cmExecutionStatus& inStatus)
  90. {
  91. auto& mf = inStatus.GetMakefile();
  92. // Invoke all the functions that were collected in the block.
  93. for (cmListFileFunction const& fn : functions) {
  94. cmExecutionStatus status(mf);
  95. mf.ExecuteCommand(fn, status);
  96. if (status.GetReturnInvoked()) {
  97. mf.RaiseScope(status.GetReturnVariables());
  98. inStatus.SetReturnInvoked(status.GetReturnVariables());
  99. return true;
  100. }
  101. if (status.GetBreakInvoked()) {
  102. inStatus.SetBreakInvoked();
  103. return true;
  104. }
  105. if (status.GetContinueInvoked()) {
  106. inStatus.SetContinueInvoked();
  107. return true;
  108. }
  109. if (cmSystemTools::GetFatalErrorOccurred()) {
  110. return true;
  111. }
  112. }
  113. return true;
  114. }
  115. } // anonymous namespace
  116. bool cmBlockCommand(std::vector<std::string> const& args,
  117. cmExecutionStatus& status)
  118. {
  119. struct Arguments : public ArgumentParser::ParseResult
  120. {
  121. cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
  122. ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
  123. };
  124. static auto const parser = cmArgumentParser<Arguments>{}
  125. .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
  126. .Bind("PROPAGATE"_s, &Arguments::Propagate);
  127. std::vector<std::string> unrecognizedArguments;
  128. auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
  129. if (!unrecognizedArguments.empty()) {
  130. status.SetError(cmStrCat("called with unsupported argument \"",
  131. unrecognizedArguments[0], '"'));
  132. cmSystemTools::SetFatalErrorOccurred();
  133. return false;
  134. }
  135. if (parsedArgs.MaybeReportError(status.GetMakefile())) {
  136. cmSystemTools::SetFatalErrorOccurred();
  137. return true;
  138. }
  139. ScopeSet scopes;
  140. if (parsedArgs.ScopeFor) {
  141. for (auto const& scope : *parsedArgs.ScopeFor) {
  142. if (scope == "VARIABLES"_s) {
  143. scopes.insert(ScopeType::VARIABLES);
  144. continue;
  145. }
  146. if (scope == "POLICIES"_s) {
  147. scopes.insert(ScopeType::POLICIES);
  148. continue;
  149. }
  150. status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
  151. cmSystemTools::SetFatalErrorOccurred();
  152. return false;
  153. }
  154. } else {
  155. scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
  156. }
  157. if (!scopes.contains(ScopeType::VARIABLES) &&
  158. !parsedArgs.Propagate.empty()) {
  159. status.SetError(
  160. "PROPAGATE cannot be specified without a new scope for VARIABLES");
  161. cmSystemTools::SetFatalErrorOccurred();
  162. return false;
  163. }
  164. // create a function blocker
  165. auto fb = cm::make_unique<cmBlockFunctionBlocker>(
  166. &status.GetMakefile(), scopes, parsedArgs.Propagate);
  167. status.GetMakefile().AddFunctionBlocker(std::move(fb));
  168. return true;
  169. }