1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
6 Date: $Date: 2009-01-21 14:49:00 $
7 Version: $Revision: 1.94 $
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 cmIfCommand::IsTrue(expandedArguments
,errorString
,&mf
);
88 if (errorString
.size())
90 std::string err
= "had incorrect arguments: ";
92 for(i
=0; i
< this->Functions
[c
].Arguments
.size(); ++i
)
94 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
95 err
+= this->Functions
[c
].Arguments
[i
].Value
;
96 err
+= (this->Functions
[c
].Arguments
[i
].Quoted
?"\"":"");
102 mf
.IssueMessage(cmake::FATAL_ERROR
, err
);
103 cmSystemTools::SetFatalErrorOccured();
109 this->IsBlocking
= false;
115 // should we execute?
116 else if (!this->IsBlocking
)
119 mf
.ExecuteCommand(this->Functions
[c
],status
);
120 if (status
.GetReturnInvoked())
122 inStatus
.SetReturnInvoked(true);
125 if (status
.GetBreakInvoked())
127 inStatus
.SetBreakInvoked(true);
136 // record the command
137 this->Functions
.push_back(lff
);
139 // always return true
143 //=========================================================================
144 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction
& lff
,
147 if (!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endif"))
149 // if the endif has arguments, then make sure
150 // they match the arguments of the matching if
151 if (lff
.Arguments
.size() == 0 ||
152 lff
.Arguments
== this->Args
)
161 //=========================================================================
163 ::InvokeInitialPass(const std::vector
<cmListFileArgument
>& args
,
166 std::string errorString
;
168 std::vector
<std::string
> expandedArguments
;
169 this->Makefile
->ExpandArguments(args
, expandedArguments
);
171 cmIfCommand::IsTrue(expandedArguments
,errorString
,this->Makefile
);
173 if (errorString
.size())
175 std::string err
= "had incorrect arguments: ";
177 for(i
=0; i
< args
.size(); ++i
)
179 err
+= (args
[i
].Quoted
?"\"":"");
180 err
+= args
[i
].Value
;
181 err
+= (args
[i
].Quoted
?"\"":"");
187 this->SetError(err
.c_str());
188 cmSystemTools::SetFatalErrorOccured();
192 cmIfFunctionBlocker
*f
= new cmIfFunctionBlocker();
193 // if is isn't true block the commands
195 f
->IsBlocking
= !isTrue
;
201 this->Makefile
->AddFunctionBlocker(f
);
208 //=========================================================================
209 void IncrementArguments(std::list
<std::string
> &newArgs
,
210 std::list
<std::string
>::iterator
&argP1
,
211 std::list
<std::string
>::iterator
&argP2
)
213 if (argP1
!= newArgs
.end())
217 if (argP1
!= newArgs
.end())
224 //=========================================================================
225 // helper function to reduce code duplication
226 void HandlePredicate(bool value
, int &reducible
,
227 std::list
<std::string
>::iterator
&arg
,
228 std::list
<std::string
> &newArgs
,
229 std::list
<std::string
>::iterator
&argP1
,
230 std::list
<std::string
>::iterator
&argP2
)
240 newArgs
.erase(argP1
);
242 IncrementArguments(newArgs
,argP1
,argP2
);
246 //=========================================================================
247 // helper function to reduce code duplication
248 void HandleBinaryOp(bool value
, int &reducible
,
249 std::list
<std::string
>::iterator
&arg
,
250 std::list
<std::string
> &newArgs
,
251 std::list
<std::string
>::iterator
&argP1
,
252 std::list
<std::string
>::iterator
&argP2
)
262 newArgs
.erase(argP2
);
263 newArgs
.erase(argP1
);
265 IncrementArguments(newArgs
,argP1
,argP2
);
269 //=========================================================================
270 enum Op
{ OpLess
, OpEqual
, OpGreater
};
271 bool HandleVersionCompare(Op op
, const char* lhs_str
, const char* rhs_str
)
273 // Parse out up to 4 components.
274 unsigned int lhs
[4] = {0,0,0,0};
275 unsigned int rhs
[4] = {0,0,0,0};
276 sscanf(lhs_str
, "%u.%u.%u.%u", &lhs
[0], &lhs
[1], &lhs
[2], &lhs
[3]);
277 sscanf(rhs_str
, "%u.%u.%u.%u", &rhs
[0], &rhs
[1], &rhs
[2], &rhs
[3]);
279 // Do component-wise comparison.
280 for(unsigned int i
=0; i
< 4; ++i
)
284 // lhs < rhs, so true if operation is LESS
287 else if(lhs
[i
] > rhs
[i
])
289 // lhs > rhs, so true if operation is GREATER
290 return op
== OpGreater
;
293 // lhs == rhs, so true if operation is EQUAL
294 return op
== OpEqual
;
297 //=========================================================================
298 // level 0 processes parenthetical expressions
299 bool HandleLevel0(std::list
<std::string
> &newArgs
,
300 cmMakefile
*makefile
,
301 std::string
&errorString
)
307 std::list
<std::string
>::iterator arg
= newArgs
.begin();
308 while (arg
!= newArgs
.end())
312 // search for the closing paren for this opening one
313 std::list
<std::string
>::iterator argClose
;
316 unsigned int depth
= 1;
317 while (argClose
!= newArgs
.end() && depth
)
319 if (*argClose
== "(")
323 if (*argClose
== ")")
331 errorString
= "mismatched parenthesis in condition";
334 // store the reduced args in this vector
335 std::vector
<std::string
> newArgs2
;
337 // copy to the list structure
338 std::list
<std::string
>::iterator argP1
= arg
;
340 for(; argP1
!= argClose
; argP1
++)
342 newArgs2
.push_back(*argP1
);
345 // now recursively invoke IsTrue to handle the values inside the
346 // parenthetical expression
348 cmIfCommand::IsTrue(newArgs2
, errorString
, makefile
);
359 // remove the now evaluated parenthetical expression
360 newArgs
.erase(argP1
,argClose
);
369 //=========================================================================
370 // level one handles most predicates except for NOT
371 bool HandleLevel1(std::list
<std::string
> &newArgs
,
372 cmMakefile
*makefile
,
379 std::list
<std::string
>::iterator arg
= newArgs
.begin();
380 std::list
<std::string
>::iterator argP1
;
381 std::list
<std::string
>::iterator argP2
;
382 while (arg
!= newArgs
.end())
385 IncrementArguments(newArgs
,argP1
,argP2
);
387 if (*arg
== "EXISTS" && argP1
!= newArgs
.end())
390 cmSystemTools::FileExists((argP1
)->c_str()),
391 reducible
, arg
, newArgs
, argP1
, argP2
);
393 // does a directory with this name exist
394 if (*arg
== "IS_DIRECTORY" && argP1
!= newArgs
.end())
397 cmSystemTools::FileIsDirectory((argP1
)->c_str()),
398 reducible
, arg
, newArgs
, argP1
, argP2
);
400 // is the given path an absolute path ?
401 if (*arg
== "IS_ABSOLUTE" && argP1
!= newArgs
.end())
404 cmSystemTools::FileIsFullPath((argP1
)->c_str()),
405 reducible
, arg
, newArgs
, argP1
, argP2
);
407 // does a command exist
408 if (*arg
== "COMMAND" && argP1
!= newArgs
.end())
411 makefile
->CommandExists((argP1
)->c_str()),
412 reducible
, arg
, newArgs
, argP1
, argP2
);
414 // does a policy exist
415 if (*arg
== "POLICY" && argP1
!= newArgs
.end())
417 cmPolicies::PolicyID pid
;
419 makefile
->GetPolicies()->GetPolicyID((argP1
)->c_str(), pid
),
420 reducible
, arg
, newArgs
, argP1
, argP2
);
422 // does a target exist
423 if (*arg
== "TARGET" && argP1
!= newArgs
.end())
426 makefile
->FindTargetToUse((argP1
)->c_str())? true:false,
427 reducible
, arg
, newArgs
, argP1
, argP2
);
429 // is a variable defined
430 if (*arg
== "DEFINED" && argP1
!= newArgs
.end())
432 size_t argP1len
= argP1
->size();
434 if(argP1len
> 4 && argP1
->substr(0, 4) == "ENV{" &&
435 argP1
->operator[](argP1len
-1) == '}')
437 std::string env
= argP1
->substr(4, argP1len
-5);
438 bdef
= cmSystemTools::GetEnv(env
.c_str())?true:false;
442 bdef
= makefile
->IsDefinitionSet((argP1
)->c_str());
444 HandlePredicate(bdef
, reducible
, arg
, newArgs
, argP1
, argP2
);
453 //=========================================================================
454 // level two handles most binary operations except for AND OR
455 bool HandleLevel2(std::list
<std::string
> &newArgs
,
456 cmMakefile
*makefile
,
457 std::string
&errorString
)
465 std::list
<std::string
>::iterator arg
= newArgs
.begin();
466 std::list
<std::string
>::iterator argP1
;
467 std::list
<std::string
>::iterator argP2
;
468 while (arg
!= newArgs
.end())
471 IncrementArguments(newArgs
,argP1
,argP2
);
472 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
473 *(argP1
) == "MATCHES")
475 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
476 const char* rex
= (argP2
)->c_str();
477 cmStringCommand::ClearMatches(makefile
);
478 cmsys::RegularExpression regEntry
;
479 if ( !regEntry
.compile(rex
) )
481 cmOStringStream error
;
482 error
<< "Regular expression \"" << rex
<< "\" cannot compile";
483 errorString
= error
.str();
486 if (regEntry
.find(def
))
488 cmStringCommand::StoreMatches(makefile
, regEntry
);
495 newArgs
.erase(argP2
);
496 newArgs
.erase(argP1
);
498 IncrementArguments(newArgs
,argP1
,argP2
);
502 if (argP1
!= newArgs
.end() && *arg
== "MATCHES")
505 newArgs
.erase(argP1
);
507 IncrementArguments(newArgs
,argP1
,argP2
);
511 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
512 (*(argP1
) == "LESS" || *(argP1
) == "GREATER" ||
513 *(argP1
) == "EQUAL"))
515 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
516 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
520 if(sscanf(def
, "%lg", &lhs
) != 1 ||
521 sscanf(def2
, "%lg", &rhs
) != 1)
525 else if (*(argP1
) == "LESS")
527 result
= (lhs
< rhs
);
529 else if (*(argP1
) == "GREATER")
531 result
= (lhs
> rhs
);
535 result
= (lhs
== rhs
);
537 HandleBinaryOp(result
,
538 reducible
, arg
, newArgs
, argP1
, argP2
);
541 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
542 (*(argP1
) == "STRLESS" ||
543 *(argP1
) == "STREQUAL" ||
544 *(argP1
) == "STRGREATER"))
546 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
547 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
548 int val
= strcmp(def
,def2
);
550 if (*(argP1
) == "STRLESS")
554 else if (*(argP1
) == "STRGREATER")
562 HandleBinaryOp(result
,
563 reducible
, arg
, newArgs
, argP1
, argP2
);
566 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
567 (*(argP1
) == "VERSION_LESS" || *(argP1
) == "VERSION_GREATER" ||
568 *(argP1
) == "VERSION_EQUAL"))
570 def
= cmIfCommand::GetVariableOrString(arg
->c_str(), makefile
);
571 def2
= cmIfCommand::GetVariableOrString((argP2
)->c_str(), makefile
);
573 if(*argP1
== "VERSION_LESS")
577 else if(*argP1
== "VERSION_GREATER")
581 bool result
= HandleVersionCompare(op
, def
, def2
);
582 HandleBinaryOp(result
,
583 reducible
, arg
, newArgs
, argP1
, argP2
);
586 // is file A newer than file B
587 if (argP1
!= newArgs
.end() && argP2
!= newArgs
.end() &&
588 *(argP1
) == "IS_NEWER_THAN")
591 bool success
=cmSystemTools::FileTimeCompare(arg
->c_str(),
595 (success
==false || fileIsNewer
==1 || fileIsNewer
==0),
596 reducible
, arg
, newArgs
, argP1
, argP2
);
606 //=========================================================================
607 // level 3 handles NOT
608 bool HandleLevel3(std::list
<std::string
> &newArgs
,
609 cmMakefile
*makefile
,
617 std::list
<std::string
>::iterator arg
= newArgs
.begin();
618 std::list
<std::string
>::iterator argP1
;
619 std::list
<std::string
>::iterator argP2
;
620 while (arg
!= newArgs
.end())
623 IncrementArguments(newArgs
,argP1
,argP2
);
624 if (argP1
!= newArgs
.end() && *arg
== "NOT")
626 def
= cmIfCommand::GetVariableOrNumber((argP1
)->c_str(), makefile
);
627 HandlePredicate(cmSystemTools::IsOff(def
),
628 reducible
, arg
, newArgs
, argP1
, argP2
);
637 //=========================================================================
638 // level 4 handles AND OR
639 bool HandleLevel4(std::list
<std::string
> &newArgs
,
640 cmMakefile
*makefile
,
649 std::list
<std::string
>::iterator arg
= newArgs
.begin();
650 std::list
<std::string
>::iterator argP1
;
651 std::list
<std::string
>::iterator argP2
;
652 while (arg
!= newArgs
.end())
655 IncrementArguments(newArgs
,argP1
,argP2
);
656 if (argP1
!= newArgs
.end() && *(argP1
) == "AND" &&
657 argP2
!= newArgs
.end())
659 def
= cmIfCommand::GetVariableOrNumber(arg
->c_str(), makefile
);
660 def2
= cmIfCommand::GetVariableOrNumber((argP2
)->c_str(), makefile
);
662 !(cmSystemTools::IsOff(def
) || cmSystemTools::IsOff(def2
)),
663 reducible
, arg
, newArgs
, argP1
, argP2
);
666 if (argP1
!= newArgs
.end() && *(argP1
) == "OR" &&
667 argP2
!= newArgs
.end())
669 def
= cmIfCommand::GetVariableOrNumber(arg
->c_str(), makefile
);
670 def2
= cmIfCommand::GetVariableOrNumber((argP2
)->c_str(), makefile
);
672 !(cmSystemTools::IsOff(def
) && cmSystemTools::IsOff(def2
)),
673 reducible
, arg
, newArgs
, argP1
, argP2
);
684 //=========================================================================
685 // order of operations,
686 // 1. ( ) -- parenthetical groups
687 // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
688 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
692 // There is an issue on whether the arguments should be values of references,
693 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
694 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
695 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
696 // take numeric values or variable names. STRLESS and STRGREATER take
697 // variable names but if the variable name is not found it will use the name
698 // directly. AND OR take variables or the values 0 or 1.
701 bool cmIfCommand::IsTrue(const std::vector
<std::string
> &args
,
702 std::string
&errorString
, cmMakefile
*makefile
)
707 // handle empty invocation
713 // store the reduced args in this vector
714 std::list
<std::string
> newArgs
;
716 // copy to the list structure
717 for(unsigned int i
= 0; i
< args
.size(); ++i
)
719 newArgs
.push_back(args
[i
]);
722 // now loop through the arguments and see if we can reduce any of them
723 // we do this multiple times. Once for each level of precedence
724 if (!HandleLevel0(newArgs
, makefile
, errorString
)) // parens
728 if (!HandleLevel1(newArgs
, makefile
, errorString
)) //predicates
732 if (!HandleLevel2(newArgs
, makefile
, errorString
)) // binary ops
736 if (!HandleLevel3(newArgs
, makefile
, errorString
)) // NOT
740 if (!HandleLevel4(newArgs
, makefile
, errorString
)) // AND OR
745 // now at the end there should only be one argument left
746 if (newArgs
.size() == 1)
748 if (*newArgs
.begin() == "0")
752 if (*newArgs
.begin() == "1")
756 def
= makefile
->GetDefinition(args
[0].c_str());
757 if(cmSystemTools::IsOff(def
))
764 errorString
= "Unknown arguments specified";
771 //=========================================================================
772 const char* cmIfCommand::GetVariableOrString(const char* str
,
773 const cmMakefile
* mf
)
775 const char* def
= mf
->GetDefinition(str
);
783 //=========================================================================
784 const char* cmIfCommand::GetVariableOrNumber(const char* str
,
785 const cmMakefile
* mf
)
787 const char* def
= mf
->GetDefinition(str
);