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