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 "cmWhileCommand.h"
9 #include <cm/string_view>
10 #include <cmext/string_view>
12 #include "cmConditionEvaluator.h"
13 #include "cmExecutionStatus.h"
14 #include "cmExpandedCommandArgument.h"
15 #include "cmFunctionBlocker.h"
16 #include "cmListFileCache.h"
17 #include "cmMakefile.h"
18 #include "cmMessageType.h"
19 #include "cmOutputConverter.h"
20 #include "cmPolicies.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h"
25 class cmWhileFunctionBlocker : public cmFunctionBlocker
28 cmWhileFunctionBlocker(cmMakefile* mf, std::vector<cmListFileArgument> args);
29 ~cmWhileFunctionBlocker() override;
31 cm::string_view StartCommandName() const override { return "while"_s; }
32 cm::string_view EndCommandName() const override { return "endwhile"_s; }
34 bool ArgumentsMatch(cmListFileFunction const& lff,
35 cmMakefile& mf) const override;
37 bool Replay(std::vector<cmListFileFunction> functions,
38 cmExecutionStatus& inStatus) override;
42 std::vector<cmListFileArgument> Args;
45 cmWhileFunctionBlocker::cmWhileFunctionBlocker(
46 cmMakefile* const mf, std::vector<cmListFileArgument> args)
48 , Args{ std::move(args) }
50 this->Makefile->PushLoopBlock();
53 cmWhileFunctionBlocker::~cmWhileFunctionBlocker()
55 this->Makefile->PopLoopBlock();
58 bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
61 return lff.Arguments().empty() || lff.Arguments() == this->Args;
64 bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
65 cmExecutionStatus& inStatus)
67 auto& mf = inStatus.GetMakefile();
69 cmListFileBacktrace whileBT =
70 mf.GetBacktrace().Push(this->GetStartingContext());
72 std::vector<cmExpandedCommandArgument> expandedArguments;
73 // At least same size expected for `expandedArguments` as `Args`
74 expandedArguments.reserve(this->Args.size());
76 auto expandArgs = [&mf](std::vector<cmListFileArgument> const& args,
77 std::vector<cmExpandedCommandArgument>& out)
78 -> std::vector<cmExpandedCommandArgument>& {
80 mf.ExpandArguments(args, out);
84 // For compatibility with projects that do not set CMP0130 to NEW,
85 // we tolerate condition errors that evaluate to false.
86 bool enforceError = true;
87 std::string errorString;
88 MessageType messageType;
90 for (cmConditionEvaluator conditionEvaluator(mf, whileBT);
91 (enforceError = /* enforce condition errors that evaluate to true */
92 conditionEvaluator.IsTrue(expandArgs(this->Args, expandedArguments),
93 errorString, messageType));) {
94 // Invoke all the functions that were collected in the block.
95 for (cmListFileFunction const& fn : functions) {
96 cmExecutionStatus status(mf);
97 mf.ExecuteCommand(fn, status);
98 if (status.GetReturnInvoked()) {
99 inStatus.SetReturnInvoked(status.GetReturnVariables());
102 if (status.GetBreakInvoked()) {
105 if (status.GetContinueInvoked()) {
108 if (cmSystemTools::GetFatalErrorOccurred()) {
114 if (!errorString.empty() && !enforceError) {
115 // This error should only be enforced if CMP0130 is NEW.
116 switch (mf.GetPolicyStatus(cmPolicies::CMP0130)) {
117 case cmPolicies::WARN:
118 // Convert the error to a warning and enforce it.
119 messageType = MessageType::AUTHOR_WARNING;
122 case cmPolicies::OLD:
123 // OLD behavior is to silently ignore the error.
125 case cmPolicies::REQUIRED_ALWAYS:
126 case cmPolicies::REQUIRED_IF_USED:
127 case cmPolicies::NEW:
128 // NEW behavior is to enforce the error.
134 if (!errorString.empty() && enforceError) {
135 std::string err = "while() given incorrect arguments:\n ";
136 for (auto const& i : expandedArguments) {
138 err += cmOutputConverter::EscapeForCMake(i.GetValue());
142 if (mf.GetPolicyStatus(cmPolicies::CMP0130) == cmPolicies::WARN) {
144 cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0130), '\n', err);
146 mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
147 if (messageType == MessageType::FATAL_ERROR) {
148 cmSystemTools::SetFatalErrorOccurred();
155 bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
156 cmExecutionStatus& status)
159 status.SetError("called with incorrect number of arguments");
163 // create a function blocker
164 auto& makefile = status.GetMakefile();
165 makefile.AddFunctionBlocker(
166 cm::make_unique<cmWhileFunctionBlocker>(&makefile, args));