Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmConditionEvaluator.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 "cmConditionEvaluator.h"
4
5
#include <array>
6
#include <cstdio>
7
#include <cstdlib>
8
#include <functional>
9
#include <iterator>
10
#include <list>
11
#include <sstream>
12
#include <utility>
13
14
#include <cm/string_view>
15
#include <cmext/algorithm>
16
17
#include "cmsys/RegularExpression.hxx"
18
19
#include "cmCMakePath.h"
20
#include "cmDiagnostics.h"
21
#include "cmExpandedCommandArgument.h"
22
#include "cmList.h"
23
#include "cmMakefile.h"
24
#include "cmMessageType.h"
25
#include "cmState.h"
26
#include "cmStringAlgorithms.h"
27
#include "cmSystemTools.h"
28
#include "cmValue.h"
29
30
namespace {
31
auto const keyAND = "AND"_s;
32
auto const keyCOMMAND = "COMMAND"_s;
33
auto const keyDEFINED = "DEFINED"_s;
34
auto const keyEQUAL = "EQUAL"_s;
35
auto const keyEXISTS = "EXISTS"_s;
36
auto const keyIS_READABLE = "IS_READABLE"_s;
37
auto const keyIS_WRITABLE = "IS_WRITABLE"_s;
38
auto const keyIS_EXECUTABLE = "IS_EXECUTABLE"_s;
39
auto const keyGREATER = "GREATER"_s;
40
auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
41
auto const keyIN_LIST = "IN_LIST"_s;
42
auto const keyIS_ABSOLUTE = "IS_ABSOLUTE"_s;
43
auto const keyIS_DIRECTORY = "IS_DIRECTORY"_s;
44
auto const keyIS_NEWER_THAN = "IS_NEWER_THAN"_s;
45
auto const keyIS_SYMLINK = "IS_SYMLINK"_s;
46
auto const keyLESS = "LESS"_s;
47
auto const keyLESS_EQUAL = "LESS_EQUAL"_s;
48
auto const keyMATCHES = "MATCHES"_s;
49
auto const keyNOT = "NOT"_s;
50
auto const keyOR = "OR"_s;
51
auto const keyParenL = "("_s;
52
auto const keyParenR = ")"_s;
53
auto const keyPOLICY = "POLICY"_s;
54
auto const keySTREQUAL = "STREQUAL"_s;
55
auto const keySTRGREATER = "STRGREATER"_s;
56
auto const keySTRGREATER_EQUAL = "STRGREATER_EQUAL"_s;
57
auto const keySTRLESS = "STRLESS"_s;
58
auto const keySTRLESS_EQUAL = "STRLESS_EQUAL"_s;
59
auto const keyTARGET = "TARGET"_s;
60
auto const keyTEST = "TEST"_s;
61
auto const keyVERSION_EQUAL = "VERSION_EQUAL"_s;
62
auto const keyVERSION_GREATER = "VERSION_GREATER"_s;
63
auto const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL"_s;
64
auto const keyVERSION_LESS = "VERSION_LESS"_s;
65
auto const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL"_s;
66
auto const keyPATH_EQUAL = "PATH_EQUAL"_s;
67
68
cmSystemTools::CompareOp const MATCH2CMPOP[5] = {
69
  cmSystemTools::OP_LESS, cmSystemTools::OP_LESS_EQUAL,
70
  cmSystemTools::OP_GREATER, cmSystemTools::OP_GREATER_EQUAL,
71
  cmSystemTools::OP_EQUAL
72
};
73
74
// Run-Time to Compile-Time template selector
75
template <template <typename> class Comp, template <typename> class... Ops>
76
struct cmRt2CtSelector
77
{
78
  template <typename T>
79
  static bool eval(int r, T lhs, T rhs)
80
0
  {
81
0
    switch (r) {
82
0
      case 0:
83
0
        return false;
84
0
      case 1:
85
0
        return Comp<T>()(lhs, rhs);
86
0
      default:
87
0
        return cmRt2CtSelector<Ops...>::eval(r - 1, lhs, rhs);
88
0
    }
89
0
  }
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::less, std::__1::less_equal, std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<double>(int, double, double)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::less_equal, std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<double>(int, double, double)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<double>(int, double, double)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::greater_equal, std::__1::equal_to>::eval<double>(int, double, double)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::less, std::__1::less_equal, std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<int>(int, int, int)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::less_equal, std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<int>(int, int, int)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::greater, std::__1::greater_equal, std::__1::equal_to>::eval<int>(int, int, int)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::greater_equal, std::__1::equal_to>::eval<int>(int, int, int)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::logical_and, std::__1::logical_or>::eval<bool>(int, bool, bool)
90
};
91
92
template <template <typename> class Comp>
93
struct cmRt2CtSelector<Comp>
94
{
95
  template <typename T>
96
  static bool eval(int r, T lhs, T rhs)
97
0
  {
98
0
    return r == 1 && Comp<T>()(lhs, rhs);
99
0
  }
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::equal_to>::eval<double>(int, double, double)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::equal_to>::eval<int>(int, int, int)
Unexecuted instantiation: cmConditionEvaluator.cxx:bool (anonymous namespace)::cmRt2CtSelector<std::__1::logical_or>::eval<bool>(int, bool, bool)
100
};
101
102
std::string bool2string(bool const value)
103
0
{
104
0
  return std::string(static_cast<std::size_t>(1),
105
0
                     static_cast<char>('0' + static_cast<int>(value)));
106
0
}
107
108
bool looksLikeSpecialVariable(std::string const& var,
109
                              cm::static_string_view prefix,
110
                              std::size_t const varNameLen)
111
0
{
112
  // NOTE Expecting a variable name at least 1 char length:
113
  // <prefix> + `{` + <varname> + `}`
114
0
  return ((prefix.size() + 3) <= varNameLen) &&
115
0
    cmHasPrefix(var, cmStrCat(prefix, '{')) && var[varNameLen - 1] == '}';
116
0
}
117
} // anonymous namespace
118
119
#if defined(__SUNPRO_CC)
120
#  define CM_INHERIT_CTOR(Class, Base, Tpl)                                   \
121
    template <typename... Args>                                               \
122
    Class(Args&&... args)                                                     \
123
      : Base Tpl(std::forward<Args>(args)...)                                 \
124
    {                                                                         \
125
    }
126
#else
127
#  define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
128
#endif
129
130
// BEGIN cmConditionEvaluator::cmArgumentList
131
class cmConditionEvaluator::cmArgumentList
132
  : public std::list<cmExpandedCommandArgument>
133
{
134
  using base_t = std::list<cmExpandedCommandArgument>;
135
136
public:
137
  CM_INHERIT_CTOR(cmArgumentList, list, <cmExpandedCommandArgument>);
138
139
  class CurrentAndNextIter
140
  {
141
    friend class cmConditionEvaluator::cmArgumentList;
142
143
  public:
144
    base_t::iterator current;
145
    base_t::iterator next;
146
147
    CurrentAndNextIter advance(base_t& args)
148
0
    {
149
0
      this->current = std::next(this->current);
150
0
      this->next =
151
0
        std::next(this->current,
152
0
                  static_cast<difference_type>(this->current != args.end()));
153
0
      return *this;
154
0
    }
155
156
  private:
157
    CurrentAndNextIter(base_t& args)
158
0
      : current(args.begin())
159
      , next(
160
0
          std::next(this->current,
161
0
                    static_cast<difference_type>(this->current != args.end())))
162
0
    {
163
0
    }
164
  };
165
166
  class CurrentAndTwoMoreIter
167
  {
168
    friend class cmConditionEvaluator::cmArgumentList;
169
170
  public:
171
    base_t::iterator current;
172
    base_t::iterator next;
173
    base_t::iterator nextnext;
174
175
    CurrentAndTwoMoreIter advance(base_t& args)
176
0
    {
177
0
      this->current = std::next(this->current);
178
0
      this->next =
179
0
        std::next(this->current,
180
0
                  static_cast<difference_type>(this->current != args.end()));
181
0
      this->nextnext = std::next(
182
0
        this->next, static_cast<difference_type>(this->next != args.end()));
183
0
      return *this;
184
0
    }
185
186
  private:
187
    CurrentAndTwoMoreIter(base_t& args)
188
0
      : current(args.begin())
189
      , next(
190
0
          std::next(this->current,
191
0
                    static_cast<difference_type>(this->current != args.end())))
192
0
      , nextnext(std::next(
193
0
          this->next, static_cast<difference_type>(this->next != args.end())))
194
0
    {
195
0
    }
196
  };
197
198
0
  CurrentAndNextIter make2ArgsIterator() { return *this; }
199
0
  CurrentAndTwoMoreIter make3ArgsIterator() { return *this; }
200
201
  template <typename Iter>
202
  void ReduceOneArg(bool const value, Iter args)
203
0
  {
204
0
    *args.current = cmExpandedCommandArgument(bool2string(value), true);
205
0
    this->erase(args.next);
206
0
  }
Unexecuted instantiation: void cmConditionEvaluator::cmArgumentList::ReduceOneArg<cmConditionEvaluator::cmArgumentList::CurrentAndNextIter>(bool, cmConditionEvaluator::cmArgumentList::CurrentAndNextIter)
Unexecuted instantiation: void cmConditionEvaluator::cmArgumentList::ReduceOneArg<cmConditionEvaluator::cmArgumentList::CurrentAndTwoMoreIter>(bool, cmConditionEvaluator::cmArgumentList::CurrentAndTwoMoreIter)
207
208
  void ReduceTwoArgs(bool const value, CurrentAndTwoMoreIter args)
209
0
  {
210
0
    *args.current = cmExpandedCommandArgument(bool2string(value), true);
211
0
    this->erase(args.nextnext);
212
0
    this->erase(args.next);
213
0
  }
214
};
215
216
// END cmConditionEvaluator::cmArgumentList
217
218
cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile,
219
                                           cmListFileBacktrace bt)
220
0
  : Makefile(makefile)
221
0
  , Backtrace(std::move(bt))
222
0
  , Policy139Status(makefile.GetPolicyStatus(cmPolicies::CMP0139))
223
0
{
224
0
}
225
226
//=========================================================================
227
// order of operations,
228
// 1.   ( )   -- parenthetical groups
229
// 2.  IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
230
// 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
231
// 4. NOT
232
// 5. AND OR
233
//
234
// There is an issue on whether the arguments should be values of references,
235
// for example IF (FOO AND BAR) should that compare the strings FOO and BAR
236
// or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
237
// EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
238
// take numeric values or variable names. STRLESS and STRGREATER take
239
// variable names but if the variable name is not found it will use the name
240
// directly. AND OR take variables or the values 0 or 1.
241
242
bool cmConditionEvaluator::IsTrue(
243
  std::vector<cmExpandedCommandArgument> const& args, std::string& errorString,
244
  MessageType& status)
245
0
{
246
0
  errorString.clear();
247
248
  // handle empty invocation
249
0
  if (args.empty()) {
250
0
    return false;
251
0
  }
252
253
  // store the reduced args in this vector
254
0
  cmArgumentList newArgs(args.begin(), args.end());
255
256
  // now loop through the arguments and see if we can reduce any of them
257
  // we do this multiple times. Once for each level of precedence
258
  // parens
259
0
  using handlerFn_t = bool (cmConditionEvaluator::*)(
260
0
    cmArgumentList&, std::string&, MessageType&);
261
0
  std::array<handlerFn_t, 5> const handlers = { {
262
0
    &cmConditionEvaluator::HandleLevel0, // parenthesis
263
0
    &cmConditionEvaluator::HandleLevel1, // predicates
264
0
    &cmConditionEvaluator::HandleLevel2, // binary ops
265
0
    &cmConditionEvaluator::HandleLevel3, // NOT
266
0
    &cmConditionEvaluator::HandleLevel4  // AND OR
267
0
  } };
268
0
  for (auto fn : handlers) {
269
    // Call the reducer 'till there is anything to reduce...
270
    // (i.e., if after an iteration the size becomes smaller)
271
0
    auto levelResult = true;
272
0
    for (auto beginSize = newArgs.size();
273
0
         (levelResult = (this->*fn)(newArgs, errorString, status)) &&
274
0
         newArgs.size() < beginSize;
275
0
         beginSize = newArgs.size()) {
276
0
    }
277
278
0
    if (!levelResult) {
279
      // NOTE `errorString` supposed to be set already
280
0
      return false;
281
0
    }
282
0
  }
283
284
  // now at the end there should only be one argument left
285
0
  if (newArgs.size() != 1) {
286
0
    errorString = "Unknown arguments specified";
287
0
    status = MessageType::FATAL_ERROR;
288
0
    return false;
289
0
  }
290
291
0
  return this->GetBooleanValue(newArgs.front());
292
0
}
293
294
//=========================================================================
295
cmValue cmConditionEvaluator::GetDefinitionIfUnquoted(
296
  cmExpandedCommandArgument const& argument) const
297
0
{
298
0
  if (argument.WasQuoted()) {
299
0
    return nullptr;
300
0
  }
301
302
0
  return this->Makefile.GetDefinition(argument.GetValue());
303
0
}
304
305
//=========================================================================
306
cmValue cmConditionEvaluator::GetVariableOrString(
307
  cmExpandedCommandArgument const& argument) const
308
0
{
309
0
  cmValue def = this->GetDefinitionIfUnquoted(argument);
310
311
0
  if (!def) {
312
0
    def = cmValue(argument.GetValue());
313
0
  }
314
315
0
  return def;
316
0
}
317
318
//=========================================================================
319
bool cmConditionEvaluator::IsKeyword(
320
  cm::static_string_view keyword,
321
  cmExpandedCommandArgument const& argument) const
322
0
{
323
0
  if (argument.WasQuoted()) {
324
0
    return false;
325
0
  }
326
327
0
  return argument.GetValue() == keyword;
328
0
}
329
330
//=========================================================================
331
bool cmConditionEvaluator::GetBooleanValue(
332
  cmExpandedCommandArgument& arg) const
333
0
{
334
  // Check basic and named constants.
335
0
  if (cmIsOn(arg.GetValue())) {
336
0
    return true;
337
0
  }
338
0
  if (cmIsOff(arg.GetValue())) {
339
0
    return false;
340
0
  }
341
342
  // Check for numbers.
343
0
  if (!arg.empty()) {
344
0
    char* end;
345
0
    double const d = std::strtod(arg.GetValue().c_str(), &end);
346
0
    if (*end == '\0') {
347
      // The whole string is a number.  Use C conversion to bool.
348
0
      return static_cast<bool>(d);
349
0
    }
350
0
  }
351
352
  // Check definition.
353
0
  cmValue def = this->GetDefinitionIfUnquoted(arg);
354
0
  return !def.IsOff();
355
0
}
356
357
template <int N>
358
inline int cmConditionEvaluator::matchKeysImpl(
359
  cmExpandedCommandArgument const&)
360
0
{
361
  // Zero means "not found"
362
0
  return 0;
363
0
}
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<6>(cmExpandedCommandArgument const&)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<3>(cmExpandedCommandArgument const&)
364
365
template <int N, typename T, typename... Keys>
366
inline int cmConditionEvaluator::matchKeysImpl(
367
  cmExpandedCommandArgument const& arg, T current, Keys... key)
368
0
{
369
0
  if (this->IsKeyword(current, arg)) {
370
    // Stop searching as soon as smth has found
371
0
    return N;
372
0
  }
373
0
  return matchKeysImpl<N + 1>(arg, key...);
374
0
}
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<1, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<2, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<3, cm::static_string_view, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<4, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<5, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<1, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeysImpl<2, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view)
375
376
template <typename... Keys>
377
inline int cmConditionEvaluator::matchKeys(
378
  cmExpandedCommandArgument const& arg, Keys... key)
379
0
{
380
  // Get index of the matched key (1-based)
381
0
  return matchKeysImpl<1>(arg, key...);
382
0
}
Unexecuted instantiation: int cmConditionEvaluator::matchKeys<cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view, cm::static_string_view)
Unexecuted instantiation: int cmConditionEvaluator::matchKeys<cm::static_string_view, cm::static_string_view>(cmExpandedCommandArgument const&, cm::static_string_view, cm::static_string_view)
383
384
//=========================================================================
385
// level 0 processes parenthetical expressions
386
bool cmConditionEvaluator::HandleLevel0(cmArgumentList& newArgs,
387
                                        std::string& errorString,
388
                                        MessageType& status)
389
0
{
390
0
  for (auto arg = newArgs.begin(); arg != newArgs.end(); ++arg) {
391
0
    if (this->IsKeyword(keyParenL, *arg)) {
392
      // search for the closing paren for this opening one
393
0
      auto depth = 1;
394
0
      auto argClose = std::next(arg);
395
0
      for (; argClose != newArgs.end() && depth; ++argClose) {
396
0
        depth += int(this->IsKeyword(keyParenL, *argClose)) -
397
0
          int(this->IsKeyword(keyParenR, *argClose));
398
0
      }
399
0
      if (depth) {
400
0
        errorString = "mismatched parenthesis in condition";
401
0
        status = MessageType::FATAL_ERROR;
402
0
        return false;
403
0
      }
404
405
      // store the reduced args in this vector
406
0
      auto argOpen = std::next(arg);
407
0
      std::vector<cmExpandedCommandArgument> const subExpr(
408
0
        argOpen, std::prev(argClose));
409
410
      // now recursively invoke IsTrue to handle the values inside the
411
      // parenthetical expression
412
0
      auto const value = this->IsTrue(subExpr, errorString, status);
413
0
      *arg = cmExpandedCommandArgument(bool2string(value), true);
414
0
      argOpen = std::next(arg);
415
      // remove the now evaluated parenthetical expression
416
0
      newArgs.erase(argOpen, argClose);
417
0
    }
418
0
  }
419
0
  return true;
420
0
}
421
422
//=========================================================================
423
// level one handles most predicates except for NOT
424
bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
425
                                        MessageType&)
426
0
{
427
0
  for (auto args = newArgs.make2ArgsIterator(); args.current != newArgs.end();
428
0
       args.advance(newArgs)) {
429
    // NOTE Fail fast: All the predicates below require the next arg to be
430
    // valid
431
0
    if (args.next == newArgs.end()) {
432
0
      continue;
433
0
    }
434
435
    // does a file exist
436
0
    if (this->IsKeyword(keyEXISTS, *args.current)) {
437
0
      newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
438
0
                           args);
439
0
    }
440
    // check if a file is readable
441
0
    else if (this->IsKeyword(keyIS_READABLE, *args.current)) {
442
0
      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
443
0
                             args.next->GetValue(), cmsys::TEST_FILE_READ),
444
0
                           args);
445
0
    }
446
    // check if a file is writable
447
0
    else if (this->IsKeyword(keyIS_WRITABLE, *args.current)) {
448
0
      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
449
0
                             args.next->GetValue(), cmsys::TEST_FILE_WRITE),
450
0
                           args);
451
0
    }
452
    // check if a file is executable
453
0
    else if (this->IsKeyword(keyIS_EXECUTABLE, *args.current)) {
454
0
      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
455
0
                             args.next->GetValue(), cmsys::TEST_FILE_EXECUTE),
456
0
                           args);
457
0
    }
458
    // does a directory with this name exist
459
0
    else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
460
0
      newArgs.ReduceOneArg(
461
0
        cmSystemTools::FileIsDirectory(args.next->GetValue()), args);
462
0
    }
463
    // does a symlink with this name exist
464
0
    else if (this->IsKeyword(keyIS_SYMLINK, *args.current)) {
465
0
      newArgs.ReduceOneArg(cmSystemTools::FileIsSymlink(args.next->GetValue()),
466
0
                           args);
467
0
    }
468
    // is the given path an absolute path ?
469
0
    else if (this->IsKeyword(keyIS_ABSOLUTE, *args.current)) {
470
0
      newArgs.ReduceOneArg(
471
0
        cmSystemTools::FileIsFullPath(args.next->GetValue()), args);
472
0
    }
473
    // does a command exist
474
0
    else if (this->IsKeyword(keyCOMMAND, *args.current)) {
475
0
      newArgs.ReduceOneArg(
476
0
        static_cast<bool>(
477
0
          this->Makefile.GetState()->GetCommand(args.next->GetValue())),
478
0
        args);
479
0
    }
480
    // does a policy exist
481
0
    else if (this->IsKeyword(keyPOLICY, *args.current)) {
482
0
      cmPolicies::PolicyID pid;
483
0
      newArgs.ReduceOneArg(
484
0
        cmPolicies::GetPolicyID(args.next->GetValue().c_str(), pid), args);
485
0
    }
486
    // does a target exist
487
0
    else if (this->IsKeyword(keyTARGET, *args.current)) {
488
0
      newArgs.ReduceOneArg(static_cast<bool>(this->Makefile.FindTargetToUse(
489
0
                             args.next->GetValue())),
490
0
                           args);
491
0
    }
492
    // is a variable defined
493
0
    else if (this->IsKeyword(keyDEFINED, *args.current)) {
494
0
      auto const& var = args.next->GetValue();
495
0
      auto const varNameLen = var.size();
496
497
0
      auto result = false;
498
0
      if (looksLikeSpecialVariable(var, "ENV"_s, varNameLen)) {
499
0
        auto const env = args.next->GetValue().substr(4, varNameLen - 5);
500
0
        result = cmSystemTools::HasEnv(env);
501
0
      }
502
503
0
      else if (looksLikeSpecialVariable(var, "CACHE"_s, varNameLen)) {
504
0
        auto const cache = args.next->GetValue().substr(6, varNameLen - 7);
505
0
        result = static_cast<bool>(
506
0
          this->Makefile.GetState()->GetCacheEntryValue(cache));
507
0
      }
508
509
0
      else {
510
0
        result = this->Makefile.IsDefinitionSet(args.next->GetValue());
511
0
      }
512
0
      newArgs.ReduceOneArg(result, args);
513
0
    }
514
    // does a test exist
515
0
    else if (this->IsKeyword(keyTEST, *args.current)) {
516
0
      newArgs.ReduceOneArg(
517
0
        static_cast<bool>(this->Makefile.GetTest(args.next->GetValue())),
518
0
        args);
519
0
    }
520
0
  }
521
0
  return true;
522
0
}
523
524
//=========================================================================
525
// level two handles most binary operations except for AND  OR
526
bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs,
527
                                        std::string& errorString,
528
                                        MessageType& status)
529
0
{
530
0
  for (auto args = newArgs.make3ArgsIterator(); args.current != newArgs.end();
531
0
       args.advance(newArgs)) {
532
533
0
    int matchNo;
534
535
    // NOTE Handle special case `if(... BLAH_BLAH MATCHES)`
536
    // (i.e., w/o regex to match which is possibly result of
537
    // variable expansion to an empty string)
538
0
    if (args.next != newArgs.end() &&
539
0
        this->IsKeyword(keyMATCHES, *args.current)) {
540
0
      newArgs.ReduceOneArg(false, args);
541
0
    }
542
543
    // NOTE Fail fast: All the binary ops below require 2 arguments.
544
0
    else if (args.next == newArgs.end() || args.nextnext == newArgs.end()) {
545
0
      continue;
546
0
    }
547
548
0
    else if (this->IsKeyword(keyMATCHES, *args.next)) {
549
0
      cmValue def = this->GetDefinitionIfUnquoted(*args.current);
550
551
0
      std::string def_buf;
552
0
      if (!def) {
553
0
        def = cmValue(args.current->GetValue());
554
0
      } else if (cmHasLiteralPrefix(args.current->GetValue(),
555
0
                                    "CMAKE_MATCH_")) {
556
        // The string to match is owned by our match result variables.
557
        // Move it to our own buffer before clearing them.
558
0
        def_buf = *def;
559
0
        def = cmValue(def_buf);
560
0
      }
561
562
0
      this->Makefile.ClearMatches();
563
564
0
      auto const& rex = args.nextnext->GetValue();
565
0
      cmsys::RegularExpression regEntry;
566
0
      if (!regEntry.compile(rex)) {
567
0
        std::ostringstream error;
568
0
        error << "Regular expression \"" << rex << "\" cannot compile";
569
0
        errorString = error.str();
570
0
        status = MessageType::FATAL_ERROR;
571
0
        return false;
572
0
      }
573
574
0
      auto const match = regEntry.find(*def);
575
0
      if (match) {
576
0
        this->Makefile.StoreMatches(regEntry);
577
0
      }
578
0
      newArgs.ReduceTwoArgs(match, args);
579
0
    }
580
581
0
    else if ((matchNo =
582
0
                this->matchKeys(*args.next, keyLESS, keyLESS_EQUAL, keyGREATER,
583
0
                                keyGREATER_EQUAL, keyEQUAL))) {
584
585
0
      cmValue ldef = this->GetVariableOrString(*args.current);
586
0
      cmValue rdef = this->GetVariableOrString(*args.nextnext);
587
588
0
      double lhs;
589
0
      double rhs;
590
0
      auto parseDoubles = [&]() {
591
0
        return std::sscanf(ldef->c_str(), "%lg", &lhs) == 1 &&
592
0
          std::sscanf(rdef->c_str(), "%lg", &rhs) == 1;
593
0
      };
594
      // clang-format off
595
0
      const auto result = parseDoubles() &&
596
0
        cmRt2CtSelector<
597
0
            std::less, std::less_equal,
598
0
            std::greater, std::greater_equal,
599
0
            std::equal_to
600
0
          >::eval(matchNo, lhs, rhs);
601
      // clang-format on
602
0
      newArgs.ReduceTwoArgs(result, args);
603
0
    }
604
605
0
    else if ((matchNo = this->matchKeys(*args.next, keySTRLESS,
606
0
                                        keySTRLESS_EQUAL, keySTRGREATER,
607
0
                                        keySTRGREATER_EQUAL, keySTREQUAL))) {
608
609
0
      cmValue const lhs = this->GetVariableOrString(*args.current);
610
0
      cmValue const rhs = this->GetVariableOrString(*args.nextnext);
611
0
      auto const val = (*lhs).compare(*rhs);
612
      // clang-format off
613
0
      const auto result = cmRt2CtSelector<
614
0
            std::less, std::less_equal,
615
0
            std::greater, std::greater_equal,
616
0
            std::equal_to
617
0
          >::eval(matchNo, val, 0);
618
      // clang-format on
619
0
      newArgs.ReduceTwoArgs(result, args);
620
0
    }
621
622
0
    else if ((matchNo =
623
0
                this->matchKeys(*args.next, keyVERSION_LESS,
624
0
                                keyVERSION_LESS_EQUAL, keyVERSION_GREATER,
625
0
                                keyVERSION_GREATER_EQUAL, keyVERSION_EQUAL))) {
626
0
      auto const op = MATCH2CMPOP[matchNo - 1];
627
0
      cmValue const lhs = this->GetVariableOrString(*args.current);
628
0
      cmValue const rhs = this->GetVariableOrString(*args.nextnext);
629
0
      auto const result = cmSystemTools::VersionCompare(op, lhs, rhs);
630
0
      newArgs.ReduceTwoArgs(result, args);
631
0
    }
632
633
    // is file A newer than file B
634
0
    else if (this->IsKeyword(keyIS_NEWER_THAN, *args.next)) {
635
0
      auto fileIsNewer = 0;
636
0
      cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
637
0
        args.current->GetValue(), args.nextnext->GetValue(), &fileIsNewer);
638
0
      newArgs.ReduceTwoArgs(
639
0
        (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), args);
640
0
    }
641
642
0
    else if (this->IsKeyword(keyIN_LIST, *args.next)) {
643
0
      cmValue lhs = this->GetVariableOrString(*args.current);
644
0
      cmValue rhs = this->Makefile.GetDefinition(args.nextnext->GetValue());
645
646
0
      newArgs.ReduceTwoArgs(
647
0
        rhs && cm::contains(cmList{ *rhs, cmList::EmptyElements::Yes }, *lhs),
648
0
        args);
649
0
    }
650
651
0
    else if (this->IsKeyword(keyPATH_EQUAL, *args.next)) {
652
653
0
      if (this->Policy139Status != cmPolicies::OLD &&
654
0
          this->Policy139Status != cmPolicies::WARN) {
655
656
0
        cmValue lhs = this->GetVariableOrString(*args.current);
657
0
        cmValue rhs = this->GetVariableOrString(*args.nextnext);
658
0
        auto const result = cmCMakePath{ *lhs } == cmCMakePath{ *rhs };
659
0
        newArgs.ReduceTwoArgs(result, args);
660
0
      }
661
662
0
      else if (this->Policy139Status == cmPolicies::WARN) {
663
0
        std::ostringstream e;
664
0
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0139)
665
0
          << "\n"
666
0
             "PATH_EQUAL will be interpreted as an operator "
667
0
             "when the policy is set to NEW.  "
668
0
             "Since the policy is not set the OLD behavior will be used.";
669
670
0
        this->Makefile.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, e.str());
671
0
      }
672
0
    }
673
0
  }
674
0
  return true;
675
0
}
676
677
//=========================================================================
678
// level 3 handles NOT
679
bool cmConditionEvaluator::HandleLevel3(cmArgumentList& newArgs, std::string&,
680
                                        MessageType&)
681
0
{
682
0
  for (auto args = newArgs.make2ArgsIterator(); args.next != newArgs.end();
683
0
       args.advance(newArgs)) {
684
0
    if (this->IsKeyword(keyNOT, *args.current)) {
685
0
      auto const rhs = this->GetBooleanValue(*args.next);
686
0
      newArgs.ReduceOneArg(!rhs, args);
687
0
    }
688
0
  }
689
0
  return true;
690
0
}
691
692
//=========================================================================
693
// level 4 handles AND OR
694
bool cmConditionEvaluator::HandleLevel4(cmArgumentList& newArgs, std::string&,
695
                                        MessageType&)
696
0
{
697
0
  for (auto args = newArgs.make3ArgsIterator(); args.nextnext != newArgs.end();
698
0
       args.advance(newArgs)) {
699
700
0
    int matchNo;
701
702
0
    if ((matchNo = this->matchKeys(*args.next, keyAND, keyOR))) {
703
0
      auto const lhs = this->GetBooleanValue(*args.current);
704
0
      auto const rhs = this->GetBooleanValue(*args.nextnext);
705
      // clang-format off
706
0
      const auto result =
707
0
        cmRt2CtSelector<
708
0
            std::logical_and, std::logical_or
709
0
          >::eval(matchNo, lhs, rhs);
710
      // clang-format on
711
0
      newArgs.ReduceTwoArgs(result, args);
712
0
    }
713
0
  }
714
0
  return true;
715
0
}