cmIfCommand.cxx 27.2 KB
Newer Older
1
2
/*=========================================================================

3
  Program:   CMake - Cross-Platform Makefile Generator
4
5
6
7
8
  Module:    $RCSfile$
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

9
10
  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
11

12
13
     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
Will Schroeder's avatar
Will Schroeder committed
14
     PURPOSE.  See the above copyright notices for more information.
15
16
17

=========================================================================*/
#include "cmIfCommand.h"
Alexander Neundorf's avatar
   
Alexander Neundorf committed
18
19
#include "cmStringCommand.h"

20
#include <stdlib.h> // required for atof
21
#include <list>
22
23
#include <cmsys/RegularExpression.hxx>

24
//=========================================================================
25
bool cmIfFunctionBlocker::
26
IsFunctionBlocked(const cmListFileFunction& lff,
27
                  cmMakefile &mf,
28
                  cmExecutionStatus &inStatus)
29
{
30
31
  // we start by recording all the functions
  if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
32
    {
33
    this->ScopeDepth++;
34
    }
35
  if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
36
    {
37
38
    this->ScopeDepth--;
    // if this is the endif for this if statement, then start executing
39
    if (!this->ScopeDepth)
Ken Martin's avatar
Ken Martin committed
40
      {
41
      // Remove the function blocker for this scope or bail.
42
43
      cmsys::auto_ptr<cmFunctionBlocker>
        fb(mf.RemoveFunctionBlocker(this, lff));
44
45
      if(!fb.get()) { return false; }

46
47
48
49
      // execute the functions for the true parts of the if statement
      cmExecutionStatus status;
      int scopeDepth = 0;
      for(unsigned int c = 0; c < this->Functions.size(); ++c)
Ken Martin's avatar
Ken Martin committed
50
        {
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
        // keep track of scope depth
        if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"if"))
          {
          scopeDepth++;
          }
        if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
          {
          scopeDepth--;
          }
        // watch for our state change
        if (scopeDepth == 0 &&
            !cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"else"))
          {
          this->IsBlocking = this->HasRun;
          this->HasRun = true;
          }
        else if (scopeDepth == 0 && !cmSystemTools::Strucmp
                 (this->Functions[c].Name.c_str(),"elseif"))
Ken Martin's avatar
Ken Martin committed
69
          {
70
          if (this->HasRun)
Ken Martin's avatar
Ken Martin committed
71
            {
72
            this->IsBlocking = true;
Ken Martin's avatar
Ken Martin committed
73
            }
74
75
          else
            {
76
77
78
79
            // Place this call on the call stack.
            cmMakefileCall stack_manager(&mf, this->Functions[c], status);
            static_cast<void>(stack_manager);

80
            std::string errorString;
81

82
            std::vector<std::string> expandedArguments;
83
            mf.ExpandArguments(this->Functions[c].Arguments,
84
                               expandedArguments);
85

Ken Martin's avatar
Ken Martin committed
86
            cmake::MessageType messType;
87
88
            bool isTrue =
              cmIfCommand::IsTrue(expandedArguments, errorString,
Ken Martin's avatar
Ken Martin committed
89
                                  &mf, messType);
90

91
            if (errorString.size())
92
              {
93
              std::string err = "given arguments\n  ";
94
95
96
97
98
99
100
101
              unsigned int i;
              for(i =0; i < this->Functions[c].Arguments.size(); ++i)
                {
                err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
                err += this->Functions[c].Arguments[i].Value;
                err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
                err += " ";
                }
102
              err += "\n";
103
              err += errorString;
Ken Martin's avatar
Ken Martin committed
104
105
              mf.IssueMessage(messType, err);
              if (messType == cmake::FATAL_ERROR)
106
107
108
109
                {
                cmSystemTools::SetFatalErrorOccured();
                return true;
                }
110
              }
111

112
113
114
115
116
117
118
            if (isTrue)
              {
              this->IsBlocking = false;
              this->HasRun = true;
              }
            }
          }
119

120
121
        // should we execute?
        else if (!this->IsBlocking)
Ken Martin's avatar
Ken Martin committed
122
          {
123
124
125
126
127
128
129
130
131
132
133
134
          status.Clear();
          mf.ExecuteCommand(this->Functions[c],status);
          if (status.GetReturnInvoked())
            {
            inStatus.SetReturnInvoked(true);
            return true;
            }
          if (status.GetBreakInvoked())
            {
            inStatus.SetBreakInvoked(true);
            return true;
            }
Ken Martin's avatar
Ken Martin committed
135
          }
Ken Martin's avatar
Ken Martin committed
136
        }
Ken Martin's avatar
Ken Martin committed
137
138
139
      return true;
      }
    }
140

141
142
  // record the command
  this->Functions.push_back(lff);
143

144
145
  // always return true
  return true;
146
147
}

148
//=========================================================================
149
bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
Bill Hoffman's avatar
Bill Hoffman committed
150
                                       cmMakefile&)
151
{
152
  if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
153
    {
154
155
156
157
    // if the endif has arguments, then make sure
    // they match the arguments of the matching if
    if (lff.Arguments.size() == 0 ||
        lff.Arguments == this->Args)
158
159
160
161
      {
      return true;
      }
    }
Ken Martin's avatar
Ken Martin committed
162

163
  return false;
164
165
}

166
//=========================================================================
Ken Martin's avatar
Ken Martin committed
167
bool cmIfCommand
168
::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
169
                    cmExecutionStatus &)
170
{
171
  std::string errorString;
172

173
  std::vector<std::string> expandedArguments;
Ken Martin's avatar
Ken Martin committed
174
  this->Makefile->ExpandArguments(args, expandedArguments);
175
176
177
178
179
180

  cmake::MessageType status;
  bool isTrue =
    cmIfCommand::IsTrue(expandedArguments,errorString,
                        this->Makefile, status);

181
  if (errorString.size())
182
    {
183
    std::string err = "given arguments\n  ";
Ken Martin's avatar
Ken Martin committed
184
185
    unsigned int i;
    for(i =0; i < args.size(); ++i)
186
      {
187
188
189
      err += (args[i].Quoted?"\"":"");
      err += args[i].Value;
      err += (args[i].Quoted?"\"":"");
190
191
      err += " ";
      }
192
    err += "\n";
193
    err += errorString;
194
195
196
197
198
199
200
201
202
203
    if (status == cmake::FATAL_ERROR)
      {
      this->SetError(err.c_str());
      cmSystemTools::SetFatalErrorOccured();
      return false;
      }
    else
      {
      this->Makefile->IssueMessage(status, err);
      }
204
    }
205

206
207
  cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
  // if is isn't true block the commands
208
  f->ScopeDepth = 1;
Ken Martin's avatar
Ken Martin committed
209
  f->IsBlocking = !isTrue;
Ken Martin's avatar
Ken Martin committed
210
211
212
213
  if (isTrue)
    {
    f->HasRun = true;
    }
Ken Martin's avatar
Ken Martin committed
214
215
  f->Args = args;
  this->Makefile->AddFunctionBlocker(f);
216

217
218
  return true;
}
Ken Martin's avatar
Ken Martin committed
219

220
namespace
Ken Martin's avatar
Ken Martin committed
221
{
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  //=========================================================================
  // returns true if succesfull, the resulting bool parsed is stored in result
  bool GetBooleanValue(std::string &newArg,
                       cmMakefile *makefile,
                       bool &result,
                       std::string &errorString,
                       cmPolicies::PolicyStatus Policy12Status,
                       cmake::MessageType &status)
  {
    if (Policy12Status != cmPolicies::OLD &&
        Policy12Status != cmPolicies::WARN)
      {
      // please note IsOn(var) does not always equal !IsOff(var)
      // that is why each is called
      if (cmSystemTools::IsOn(newArg.c_str()))
        {
        result = true;
        return true;
        }
      if (cmSystemTools::IsOff(newArg.c_str()))
        {
        result = false;
        return true;
        }
      return false;
      }

    // Old policy is more complex...
    // 0 and 1 are very common, test for them first quickly
    if (newArg == "0")
    {
      result = false;
      return true;
    }
    if (newArg == "1")
    {
      result = true;
      return true;
    }

    // old behavior is to dereference the var
    if (Policy12Status == cmPolicies::OLD)
    {
      return false;
    }

    // now test for values that may be the name of a variable
    // warn if used
    if (cmSystemTools::IsOn(newArg.c_str()))
      {
      // only warn if the value would change
      const char *def = makefile->GetDefinition(newArg.c_str());
      if (cmSystemTools::IsOff(def))
        {
        cmPolicies* policies = makefile->GetPolicies();
        errorString = "You have used a variable or argument named \""
          + newArg
          + "\" in a conditional statement. Please be aware of issues "
          + "related to policy CMP0012. "
          + policies->GetPolicyWarning(cmPolicies::CMP0012);
        status = cmake::AUTHOR_WARNING;
        }
      return false;
      }
    if (cmSystemTools::IsOff(newArg.c_str()))
      {
      // only warn if the value would change
      const char *def = makefile->GetDefinition(newArg.c_str());
      if (!cmSystemTools::IsOff(def))
        {
        cmPolicies* policies = makefile->GetPolicies();
        errorString = "You have used a variable or argument named \""
          + newArg
          + "\" in a conditional statement. Please be aware of issues "
          + "related to policy CMP0012. "
          + policies->GetPolicyWarning(cmPolicies::CMP0012);
        status = cmake::AUTHOR_WARNING;
        }
      return false;
      }
    return false;
  }

  //=========================================================================
  // returns the resulting boolean value
  bool GetBooleanValueWithAutoDereference(
    std::string &newArg,
    cmMakefile *makefile,
    std::string &errorString,
    cmPolicies::PolicyStatus Policy12Status,
    cmake::MessageType &status)
  {
    bool result = false;
    if (GetBooleanValue(newArg, makefile, result,
                        errorString, Policy12Status, status))
    {
      return result;
    }
    const char *def = makefile->GetDefinition(newArg.c_str());
    return !cmSystemTools::IsOff(def);
  }

  //=========================================================================
Ken Martin's avatar
Ken Martin committed
325
326
327
328
  void IncrementArguments(std::list<std::string> &newArgs,
                          std::list<std::string>::iterator &argP1,
                          std::list<std::string>::iterator &argP2)
  {
Ken Martin's avatar
Ken Martin committed
329
330
    if (argP1  != newArgs.end())
      {
Ken Martin's avatar
Ken Martin committed
331
332
333
334
335
336
      argP1++;
      argP2 = argP1;
      if (argP1  != newArgs.end())
        {
        argP2++;
        }
Ken Martin's avatar
Ken Martin committed
337
      }
Ken Martin's avatar
Ken Martin committed
338
  }
Ken Martin's avatar
Ken Martin committed
339

340
341
342
343
344
345
346
347
348
349
350
351
  //=========================================================================
  // helper function to reduce code duplication
  void HandlePredicate(bool value, int &reducible,
                       std::list<std::string>::iterator &arg,
                       std::list<std::string> &newArgs,
                       std::list<std::string>::iterator &argP1,
                       std::list<std::string>::iterator &argP2)
  {
    if(value)
      {
      *arg = "1";
      }
352
    else
353
354
355
356
357
358
359
360
      {
      *arg = "0";
      }
    newArgs.erase(argP1);
    argP1 = arg;
    IncrementArguments(newArgs,argP1,argP2);
    reducible = 1;
  }
Ken Martin's avatar
Ken Martin committed
361

362
363
364
365
366
367
368
369
370
371
372
373
  //=========================================================================
  // helper function to reduce code duplication
  void HandleBinaryOp(bool value, int &reducible,
                       std::list<std::string>::iterator &arg,
                       std::list<std::string> &newArgs,
                       std::list<std::string>::iterator &argP1,
                       std::list<std::string>::iterator &argP2)
  {
    if(value)
      {
      *arg = "1";
      }
374
    else
375
376
377
378
379
380
381
382
383
      {
      *arg = "0";
      }
    newArgs.erase(argP2);
    newArgs.erase(argP1);
    argP1 = arg;
    IncrementArguments(newArgs,argP1,argP2);
    reducible = 1;
  }
Ken Martin's avatar
Ken Martin committed
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
  //=========================================================================
  enum Op { OpLess, OpEqual, OpGreater };
  bool HandleVersionCompare(Op op, const char* lhs_str, const char* rhs_str)
  {
  // Parse out up to 4 components.
  unsigned int lhs[4] = {0,0,0,0};
  unsigned int rhs[4] = {0,0,0,0};
  sscanf(lhs_str, "%u.%u.%u.%u", &lhs[0], &lhs[1], &lhs[2], &lhs[3]);
  sscanf(rhs_str, "%u.%u.%u.%u", &rhs[0], &rhs[1], &rhs[2], &rhs[3]);

  // Do component-wise comparison.
  for(unsigned int i=0; i < 4; ++i)
    {
    if(lhs[i] < rhs[i])
      {
      // lhs < rhs, so true if operation is LESS
      return op == OpLess;
      }
    else if(lhs[i] > rhs[i])
      {
      // lhs > rhs, so true if operation is GREATER
      return op == OpGreater;
      }
    }
  // lhs == rhs, so true if operation is EQUAL
  return op == OpEqual;
  }

413
414
415
416
  //=========================================================================
  // level 0 processes parenthetical expressions
  bool HandleLevel0(std::list<std::string> &newArgs,
                    cmMakefile *makefile,
417
418
                    std::string &errorString,
                    cmake::MessageType &status)
419
  {
Ken Martin's avatar
Ken Martin committed
420
  int reducible;
Ken Martin's avatar
Ken Martin committed
421
  do
Ken Martin's avatar
Ken Martin committed
422
    {
Ken Martin's avatar
Ken Martin committed
423
    reducible = 0;
424
    std::list<std::string>::iterator arg = newArgs.begin();
Ken Martin's avatar
Ken Martin committed
425
    while (arg != newArgs.end())
Ken Martin's avatar
Ken Martin committed
426
      {
427
      if (*arg == "(")
Ken Martin's avatar
Ken Martin committed
428
        {
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
        // search for the closing paren for this opening one
        std::list<std::string>::iterator argClose;
        argClose = arg;
        argClose++;
        unsigned int depth = 1;
        while (argClose != newArgs.end() && depth)
          {
          if (*argClose == "(")
            {
              depth++;
            }
          if (*argClose == ")")
            {
              depth--;
            }
          argClose++;
          }
        if (depth)
          {
448
          errorString = "mismatched parenthesis in condition";
449
          status = cmake::FATAL_ERROR;
450
451
452
453
454
455
456
457
458
          return false;
          }
        // store the reduced args in this vector
        std::vector<std::string> newArgs2;

        // copy to the list structure
        std::list<std::string>::iterator argP1 = arg;
        argP1++;
        for(; argP1 != argClose; argP1++)
459
          {
460
461
462
          newArgs2.push_back(*argP1);
          }
        newArgs2.pop_back();
463
464
        // now recursively invoke IsTrue to handle the values inside the
        // parenthetical expression
465
466
        bool value =
          cmIfCommand::IsTrue(newArgs2, errorString, makefile, status);
467
        if(value)
Ken Martin's avatar
Ken Martin committed
468
469
470
          {
          *arg = "1";
          }
471
        else
Ken Martin's avatar
Ken Martin committed
472
          {
Ken Martin's avatar
Ken Martin committed
473
474
475
          *arg = "0";
          }
        argP1 = arg;
476
477
        argP1++;
        // remove the now evaluated parenthetical expression
478
        newArgs.erase(argP1,argClose);
479
480
481
482
483
484
485
        }
      ++arg;
      }
    }
  while (reducible);
  return true;
  }
486

487
488
489
490
  //=========================================================================
  // level one handles most predicates except for NOT
  bool HandleLevel1(std::list<std::string> &newArgs,
                    cmMakefile *makefile,
491
                    std::string &, cmake::MessageType &)
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  {
  int reducible;
  do
    {
    reducible = 0;
    std::list<std::string>::iterator arg = newArgs.begin();
    std::list<std::string>::iterator argP1;
    std::list<std::string>::iterator argP2;
    while (arg != newArgs.end())
      {
      argP1 = arg;
      IncrementArguments(newArgs,argP1,argP2);
      // does a file exist
      if (*arg == "EXISTS" && argP1  != newArgs.end())
        {
        HandlePredicate(
          cmSystemTools::FileExists((argP1)->c_str()),
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
510
        }
511
      // does a directory with this name exist
Ken Martin's avatar
Ken Martin committed
512
513
      if (*arg == "IS_DIRECTORY" && argP1  != newArgs.end())
        {
514
515
516
        HandlePredicate(
          cmSystemTools::FileIsDirectory((argP1)->c_str()),
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
517
        }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
518
519
520
      // is the given path an absolute path ?
      if (*arg == "IS_ABSOLUTE" && argP1  != newArgs.end())
        {
521
522
523
        HandlePredicate(
          cmSystemTools::FileIsFullPath((argP1)->c_str()),
          reducible, arg, newArgs, argP1, argP2);
Alexander Neundorf's avatar
   
Alexander Neundorf committed
524
        }
Ken Martin's avatar
Ken Martin committed
525
      // does a command exist
526
      if (*arg == "COMMAND" && argP1  != newArgs.end())
Ken Martin's avatar
Ken Martin committed
527
        {
528
529
530
        HandlePredicate(
          makefile->CommandExists((argP1)->c_str()),
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
531
        }
532
533
534
535
      // does a policy exist
      if (*arg == "POLICY" && argP1 != newArgs.end())
        {
        cmPolicies::PolicyID pid;
536
537
538
        HandlePredicate(
          makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid),
          reducible, arg, newArgs, argP1, argP2);
539
        }
Brad King's avatar
Brad King committed
540
541
542
543
544
545
546
      // does a target exist
      if (*arg == "TARGET" && argP1 != newArgs.end())
        {
        HandlePredicate(
          makefile->FindTargetToUse((argP1)->c_str())? true:false,
          reducible, arg, newArgs, argP1, argP2);
        }
Ken Martin's avatar
Ken Martin committed
547
      // is a variable defined
548
      if (*arg == "DEFINED" && argP1  != newArgs.end())
Ken Martin's avatar
Ken Martin committed
549
        {
550
        size_t argP1len = argP1->size();
551
        bool bdef = false;
552
553
554
555
        if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
           argP1->operator[](argP1len-1) == '}')
          {
          std::string env = argP1->substr(4, argP1len-5);
556
          bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
557
558
559
          }
        else
          {
560
          bdef = makefile->IsDefinitionSet((argP1)->c_str());
561
          }
562
        HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
563
564
        }
      ++arg;
Ken Martin's avatar
Ken Martin committed
565
566
      }
    }
Ken Martin's avatar
Ken Martin committed
567
  while (reducible);
568
569
  return true;
  }
570

571
572
573
574
  //=========================================================================
  // level two handles most binary operations except for AND  OR
  bool HandleLevel2(std::list<std::string> &newArgs,
                    cmMakefile *makefile,
575
576
                    std::string &errorString,
                    cmake::MessageType &status)
577
578
579
580
  {
  int reducible;
  const char *def;
  const char *def2;
Ken Martin's avatar
Ken Martin committed
581
  do
582
    {
Ken Martin's avatar
Ken Martin committed
583
    reducible = 0;
584
    std::list<std::string>::iterator arg = newArgs.begin();
585
586
    std::list<std::string>::iterator argP1;
    std::list<std::string>::iterator argP2;
Ken Martin's avatar
Ken Martin committed
587
    while (arg != newArgs.end())
Ken Martin's avatar
Ken Martin committed
588
      {
589
      argP1 = arg;
Ken Martin's avatar
Ken Martin committed
590
      IncrementArguments(newArgs,argP1,argP2);
591
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
592
        *(argP1) == "MATCHES")
Ken Martin's avatar
Ken Martin committed
593
594
        {
        def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
595
        const char* rex = (argP2)->c_str();
Alexander Neundorf's avatar
   
Alexander Neundorf committed
596
        cmStringCommand::ClearMatches(makefile);
597
598
599
600
601
        cmsys::RegularExpression regEntry;
        if ( !regEntry.compile(rex) )
          {
          cmOStringStream error;
          error << "Regular expression \"" << rex << "\" cannot compile";
602
          errorString = error.str();
603
          status = cmake::FATAL_ERROR;
604
605
          return false;
          }
Ken Martin's avatar
Ken Martin committed
606
607
        if (regEntry.find(def))
          {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
608
          cmStringCommand::StoreMatches(makefile, regEntry);
609
          *arg = "1";
Ken Martin's avatar
Ken Martin committed
610
611
612
613
614
          }
        else
          {
          *arg = "0";
          }
615
616
617
        newArgs.erase(argP2);
        newArgs.erase(argP1);
        argP1 = arg;
Ken Martin's avatar
Ken Martin committed
618
        IncrementArguments(newArgs,argP1,argP2);
Ken Martin's avatar
Ken Martin committed
619
620
        reducible = 1;
        }
621

622
      if (argP1 != newArgs.end() && *arg == "MATCHES")
Ken Martin's avatar
Ken Martin committed
623
624
        {
        *arg = "0";
625
626
        newArgs.erase(argP1);
        argP1 = arg;
Ken Martin's avatar
Ken Martin committed
627
        IncrementArguments(newArgs,argP1,argP2);
Ken Martin's avatar
Ken Martin committed
628
629
        reducible = 1;
        }
Ken Martin's avatar
Ken Martin committed
630

631
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
632
633
        (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
         *(argP1) == "EQUAL"))
Ken Martin's avatar
Ken Martin committed
634
635
        {
        def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
636
        def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
637
638
        double lhs;
        double rhs;
639
        bool result;
640
641
        if(sscanf(def, "%lg", &lhs) != 1 ||
           sscanf(def2, "%lg", &rhs) != 1)
642
          {
643
          result = false;
644
          }
645
        else if (*(argP1) == "LESS")
Ken Martin's avatar
Ken Martin committed
646
          {
647
          result = (lhs < rhs);
Ken Martin's avatar
Ken Martin committed
648
          }
649
        else if (*(argP1) == "GREATER")
Ken Martin's avatar
Ken Martin committed
650
          {
651
          result = (lhs > rhs);
652
          }
Ken Martin's avatar
Ken Martin committed
653
654
        else
          {
655
          result = (lhs == rhs);
656
          }
657
658
        HandleBinaryOp(result,
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
659
        }
660

661
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
662
663
664
        (*(argP1) == "STRLESS" ||
         *(argP1) == "STREQUAL" ||
         *(argP1) == "STRGREATER"))
Ken Martin's avatar
Ken Martin committed
665
666
        {
        def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
667
        def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
Ken Martin's avatar
Ken Martin committed
668
        int val = strcmp(def,def2);
669
        bool result;
670
        if (*(argP1) == "STRLESS")
Ken Martin's avatar
Ken Martin committed
671
          {
Ken Martin's avatar
Ken Martin committed
672
673
674
675
676
677
678
679
680
681
          result = (val < 0);
          }
        else if (*(argP1) == "STRGREATER")
          {
          result = (val > 0);
          }
        else // strequal
          {
          result = (val == 0);
          }
682
683
        HandleBinaryOp(result,
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
684
        }
685

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
        (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" ||
         *(argP1) == "VERSION_EQUAL"))
        {
        def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
        def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
        Op op = OpEqual;
        if(*argP1 == "VERSION_LESS")
          {
          op = OpLess;
          }
        else if(*argP1 == "VERSION_GREATER")
          {
          op = OpGreater;
          }
        bool result = HandleVersionCompare(op, def, def2);
        HandleBinaryOp(result,
          reducible, arg, newArgs, argP1, argP2);
        }

706
707
708
709
710
711
712
713
      // is file A newer than file B
      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
          *(argP1) == "IS_NEWER_THAN")
        {
        int fileIsNewer=0;
        bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
            (argP2)->c_str(),
            &fileIsNewer);
714
715
716
        HandleBinaryOp(
          (success==false || fileIsNewer==1 || fileIsNewer==0),
          reducible, arg, newArgs, argP1, argP2);
717
718
        }

Ken Martin's avatar
Ken Martin committed
719
      ++arg;
720
721
      }
    }
Ken Martin's avatar
Ken Martin committed
722
  while (reducible);
723
724
  return true;
  }
725

726
727
728
729
  //=========================================================================
  // level 3 handles NOT
  bool HandleLevel3(std::list<std::string> &newArgs,
                    cmMakefile *makefile,
730
731
732
                    std::string &errorString,
                    cmPolicies::PolicyStatus Policy12Status,
                    cmake::MessageType &status)
733
734
  {
  int reducible;
Ken Martin's avatar
Ken Martin committed
735
  do
Ken Martin's avatar
Ken Martin committed
736
    {
Ken Martin's avatar
Ken Martin committed
737
    reducible = 0;
738
    std::list<std::string>::iterator arg = newArgs.begin();
739
740
    std::list<std::string>::iterator argP1;
    std::list<std::string>::iterator argP2;
Ken Martin's avatar
Ken Martin committed
741
    while (arg != newArgs.end())
Ken Martin's avatar
Ken Martin committed
742
      {
743
      argP1 = arg;
Ken Martin's avatar
Ken Martin committed
744
      IncrementArguments(newArgs,argP1,argP2);
745
      if (argP1 != newArgs.end() && *arg == "NOT")
Ken Martin's avatar
Ken Martin committed
746
        {
747
748
749
750
751
        bool rhs = GetBooleanValueWithAutoDereference(*argP1, makefile,
                                                      errorString,
                                                      Policy12Status,
                                                      status);
        HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
752
753
        }
      ++arg;
Ken Martin's avatar
Ken Martin committed
754
      }
755
    }
Ken Martin's avatar
Ken Martin committed
756
  while (reducible);
757
758
  return true;
  }
759

760
761
762
763
  //=========================================================================
  // level 4 handles AND OR
  bool HandleLevel4(std::list<std::string> &newArgs,
                    cmMakefile *makefile,
764
765
766
                    std::string &errorString,
                    cmPolicies::PolicyStatus Policy12Status,
                    cmake::MessageType &status)
767
768
  {
  int reducible;
769
770
  bool lhs;
  bool rhs;
Ken Martin's avatar
Ken Martin committed
771
  do
772
    {
Ken Martin's avatar
Ken Martin committed
773
    reducible = 0;
774
    std::list<std::string>::iterator arg = newArgs.begin();
775
776
    std::list<std::string>::iterator argP1;
    std::list<std::string>::iterator argP2;
Ken Martin's avatar
Ken Martin committed
777
    while (arg != newArgs.end())
Ken Martin's avatar
Ken Martin committed
778
      {
779
      argP1 = arg;
Ken Martin's avatar
Ken Martin committed
780
      IncrementArguments(newArgs,argP1,argP2);
781
      if (argP1 != newArgs.end() && *(argP1) == "AND" &&
782
        argP2 != newArgs.end())
Ken Martin's avatar
Ken Martin committed
783
        {
784
785
786
787
788
789
790
791
792
        lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
                                                 errorString,
                                                 Policy12Status,
                                                 status);
        rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
                                                 errorString,
                                                 Policy12Status,
                                                 status);
        HandleBinaryOp((lhs && rhs),
793
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
794
        }
Ken Martin's avatar
Ken Martin committed
795

796
      if (argP1 != newArgs.end() && *(argP1) == "OR" &&
797
        argP2 != newArgs.end())
Ken Martin's avatar
Ken Martin committed
798
        {
799
800
801
802
803
804
805
806
807
        lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
                                                 errorString,
                                                 Policy12Status,
                                                 status);
        rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
                                                 errorString,
                                                 Policy12Status,
                                                 status);
        HandleBinaryOp((lhs || rhs),
808
          reducible, arg, newArgs, argP1, argP2);
Ken Martin's avatar
Ken Martin committed
809
810
        }
      ++arg;
Ken Martin's avatar
Ken Martin committed
811
812
      }
    }
Ken Martin's avatar
Ken Martin committed
813
  while (reducible);
814
815
816
817
818
819
820
  return true;
  }
}


//=========================================================================
// order of operations,
821
822
823
// 1.   ( )   -- parenthetical groups
// 2.  IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
// 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
824
825
826
827
828
829
830
831
832
833
834
835
836
// 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 cmIfCommand::IsTrue(const std::vector<std::string> &args,
837
838
                         std::string &errorString, cmMakefile *makefile,
                         cmake::MessageType &status)
839
{
840
  errorString = "";
841
842
843
844
845
846
847
848
849
850
851
852

  // handle empty invocation
  if (args.size() < 1)
    {
    return false;
    }

  // store the reduced args in this vector
  std::list<std::string> newArgs;

  // copy to the list structure
  for(unsigned int i = 0; i < args.size(); ++i)
853
    {
854
855
856
857
858
    newArgs.push_back(args[i]);
    }

  // 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
859
860
  // parens
  if (!HandleLevel0(newArgs, makefile, errorString, status))
861
862
863
    {
    return false;
    }
864
865
  //predicates
  if (!HandleLevel1(newArgs, makefile, errorString, status))
866
867
868
    {
    return false;
    }
869
870
  // binary ops
  if (!HandleLevel2(newArgs, makefile, errorString, status))
871
872
873
    {
    return false;
    }
874
875
876
877
878
879
880
881

  // used to store the value of policy CMP0012 for performance
  cmPolicies::PolicyStatus Policy12Status =
    makefile->GetPolicyStatus(cmPolicies::CMP0012);

  // NOT
  if (!HandleLevel3(newArgs, makefile, errorString,
                    Policy12Status, status))
882
883
884
    {
    return false;
    }
885
886
887
  // AND OR
  if (!HandleLevel4(newArgs, makefile, errorString,
                    Policy12Status, status))
888
889
890
    {
    return false;
    }
Ken Martin's avatar
Ken Martin committed
891

Ken Martin's avatar
Ken Martin committed
892
  // now at the end there should only be one argument left
893
  if (newArgs.size() != 1)
894
895
896
897
    {
    errorString = "Unknown arguments specified";
    return false;
    }
898
899
900
901
902
903

  return GetBooleanValueWithAutoDereference(*(newArgs.begin()),
                                            makefile,
                                            errorString,
                                            Policy12Status,
                                            status);
904
}
905

906
//=========================================================================
907
908
909
910
911
912
913
914
915
916
const char* cmIfCommand::GetVariableOrString(const char* str,
                                             const cmMakefile* mf)
{
  const char* def = mf->GetDefinition(str);
  if(!def)
    {
    def = str;
    }
  return def;
}