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