cmConditionEvaluator.cxx 26.7 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmConditionEvaluator.h"
Brad King's avatar
Brad King committed
4

5
6
7
8
9
10
11
12
#include <algorithm>
#include <cmConfigure.h>
#include <cmsys/RegularExpression.hxx>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

13
#include "cmAlgorithms.h"
14
#include "cmMakefile.h"
15
#include "cmState.h"
16
#include "cmSystemTools.h"
17

18
19
20
class cmCommand;
class cmTest;

21
22
23
24
25
26
static std::string const keyAND = "AND";
static std::string const keyCOMMAND = "COMMAND";
static std::string const keyDEFINED = "DEFINED";
static std::string const keyEQUAL = "EQUAL";
static std::string const keyEXISTS = "EXISTS";
static std::string const keyGREATER = "GREATER";
27
static std::string const keyGREATER_EQUAL = "GREATER_EQUAL";
28
29
30
31
32
33
static std::string const keyIN_LIST = "IN_LIST";
static std::string const keyIS_ABSOLUTE = "IS_ABSOLUTE";
static std::string const keyIS_DIRECTORY = "IS_DIRECTORY";
static std::string const keyIS_NEWER_THAN = "IS_NEWER_THAN";
static std::string const keyIS_SYMLINK = "IS_SYMLINK";
static std::string const keyLESS = "LESS";
34
static std::string const keyLESS_EQUAL = "LESS_EQUAL";
35
36
37
38
39
40
41
42
static std::string const keyMATCHES = "MATCHES";
static std::string const keyNOT = "NOT";
static std::string const keyOR = "OR";
static std::string const keyParenL = "(";
static std::string const keyParenR = ")";
static std::string const keyPOLICY = "POLICY";
static std::string const keySTREQUAL = "STREQUAL";
static std::string const keySTRGREATER = "STRGREATER";
43
static std::string const keySTRGREATER_EQUAL = "STRGREATER_EQUAL";
44
static std::string const keySTRLESS = "STRLESS";
45
static std::string const keySTRLESS_EQUAL = "STRLESS_EQUAL";
46
47
48
49
static std::string const keyTARGET = "TARGET";
static std::string const keyTEST = "TEST";
static std::string const keyVERSION_EQUAL = "VERSION_EQUAL";
static std::string const keyVERSION_GREATER = "VERSION_GREATER";
50
static std::string const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL";
51
static std::string const keyVERSION_LESS = "VERSION_LESS";
52
static std::string const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL";
53

54
cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile,
55
56
57
58
59
60
61
62
63
                                           const cmListFileContext& context,
                                           const cmListFileBacktrace& bt)
  : Makefile(makefile)
  , ExecutionContext(context)
  , Backtrace(bt)
  , Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012))
  , Policy54Status(makefile.GetPolicyStatus(cmPolicies::CMP0054))
  , Policy57Status(makefile.GetPolicyStatus(cmPolicies::CMP0057))
  , Policy64Status(makefile.GetPolicyStatus(cmPolicies::CMP0064))
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
{
}

//=========================================================================
// order of operations,
// 1.   ( )   -- parenthetical groups
// 2.  IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
// 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
// 4. NOT
// 5. AND OR
//
// There is an issue on whether the arguments should be values of references,
// for example IF (FOO AND BAR) should that compare the strings FOO and BAR
// or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
// EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
// take numeric values or variable names. STRLESS and STRGREATER take
// variable names but if the variable name is not found it will use the name
// directly. AND OR take variables or the values 0 or 1.

bool cmConditionEvaluator::IsTrue(
84
85
  const std::vector<cmExpandedCommandArgument>& args, std::string& errorString,
  cmake::MessageType& status)
86
87
88
89
{
  errorString = "";

  // handle empty invocation
90
  if (args.empty()) {
91
    return false;
92
  }
93
94
95
96
97

  // store the reduced args in this vector
  cmArgumentList newArgs;

  // copy to the list structure
98
  newArgs.insert(newArgs.end(), args.begin(), args.end());
99
100
101
102

  // now loop through the arguments and see if we can reduce any of them
  // we do this multiple times. Once for each level of precedence
  // parens
103
  if (!this->HandleLevel0(newArgs, errorString, status)) {
104
    return false;
105
106
107
  }
  // predicates
  if (!this->HandleLevel1(newArgs, errorString, status)) {
108
    return false;
109
  }
110
  // binary ops
111
  if (!this->HandleLevel2(newArgs, errorString, status)) {
112
    return false;
113
  }
114
115

  // NOT
116
  if (!this->HandleLevel3(newArgs, errorString, status)) {
117
    return false;
118
  }
119
  // AND OR
120
  if (!this->HandleLevel4(newArgs, errorString, status)) {
121
    return false;
122
  }
123
124

  // now at the end there should only be one argument left
125
  if (newArgs.size() != 1) {
126
127
128
    errorString = "Unknown arguments specified";
    status = cmake::FATAL_ERROR;
    return false;
129
  }
130
131

  return this->GetBooleanValueWithAutoDereference(*(newArgs.begin()),
132
                                                  errorString, status, true);
133
134
}

135
136
137
138
//=========================================================================
const char* cmConditionEvaluator::GetDefinitionIfUnquoted(
  cmExpandedCommandArgument const& argument) const
{
139
140
141
  if ((this->Policy54Status != cmPolicies::WARN &&
       this->Policy54Status != cmPolicies::OLD) &&
      argument.WasQuoted()) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
142
    return CM_NULLPTR;
143
  }
144
145
146

  const char* def = this->Makefile.GetDefinition(argument.GetValue());

147
148
149
150
  if (def && argument.WasQuoted() &&
      this->Policy54Status == cmPolicies::WARN) {
    if (!this->Makefile.HasCMP0054AlreadyBeenReported(
          this->ExecutionContext)) {
151
      std::ostringstream e;
Stephen Kelly's avatar
Stephen Kelly committed
152
      e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)) << "\n";
153
154
155
156
157
158
159
      e << "Quoted variables like \"" << argument.GetValue()
        << "\" will no longer be dereferenced "
           "when the policy is set to NEW.  "
           "Since the policy is not set the OLD behavior will be used.";

      this->Makefile.GetCMakeInstance()->IssueMessage(
        cmake::AUTHOR_WARNING, e.str(), this->Backtrace);
160
    }
161
  }
162
163
164
165

  return def;
}

166
167
//=========================================================================
const char* cmConditionEvaluator::GetVariableOrString(
168
  const cmExpandedCommandArgument& argument) const
169
{
170
  const char* def = this->GetDefinitionIfUnquoted(argument);
171

172
  if (!def) {
173
    def = argument.c_str();
174
  }
175
176
177
178

  return def;
}

179
180
//=========================================================================
bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
181
                                     cmExpandedCommandArgument& argument) const
182
{
183
184
185
  if ((this->Policy54Status != cmPolicies::WARN &&
       this->Policy54Status != cmPolicies::OLD) &&
      argument.WasQuoted()) {
186
    return false;
187
  }
188
189
190

  bool isKeyword = argument.GetValue() == keyword;

191
192
193
194
  if (isKeyword && argument.WasQuoted() &&
      this->Policy54Status == cmPolicies::WARN) {
    if (!this->Makefile.HasCMP0054AlreadyBeenReported(
          this->ExecutionContext)) {
195
      std::ostringstream e;
Stephen Kelly's avatar
Stephen Kelly committed
196
      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054) << "\n";
197
198
199
200
201
202
203
      e << "Quoted keywords like \"" << argument.GetValue()
        << "\" will no longer be interpreted as keywords "
           "when the policy is set to NEW.  "
           "Since the policy is not set the OLD behavior will be used.";

      this->Makefile.GetCMakeInstance()->IssueMessage(
        cmake::AUTHOR_WARNING, e.str(), this->Backtrace);
204
    }
205
  }
206
207
208
209

  return isKeyword;
}

210
211
//=========================================================================
bool cmConditionEvaluator::GetBooleanValue(
212
  cmExpandedCommandArgument& arg) const
213
214
{
  // Check basic constants.
215
  if (arg == "0") {
216
    return false;
217
218
  }
  if (arg == "1") {
219
    return true;
220
  }
221
222

  // Check named constants.
223
  if (cmSystemTools::IsOn(arg.c_str())) {
224
    return true;
225
226
  }
  if (cmSystemTools::IsOff(arg.c_str())) {
227
    return false;
228
  }
229
230

  // Check for numbers.
231
  if (!arg.empty()) {
232
233
    char* end;
    double d = strtod(arg.c_str(), &end);
234
    if (*end == '\0') {
235
      // The whole string is a number.  Use C conversion to bool.
236
      return static_cast<bool>(d);
237
    }
238
  }
239
240

  // Check definition.
241
  const char* def = this->GetDefinitionIfUnquoted(arg);
242
243
244
245
246
247
  return !cmSystemTools::IsOff(def);
}

//=========================================================================
// Boolean value behavior from CMake 2.6.4 and below.
bool cmConditionEvaluator::GetBooleanValueOld(
248
  cmExpandedCommandArgument const& arg, bool one) const
249
{
250
  if (one) {
251
    // Old IsTrue behavior for single argument.
252
253
    if (arg == "0") {
      return false;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
254
255
    }
    if (arg == "1") {
256
      return true;
257
    }
258
    const char* def = this->GetDefinitionIfUnquoted(arg);
259
260
    return !cmSystemTools::IsOff(def);
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
261
262
263
264
265
266
  // Old GetVariableOrNumber behavior.
  const char* def = this->GetDefinitionIfUnquoted(arg);
  if (!def && atoi(arg.c_str())) {
    def = arg.c_str();
  }
  return !cmSystemTools::IsOff(def);
267
268
269
270
271
}

//=========================================================================
// returns the resulting boolean value
bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
272
273
  cmExpandedCommandArgument& newArg, std::string& errorString,
  cmake::MessageType& status, bool oneArg) const
274
275
{
  // Use the policy if it is set.
276
  if (this->Policy12Status == cmPolicies::NEW) {
277
    return GetBooleanValue(newArg);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
278
279
  }
  if (this->Policy12Status == cmPolicies::OLD) {
280
    return GetBooleanValueOld(newArg, oneArg);
281
  }
282
283
284
285

  // Check policy only if old and new results differ.
  bool newResult = this->GetBooleanValue(newArg);
  bool oldResult = this->GetBooleanValueOld(newArg, oneArg);
286
287
288
289
290
291
  if (newResult != oldResult) {
    switch (this->Policy12Status) {
      case cmPolicies::WARN: {
        errorString = "An argument named \"" + newArg.GetValue() +
          "\" appears in a conditional statement.  " +
          cmPolicies::GetPolicyWarning(cmPolicies::CMP0012);
292
        status = cmake::AUTHOR_WARNING;
293
      }
294
295
296
      case cmPolicies::OLD:
        return oldResult;
      case cmPolicies::REQUIRED_IF_USED:
297
298
299
300
      case cmPolicies::REQUIRED_ALWAYS: {
        errorString = "An argument named \"" + newArg.GetValue() +
          "\" appears in a conditional statement.  " +
          cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0012);
301
        status = cmake::FATAL_ERROR;
302
      }
303
304
305
      case cmPolicies::NEW:
        break;
    }
306
  }
307
308
309
310
  return newResult;
}

//=========================================================================
311
312
313
void cmConditionEvaluator::IncrementArguments(
  cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
  cmArgumentList::iterator& argP2) const
314
{
315
  if (argP1 != newArgs.end()) {
316
317
    argP1++;
    argP2 = argP1;
318
    if (argP1 != newArgs.end()) {
319
320
      argP2++;
    }
321
  }
322
323
324
325
}

//=========================================================================
// helper function to reduce code duplication
326
327
328
329
void cmConditionEvaluator::HandlePredicate(
  bool value, int& reducible, cmArgumentList::iterator& arg,
  cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
  cmArgumentList::iterator& argP2) const
330
{
331
  if (value) {
332
    *arg = cmExpandedCommandArgument("1", true);
333
  } else {
334
    *arg = cmExpandedCommandArgument("0", true);
335
  }
336
337
  newArgs.erase(argP1);
  argP1 = arg;
338
  this->IncrementArguments(newArgs, argP1, argP2);
339
340
341
342
343
  reducible = 1;
}

//=========================================================================
// helper function to reduce code duplication
344
345
346
347
348
void cmConditionEvaluator::HandleBinaryOp(bool value, int& reducible,
                                          cmArgumentList::iterator& arg,
                                          cmArgumentList& newArgs,
                                          cmArgumentList::iterator& argP1,
                                          cmArgumentList::iterator& argP2)
349
{
350
  if (value) {
351
    *arg = cmExpandedCommandArgument("1", true);
352
  } else {
353
    *arg = cmExpandedCommandArgument("0", true);
354
  }
355
356
357
  newArgs.erase(argP2);
  newArgs.erase(argP1);
  argP1 = arg;
358
  this->IncrementArguments(newArgs, argP1, argP2);
359
360
361
362
363
  reducible = 1;
}

//=========================================================================
// level 0 processes parenthetical expressions
364
365
366
bool cmConditionEvaluator::HandleLevel0(cmArgumentList& newArgs,
                                        std::string& errorString,
                                        cmake::MessageType& status)
367
368
{
  int reducible;
369
  do {
370
    reducible = 0;
371
    cmArgumentList::iterator arg = newArgs.begin();
372
373
    while (arg != newArgs.end()) {
      if (IsKeyword(keyParenL, *arg)) {
374
        // search for the closing paren for this opening one
375
        cmArgumentList::iterator argClose;
376
377
378
        argClose = arg;
        argClose++;
        unsigned int depth = 1;
379
380
381
382
383
384
        while (argClose != newArgs.end() && depth) {
          if (this->IsKeyword(keyParenL, *argClose)) {
            depth++;
          }
          if (this->IsKeyword(keyParenR, *argClose)) {
            depth--;
385
          }
386
387
388
          argClose++;
        }
        if (depth) {
389
390
391
          errorString = "mismatched parenthesis in condition";
          status = cmake::FATAL_ERROR;
          return false;
392
        }
393
        // store the reduced args in this vector
394
        std::vector<cmExpandedCommandArgument> newArgs2;
395
396

        // copy to the list structure
397
        cmArgumentList::iterator argP1 = arg;
398
        argP1++;
399
        newArgs2.insert(newArgs2.end(), argP1, argClose);
400
401
402
        newArgs2.pop_back();
        // now recursively invoke IsTrue to handle the values inside the
        // parenthetical expression
403
404
        bool value = this->IsTrue(newArgs2, errorString, status);
        if (value) {
405
          *arg = cmExpandedCommandArgument("1", true);
406
        } else {
407
          *arg = cmExpandedCommandArgument("0", true);
408
        }
409
410
411
        argP1 = arg;
        argP1++;
        // remove the now evaluated parenthetical expression
412
        newArgs.erase(argP1, argClose);
413
      }
414
      ++arg;
415
    }
416
  } while (reducible);
417
418
419
420
421
  return true;
}

//=========================================================================
// level one handles most predicates except for NOT
422
423
bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
                                        cmake::MessageType&)
424
425
{
  int reducible;
426
  do {
427
    reducible = 0;
428
429
430
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
431
    while (arg != newArgs.end()) {
432
      argP1 = arg;
433
      this->IncrementArguments(newArgs, argP1, argP2);
434
      // does a file exist
435
436
437
438
      if (this->IsKeyword(keyEXISTS, *arg) && argP1 != newArgs.end()) {
        this->HandlePredicate(cmSystemTools::FileExists(argP1->c_str()),
                              reducible, arg, newArgs, argP1, argP2);
      }
439
      // does a directory with this name exist
440
441
442
443
      if (this->IsKeyword(keyIS_DIRECTORY, *arg) && argP1 != newArgs.end()) {
        this->HandlePredicate(cmSystemTools::FileIsDirectory(argP1->c_str()),
                              reducible, arg, newArgs, argP1, argP2);
      }
444
      // does a symlink with this name exist
445
446
447
448
      if (this->IsKeyword(keyIS_SYMLINK, *arg) && argP1 != newArgs.end()) {
        this->HandlePredicate(cmSystemTools::FileIsSymlink(argP1->c_str()),
                              reducible, arg, newArgs, argP1, argP2);
      }
449
      // is the given path an absolute path ?
450
451
452
453
      if (this->IsKeyword(keyIS_ABSOLUTE, *arg) && argP1 != newArgs.end()) {
        this->HandlePredicate(cmSystemTools::FileIsFullPath(argP1->c_str()),
                              reducible, arg, newArgs, argP1, argP2);
      }
454
      // does a command exist
455
      if (this->IsKeyword(keyCOMMAND, *arg) && argP1 != newArgs.end()) {
456
        cmCommand* command =
457
          this->Makefile.GetState()->GetCommand(argP1->c_str());
458
        this->HandlePredicate(command != CM_NULLPTR, reducible, arg, newArgs,
459
460
                              argP1, argP2);
      }
461
      // does a policy exist
462
      if (this->IsKeyword(keyPOLICY, *arg) && argP1 != newArgs.end()) {
463
        cmPolicies::PolicyID pid;
464
465
466
        this->HandlePredicate(cmPolicies::GetPolicyID(argP1->c_str(), pid),
                              reducible, arg, newArgs, argP1, argP2);
      }
467
      // does a target exist
468
      if (this->IsKeyword(keyTARGET, *arg) && argP1 != newArgs.end()) {
469
        this->HandlePredicate(
470
          this->Makefile.FindTargetToUse(argP1->GetValue()) != CM_NULLPTR,
471
          reducible, arg, newArgs, argP1, argP2);
472
      }
473
      // does a test exist
474
475
476
      if (this->Policy64Status != cmPolicies::OLD &&
          this->Policy64Status != cmPolicies::WARN) {
        if (this->IsKeyword(keyTEST, *arg) && argP1 != newArgs.end()) {
477
          const cmTest* haveTest = this->Makefile.GetTest(argP1->c_str());
478
          this->HandlePredicate(haveTest != CM_NULLPTR, reducible, arg,
479
                                newArgs, argP1, argP2);
480
        }
481
482
      } else if (this->Policy64Status == cmPolicies::WARN &&
                 this->IsKeyword(keyTEST, *arg)) {
483
484
485
        std::ostringstream e;
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0064) << "\n";
        e << "TEST will be interpreted as an operator "
486
487
             "when the policy is set to NEW.  "
             "Since the policy is not set the OLD behavior will be used.";
488
489

        this->Makefile.IssueMessage(cmake::AUTHOR_WARNING, e.str());
490
      }
491
      // is a variable defined
492
      if (this->IsKeyword(keyDEFINED, *arg) && argP1 != newArgs.end()) {
493
        size_t argP1len = argP1->GetValue().size();
494
        bool bdef = false;
495
496
497
        if (argP1len > 4 && argP1->GetValue().substr(0, 4) == "ENV{" &&
            argP1->GetValue().operator[](argP1len - 1) == '}') {
          std::string env = argP1->GetValue().substr(4, argP1len - 5);
498
          bdef = cmSystemTools::HasEnv(env.c_str());
499
        } else {
500
          bdef = this->Makefile.IsDefinitionSet(argP1->GetValue());
501
        }
502
        this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
503
      }
504
      ++arg;
505
    }
506
  } while (reducible);
507
508
509
510
511
  return true;
}

//=========================================================================
// level two handles most binary operations except for AND  OR
512
513
514
bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs,
                                        std::string& errorString,
                                        cmake::MessageType& status)
515
516
{
  int reducible;
517
  std::string def_buf;
518
519
520
  const char* def;
  const char* def2;
  do {
521
    reducible = 0;
522
523
524
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
525
    while (arg != newArgs.end()) {
526
      argP1 = arg;
527
      this->IncrementArguments(newArgs, argP1, argP2);
528
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
529
          IsKeyword(keyMATCHES, *argP1)) {
530
        def = this->GetVariableOrString(*arg);
531
        if (def != arg->c_str() // yes, we compare the pointer value
532
            && cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) {
533
534
535
536
          // The string to match is owned by our match result variables.
          // Move it to our own buffer before clearing them.
          def_buf = def;
          def = def_buf.c_str();
537
        }
538
        const char* rex = argP2->c_str();
539
        this->Makefile.ClearMatches();
540
        cmsys::RegularExpression regEntry;
541
        if (!regEntry.compile(rex)) {
542
          std::ostringstream error;
543
544
545
546
          error << "Regular expression \"" << rex << "\" cannot compile";
          errorString = error.str();
          status = cmake::FATAL_ERROR;
          return false;
547
548
        }
        if (regEntry.find(def)) {
549
          this->Makefile.StoreMatches(regEntry);
550
          *arg = cmExpandedCommandArgument("1", true);
551
        } else {
552
          *arg = cmExpandedCommandArgument("0", true);
553
        }
554
555
556
        newArgs.erase(argP2);
        newArgs.erase(argP1);
        argP1 = arg;
557
        this->IncrementArguments(newArgs, argP1, argP2);
558
        reducible = 1;
559
      }
560

561
      if (argP1 != newArgs.end() && this->IsKeyword(keyMATCHES, *arg)) {
562
        *arg = cmExpandedCommandArgument("0", true);
563
564
        newArgs.erase(argP1);
        argP1 = arg;
565
        this->IncrementArguments(newArgs, argP1, argP2);
566
        reducible = 1;
567
      }
568
569

      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
570
          (this->IsKeyword(keyLESS, *argP1) ||
571
           this->IsKeyword(keyLESS_EQUAL, *argP1) ||
572
           this->IsKeyword(keyGREATER, *argP1) ||
573
           this->IsKeyword(keyGREATER_EQUAL, *argP1) ||
574
           this->IsKeyword(keyEQUAL, *argP1))) {
575
576
577
578
579
        def = this->GetVariableOrString(*arg);
        def2 = this->GetVariableOrString(*argP2);
        double lhs;
        double rhs;
        bool result;
580
        if (sscanf(def, "%lg", &lhs) != 1 || sscanf(def2, "%lg", &rhs) != 1) {
581
          result = false;
582
        } else if (*(argP1) == keyLESS) {
583
          result = (lhs < rhs);
584
585
        } else if (*(argP1) == keyLESS_EQUAL) {
          result = (lhs <= rhs);
586
        } else if (*(argP1) == keyGREATER) {
587
          result = (lhs > rhs);
588
589
        } else if (*(argP1) == keyGREATER_EQUAL) {
          result = (lhs >= rhs);
590
        } else {
591
592
          result = (lhs == rhs);
        }
593
594
        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
      }
595
596

      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
597
          (this->IsKeyword(keySTRLESS, *argP1) ||
598
599
600
601
           this->IsKeyword(keySTRLESS_EQUAL, *argP1) ||
           this->IsKeyword(keySTRGREATER, *argP1) ||
           this->IsKeyword(keySTRGREATER_EQUAL, *argP1) ||
           this->IsKeyword(keySTREQUAL, *argP1))) {
602
603
        def = this->GetVariableOrString(*arg);
        def2 = this->GetVariableOrString(*argP2);
604
        int val = strcmp(def, def2);
605
        bool result;
606
        if (*(argP1) == keySTRLESS) {
607
          result = (val < 0);
608
609
        } else if (*(argP1) == keySTRLESS_EQUAL) {
          result = (val <= 0);
610
        } else if (*(argP1) == keySTRGREATER) {
611
          result = (val > 0);
612
613
        } else if (*(argP1) == keySTRGREATER_EQUAL) {
          result = (val >= 0);
614
615
        } else // strequal
        {
616
617
          result = (val == 0);
        }
618
619
        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
      }
620
621

      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
622
          (this->IsKeyword(keyVERSION_LESS, *argP1) ||
623
           this->IsKeyword(keyVERSION_LESS_EQUAL, *argP1) ||
624
           this->IsKeyword(keyVERSION_GREATER, *argP1) ||
625
           this->IsKeyword(keyVERSION_GREATER_EQUAL, *argP1) ||
626
           this->IsKeyword(keyVERSION_EQUAL, *argP1))) {
627
628
        def = this->GetVariableOrString(*arg);
        def2 = this->GetVariableOrString(*argP2);
629
        cmSystemTools::CompareOp op;
630
        if (*argP1 == keyVERSION_LESS) {
631
          op = cmSystemTools::OP_LESS;
632
633
        } else if (*argP1 == keyVERSION_LESS_EQUAL) {
          op = cmSystemTools::OP_LESS_EQUAL;
634
        } else if (*argP1 == keyVERSION_GREATER) {
635
          op = cmSystemTools::OP_GREATER;
636
637
638
639
        } else if (*argP1 == keyVERSION_GREATER_EQUAL) {
          op = cmSystemTools::OP_GREATER_EQUAL;
        } else { // version_equal
          op = cmSystemTools::OP_EQUAL;
640
        }
641
642
643
        bool result = cmSystemTools::VersionCompare(op, def, def2);
        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
      }
644
645
646

      // is file A newer than file B
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
647
648
649
650
          this->IsKeyword(keyIS_NEWER_THAN, *argP1)) {
        int fileIsNewer = 0;
        bool success = cmSystemTools::FileTimeCompare(
          arg->GetValue(), (argP2)->GetValue(), &fileIsNewer);
651
        this->HandleBinaryOp(
652
653
          (!success || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
          newArgs, argP1, argP2);
654
      }
655

656
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
657
658
659
          this->IsKeyword(keyIN_LIST, *argP1)) {
        if (this->Policy57Status != cmPolicies::OLD &&
            this->Policy57Status != cmPolicies::WARN) {
660
661
662
663
664
          bool result = false;

          def = this->GetVariableOrString(*arg);
          def2 = this->Makefile.GetDefinition(argP2->GetValue());

665
          if (def2) {
666
667
668
669
670
            std::vector<std::string> list;
            cmSystemTools::ExpandListArgument(def2, list, true);

            result = std::find(list.begin(), list.end(), def) != list.end();
          }
671
672
673
674
675
676
677
678
679
680

          this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
        } else if (this->Policy57Status == cmPolicies::WARN) {
          std::ostringstream e;
          e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057) << "\n";
          e << "IN_LIST will be interpreted as an operator "
               "when the policy is set to NEW.  "
               "Since the policy is not set the OLD behavior will be used.";

          this->Makefile.IssueMessage(cmake::AUTHOR_WARNING, e.str());
681
        }
682
      }
683

684
685
      ++arg;
    }
686
  } while (reducible);
687
688
689
690
691
  return true;
}

//=========================================================================
// level 3 handles NOT
692
693
694
bool cmConditionEvaluator::HandleLevel3(cmArgumentList& newArgs,
                                        std::string& errorString,
                                        cmake::MessageType& status)
695
696
{
  int reducible;
697
  do {
698
    reducible = 0;
699
700
701
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
702
    while (arg != newArgs.end()) {
703
      argP1 = arg;
704
705
706
707
      IncrementArguments(newArgs, argP1, argP2);
      if (argP1 != newArgs.end() && IsKeyword(keyNOT, *arg)) {
        bool rhs = this->GetBooleanValueWithAutoDereference(
          *argP1, errorString, status);
708
709
        this->HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
      }
710
      ++arg;
711
    }
712
  } while (reducible);
713
714
715
716
717
  return true;
}

//=========================================================================
// level 4 handles AND OR
718
719
720
bool cmConditionEvaluator::HandleLevel4(cmArgumentList& newArgs,
                                        std::string& errorString,
                                        cmake::MessageType& status)
721
722
723
724
{
  int reducible;
  bool lhs;
  bool rhs;
725
  do {
726
    reducible = 0;
727
728
729
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
730
    while (arg != newArgs.end()) {
731
      argP1 = arg;
732
      IncrementArguments(newArgs, argP1, argP2);
733
      if (argP1 != newArgs.end() && IsKeyword(keyAND, *argP1) &&
734
735
736
737
738
739
740
741
          argP2 != newArgs.end()) {
        lhs =
          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
                                                       status);
        this->HandleBinaryOp((lhs && rhs), reducible, arg, newArgs, argP1,
                             argP2);
      }
742

743
      if (argP1 != newArgs.end() && this->IsKeyword(keyOR, *argP1) &&
744
745
746
747
748
749
750
          argP2 != newArgs.end()) {
        lhs =
          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
                                                       status);
        this->HandleBinaryOp((lhs || rhs), reducible, arg, newArgs, argP1,
                             argP2);
751
      }
752
      ++arg;
753
    }
754
  } while (reducible);
755
756
  return true;
}