cmBlockCommand.cxx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. BlockScopePushPop BlockScope;
  62. std::vector<std::string> VariableNames;
  63. };
  64. cmBlockFunctionBlocker::cmBlockFunctionBlocker(
  65. cmMakefile* const mf, const ScopeSet& scopes,
  66. std::vector<std::string> variableNames)
  67. : Makefile{ mf }
  68. , BlockScope{ mf, scopes }
  69. , VariableNames{ std::move(variableNames) }
  70. {
  71. }
  72. cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
  73. {
  74. for (auto const& varName : this->VariableNames) {
  75. if (this->Makefile->IsNormalDefinitionSet(varName)) {
  76. this->Makefile->RaiseScope(varName,
  77. this->Makefile->GetDefinition(varName));
  78. } else {
  79. // unset variable in parent scope
  80. this->Makefile->RaiseScope(varName, nullptr);
  81. }
  82. }
  83. }
  84. bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
  85. cmMakefile&) const
  86. {
  87. // no arguments expected for endblock()
  88. // but this method should not be called because EndCommandHasArguments()
  89. // returns false.
  90. return lff.Arguments().empty();
  91. }
  92. bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
  93. cmExecutionStatus& inStatus)
  94. {
  95. auto& mf = inStatus.GetMakefile();
  96. // Invoke all the functions that were collected in the block.
  97. for (cmListFileFunction const& fn : functions) {
  98. cmExecutionStatus status(mf);
  99. mf.ExecuteCommand(fn, status);
  100. if (status.GetReturnInvoked()) {
  101. inStatus.SetReturnInvoked();
  102. return true;
  103. }
  104. if (status.GetBreakInvoked()) {
  105. inStatus.SetBreakInvoked();
  106. return true;
  107. }
  108. if (status.GetContinueInvoked()) {
  109. inStatus.SetContinueInvoked();
  110. return true;
  111. }
  112. if (cmSystemTools::GetFatalErrorOccurred()) {
  113. return true;
  114. }
  115. }
  116. return true;
  117. }
  118. } // anonymous namespace
  119. bool cmBlockCommand(std::vector<std::string> const& args,
  120. cmExecutionStatus& status)
  121. {
  122. struct Arguments : public ArgumentParser::ParseResult
  123. {
  124. cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
  125. ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
  126. };
  127. static auto const parser = cmArgumentParser<Arguments>{}
  128. .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
  129. .Bind("PROPAGATE"_s, &Arguments::Propagate);
  130. std::vector<std::string> unrecognizedArguments;
  131. auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
  132. if (!unrecognizedArguments.empty()) {
  133. status.SetError(cmStrCat("called with unsupported argument \"",
  134. unrecognizedArguments[0], '"'));
  135. cmSystemTools::SetFatalErrorOccurred();
  136. return false;
  137. }
  138. if (parsedArgs.MaybeReportError(status.GetMakefile())) {
  139. cmSystemTools::SetFatalErrorOccurred();
  140. return true;
  141. }
  142. ScopeSet scopes;
  143. if (parsedArgs.ScopeFor) {
  144. for (auto const& scope : *parsedArgs.ScopeFor) {
  145. if (scope == "VARIABLES"_s) {
  146. scopes.insert(ScopeType::VARIABLES);
  147. continue;
  148. }
  149. if (scope == "POLICIES"_s) {
  150. scopes.insert(ScopeType::POLICIES);
  151. continue;
  152. }
  153. status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
  154. cmSystemTools::SetFatalErrorOccurred();
  155. return false;
  156. }
  157. } else {
  158. scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
  159. }
  160. if (!scopes.contains(ScopeType::VARIABLES) &&
  161. !parsedArgs.Propagate.empty()) {
  162. status.SetError(
  163. "PROPAGATE cannot be specified without a new scope for VARIABLES");
  164. cmSystemTools::SetFatalErrorOccurred();
  165. return false;
  166. }
  167. // create a function blocker
  168. auto fb = cm::make_unique<cmBlockFunctionBlocker>(
  169. &status.GetMakefile(), scopes, parsedArgs.Propagate);
  170. status.GetMakefile().AddFunctionBlocker(std::move(fb));
  171. return true;
  172. }