Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmIfCommand.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmIfCommand.h"
13 #include "cmStringCommand.h"
14
15 #include <stdlib.h> // required for atof
16 #include <list>
17 #include <cmsys/RegularExpression.hxx>
18
19
20 static std::string cmIfCommandError(
21   cmMakefile* mf, std::vector<std::string> const& args)
22 {
23   cmLocalGenerator* lg = mf->GetLocalGenerator();
24   std::string err = "given arguments:\n ";
25   for(std::vector<std::string>::const_iterator i = args.begin();
26       i != args.end(); ++i)
27     {
28     err += " ";
29     err += lg->EscapeForCMake(i->c_str());
30     }
31   err += "\n";
32   return err;
33 }
34
35 //=========================================================================
36 bool cmIfFunctionBlocker::
37 IsFunctionBlocked(const cmListFileFunction& lff,
38                   cmMakefile &mf,
39                   cmExecutionStatus &inStatus)
40 {
41   // we start by recording all the functions
42   if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
43     {
44     this->ScopeDepth++;
45     }
46   if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
47     {
48     this->ScopeDepth--;
49     // if this is the endif for this if statement, then start executing
50     if (!this->ScopeDepth)
51       {
52       // Remove the function blocker for this scope or bail.
53       cmsys::auto_ptr<cmFunctionBlocker>
54         fb(mf.RemoveFunctionBlocker(this, lff));
55       if(!fb.get()) { return false; }
56
57       // execute the functions for the true parts of the if statement
58       cmExecutionStatus status;
59       int scopeDepth = 0;
60       for(unsigned int c = 0; c < this->Functions.size(); ++c)
61         {
62         // keep track of scope depth
63         if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"if"))
64           {
65           scopeDepth++;
66           }
67         if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
68           {
69           scopeDepth--;
70           }
71         // watch for our state change
72         if (scopeDepth == 0 &&
73             !cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"else"))
74           {
75           this->IsBlocking = this->HasRun;
76           this->HasRun = true;
77
78           // if trace is enabled, print a (trivially) evaluated "else"
79           // statement
80           if(!this->IsBlocking && mf.GetCMakeInstance()->GetTrace())
81             {
82             mf.PrintCommandTrace(this->Functions[c]);
83             }
84           }
85         else if (scopeDepth == 0 && !cmSystemTools::Strucmp
86                  (this->Functions[c].Name.c_str(),"elseif"))
87           {
88           if (this->HasRun)
89             {
90             this->IsBlocking = true;
91             }
92           else
93             {
94             // Place this call on the call stack.
95             cmMakefileCall stack_manager(&mf, this->Functions[c], status);
96             static_cast<void>(stack_manager);
97
98             // if trace is enabled, print the evaluated "elseif" statement
99             if(mf.GetCMakeInstance()->GetTrace())
100               {
101               mf.PrintCommandTrace(this->Functions[c]);
102               }
103
104             std::string errorString;
105
106             std::vector<std::string> expandedArguments;
107             mf.ExpandArguments(this->Functions[c].Arguments,
108                                expandedArguments);
109
110             cmake::MessageType messType;
111             bool isTrue =
112               cmIfCommand::IsTrue(expandedArguments, errorString,
113                                   &mf, messType);
114
115             if (errorString.size())
116               {
117               std::string err = cmIfCommandError(&mf, expandedArguments);
118               err += errorString;
119               mf.IssueMessage(messType, err);
120               if (messType == cmake::FATAL_ERROR)
121                 {
122                 cmSystemTools::SetFatalErrorOccured();
123                 return true;
124                 }
125               }
126
127             if (isTrue)
128               {
129               this->IsBlocking = false;
130               this->HasRun = true;
131               }
132             }
133           }
134
135         // should we execute?
136         else if (!this->IsBlocking)
137           {
138           status.Clear();
139           mf.ExecuteCommand(this->Functions[c],status);
140           if (status.GetReturnInvoked())
141             {
142             inStatus.SetReturnInvoked(true);
143             return true;
144             }
145           if (status.GetBreakInvoked())
146             {
147             inStatus.SetBreakInvoked(true);
148             return true;
149             }
150           }
151         }
152       return true;
153       }
154     }
155
156   // record the command
157   this->Functions.push_back(lff);
158
159   // always return true
160   return true;
161 }
162
163 //=========================================================================
164 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
165                                        cmMakefile&)
166 {
167   if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
168     {
169     // if the endif has arguments, then make sure
170     // they match the arguments of the matching if
171     if (lff.Arguments.size() == 0 ||
172         lff.Arguments == this->Args)
173       {
174       return true;
175       }
176     }
177
178   return false;
179 }
180
181 //=========================================================================
182 bool cmIfCommand
183 ::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
184                     cmExecutionStatus &)
185 {
186   std::string errorString;
187
188   std::vector<std::string> expandedArguments;
189   this->Makefile->ExpandArguments(args, expandedArguments);
190
191   cmake::MessageType status;
192   bool isTrue =
193     cmIfCommand::IsTrue(expandedArguments,errorString,
194                         this->Makefile, status);
195
196   if (errorString.size())
197     {
198     std::string err = cmIfCommandError(this->Makefile, expandedArguments);
199     err += errorString;
200     if (status == cmake::FATAL_ERROR)
201       {
202       this->SetError(err.c_str());
203       cmSystemTools::SetFatalErrorOccured();
204       return false;
205       }
206     else
207       {
208       this->Makefile->IssueMessage(status, err);
209       }
210     }
211
212   cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
213   // if is isn't true block the commands
214   f->ScopeDepth = 1;
215   f->IsBlocking = !isTrue;
216   if (isTrue)
217     {
218     f->HasRun = true;
219     }
220   f->Args = args;
221   this->Makefile->AddFunctionBlocker(f);
222
223   return true;
224 }
225
226 namespace
227 {
228   //=========================================================================
229   bool GetBooleanValue(std::string& arg, cmMakefile* mf)
230   {
231   // Check basic constants.
232   if (arg == "0")
233     {
234     return false;
235     }
236   if (arg == "1")
237     {
238     return true;
239     }
240
241   // Check named constants.
242   if (cmSystemTools::IsOn(arg.c_str()))
243     {
244     return true;
245     }
246   if (cmSystemTools::IsOff(arg.c_str()))
247     {
248     return false;
249     }
250
251   // Check for numbers.
252   if(!arg.empty())
253     {
254     char* end;
255     double d = strtod(arg.c_str(), &end);
256     if(*end == '\0')
257       {
258       // The whole string is a number.  Use C conversion to bool.
259       return d? true:false;
260       }
261     }
262
263   // Check definition.
264   const char* def = mf->GetDefinition(arg.c_str());
265   return !cmSystemTools::IsOff(def);
266   }
267
268   //=========================================================================
269   // Boolean value behavior from CMake 2.6.4 and below.
270   bool GetBooleanValueOld(std::string const& arg, cmMakefile* mf, bool one)
271   {
272   if(one)
273     {
274     // Old IsTrue behavior for single argument.
275     if(arg == "0")
276       { return false; }
277     else if(arg == "1")
278       { return true; }
279     else
280       { return !cmSystemTools::IsOff(mf->GetDefinition(arg.c_str())); }
281     }
282   else
283     {
284     // Old GetVariableOrNumber behavior.
285     const char* def = mf->GetDefinition(arg.c_str());
286     if(!def && atoi(arg.c_str()))
287       {
288       def = arg.c_str();
289       }
290     return !cmSystemTools::IsOff(def);
291     }
292   }
293
294   //=========================================================================
295   // returns the resulting boolean value
296   bool GetBooleanValueWithAutoDereference(
297     std::string &newArg,
298     cmMakefile *makefile,
299     std::string &errorString,
300     cmPolicies::PolicyStatus Policy12Status,
301     cmake::MessageType &status,
302     bool oneArg = false)
303   {
304   // Use the policy if it is set.
305   if (Policy12Status == cmPolicies::NEW)
306     {
307     return GetBooleanValue(newArg, makefile);
308     }
309   else if (Policy12Status == cmPolicies::OLD)
310     {
311     return GetBooleanValueOld(newArg, makefile, oneArg);
312     }
313
314   // Check policy only if old and new results differ.
315   bool newResult = GetBooleanValue(newArg, makefile);
316   bool oldResult = GetBooleanValueOld(newArg, makefile, oneArg);
317   if(newResult != oldResult)
318     {
319     switch(Policy12Status)
320       {
321       case cmPolicies::WARN:
322         {
323         cmPolicies* policies = makefile->GetPolicies();
324         errorString = "An argument named \"" + newArg
325           + "\" appears in a conditional statement.  "
326           + policies->GetPolicyWarning(cmPolicies::CMP0012);
327         status = cmake::AUTHOR_WARNING;
328         }
329       case cmPolicies::OLD:
330         return oldResult;
331       case cmPolicies::REQUIRED_IF_USED:
332       case cmPolicies::REQUIRED_ALWAYS:
333         {
334         cmPolicies* policies = makefile->GetPolicies();
335         errorString = "An argument named \"" + newArg
336           + "\" appears in a conditional statement.  "
337           + policies->GetRequiredPolicyError(cmPolicies::CMP0012);
338         status = cmake::FATAL_ERROR;
339         }
340       case cmPolicies::NEW:
341         break;
342       }
343     }
344   return newResult;
345   }
346
347   //=========================================================================
348   void IncrementArguments(std::list<std::string> &newArgs,
349                           std::list<std::string>::iterator &argP1,
350                           std::list<std::string>::iterator &argP2)
351   {
352     if (argP1  != newArgs.end())
353       {
354       argP1++;
355       argP2 = argP1;
356       if (argP1  != newArgs.end())
357         {
358         argP2++;
359         }
360       }
361   }
362
363   //=========================================================================
364   // helper function to reduce code duplication
365   void HandlePredicate(bool value, int &reducible,
366                        std::list<std::string>::iterator &arg,
367                        std::list<std::string> &newArgs,
368                        std::list<std::string>::iterator &argP1,
369                        std::list<std::string>::iterator &argP2)
370   {
371     if(value)
372       {
373       *arg = "1";
374       }
375     else
376       {
377       *arg = "0";
378       }
379     newArgs.erase(argP1);
380     argP1 = arg;
381     IncrementArguments(newArgs,argP1,argP2);
382     reducible = 1;
383   }
384
385   //=========================================================================
386   // helper function to reduce code duplication
387   void HandleBinaryOp(bool value, int &reducible,
388                        std::list<std::string>::iterator &arg,
389                        std::list<std::string> &newArgs,
390                        std::list<std::string>::iterator &argP1,
391                        std::list<std::string>::iterator &argP2)
392   {
393     if(value)
394       {
395       *arg = "1";
396       }
397     else
398       {
399       *arg = "0";
400       }
401     newArgs.erase(argP2);
402     newArgs.erase(argP1);
403     argP1 = arg;
404     IncrementArguments(newArgs,argP1,argP2);
405     reducible = 1;
406   }
407
408   //=========================================================================
409   enum Op { OpLess, OpEqual, OpGreater };
410   bool HandleVersionCompare(Op op, const char* lhs_str, const char* rhs_str)
411   {
412   // Parse out up to 4 components.
413   unsigned int lhs[4] = {0,0,0,0};
414   unsigned int rhs[4] = {0,0,0,0};
415   sscanf(lhs_str, "%u.%u.%u.%u", &lhs[0], &lhs[1], &lhs[2], &lhs[3]);
416   sscanf(rhs_str, "%u.%u.%u.%u", &rhs[0], &rhs[1], &rhs[2], &rhs[3]);
417
418   // Do component-wise comparison.
419   for(unsigned int i=0; i < 4; ++i)
420     {
421     if(lhs[i] < rhs[i])
422       {
423       // lhs < rhs, so true if operation is LESS
424       return op == OpLess;
425       }
426     else if(lhs[i] > rhs[i])
427       {
428       // lhs > rhs, so true if operation is GREATER
429       return op == OpGreater;
430       }
431     }
432   // lhs == rhs, so true if operation is EQUAL
433   return op == OpEqual;
434   }
435
436   //=========================================================================
437   // level 0 processes parenthetical expressions
438   bool HandleLevel0(std::list<std::string> &newArgs,
439                     cmMakefile *makefile,
440                     std::string &errorString,
441                     cmake::MessageType &status)
442   {
443   int reducible;
444   do
445     {
446     reducible = 0;
447     std::list<std::string>::iterator arg = newArgs.begin();
448     while (arg != newArgs.end())
449       {
450       if (*arg == "(")
451         {
452         // search for the closing paren for this opening one
453         std::list<std::string>::iterator argClose;
454         argClose = arg;
455         argClose++;
456         unsigned int depth = 1;
457         while (argClose != newArgs.end() && depth)
458           {
459           if (*argClose == "(")
460             {
461               depth++;
462             }
463           if (*argClose == ")")
464             {
465               depth--;
466             }
467           argClose++;
468           }
469         if (depth)
470           {
471           errorString = "mismatched parenthesis in condition";
472           status = cmake::FATAL_ERROR;
473           return false;
474           }
475         // store the reduced args in this vector
476         std::vector<std::string> newArgs2;
477
478         // copy to the list structure
479         std::list<std::string>::iterator argP1 = arg;
480         argP1++;
481         for(; argP1 != argClose; argP1++)
482           {
483           newArgs2.push_back(*argP1);
484           }
485         newArgs2.pop_back();
486         // now recursively invoke IsTrue to handle the values inside the
487         // parenthetical expression
488         bool value =
489           cmIfCommand::IsTrue(newArgs2, errorString, makefile, status);
490         if(value)
491           {
492           *arg = "1";
493           }
494         else
495           {
496           *arg = "0";
497           }
498         argP1 = arg;
499         argP1++;
500         // remove the now evaluated parenthetical expression
501         newArgs.erase(argP1,argClose);
502         }
503       ++arg;
504       }
505     }
506   while (reducible);
507   return true;
508   }
509
510   //=========================================================================
511   // level one handles most predicates except for NOT
512   bool HandleLevel1(std::list<std::string> &newArgs,
513                     cmMakefile *makefile,
514                     std::string &, cmake::MessageType &)
515   {
516   int reducible;
517   do
518     {
519     reducible = 0;
520     std::list<std::string>::iterator arg = newArgs.begin();
521     std::list<std::string>::iterator argP1;
522     std::list<std::string>::iterator argP2;
523     while (arg != newArgs.end())
524       {
525       argP1 = arg;
526       IncrementArguments(newArgs,argP1,argP2);
527       // does a file exist
528       if (*arg == "EXISTS" && argP1  != newArgs.end())
529         {
530         HandlePredicate(
531           cmSystemTools::FileExists((argP1)->c_str()),
532           reducible, arg, newArgs, argP1, argP2);
533         }
534       // does a directory with this name exist
535       if (*arg == "IS_DIRECTORY" && argP1  != newArgs.end())
536         {
537         HandlePredicate(
538           cmSystemTools::FileIsDirectory((argP1)->c_str()),
539           reducible, arg, newArgs, argP1, argP2);
540         }
541       // does a symlink with this name exist
542       if (*arg == "IS_SYMLINK" && argP1  != newArgs.end())
543         {
544         HandlePredicate(
545           cmSystemTools::FileIsSymlink((argP1)->c_str()),
546           reducible, arg, newArgs, argP1, argP2);
547         }
548       // is the given path an absolute path ?
549       if (*arg == "IS_ABSOLUTE" && argP1  != newArgs.end())
550         {
551         HandlePredicate(
552           cmSystemTools::FileIsFullPath((argP1)->c_str()),
553           reducible, arg, newArgs, argP1, argP2);
554         }
555       // does a command exist
556       if (*arg == "COMMAND" && argP1  != newArgs.end())
557         {
558         HandlePredicate(
559           makefile->CommandExists((argP1)->c_str()),
560           reducible, arg, newArgs, argP1, argP2);
561         }
562       // does a policy exist
563       if (*arg == "POLICY" && argP1 != newArgs.end())
564         {
565         cmPolicies::PolicyID pid;
566         HandlePredicate(
567           makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid),
568           reducible, arg, newArgs, argP1, argP2);
569         }
570       // does a target exist
571       if (*arg == "TARGET" && argP1 != newArgs.end())
572         {
573         HandlePredicate(
574           makefile->FindTargetToUse((argP1)->c_str())? true:false,
575           reducible, arg, newArgs, argP1, argP2);
576         }
577       // is a variable defined
578       if (*arg == "DEFINED" && argP1  != newArgs.end())
579         {
580         size_t argP1len = argP1->size();
581         bool bdef = false;
582         if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
583            argP1->operator[](argP1len-1) == '}')
584           {
585           std::string env = argP1->substr(4, argP1len-5);
586           bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
587           }
588         else
589           {
590           bdef = makefile->IsDefinitionSet((argP1)->c_str());
591           }
592         HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
593         }
594       ++arg;
595       }
596     }
597   while (reducible);
598   return true;
599   }
600
601   //=========================================================================
602   // level two handles most binary operations except for AND  OR
603   bool HandleLevel2(std::list<std::string> &newArgs,
604                     cmMakefile *makefile,
605                     std::string &errorString,
606                     cmake::MessageType &status)
607   {
608   int reducible;
609   const char *def;
610   const char *def2;
611   do
612     {
613     reducible = 0;
614     std::list<std::string>::iterator arg = newArgs.begin();
615     std::list<std::string>::iterator argP1;
616     std::list<std::string>::iterator argP2;
617     while (arg != newArgs.end())
618       {
619       argP1 = arg;
620       IncrementArguments(newArgs,argP1,argP2);
621       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
622         *(argP1) == "MATCHES")
623         {
624         def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
625         const char* rex = (argP2)->c_str();
626         cmStringCommand::ClearMatches(makefile);
627         cmsys::RegularExpression regEntry;
628         if ( !regEntry.compile(rex) )
629           {
630           cmOStringStream error;
631           error << "Regular expression \"" << rex << "\" cannot compile";
632           errorString = error.str();
633           status = cmake::FATAL_ERROR;
634           return false;
635           }
636         if (regEntry.find(def))
637           {
638           cmStringCommand::StoreMatches(makefile, regEntry);
639           *arg = "1";
640           }
641         else
642           {
643           *arg = "0";
644           }
645         newArgs.erase(argP2);
646         newArgs.erase(argP1);
647         argP1 = arg;
648         IncrementArguments(newArgs,argP1,argP2);
649         reducible = 1;
650         }
651
652       if (argP1 != newArgs.end() && *arg == "MATCHES")
653         {
654         *arg = "0";
655         newArgs.erase(argP1);
656         argP1 = arg;
657         IncrementArguments(newArgs,argP1,argP2);
658         reducible = 1;
659         }
660
661       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
662         (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
663          *(argP1) == "EQUAL"))
664         {
665         def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
666         def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
667         double lhs;
668         double rhs;
669         bool result;
670         if(sscanf(def, "%lg", &lhs) != 1 ||
671            sscanf(def2, "%lg", &rhs) != 1)
672           {
673           result = false;
674           }
675         else if (*(argP1) == "LESS")
676           {
677           result = (lhs < rhs);
678           }
679         else if (*(argP1) == "GREATER")
680           {
681           result = (lhs > rhs);
682           }
683         else
684           {
685           result = (lhs == rhs);
686           }
687         HandleBinaryOp(result,
688           reducible, arg, newArgs, argP1, argP2);
689         }
690
691       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
692         (*(argP1) == "STRLESS" ||
693          *(argP1) == "STREQUAL" ||
694          *(argP1) == "STRGREATER"))
695         {
696         def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
697         def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
698         int val = strcmp(def,def2);
699         bool result;
700         if (*(argP1) == "STRLESS")
701           {
702           result = (val < 0);
703           }
704         else if (*(argP1) == "STRGREATER")
705           {
706           result = (val > 0);
707           }
708         else // strequal
709           {
710           result = (val == 0);
711           }
712         HandleBinaryOp(result,
713           reducible, arg, newArgs, argP1, argP2);
714         }
715
716       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
717         (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" ||
718          *(argP1) == "VERSION_EQUAL"))
719         {
720         def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
721         def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
722         Op op = OpEqual;
723         if(*argP1 == "VERSION_LESS")
724           {
725           op = OpLess;
726           }
727         else if(*argP1 == "VERSION_GREATER")
728           {
729           op = OpGreater;
730           }
731         bool result = HandleVersionCompare(op, def, def2);
732         HandleBinaryOp(result,
733           reducible, arg, newArgs, argP1, argP2);
734         }
735
736       // is file A newer than file B
737       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
738           *(argP1) == "IS_NEWER_THAN")
739         {
740         int fileIsNewer=0;
741         bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
742             (argP2)->c_str(),
743             &fileIsNewer);
744         HandleBinaryOp(
745           (success==false || fileIsNewer==1 || fileIsNewer==0),
746           reducible, arg, newArgs, argP1, argP2);
747         }
748
749       ++arg;
750       }
751     }
752   while (reducible);
753   return true;
754   }
755
756   //=========================================================================
757   // level 3 handles NOT
758   bool HandleLevel3(std::list<std::string> &newArgs,
759                     cmMakefile *makefile,
760                     std::string &errorString,
761                     cmPolicies::PolicyStatus Policy12Status,
762                     cmake::MessageType &status)
763   {
764   int reducible;
765   do
766     {
767     reducible = 0;
768     std::list<std::string>::iterator arg = newArgs.begin();
769     std::list<std::string>::iterator argP1;
770     std::list<std::string>::iterator argP2;
771     while (arg != newArgs.end())
772       {
773       argP1 = arg;
774       IncrementArguments(newArgs,argP1,argP2);
775       if (argP1 != newArgs.end() && *arg == "NOT")
776         {
777         bool rhs = GetBooleanValueWithAutoDereference(*argP1, makefile,
778                                                       errorString,
779                                                       Policy12Status,
780                                                       status);
781         HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
782         }
783       ++arg;
784       }
785     }
786   while (reducible);
787   return true;
788   }
789
790   //=========================================================================
791   // level 4 handles AND OR
792   bool HandleLevel4(std::list<std::string> &newArgs,
793                     cmMakefile *makefile,
794                     std::string &errorString,
795                     cmPolicies::PolicyStatus Policy12Status,
796                     cmake::MessageType &status)
797   {
798   int reducible;
799   bool lhs;
800   bool rhs;
801   do
802     {
803     reducible = 0;
804     std::list<std::string>::iterator arg = newArgs.begin();
805     std::list<std::string>::iterator argP1;
806     std::list<std::string>::iterator argP2;
807     while (arg != newArgs.end())
808       {
809       argP1 = arg;
810       IncrementArguments(newArgs,argP1,argP2);
811       if (argP1 != newArgs.end() && *(argP1) == "AND" &&
812         argP2 != newArgs.end())
813         {
814         lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
815                                                  errorString,
816                                                  Policy12Status,
817                                                  status);
818         rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
819                                                  errorString,
820                                                  Policy12Status,
821                                                  status);
822         HandleBinaryOp((lhs && rhs),
823           reducible, arg, newArgs, argP1, argP2);
824         }
825
826       if (argP1 != newArgs.end() && *(argP1) == "OR" &&
827         argP2 != newArgs.end())
828         {
829         lhs = GetBooleanValueWithAutoDereference(*arg, makefile,
830                                                  errorString,
831                                                  Policy12Status,
832                                                  status);
833         rhs = GetBooleanValueWithAutoDereference(*argP2, makefile,
834                                                  errorString,
835                                                  Policy12Status,
836                                                  status);
837         HandleBinaryOp((lhs || rhs),
838           reducible, arg, newArgs, argP1, argP2);
839         }
840       ++arg;
841       }
842     }
843   while (reducible);
844   return true;
845   }
846 }
847
848
849 //=========================================================================
850 // order of operations,
851 // 1.   ( )   -- parenthetical groups
852 // 2.  IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
853 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
854 // 4. NOT
855 // 5. AND OR
856 //
857 // There is an issue on whether the arguments should be values of references,
858 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
859 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
860 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
861 // take numeric values or variable names. STRLESS and STRGREATER take
862 // variable names but if the variable name is not found it will use the name
863 // directly. AND OR take variables or the values 0 or 1.
864
865
866 bool cmIfCommand::IsTrue(const std::vector<std::string> &args,
867                          std::string &errorString, cmMakefile *makefile,
868                          cmake::MessageType &status)
869 {
870   errorString = "";
871
872   // handle empty invocation
873   if (args.size() < 1)
874     {
875     return false;
876     }
877
878   // store the reduced args in this vector
879   std::list<std::string> newArgs;
880
881   // copy to the list structure
882   for(unsigned int i = 0; i < args.size(); ++i)
883     {
884     newArgs.push_back(args[i]);
885     }
886
887   // now loop through the arguments and see if we can reduce any of them
888   // we do this multiple times. Once for each level of precedence
889   // parens
890   if (!HandleLevel0(newArgs, makefile, errorString, status))
891     {
892     return false;
893     }
894   //predicates
895   if (!HandleLevel1(newArgs, makefile, errorString, status))
896     {
897     return false;
898     }
899   // binary ops
900   if (!HandleLevel2(newArgs, makefile, errorString, status))
901     {
902     return false;
903     }
904
905   // used to store the value of policy CMP0012 for performance
906   cmPolicies::PolicyStatus Policy12Status =
907     makefile->GetPolicyStatus(cmPolicies::CMP0012);
908
909   // NOT
910   if (!HandleLevel3(newArgs, makefile, errorString,
911                     Policy12Status, status))
912     {
913     return false;
914     }
915   // AND OR
916   if (!HandleLevel4(newArgs, makefile, errorString,
917                     Policy12Status, status))
918     {
919     return false;
920     }
921
922   // now at the end there should only be one argument left
923   if (newArgs.size() != 1)
924     {
925     errorString = "Unknown arguments specified";
926     status = cmake::FATAL_ERROR;
927     return false;
928     }
929
930   return GetBooleanValueWithAutoDereference(*(newArgs.begin()),
931                                             makefile,
932                                             errorString,
933                                             Policy12Status,
934                                             status, true);
935 }
936
937 //=========================================================================
938 const char* cmIfCommand::GetVariableOrString(const char* str,
939                                              const cmMakefile* mf)
940 {
941   const char* def = mf->GetDefinition(str);
942   if(!def)
943     {
944     def = str;
945     }
946   return def;
947 }