Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmWhileCommand.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
#include "cmWhileCommand.h"
4
5
#include <string>
6
#include <utility>
7
8
#include <cm/memory>
9
#include <cm/string_view>
10
#include <cmext/string_view>
11
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"
23
#include "cmake.h"
24
25
class cmWhileFunctionBlocker : public cmFunctionBlocker
26
{
27
public:
28
  cmWhileFunctionBlocker(cmMakefile* mf, std::vector<cmListFileArgument> args);
29
  ~cmWhileFunctionBlocker() override;
30
31
0
  cm::string_view StartCommandName() const override { return "while"_s; }
32
0
  cm::string_view EndCommandName() const override { return "endwhile"_s; }
33
34
  bool ArgumentsMatch(cmListFileFunction const& lff,
35
                      cmMakefile& mf) const override;
36
37
  bool Replay(std::vector<cmListFileFunction> functions,
38
              cmExecutionStatus& inStatus) override;
39
40
private:
41
  cmMakefile* Makefile;
42
  std::vector<cmListFileArgument> Args;
43
};
44
45
cmWhileFunctionBlocker::cmWhileFunctionBlocker(
46
  cmMakefile* const mf, std::vector<cmListFileArgument> args)
47
0
  : Makefile{ mf }
48
0
  , Args{ std::move(args) }
49
0
{
50
0
  this->Makefile->PushLoopBlock();
51
0
}
52
53
cmWhileFunctionBlocker::~cmWhileFunctionBlocker()
54
0
{
55
0
  this->Makefile->PopLoopBlock();
56
0
}
57
58
bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
59
                                            cmMakefile&) const
60
0
{
61
0
  return lff.Arguments().empty() || lff.Arguments() == this->Args;
62
0
}
63
64
bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
65
                                    cmExecutionStatus& inStatus)
66
0
{
67
0
  auto& mf = inStatus.GetMakefile();
68
69
0
  cmListFileBacktrace whileBT =
70
0
    mf.GetBacktrace().Push(this->GetStartingContext());
71
72
0
  std::vector<cmExpandedCommandArgument> expandedArguments;
73
  // At least same size expected for `expandedArguments` as `Args`
74
0
  expandedArguments.reserve(this->Args.size());
75
76
0
  auto expandArgs = [&mf](std::vector<cmListFileArgument> const& args,
77
0
                          std::vector<cmExpandedCommandArgument>& out)
78
0
    -> std::vector<cmExpandedCommandArgument>& {
79
0
    out.clear();
80
0
    mf.ExpandArguments(args, out);
81
0
    return out;
82
0
  };
83
84
  // For compatibility with projects that do not set CMP0130 to NEW,
85
  // we tolerate condition errors that evaluate to false.
86
0
  bool enforceError = true;
87
0
  std::string errorString;
88
0
  MessageType messageType;
89
90
0
  for (cmConditionEvaluator conditionEvaluator(mf, whileBT);
91
0
       (enforceError = /* enforce condition errors that evaluate to true */
92
0
        conditionEvaluator.IsTrue(expandArgs(this->Args, expandedArguments),
93
0
                                  errorString, messageType));) {
94
    // Invoke all the functions that were collected in the block.
95
0
    for (cmListFileFunction const& fn : functions) {
96
0
      cmExecutionStatus status(mf);
97
0
      mf.ExecuteCommand(fn, status);
98
0
      if (status.GetReturnInvoked()) {
99
0
        inStatus.SetReturnInvoked(status.GetReturnVariables());
100
0
        return true;
101
0
      }
102
0
      if (status.GetBreakInvoked()) {
103
0
        return true;
104
0
      }
105
0
      if (status.GetContinueInvoked()) {
106
0
        break;
107
0
      }
108
0
      if (status.HasExitCode()) {
109
0
        inStatus.SetExitCode(status.GetExitCode());
110
0
        return true;
111
0
      }
112
0
      if (cmSystemTools::GetFatalErrorOccurred()) {
113
0
        return true;
114
0
      }
115
0
    }
116
0
  }
117
118
0
  if (!errorString.empty() && !enforceError) {
119
    // This error should only be enforced if CMP0130 is NEW.
120
0
    switch (mf.GetPolicyStatus(cmPolicies::CMP0130)) {
121
0
      case cmPolicies::WARN:
122
        // Convert the error to a warning and enforce it.
123
0
        messageType = MessageType::AUTHOR_WARNING;
124
0
        enforceError = true;
125
0
        break;
126
0
      case cmPolicies::OLD:
127
        // OLD behavior is to silently ignore the error.
128
0
        break;
129
0
      case cmPolicies::NEW:
130
        // NEW behavior is to enforce the error.
131
0
        enforceError = true;
132
0
        break;
133
0
    }
134
0
  }
135
136
0
  if (!errorString.empty() && enforceError) {
137
0
    std::string err = "while() given incorrect arguments:\n ";
138
0
    for (auto const& i : expandedArguments) {
139
0
      err += " ";
140
0
      err += cmOutputConverter::EscapeForCMake(i.GetValue());
141
0
    }
142
0
    err += "\n";
143
0
    err += errorString;
144
0
    if (mf.GetPolicyStatus(cmPolicies::CMP0130) == cmPolicies::WARN) {
145
0
      err =
146
0
        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0130), '\n', err);
147
0
    }
148
0
    mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
149
0
    if (messageType == MessageType::FATAL_ERROR) {
150
0
      cmSystemTools::SetFatalErrorOccurred();
151
0
    }
152
0
  }
153
154
0
  return true;
155
0
}
156
157
bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
158
                    cmExecutionStatus& status)
159
0
{
160
0
  if (args.empty()) {
161
0
    status.SetError("called with incorrect number of arguments");
162
0
    return false;
163
0
  }
164
165
  // create a function blocker
166
0
  auto& makefile = status.GetMakefile();
167
0
  makefile.AddFunctionBlocker(
168
0
    cm::make_unique<cmWhileFunctionBlocker>(&makefile, args));
169
170
0
  return true;
171
0
}