1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
6 Date: $Date: 2009-06-17 18:18:08 $
7 Version: $Revision: 1.97 $
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 // we start by recording all the functions
31 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"if"))
35 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endif"))
38 // if this is the endif for this if statement, then start executing
39 if (!this->ScopeDepth
)
41 // Remove the function blocker for this scope or bail.
42 cmsys::auto_ptr
<cmFunctionBlocker
>
43 fb(mf
.RemoveFunctionBlocker(this, lff
));
44 if(!fb
.get()) { return false; }
46 // execute the functions for the true parts of the if statement
47 cmExecutionStatus status
;
49 for(unsigned int c
= 0; c
< this->Functions
.size(); ++c
)
51 // keep track of scope depth
52 if (!cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"if"))
56 if (!cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"endif"))
60 // watch for our state change
61 if (scopeDepth
== 0 &&
62 !cmSystemTools::Strucmp(this->Functions
[c
].Name
.c_str(),"else"))
64 this->IsBlocking
= this->HasRun
;
67 else if (scopeDepth
== 0 && !cmSystemTools::Strucmp
68 (this->Functions
[c
].Name
.c_str(),"elseif"))
72 this->IsBlocking
= true;
76 // Place this call on the call stack.
77 cmMakefileCall
stack_manager(&mf
, this->Functions
[c
], status
);
78 static_cast<void>(stack_manager
);
80 std::string errorString
;
82 std::vector
<std::string
> expandedArguments
;
83 mf
.ExpandArguments(this->Functions
[c
].Arguments
,
86 cmake::MessageType messType
;
88 cmIfCommand::IsTrue(expandedArguments
, errorString
,
91 if (errorString
.size())
93 std::string err
= "given arguments\n ";
95 for(i
=0; i
< this->Functions
[c
].Arguments
.size(); ++i
)
97 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
98 err
+= this->Functions
[c
].Arguments
[i
].Value
;
99 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
104 mf
.IssueMessage(messType
, err
);
105 if (messType
== cmake::FATAL_ERROR
)
107 cmSystemTools::SetFatalErrorOccured();
114 this->IsBlocking
= false;
120 // should we execute?
121 else if (!this->IsBlocking
)
124 mf
.ExecuteCommand(this->Functions
[c
],status
);
125 if (status
.GetReturnInvoked())
127 inStatus
.SetReturnInvoked(true);
130 if (status
.GetBreakInvoked())
132 inStatus
.SetBreakInvoked(true);
141 // record the command
142 this->Functions
.push_back(lff
);
144 // always return true
148 //=========================================================================
149 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction
& lff
,
152 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endif"))
154 // if the endif has arguments, then make sure
155 // they match the arguments of the matching if
156 if (lff
.Arguments
.size() == 0 ||
157 lff
.Arguments
== this->Args
)
166 //=========================================================================
168 ::InvokeInitialPass(const std::vector
<cmListFileArgument
>& args
,
171 std::string errorString
;
173 std::vector
<std::string
> expandedArguments
;
174 this->Makefile
->ExpandArguments(args
, expandedArguments
);
176 cmake::MessageType status
;
178 cmIfCommand::IsTrue(expandedArguments
,errorString
,
179 this->Makefile
, status
);
181 if (errorString
.size())
183 std::string err
= "given arguments\n ";
185 for(i
=0; i
< args
.size(); ++i
)
187 err
+= (args
[i
].Quoted
?"\"":"");
188 err
+= args
[i
].Value
;
189 err
+= (args
[i
].Quoted
?"\"":"");
194 if (status
== cmake::FATAL_ERROR
)
196 this->SetError(err
.c_str());
197 cmSystemTools::SetFatalErrorOccured();
202 this->Makefile
->IssueMessage(status
, err
);
206 cmIfFunctionBlocker
*f
= new cmIfFunctionBlocker();
207 // if is isn't true block the commands
209 f
->IsBlocking
= !isTrue
;
215 this->Makefile
->AddFunctionBlocker(f
);
222 //=========================================================================
223 // returns true if succesfull, the resulting bool parsed is stored in result
224 bool GetBooleanValue(std::string
&newArg
,
225 cmMakefile
*makefile
,
227 std::string
&errorString
,
228 cmPolicies::PolicyStatus Policy12Status
,
229 cmake::MessageType
&status
)
231 if (Policy12Status
!= cmPolicies::OLD
&&
232 Policy12Status
!= cmPolicies::WARN
)
234 // please note IsOn(var) does not always equal !IsOff(var)
235 // that is why each is called
236 if (cmSystemTools::IsOn(newArg
.c_str()))
241 if (cmSystemTools::IsOff(newArg
.c_str()))
249 // Old policy is more complex...
250 // 0 and 1 are very common, test for them first quickly
262 // old behavior is to dereference the var
263 if (Policy12Status
== cmPolicies::OLD
)
268 // now test for values that may be the name of a variable
270 if (cmSystemTools::IsOn(newArg
.c_str()))
272 // only warn if the value would change
273 const char *def
= makefile
->GetDefinition(newArg
.c_str());
274 if (cmSystemTools::IsOff(def
))
276 cmPolicies
* policies
= makefile
->GetPolicies();
277 errorString
= "You have used a variable or argument named \""
279 + "\" in a conditional statement. Please be aware of issues "
280 + "related to policy CMP0012. "
281 + policies
->GetPolicyWarning(cmPolicies::CMP0012
);
282 status
= cmake::AUTHOR_WARNING
;
286 if (cmSystemTools::IsOff(newArg
.c_str()))
288 // only warn if the value would change
289 const char *def
= makefile
->GetDefinition(newArg
.c_str());
290 if (!cmSystemTools::IsOff(def
))
292 cmPolicies
* policies
= makefile
->GetPolicies();
293 errorString
= "You have used a variable or argument named \""
295 + "\" in a conditional statement. Please be aware of issues "
296 + "related to policy CMP0012. "
297 + policies
->GetPolicyWarning(cmPolicies::CMP0012
);
298 status
= cmake::AUTHOR_WARNING
;
305 //=========================================================================
306 // returns the resulting boolean value
307 bool GetBooleanValueWithAutoDereference(
309 cmMakefile
*makefile
,
310 std::string
&errorString
,
311 cmPolicies::PolicyStatus Policy12Status
,
312 cmake::MessageType
&status
)
315 if (GetBooleanValue(newArg
, makefile
, result
,
316 errorString
, Policy12Status
, status
))
320 const char *def
= makefile
->GetDefinition(newArg
.c_str());
321 return !cmSystemTools::IsOff(def
);
324 //=========================================================================
325 void IncrementArguments(std::list
<std::string
> &newArgs
,
326 std::list
<std::string
>::iterator
&argP1
,
327 std::list
<std::string
>::iterator
&argP2
)
329 if (argP1
!= newArgs
.end())
333 if (argP1
!= newArgs
.end())
340 //=========================================================================
341 // helper function to reduce code duplication
342 void HandlePredicate(bool value
, int &reducible
,
343 std::list
<std::string
>::iterator
&arg
,
344 std::list
<std::string
> &newArgs
,
345 std::list
<std::string
>::iterator
&argP1
,
346 std::list
<std::string
>::iterator
&argP2
)
356 newArgs
.erase(argP1
);
358 IncrementArguments(newArgs
,argP1
,argP2
);
362 //=========================================================================
363 // helper function to reduce code duplication
364 void HandleBinaryOp(bool value
, int &reducible
,
365 std::list
<std::string
>::iterator
&arg
,
366 std::list
<std::string
> &newArgs
,
367 std::list
<std::string
>::iterator
&argP1
,
368 std::list
<std::string
>::iterator
&argP2
)
378 newArgs
.erase(argP2
);
379 newArgs
.erase(argP1
);
381 IncrementArguments(newArgs
,argP1
,argP2
);
385 //=========================================================================
386 enum Op
{ OpLess
, OpEqual
, OpGreater
};
387 bool HandleVersionCompare(Op op
, const char* lhs_str
, const char* rhs_str
)
389 // Parse out up to 4 components.
390 unsigned int lhs
[4] = {0,0,0,0};
391 unsigned int rhs
[4] = {0,0,0,0};
392 sscanf(lhs_str
, "%u.%u.%u.%u", &lhs
[0], &lhs
[1], &lhs
[2], &lhs
[3]);
393 sscanf(rhs_str
, "%u.%u.%u.%u", &rhs
[0], &rhs
[1], &rhs
[2], &rhs
[3]);
395 // Do component-wise comparison.
396 for(unsigned int i
=0; i
< 4; ++i
)
400 // lhs < rhs, so true if operation is LESS
403 else if(lhs
[i
] > rhs
[i
])
405 // lhs > rhs, so true if operation is GREATER
406 return op
== OpGreater
;
409 // lhs == rhs, so true if operation is EQUAL
410 return op
== OpEqual
;
413 //=========================================================================
414 // level 0 processes parenthetical expressions
415 bool HandleLevel0(std::list
<std::string
> &newArgs
,
416 cmMakefile
*makefile
,
417 std::string
&errorString
,
418 cmake::MessageType
&status
)
424 std::list
<std::string
>::iterator arg
= newArgs
.begin();
425 while (arg
!= newArgs
.end())
429 // search for the closing paren for this opening one
430 std::list
<std::string
>::iterator argClose
;
433 unsigned int depth
= 1;
434 while (argClose
!= newArgs
.end() && depth
)
436 if (*argClose
== "(")
440 if (*argClose
== ")")
448 errorString
= "mismatched parenthesis in condition";
449 status
= cmake::FATAL_ERROR
;
452 // store the reduced args in this vector
453 std::vector
<std::string
> newArgs2
;
455 // copy to the list structure
456 std::list
<std::string
>::iterator argP1
= arg
;
458 for(; argP1
!= argClose
; argP1
++)
460 newArgs2
.push_back(*argP1
);
463 // now recursively invoke IsTrue to handle the values inside the
464 // parenthetical expression
466 cmIfCommand::IsTrue(newArgs2
, errorString
, makefile
, status
);
477 // remove the now evaluated parenthetical expression
478 newArgs
.erase(argP1
,argClose
);
487 //=========================================================================
488 // level one handles most predicates except for NOT
489 bool HandleLevel1(std::list
<std::string
> &newArgs
,
490 cmMakefile
*makefile
,
491 std::string
&, cmake::MessageType
&)
497 std::list
<std::string
>::iterator arg
= newArgs
.begin();
498 std::list
<std::string
>::iterator argP1
;
499 std::list
<std::string
>::iterator argP2
;
500 while (arg
!= newArgs
.end())
503 IncrementArguments(newArgs
,argP1
,argP2
);
505 if (*arg
== "EXISTS" && argP1
!= newArgs
.end())
508 cmSystemTools::FileExists((argP1
)->c_str()),
509 reducible
, arg
, newArgs
, argP1
, argP2
);
511 // does a directory with this name exist
512 if (*arg
== "IS_DIRECTORY" && argP1
!= newArgs
.end())
515 cmSystemTools::FileIsDirectory((argP1
)->c_str()),
516 reducible
, arg
, newArgs
, argP1
, argP2
);
518 // is the given path an absolute path ?
519 if (*arg
== "IS_ABSOLUTE" && argP1
!= newArgs
.end())
522 cmSystemTools::FileIsFullPath((argP1
)->c_str()),
523 reducible
, arg
, newArgs
, argP1
, argP2
);
525 // does a command exist
526 if (*arg
== "COMMAND" && argP1
!= newArgs
.end())
529 makefile
->CommandExists((argP1
)->c_str()),
530 reducible
, arg
, newArgs
, argP1
, argP2
);
532 // does a policy exist
533 if (*arg
== "POLICY" && argP1
!= newArgs
.end())
535 cmPolicies::PolicyID pid
;
537 makefile
->GetPolicies()->GetPolicyID((argP1
)->c_str(), pid
),
538 reducible
, arg
, newArgs
, argP1
, argP2
);
540 // does a target exist
541 if (*arg
== "TARGET" && argP1
!= newArgs
.end())
544 makefile
->FindTargetToUse((argP1
)->c_str())? true:false,
545 reducible
, arg
, newArgs
, argP1
, argP2
);
547 // is a variable defined
548 if (*arg
== "DEFINED" && argP1
!= newArgs
.end())
550 size_t argP1len
= argP1
->size();
552 if(argP1len
> 4 && argP1
->substr(0, 4) == "ENV{" &&
553 argP1
->operator[](argP1len
-1) == '}')
555 std::string env
= argP1
->substr(4, argP1len
-5);
556 bdef
= cmSystemTools::GetEnv(env
.c_str())?true:false;
560 bdef
= makefile
->IsDefinitionSet((argP1
)->c_str());
562 HandlePredicate(bdef
, reducible
, arg
, newArgs
, argP1
, argP2
);
571 //=========================================================================
572 // level two handles most binary operations except for AND OR
573 bool HandleLevel2(std::list
<std::string
> &newArgs
,
574 cmMakefile
*makefile
,
575 std::string
&errorString
,
576 cmake::MessageType
&status
)
584 std::list
<std::string
>::iterator arg
= newArgs
.begin();
585 std::list
<std::string
>::iterator argP1
;
586 std::list
<std::string
>::iterator argP2
;
587 while (arg
!= newArgs
.end())
590 IncrementArguments(newArgs
,argP1
,argP2
);
591 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
592 *(argP1
) == "MATCHES")
594 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
595 const char* rex
= (argP2
)->c_str();
596 cmStringCommand::ClearMatches(makefile
);
597 cmsys::RegularExpression regEntry
;
598 if ( !regEntry
.compile(rex
) )
600 cmOStringStream error
;
601 error
<< "Regular expression \"" << rex
<< "\" cannot compile";
602 errorString
= error
.str();
603 status
= cmake::FATAL_ERROR
;
606 if (regEntry
.find(def
))
608 cmStringCommand::StoreMatches(makefile
, regEntry
);
615 newArgs
.erase(argP2
);
616 newArgs
.erase(argP1
);
618 IncrementArguments(newArgs
,argP1
,argP2
);
622 if (argP1
!= newArgs
.end() && *arg
== "MATCHES")
625 newArgs
.erase(argP1
);
627 IncrementArguments(newArgs
,argP1
,argP2
);
631 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
632 (*(argP1
) == "LESS" || *(argP1
) == "GREATER" ||
633 *(argP1
) == "EQUAL"))
635 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
636 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
640 if(sscanf(def
, "%lg", &lhs
) != 1 ||
641 sscanf(def2
, "%lg", &rhs
) != 1)
645 else if (*(argP1
) == "LESS")
647 result
= (lhs
< rhs
);
649 else if (*(argP1
) == "GREATER")
651 result
= (lhs
> rhs
);
655 result
= (lhs
== rhs
);
657 HandleBinaryOp(result
,
658 reducible
, arg
, newArgs
, argP1
, argP2
);
661 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
662 (*(argP1
) == "STRLESS" ||
663 *(argP1
) == "STREQUAL" ||
664 *(argP1
) == "STRGREATER"))
666 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
667 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
668 int val
= strcmp(def
,def2
);
670 if (*(argP1
) == "STRLESS")
674 else if (*(argP1
) == "STRGREATER")
682 HandleBinaryOp(result
,
683 reducible
, arg
, newArgs
, argP1
, argP2
);
686 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
687 (*(argP1
) == "VERSION_LESS" || *(argP1
) == "VERSION_GREATER" ||
688 *(argP1
) == "VERSION_EQUAL"))
690 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
691 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
693 if(*argP1
== "VERSION_LESS")
697 else if(*argP1
== "VERSION_GREATER")
701 bool result
= HandleVersionCompare(op
, def
, def2
);
702 HandleBinaryOp(result
,
703 reducible
, arg
, newArgs
, argP1
, argP2
);
706 // is file A newer than file B
707 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
708 *(argP1
) == "IS_NEWER_THAN")
711 bool success
=cmSystemTools::FileTimeCompare(arg
->c_str(),
715 (success
==false || fileIsNewer
==1 || fileIsNewer
==0),
716 reducible
, arg
, newArgs
, argP1
, argP2
);
726 //=========================================================================
727 // level 3 handles NOT
728 bool HandleLevel3(std::list
<std::string
> &newArgs
,
729 cmMakefile
*makefile
,
730 std::string
&errorString
,
731 cmPolicies::PolicyStatus Policy12Status
,
732 cmake::MessageType
&status
)
738 std::list
<std::string
>::iterator arg
= newArgs
.begin();
739 std::list
<std::string
>::iterator argP1
;
740 std::list
<std::string
>::iterator argP2
;
741 while (arg
!= newArgs
.end())
744 IncrementArguments(newArgs
,argP1
,argP2
);
745 if (argP1
!= newArgs
.end() && *arg
== "NOT")
747 bool rhs
= GetBooleanValueWithAutoDereference(*argP1
, makefile
,
751 HandlePredicate(!rhs
, reducible
, arg
, newArgs
, argP1
, argP2
);
760 //=========================================================================
761 // level 4 handles AND OR
762 bool HandleLevel4(std::list
<std::string
> &newArgs
,
763 cmMakefile
*makefile
,
764 std::string
&errorString
,
765 cmPolicies::PolicyStatus Policy12Status
,
766 cmake::MessageType
&status
)
774 std::list
<std::string
>::iterator arg
= newArgs
.begin();
775 std::list
<std::string
>::iterator argP1
;
776 std::list
<std::string
>::iterator argP2
;
777 while (arg
!= newArgs
.end())
780 IncrementArguments(newArgs
,argP1
,argP2
);
781 if (argP1
!= newArgs
.end() && *(argP1
) == "AND" &&
782 argP2
!= newArgs
.end())
784 lhs
= GetBooleanValueWithAutoDereference(*arg
, makefile
,
788 rhs
= GetBooleanValueWithAutoDereference(*argP2
, makefile
,
792 HandleBinaryOp((lhs
&& rhs
),
793 reducible
, arg
, newArgs
, argP1
, argP2
);
796 if (argP1
!= newArgs
.end() && *(argP1
) == "OR" &&
797 argP2
!= newArgs
.end())
799 lhs
= GetBooleanValueWithAutoDereference(*arg
, makefile
,
803 rhs
= GetBooleanValueWithAutoDereference(*argP2
, makefile
,
807 HandleBinaryOp((lhs
|| rhs
),
808 reducible
, arg
, newArgs
, argP1
, argP2
);
819 //=========================================================================
820 // order of operations,
821 // 1. ( ) -- parenthetical groups
822 // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
823 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
827 // There is an issue on whether the arguments should be values of references,
828 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
829 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
830 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
831 // take numeric values or variable names. STRLESS and STRGREATER take
832 // variable names but if the variable name is not found it will use the name
833 // directly. AND OR take variables or the values 0 or 1.
836 bool cmIfCommand::IsTrue(const std::vector
<std::string
> &args
,
837 std::string
&errorString
, cmMakefile
*makefile
,
838 cmake::MessageType
&status
)
842 // handle empty invocation
848 // store the reduced args in this vector
849 std::list
<std::string
> newArgs
;
851 // copy to the list structure
852 for(unsigned int i
= 0; i
< args
.size(); ++i
)
854 newArgs
.push_back(args
[i
]);
857 // now loop through the arguments and see if we can reduce any of them
858 // we do this multiple times. Once for each level of precedence
860 if (!HandleLevel0(newArgs
, makefile
, errorString
, status
))
865 if (!HandleLevel1(newArgs
, makefile
, errorString
, status
))
870 if (!HandleLevel2(newArgs
, makefile
, errorString
, status
))
875 // used to store the value of policy CMP0012 for performance
876 cmPolicies::PolicyStatus Policy12Status
=
877 makefile
->GetPolicyStatus(cmPolicies::CMP0012
);
880 if (!HandleLevel3(newArgs
, makefile
, errorString
,
881 Policy12Status
, status
))
886 if (!HandleLevel4(newArgs
, makefile
, errorString
,
887 Policy12Status
, status
))
892 // now at the end there should only be one argument left
893 if (newArgs
.size() != 1)
895 errorString
= "Unknown arguments specified";
899 return GetBooleanValueWithAutoDereference(*(newArgs
.begin()),
906 //=========================================================================
907 const char* cmIfCommand::GetVariableOrString(const char* str
,
908 const cmMakefile
* mf
)
910 const char* def
= mf
->GetDefinition(str
);