cmBlockCommand.cxx 5.7 KB

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