1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
6 Date: $Date: 2008-10-01 13:04:27 $
7 Version: $Revision: 1.90 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmIfCommand.h"
18 #include "cmStringCommand.h"
20 #include <stdlib.h> // required for atof
22 #include <cmsys/RegularExpression.hxx>
24 //=========================================================================
25 bool cmIfFunctionBlocker::
26 IsFunctionBlocked(const cmListFileFunction
& lff
,
28 cmExecutionStatus
&inStatus
)
30 // Prevent recusion and don't let this blocker block its own
37 // we start by recording all the functions
38 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"if"))
42 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endif"))
45 // if this is the endif for this if statement, then start executing
46 if (!this->ScopeDepth
)
48 // execute the functions for the true parts of the if statement
49 this->Executing
= true;
50 cmExecutionStatus status
;
52 for(unsigned int c
= 0; c
< this->Functions
.size(); ++c
)
54 // keep track of scope depth
55 if (!cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"if"))
59 if (!cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"endif"))
63 // watch for our state change
64 if (scopeDepth
== 0 &&
65 !cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"else"))
67 this->IsBlocking
= this->HasRun
;
70 else if (scopeDepth
== 0 && !cmSystemTools::Strucmp
71 (this->Functions
[c
].Name
.c_str(),"elseif"))
75 this->IsBlocking
= true;
79 std::string errorString
;
81 std::vector
<std::string
> expandedArguments
;
82 mf
.ExpandArguments(this->Functions
[c
].Arguments
,
85 cmIfCommand::IsTrue(expandedArguments
,errorString
,&mf
);
87 if (errorString
.size())
89 std::string err
= "had incorrect arguments: ";
91 for(i
=0; i
< this->Functions
[c
].Arguments
.size(); ++i
)
93 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
94 err
+= this->Functions
[c
].Arguments
[i
].Value
;
95 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
101 cmSystemTools::Error(err
.c_str());
107 this->IsBlocking
= false;
113 // should we execute?
114 else if (!this->IsBlocking
)
117 mf
.ExecuteCommand(this->Functions
[c
],status
);
118 if (status
.GetReturnInvoked())
120 inStatus
.SetReturnInvoked(true);
121 mf
.RemoveFunctionBlocker(lff
);
124 if (status
.GetBreakInvoked())
126 inStatus
.SetBreakInvoked(true);
127 mf
.RemoveFunctionBlocker(lff
);
132 mf
.RemoveFunctionBlocker(lff
);
137 // record the command
138 this->Functions
.push_back(lff
);
140 // always return true
144 //=========================================================================
145 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction
& lff
,
148 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endif"))
150 // if the endif has arguments, then make sure
151 // they match the arguments of the matching if
152 if (lff
.Arguments
.size() == 0 ||
153 lff
.Arguments
== this->Args
)
162 //=========================================================================
163 void cmIfFunctionBlocker::ScopeEnded(cmMakefile
&mf
)
165 std::string errmsg
= "The end of a CMakeLists file was reached with an "
166 "IF statement that was not closed properly.\nWithin the directory: ";
167 errmsg
+= mf
.GetCurrentDirectory();
168 errmsg
+= "\nThe arguments are: ";
169 for(std::vector
<cmListFileArgument
>::const_iterator j
= this->Args
.begin();
170 j
!= this->Args
.end(); ++j
)
172 errmsg
+= (j
->Quoted
?"\"":"");
174 errmsg
+= (j
->Quoted
?"\"":"");
177 cmSystemTools::Message(errmsg
.c_str(), "Warning");
180 //=========================================================================
182 ::InvokeInitialPass(const std::vector
<cmListFileArgument
>& args
,
185 std::string errorString
;
187 std::vector
<std::string
> expandedArguments
;
188 this->Makefile
->ExpandArguments(args
, expandedArguments
);
190 cmIfCommand::IsTrue(expandedArguments
,errorString
,this->Makefile
);
192 if (errorString
.size())
194 std::string err
= "had incorrect arguments: ";
196 for(i
=0; i
< args
.size(); ++i
)
198 err
+= (args
[i
].Quoted
?"\"":"");
199 err
+= args
[i
].Value
;
200 err
+= (args
[i
].Quoted
?"\"":"");
206 this->SetError(err
.c_str());
210 cmIfFunctionBlocker
*f
= new cmIfFunctionBlocker();
211 // if is isn't true block the commands
213 f
->IsBlocking
= !isTrue
;
219 this->Makefile
->AddFunctionBlocker(f
);
226 //=========================================================================
227 void IncrementArguments(std::list
<std::string
> &newArgs
,
228 std::list
<std::string
>::iterator
&argP1
,
229 std::list
<std::string
>::iterator
&argP2
)
231 if (argP1
!= newArgs
.end())
235 if (argP1
!= newArgs
.end())
242 //=========================================================================
243 // helper function to reduce code duplication
244 void HandlePredicate(bool value
, int &reducible
,
245 std::list
<std::string
>::iterator
&arg
,
246 std::list
<std::string
> &newArgs
,
247 std::list
<std::string
>::iterator
&argP1
,
248 std::list
<std::string
>::iterator
&argP2
)
258 newArgs
.erase(argP1
);
260 IncrementArguments(newArgs
,argP1
,argP2
);
264 //=========================================================================
265 // helper function to reduce code duplication
266 void HandleBinaryOp(bool value
, int &reducible
,
267 std::list
<std::string
>::iterator
&arg
,
268 std::list
<std::string
> &newArgs
,
269 std::list
<std::string
>::iterator
&argP1
,
270 std::list
<std::string
>::iterator
&argP2
)
280 newArgs
.erase(argP2
);
281 newArgs
.erase(argP1
);
283 IncrementArguments(newArgs
,argP1
,argP2
);
287 //=========================================================================
288 enum Op
{ OpLess
, OpEqual
, OpGreater
};
289 bool HandleVersionCompare(Op op
, const char* lhs_str
, const char* rhs_str
)
291 // Parse out up to 4 components.
292 unsigned int lhs
[4] = {0,0,0,0};
293 unsigned int rhs
[4] = {0,0,0,0};
294 sscanf(lhs_str
, "%u.%u.%u.%u", &lhs
[0], &lhs
[1], &lhs
[2], &lhs
[3]);
295 sscanf(rhs_str
, "%u.%u.%u.%u", &rhs
[0], &rhs
[1], &rhs
[2], &rhs
[3]);
297 // Do component-wise comparison.
298 for(unsigned int i
=0; i
< 4; ++i
)
302 // lhs < rhs, so true if operation is LESS
305 else if(lhs
[i
] > rhs
[i
])
307 // lhs > rhs, so true if operation is GREATER
308 return op
== OpGreater
;
311 // lhs == rhs, so true if operation is EQUAL
312 return op
== OpEqual
;
315 //=========================================================================
316 // level 0 processes parenthetical expressions
317 bool HandleLevel0(std::list
<std::string
> &newArgs
,
318 cmMakefile
*makefile
,
319 std::string
&errorString
)
325 std::list
<std::string
>::iterator arg
= newArgs
.begin();
326 while (arg
!= newArgs
.end())
330 // search for the closing paren for this opening one
331 std::list
<std::string
>::iterator argClose
;
334 unsigned int depth
= 1;
335 while (argClose
!= newArgs
.end() && depth
)
337 if (*argClose
== "(")
341 if (*argClose
== ")")
349 errorString
= "mismatched parenthesis in condition";
352 // store the reduced args in this vector
353 std::vector
<std::string
> newArgs2
;
355 // copy to the list structure
356 std::list
<std::string
>::iterator argP1
= arg
;
358 for(; argP1
!= argClose
; argP1
++)
360 newArgs2
.push_back(*argP1
);
363 // now recursively invoke IsTrue to handle the values inside the
364 // parenthetical expression
366 cmIfCommand::IsTrue(newArgs2
, errorString
, makefile
);
377 // remove the now evaluated parenthetical expression
378 newArgs
.erase(argP1
,argClose
);
387 //=========================================================================
388 // level one handles most predicates except for NOT
389 bool HandleLevel1(std::list
<std::string
> &newArgs
,
390 cmMakefile
*makefile
,
397 std::list
<std::string
>::iterator arg
= newArgs
.begin();
398 std::list
<std::string
>::iterator argP1
;
399 std::list
<std::string
>::iterator argP2
;
400 while (arg
!= newArgs
.end())
403 IncrementArguments(newArgs
,argP1
,argP2
);
405 if (*arg
== "EXISTS" && argP1
!= newArgs
.end())
408 cmSystemTools::FileExists((argP1
)->c_str()),
409 reducible
, arg
, newArgs
, argP1
, argP2
);
411 // does a directory with this name exist
412 if (*arg
== "IS_DIRECTORY" && argP1
!= newArgs
.end())
415 cmSystemTools::FileIsDirectory((argP1
)->c_str()),
416 reducible
, arg
, newArgs
, argP1
, argP2
);
418 // is the given path an absolute path ?
419 if (*arg
== "IS_ABSOLUTE" && argP1
!= newArgs
.end())
422 cmSystemTools::FileIsFullPath((argP1
)->c_str()),
423 reducible
, arg
, newArgs
, argP1
, argP2
);
425 // does a command exist
426 if (*arg
== "COMMAND" && argP1
!= newArgs
.end())
429 makefile
->CommandExists((argP1
)->c_str()),
430 reducible
, arg
, newArgs
, argP1
, argP2
);
432 // does a policy exist
433 if (*arg
== "POLICY" && argP1
!= newArgs
.end())
435 cmPolicies::PolicyID pid
;
437 makefile
->GetPolicies()->GetPolicyID((argP1
)->c_str(), pid
),
438 reducible
, arg
, newArgs
, argP1
, argP2
);
440 // does a target exist
441 if (*arg
== "TARGET" && argP1
!= newArgs
.end())
444 makefile
->FindTargetToUse((argP1
)->c_str())? true:false,
445 reducible
, arg
, newArgs
, argP1
, argP2
);
447 // is a variable defined
448 if (*arg
== "DEFINED" && argP1
!= newArgs
.end())
450 size_t argP1len
= argP1
->size();
452 if(argP1len
> 4 && argP1
->substr(0, 4) == "ENV{" &&
453 argP1
->operator[](argP1len
-1) == '}')
455 std::string env
= argP1
->substr(4, argP1len
-5);
456 bdef
= cmSystemTools::GetEnv(env
.c_str())?true:false;
460 bdef
= makefile
->IsDefinitionSet((argP1
)->c_str());
462 HandlePredicate(bdef
, reducible
, arg
, newArgs
, argP1
, argP2
);
471 //=========================================================================
472 // level two handles most binary operations except for AND OR
473 bool HandleLevel2(std::list
<std::string
> &newArgs
,
474 cmMakefile
*makefile
,
475 std::string
&errorString
)
483 std::list
<std::string
>::iterator arg
= newArgs
.begin();
484 std::list
<std::string
>::iterator argP1
;
485 std::list
<std::string
>::iterator argP2
;
486 while (arg
!= newArgs
.end())
489 IncrementArguments(newArgs
,argP1
,argP2
);
490 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
491 *(argP1
) == "MATCHES")
493 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
494 const char* rex
= (argP2
)->c_str();
495 cmStringCommand::ClearMatches(makefile
);
496 cmsys::RegularExpression regEntry
;
497 if ( !regEntry
.compile(rex
) )
499 cmOStringStream error
;
500 error
<< "Regular expression \"" << rex
<< "\" cannot compile";
501 errorString
= error
.str();
504 if (regEntry
.find(def
))
506 cmStringCommand::StoreMatches(makefile
, regEntry
);
513 newArgs
.erase(argP2
);
514 newArgs
.erase(argP1
);
516 IncrementArguments(newArgs
,argP1
,argP2
);
520 if (argP1
!= newArgs
.end() && *arg
== "MATCHES")
523 newArgs
.erase(argP1
);
525 IncrementArguments(newArgs
,argP1
,argP2
);
529 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
530 (*(argP1
) == "LESS" || *(argP1
) == "GREATER" ||
531 *(argP1
) == "EQUAL"))
533 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
534 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
538 if(sscanf(def
, "%lg", &lhs
) != 1 ||
539 sscanf(def2
, "%lg", &rhs
) != 1)
543 else if (*(argP1
) == "LESS")
545 result
= (lhs
< rhs
);
547 else if (*(argP1
) == "GREATER")
549 result
= (lhs
> rhs
);
553 result
= (lhs
== rhs
);
555 HandleBinaryOp(result
,
556 reducible
, arg
, newArgs
, argP1
, argP2
);
559 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
560 (*(argP1
) == "STRLESS" ||
561 *(argP1
) == "STREQUAL" ||
562 *(argP1
) == "STRGREATER"))
564 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
565 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
566 int val
= strcmp(def
,def2
);
568 if (*(argP1
) == "STRLESS")
572 else if (*(argP1
) == "STRGREATER")
580 HandleBinaryOp(result
,
581 reducible
, arg
, newArgs
, argP1
, argP2
);
584 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
585 (*(argP1
) == "VERSION_LESS" || *(argP1
) == "VERSION_GREATER" ||
586 *(argP1
) == "VERSION_EQUAL"))
588 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
589 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
591 if(*argP1
== "VERSION_LESS")
595 else if(*argP1
== "VERSION_GREATER")
599 bool result
= HandleVersionCompare(op
, def
, def2
);
600 HandleBinaryOp(result
,
601 reducible
, arg
, newArgs
, argP1
, argP2
);
604 // is file A newer than file B
605 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
606 *(argP1
) == "IS_NEWER_THAN")
609 bool success
=cmSystemTools::FileTimeCompare(arg
->c_str(),
613 (success
==false || fileIsNewer
==1 || fileIsNewer
==0),
614 reducible
, arg
, newArgs
, argP1
, argP2
);
624 //=========================================================================
625 // level 3 handles NOT
626 bool HandleLevel3(std::list
<std::string
> &newArgs
,
627 cmMakefile
*makefile
,
635 std::list
<std::string
>::iterator arg
= newArgs
.begin();
636 std::list
<std::string
>::iterator argP1
;
637 std::list
<std::string
>::iterator argP2
;
638 while (arg
!= newArgs
.end())
641 IncrementArguments(newArgs
,argP1
,argP2
);
642 if (argP1
!= newArgs
.end() && *arg
== "NOT")
644 def
= cmIfCommand::GetVariableOrNumber((argP1
)->c_str(), makefile
);
645 HandlePredicate(cmSystemTools::IsOff(def
),
646 reducible
, arg
, newArgs
, argP1
, argP2
);
655 //=========================================================================
656 // level 4 handles AND OR
657 bool HandleLevel4(std::list
<std::string
> &newArgs
,
658 cmMakefile
*makefile
,
667 std::list
<std::string
>::iterator arg
= newArgs
.begin();
668 std::list
<std::string
>::iterator argP1
;
669 std::list
<std::string
>::iterator argP2
;
670 while (arg
!= newArgs
.end())
673 IncrementArguments(newArgs
,argP1
,argP2
);
674 if (argP1
!= newArgs
.end() && *(argP1
) == "AND" &&
675 argP2
!= newArgs
.end())
677 def
= cmIfCommand::GetVariableOrNumber(arg
->c_str(), makefile
);
678 def2
= cmIfCommand::GetVariableOrNumber((argP2
)->c_str(), makefile
);
680 !(cmSystemTools::IsOff(def
) || cmSystemTools::IsOff(def2
)),
681 reducible
, arg
, newArgs
, argP1
, argP2
);
684 if (argP1
!= newArgs
.end() && *(argP1
) == "OR" &&
685 argP2
!= newArgs
.end())
687 def
= cmIfCommand::GetVariableOrNumber(arg
->c_str(), makefile
);
688 def2
= cmIfCommand::GetVariableOrNumber((argP2
)->c_str(), makefile
);
690 !(cmSystemTools::IsOff(def
) && cmSystemTools::IsOff(def2
)),
691 reducible
, arg
, newArgs
, argP1
, argP2
);
702 //=========================================================================
703 // order of operations,
704 // 1. ( ) -- parenthetical groups
705 // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
706 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
710 // There is an issue on whether the arguments should be values of references,
711 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
712 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
713 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
714 // take numeric values or variable names. STRLESS and STRGREATER take
715 // variable names but if the variable name is not found it will use the name
716 // directly. AND OR take variables or the values 0 or 1.
719 bool cmIfCommand::IsTrue(const std::vector
<std::string
> &args
,
720 std::string
&errorString
, cmMakefile
*makefile
)
725 // handle empty invocation
731 // store the reduced args in this vector
732 std::list
<std::string
> newArgs
;
734 // copy to the list structure
735 for(unsigned int i
= 0; i
< args
.size(); ++i
)
737 newArgs
.push_back(args
[i
]);
740 // now loop through the arguments and see if we can reduce any of them
741 // we do this multiple times. Once for each level of precedence
742 if (!HandleLevel0(newArgs
, makefile
, errorString
)) // parens
746 if (!HandleLevel1(newArgs
, makefile
, errorString
)) //predicates
750 if (!HandleLevel2(newArgs
, makefile
, errorString
)) // binary ops
754 if (!HandleLevel3(newArgs
, makefile
, errorString
)) // NOT
758 if (!HandleLevel4(newArgs
, makefile
, errorString
)) // AND OR
763 // now at the end there should only be one argument left
764 if (newArgs
.size() == 1)
766 if (*newArgs
.begin() == "0")
770 if (*newArgs
.begin() == "1")
774 def
= makefile
->GetDefinition(args
[0].c_str());
775 if(cmSystemTools::IsOff(def
))
782 errorString
= "Unknown arguments specified";
789 //=========================================================================
790 const char* cmIfCommand::GetVariableOrString(const char* str
,
791 const cmMakefile
* mf
)
793 const char* def
= mf
->GetDefinition(str
);
801 //=========================================================================
802 const char* cmIfCommand::GetVariableOrNumber(const char* str
,
803 const cmMakefile
* mf
)
805 const char* def
= mf
->GetDefinition(str
);