1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmBlockCommand.h"
6 #include <cstdint> // IWYU pragma: keep
10 #include <cm/optional>
11 #include <cm/string_view>
12 #include <cmext/enum_set>
13 #include <cmext/string_view>
15 #include "cmArgumentParser.h"
16 #include "cmArgumentParserTypes.h"
17 #include "cmExecutionStatus.h"
18 #include "cmFunctionBlocker.h"
19 #include "cmListFileCache.h"
20 #include "cmMakefile.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h"
25 enum class ScopeType : std::uint8_t
30 using ScopeSet = cm::enum_set<ScopeType>;
32 class BlockScopePushPop
35 BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes);
36 ~BlockScopePushPop() = default;
38 BlockScopePushPop(const BlockScopePushPop&) = delete;
39 BlockScopePushPop& operator=(const BlockScopePushPop&) = delete;
42 std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
43 std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
46 BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes)
48 if (scopes.contains(ScopeType::POLICIES)) {
49 this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
51 if (scopes.contains(ScopeType::VARIABLES)) {
52 this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
56 class cmBlockFunctionBlocker : public cmFunctionBlocker
59 cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
60 std::vector<std::string> variableNames);
61 ~cmBlockFunctionBlocker() override;
63 cm::string_view StartCommandName() const override { return "block"_s; }
64 cm::string_view EndCommandName() const override { return "endblock"_s; }
66 bool EndCommandSupportsArguments() const override { return false; }
68 bool ArgumentsMatch(cmListFileFunction const& lff,
69 cmMakefile& mf) const override;
71 bool Replay(std::vector<cmListFileFunction> functions,
72 cmExecutionStatus& inStatus) override;
77 BlockScopePushPop BlockScope;
78 std::vector<std::string> VariableNames;
81 cmBlockFunctionBlocker::cmBlockFunctionBlocker(
82 cmMakefile* const mf, const ScopeSet& scopes,
83 std::vector<std::string> variableNames)
86 , BlockScope{ mf, scopes }
87 , VariableNames{ std::move(variableNames) }
91 cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
93 if (this->Scopes.contains(ScopeType::VARIABLES)) {
94 this->Makefile->RaiseScope(this->VariableNames);
98 bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
101 // no arguments expected for endblock()
102 // but this method should not be called because EndCommandHasArguments()
104 return lff.Arguments().empty();
107 bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
108 cmExecutionStatus& inStatus)
110 auto& mf = inStatus.GetMakefile();
112 // Invoke all the functions that were collected in the block.
113 for (cmListFileFunction const& fn : functions) {
114 cmExecutionStatus status(mf);
115 mf.ExecuteCommand(fn, status);
116 if (status.GetReturnInvoked()) {
117 mf.RaiseScope(status.GetReturnVariables());
118 inStatus.SetReturnInvoked(status.GetReturnVariables());
121 if (status.GetBreakInvoked()) {
122 inStatus.SetBreakInvoked();
125 if (status.GetContinueInvoked()) {
126 inStatus.SetContinueInvoked();
129 if (cmSystemTools::GetFatalErrorOccurred()) {
136 } // anonymous namespace
138 bool cmBlockCommand(std::vector<std::string> const& args,
139 cmExecutionStatus& status)
141 struct Arguments : public ArgumentParser::ParseResult
143 cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
144 ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
146 static auto const parser = cmArgumentParser<Arguments>{}
147 .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
148 .Bind("PROPAGATE"_s, &Arguments::Propagate);
149 std::vector<std::string> unrecognizedArguments;
150 auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
152 if (!unrecognizedArguments.empty()) {
153 status.SetError(cmStrCat("called with unsupported argument \"",
154 unrecognizedArguments[0], '"'));
155 cmSystemTools::SetFatalErrorOccurred();
159 if (parsedArgs.MaybeReportError(status.GetMakefile())) {
160 cmSystemTools::SetFatalErrorOccurred();
166 if (parsedArgs.ScopeFor) {
167 for (auto const& scope : *parsedArgs.ScopeFor) {
168 if (scope == "VARIABLES"_s) {
169 scopes.insert(ScopeType::VARIABLES);
172 if (scope == "POLICIES"_s) {
173 scopes.insert(ScopeType::POLICIES);
176 status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
177 cmSystemTools::SetFatalErrorOccurred();
181 scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
183 if (!scopes.contains(ScopeType::VARIABLES) &&
184 !parsedArgs.Propagate.empty()) {
186 "PROPAGATE cannot be specified without a new scope for VARIABLES");
187 cmSystemTools::SetFatalErrorOccurred();
191 // create a function blocker
192 auto fb = cm::make_unique<cmBlockFunctionBlocker>(
193 &status.GetMakefile(), scopes, parsedArgs.Propagate);
194 status.GetMakefile().AddFunctionBlocker(std::move(fb));