cmConditionEvaluator.cxx 27.1 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
#include "cmsys/RegularExpression.hxx"
6 7 8 9 10
#include <algorithm>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
wahikihiki's avatar
wahikihiki committed
11
#include <utility>
12

13
#include "cmAlgorithms.h"
14
#include "cmMakefile.h"
15
#include "cmMessageType.h"
16
#include "cmState.h"
17
#include "cmSystemTools.h"
18
#include "cmake.h"
19

20 21 22
class cmCommand;
class cmTest;

23 24 25 26 27 28
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";
29
static std::string const keyGREATER_EQUAL = "GREATER_EQUAL";
30 31 32 33 34 35
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";
36
static std::string const keyLESS_EQUAL = "LESS_EQUAL";
37 38 39 40 41 42 43 44
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";
45
static std::string const keySTRGREATER_EQUAL = "STRGREATER_EQUAL";
46
static std::string const keySTRLESS = "STRLESS";
47
static std::string const keySTRLESS_EQUAL = "STRLESS_EQUAL";
48 49 50 51
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";
52
static std::string const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL";
53
static std::string const keyVERSION_LESS = "VERSION_LESS";
54
static std::string const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL";
55

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

//=========================================================================
// 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(
86
  const std::vector<cmExpandedCommandArgument>& args, std::string& errorString,
87
  MessageType& status)
88
{
89
  errorString.clear();
90 91

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

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

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

  // 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
105
  if (!this->HandleLevel0(newArgs, errorString, status)) {
106
    return false;
107 108 109
  }
  // predicates
  if (!this->HandleLevel1(newArgs, errorString, status)) {
110
    return false;
111
  }
112
  // binary ops
113
  if (!this->HandleLevel2(newArgs, errorString, status)) {
114
    return false;
115
  }
116 117

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

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

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

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

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

149 150 151 152
  if (def && argument.WasQuoted() &&
      this->Policy54Status == cmPolicies::WARN) {
    if (!this->Makefile.HasCMP0054AlreadyBeenReported(
          this->ExecutionContext)) {
153
      std::ostringstream e;
154
      e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)) << "\n";
155 156 157 158 159 160
      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(
161
        MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
162
    }
163
  }
164 165 166 167

  return def;
}

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

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

  return def;
}

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

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

193 194 195 196
  if (isKeyword && argument.WasQuoted() &&
      this->Policy54Status == cmPolicies::WARN) {
    if (!this->Makefile.HasCMP0054AlreadyBeenReported(
          this->ExecutionContext)) {
197
      std::ostringstream e;
198
      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054) << "\n";
199 200 201 202 203 204
      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(
205
        MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
206
    }
207
  }
208 209 210 211

  return isKeyword;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

//=========================================================================
// level two handles most binary operations except for AND  OR
520 521
bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs,
                                        std::string& errorString,
522
                                        MessageType& status)
523 524
{
  int reducible;
525
  std::string def_buf;
526 527 528
  const char* def;
  const char* def2;
  do {
529
    reducible = 0;
530 531 532
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
533
    while (arg != newArgs.end()) {
534
      argP1 = arg;
535
      this->IncrementArguments(newArgs, argP1, argP2);
536
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
537
          IsKeyword(keyMATCHES, *argP1)) {
538
        def = this->GetVariableOrString(*arg);
539
        if (def != arg->c_str() // yes, we compare the pointer value
540
            && cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) {
541 542 543 544
          // 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();
545
        }
546
        const char* rex = argP2->c_str();
547
        this->Makefile.ClearMatches();
548
        cmsys::RegularExpression regEntry;
549
        if (!regEntry.compile(rex)) {
550
          std::ostringstream error;
551 552
          error << "Regular expression \"" << rex << "\" cannot compile";
          errorString = error.str();
553
          status = MessageType::FATAL_ERROR;
554
          return false;
555 556
        }
        if (regEntry.find(def)) {
557
          this->Makefile.StoreMatches(regEntry);
558
          *arg = cmExpandedCommandArgument("1", true);
559
        } else {
560
          *arg = cmExpandedCommandArgument("0", true);
561
        }
562 563 564
        newArgs.erase(argP2);
        newArgs.erase(argP1);
        argP1 = arg;
565
        this->IncrementArguments(newArgs, argP1, argP2);
566
        reducible = 1;
567
      }
568

569
      if (argP1 != newArgs.end() && this->IsKeyword(keyMATCHES, *arg)) {
570
        *arg = cmExpandedCommandArgument("0", true);
571 572
        newArgs.erase(argP1);
        argP1 = arg;
573
        this->IncrementArguments(newArgs, argP1, argP2);
574
        reducible = 1;
575
      }
576 577

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

      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
605
          (this->IsKeyword(keySTRLESS, *argP1) ||
606 607 608 609
           this->IsKeyword(keySTRLESS_EQUAL, *argP1) ||
           this->IsKeyword(keySTRGREATER, *argP1) ||
           this->IsKeyword(keySTRGREATER_EQUAL, *argP1) ||
           this->IsKeyword(keySTREQUAL, *argP1))) {
610 611
        def = this->GetVariableOrString(*arg);
        def2 = this->GetVariableOrString(*argP2);
612
        int val = strcmp(def, def2);
613
        bool result;
614
        if (*(argP1) == keySTRLESS) {
615
          result = (val < 0);
616 617
        } else if (*(argP1) == keySTRLESS_EQUAL) {
          result = (val <= 0);
618
        } else if (*(argP1) == keySTRGREATER) {
619
          result = (val > 0);
620 621
        } else if (*(argP1) == keySTRGREATER_EQUAL) {
          result = (val >= 0);
622 623
        } else // strequal
        {
624 625
          result = (val == 0);
        }
626 627
        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
      }
628 629

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

      // is file A newer than file B
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
655 656 657 658
          this->IsKeyword(keyIS_NEWER_THAN, *argP1)) {
        int fileIsNewer = 0;
        bool success = cmSystemTools::FileTimeCompare(
          arg->GetValue(), (argP2)->GetValue(), &fileIsNewer);
659
        this->HandleBinaryOp(
660 661
          (!success || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
          newArgs, argP1, argP2);
662
      }
663

664
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
665 666 667
          this->IsKeyword(keyIN_LIST, *argP1)) {
        if (this->Policy57Status != cmPolicies::OLD &&
            this->Policy57Status != cmPolicies::WARN) {
668 669 670 671 672
          bool result = false;

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

673
          if (def2) {
674 675 676 677 678
            std::vector<std::string> list;
            cmSystemTools::ExpandListArgument(def2, list, true);

            result = std::find(list.begin(), list.end(), def) != list.end();
          }
679 680 681 682 683 684 685 686 687

          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.";

688
          this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
689
        }
690
      }
691

692 693
      ++arg;
    }
694
  } while (reducible);
695 696 697 698 699
  return true;
}

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

//=========================================================================
// level 4 handles AND OR
726 727
bool cmConditionEvaluator::HandleLevel4(cmArgumentList& newArgs,
                                        std::string& errorString,
728
                                        MessageType& status)
729 730 731 732
{
  int reducible;
  bool lhs;
  bool rhs;
733
  do {
734
    reducible = 0;
735 736 737
    cmArgumentList::iterator arg = newArgs.begin();
    cmArgumentList::iterator argP1;
    cmArgumentList::iterator argP2;
738
    while (arg != newArgs.end()) {
739
      argP1 = arg;
740
      IncrementArguments(newArgs, argP1, argP2);
741
      if (argP1 != newArgs.end() && IsKeyword(keyAND, *argP1) &&
742 743 744 745 746 747 748 749
          argP2 != newArgs.end()) {
        lhs =
          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
                                                       status);
        this->HandleBinaryOp((lhs && rhs), reducible, arg, newArgs, argP1,
                             argP2);
      }
750

751
      if (argP1 != newArgs.end() && this->IsKeyword(keyOR, *argP1) &&
752 753 754 755 756 757 758
          argP2 != newArgs.end()) {
        lhs =
          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
                                                       status);
        this->HandleBinaryOp((lhs || rhs), reducible, arg, newArgs, argP1,
                             argP2);
759
      }
760
      ++arg;
761
    }
762
  } while (reducible);
763 764
  return true;
}