resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmBlockCommand.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3
4 #include "cmBlockCommand.h"
5
6 #include <cstdint> // IWYU pragma: keep
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, const ScopeSet& scopes);
36   ~BlockScopePushPop() = default;
37
38   BlockScopePushPop(const BlockScopePushPop&) = delete;
39   BlockScopePushPop& operator=(const BlockScopePushPop&) = 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, const ScopeSet& scopes)
47 {
48   if (scopes.contains(ScopeType::POLICIES)) {
49     this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
50   }
51   if (scopes.contains(ScopeType::VARIABLES)) {
52     this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
53   }
54 }
55
56 class cmBlockFunctionBlocker : public cmFunctionBlocker
57 {
58 public:
59   cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
60                          std::vector<std::string> variableNames);
61   ~cmBlockFunctionBlocker() override;
62
63   cm::string_view StartCommandName() const override { return "block"_s; }
64   cm::string_view EndCommandName() const override { return "endblock"_s; }
65
66   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, const ScopeSet& scopes,
83   std::vector<std::string> variableNames)
84   : Makefile{ mf }
85   , Scopes{ scopes }
86   , BlockScope{ mf, scopes }
87   , VariableNames{ std::move(variableNames) }
88 {
89 }
90
91 cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
92 {
93   if (this->Scopes.contains(ScopeType::VARIABLES)) {
94     this->Makefile->RaiseScope(this->VariableNames);
95   }
96 }
97
98 bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
99                                             cmMakefile&) const
100 {
101   // no arguments expected for endblock()
102   // but this method should not be called because EndCommandHasArguments()
103   // returns false.
104   return lff.Arguments().empty();
105 }
106
107 bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
108                                     cmExecutionStatus& inStatus)
109 {
110   auto& mf = inStatus.GetMakefile();
111
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());
119       return true;
120     }
121     if (status.GetBreakInvoked()) {
122       inStatus.SetBreakInvoked();
123       return true;
124     }
125     if (status.GetContinueInvoked()) {
126       inStatus.SetContinueInvoked();
127       return true;
128     }
129     if (cmSystemTools::GetFatalErrorOccurred()) {
130       return true;
131     }
132   }
133   return true;
134 }
135
136 } // anonymous namespace
137
138 bool cmBlockCommand(std::vector<std::string> const& args,
139                     cmExecutionStatus& status)
140 {
141   struct Arguments : public ArgumentParser::ParseResult
142   {
143     cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
144     ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
145   };
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);
151
152   if (!unrecognizedArguments.empty()) {
153     status.SetError(cmStrCat("called with unsupported argument \"",
154                              unrecognizedArguments[0], '"'));
155     cmSystemTools::SetFatalErrorOccurred();
156     return false;
157   }
158
159   if (parsedArgs.MaybeReportError(status.GetMakefile())) {
160     cmSystemTools::SetFatalErrorOccurred();
161     return true;
162   }
163
164   ScopeSet scopes;
165
166   if (parsedArgs.ScopeFor) {
167     for (auto const& scope : *parsedArgs.ScopeFor) {
168       if (scope == "VARIABLES"_s) {
169         scopes.insert(ScopeType::VARIABLES);
170         continue;
171       }
172       if (scope == "POLICIES"_s) {
173         scopes.insert(ScopeType::POLICIES);
174         continue;
175       }
176       status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
177       cmSystemTools::SetFatalErrorOccurred();
178       return false;
179     }
180   } else {
181     scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
182   }
183   if (!scopes.contains(ScopeType::VARIABLES) &&
184       !parsedArgs.Propagate.empty()) {
185     status.SetError(
186       "PROPAGATE cannot be specified without a new scope for VARIABLES");
187     cmSystemTools::SetFatalErrorOccurred();
188     return false;
189   }
190
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));
195
196   return true;
197 }