ENH: fix uppercase version so defines are not upper as well
[cmake.git] / Source / cmIfCommand.cxx
blobb3019fe0040d1ec2196cb1d24d871877a444ddb3
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
5 Language: C++
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
21 #include <list>
22 #include <cmsys/RegularExpression.hxx>
24 //=========================================================================
25 bool cmIfFunctionBlocker::
26 IsFunctionBlocked(const cmListFileFunction& lff,
27 cmMakefile &mf,
28 cmExecutionStatus &inStatus)
30 // Prevent recusion and don't let this blocker block its own
31 // commands.
32 if (this->Executing)
34 return false;
37 // we start by recording all the functions
38 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
40 this->ScopeDepth++;
42 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
44 this->ScopeDepth--;
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;
51 int scopeDepth = 0;
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"))
57 scopeDepth++;
59 if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
61 scopeDepth--;
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;
68 this->HasRun = true;
70 else if (scopeDepth == 0 && !cmSystemTools::Strucmp
71 (this->Functions[c].Name.c_str(),"elseif"))
73 if (this->HasRun)
75 this->IsBlocking = true;
77 else
79 std::string errorString;
81 std::vector<std::string> expandedArguments;
82 mf.ExpandArguments(this->Functions[c].Arguments,
83 expandedArguments);
84 bool isTrue =
85 cmIfCommand::IsTrue(expandedArguments,errorString,&mf);
87 if (errorString.size())
89 std::string err = "had incorrect arguments: ";
90 unsigned int i;
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?"\"":"");
96 err += " ";
98 err += "(";
99 err += errorString;
100 err += ").";
101 cmSystemTools::Error(err.c_str());
102 return false;
105 if (isTrue)
107 this->IsBlocking = false;
108 this->HasRun = true;
113 // should we execute?
114 else if (!this->IsBlocking)
116 status.Clear();
117 mf.ExecuteCommand(this->Functions[c],status);
118 if (status.GetReturnInvoked())
120 inStatus.SetReturnInvoked(true);
121 mf.RemoveFunctionBlocker(lff);
122 return true;
124 if (status.GetBreakInvoked())
126 inStatus.SetBreakInvoked(true);
127 mf.RemoveFunctionBlocker(lff);
128 return true;
132 mf.RemoveFunctionBlocker(lff);
133 return true;
137 // record the command
138 this->Functions.push_back(lff);
140 // always return true
141 return true;
144 //=========================================================================
145 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
146 cmMakefile&)
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)
155 return true;
159 return false;
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?"\"":"");
173 errmsg += j->Value;
174 errmsg += (j->Quoted?"\"":"");
175 errmsg += " ";
177 cmSystemTools::Message(errmsg.c_str(), "Warning");
180 //=========================================================================
181 bool cmIfCommand
182 ::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
183 cmExecutionStatus &)
185 std::string errorString;
187 std::vector<std::string> expandedArguments;
188 this->Makefile->ExpandArguments(args, expandedArguments);
189 bool isTrue =
190 cmIfCommand::IsTrue(expandedArguments,errorString,this->Makefile);
192 if (errorString.size())
194 std::string err = "had incorrect arguments: ";
195 unsigned int i;
196 for(i =0; i < args.size(); ++i)
198 err += (args[i].Quoted?"\"":"");
199 err += args[i].Value;
200 err += (args[i].Quoted?"\"":"");
201 err += " ";
203 err += "(";
204 err += errorString;
205 err += ").";
206 this->SetError(err.c_str());
207 return false;
210 cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
211 // if is isn't true block the commands
212 f->ScopeDepth = 1;
213 f->IsBlocking = !isTrue;
214 if (isTrue)
216 f->HasRun = true;
218 f->Args = args;
219 this->Makefile->AddFunctionBlocker(f);
221 return true;
224 namespace
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())
233 argP1++;
234 argP2 = argP1;
235 if (argP1 != newArgs.end())
237 argP2++;
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)
250 if(value)
252 *arg = "1";
254 else
256 *arg = "0";
258 newArgs.erase(argP1);
259 argP1 = arg;
260 IncrementArguments(newArgs,argP1,argP2);
261 reducible = 1;
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)
272 if(value)
274 *arg = "1";
276 else
278 *arg = "0";
280 newArgs.erase(argP2);
281 newArgs.erase(argP1);
282 argP1 = arg;
283 IncrementArguments(newArgs,argP1,argP2);
284 reducible = 1;
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)
300 if(lhs[i] < rhs[i])
302 // lhs < rhs, so true if operation is LESS
303 return op == OpLess;
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)
321 int reducible;
324 reducible = 0;
325 std::list<std::string>::iterator arg = newArgs.begin();
326 while (arg != newArgs.end())
328 if (*arg == "(")
330 // search for the closing paren for this opening one
331 std::list<std::string>::iterator argClose;
332 argClose = arg;
333 argClose++;
334 unsigned int depth = 1;
335 while (argClose != newArgs.end() && depth)
337 if (*argClose == "(")
339 depth++;
341 if (*argClose == ")")
343 depth--;
345 argClose++;
347 if (depth)
349 errorString = "mismatched parenthesis in condition";
350 return false;
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;
357 argP1++;
358 for(; argP1 != argClose; argP1++)
360 newArgs2.push_back(*argP1);
362 newArgs2.pop_back();
363 // now recursively invoke IsTrue to handle the values inside the
364 // parenthetical expression
365 bool value =
366 cmIfCommand::IsTrue(newArgs2, errorString, makefile);
367 if(value)
369 *arg = "1";
371 else
373 *arg = "0";
375 argP1 = arg;
376 argP1++;
377 // remove the now evaluated parenthetical expression
378 newArgs.erase(argP1,argClose);
380 ++arg;
383 while (reducible);
384 return true;
387 //=========================================================================
388 // level one handles most predicates except for NOT
389 bool HandleLevel1(std::list<std::string> &newArgs,
390 cmMakefile *makefile,
391 std::string &)
393 int reducible;
396 reducible = 0;
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())
402 argP1 = arg;
403 IncrementArguments(newArgs,argP1,argP2);
404 // does a file exist
405 if (*arg == "EXISTS" && argP1 != newArgs.end())
407 HandlePredicate(
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())
414 HandlePredicate(
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())
421 HandlePredicate(
422 cmSystemTools::FileIsFullPath((argP1)->c_str()),
423 reducible, arg, newArgs, argP1, argP2);
425 // does a command exist
426 if (*arg == "COMMAND" && argP1 != newArgs.end())
428 HandlePredicate(
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;
436 HandlePredicate(
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())
443 HandlePredicate(
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();
451 bool bdef = false;
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;
458 else
460 bdef = makefile->IsDefinitionSet((argP1)->c_str());
462 HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
464 ++arg;
467 while (reducible);
468 return true;
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)
477 int reducible;
478 const char *def;
479 const char *def2;
482 reducible = 0;
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())
488 argP1 = arg;
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();
502 return false;
504 if (regEntry.find(def))
506 cmStringCommand::StoreMatches(makefile, regEntry);
507 *arg = "1";
509 else
511 *arg = "0";
513 newArgs.erase(argP2);
514 newArgs.erase(argP1);
515 argP1 = arg;
516 IncrementArguments(newArgs,argP1,argP2);
517 reducible = 1;
520 if (argP1 != newArgs.end() && *arg == "MATCHES")
522 *arg = "0";
523 newArgs.erase(argP1);
524 argP1 = arg;
525 IncrementArguments(newArgs,argP1,argP2);
526 reducible = 1;
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);
535 double lhs;
536 double rhs;
537 bool result;
538 if(sscanf(def, "%lg", &lhs) != 1 ||
539 sscanf(def2, "%lg", &rhs) != 1)
541 result = false;
543 else if (*(argP1) == "LESS")
545 result = (lhs < rhs);
547 else if (*(argP1) == "GREATER")
549 result = (lhs > rhs);
551 else
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);
567 bool result;
568 if (*(argP1) == "STRLESS")
570 result = (val < 0);
572 else if (*(argP1) == "STRGREATER")
574 result = (val > 0);
576 else // strequal
578 result = (val == 0);
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);
590 Op op = OpEqual;
591 if(*argP1 == "VERSION_LESS")
593 op = OpLess;
595 else if(*argP1 == "VERSION_GREATER")
597 op = OpGreater;
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")
608 int fileIsNewer=0;
609 bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
610 (argP2)->c_str(),
611 &fileIsNewer);
612 HandleBinaryOp(
613 (success==false || fileIsNewer==1 || fileIsNewer==0),
614 reducible, arg, newArgs, argP1, argP2);
617 ++arg;
620 while (reducible);
621 return true;
624 //=========================================================================
625 // level 3 handles NOT
626 bool HandleLevel3(std::list<std::string> &newArgs,
627 cmMakefile *makefile,
628 std::string &)
630 int reducible;
631 const char *def;
634 reducible = 0;
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())
640 argP1 = arg;
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);
648 ++arg;
651 while (reducible);
652 return true;
655 //=========================================================================
656 // level 4 handles AND OR
657 bool HandleLevel4(std::list<std::string> &newArgs,
658 cmMakefile *makefile,
659 std::string &)
661 int reducible;
662 const char *def;
663 const char *def2;
666 reducible = 0;
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())
672 argP1 = arg;
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);
679 HandleBinaryOp(
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);
689 HandleBinaryOp(
690 !(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)),
691 reducible, arg, newArgs, argP1, argP2);
693 ++arg;
696 while (reducible);
697 return true;
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
707 // 4. NOT
708 // 5. AND OR
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)
722 const char *def;
723 errorString = "";
725 // handle empty invocation
726 if (args.size() < 1)
728 return false;
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
744 return false;
746 if (!HandleLevel1(newArgs, makefile, errorString)) //predicates
748 return false;
750 if (!HandleLevel2(newArgs, makefile, errorString)) // binary ops
752 return false;
754 if (!HandleLevel3(newArgs, makefile, errorString)) // NOT
756 return false;
758 if (!HandleLevel4(newArgs, makefile, errorString)) // AND OR
760 return false;
763 // now at the end there should only be one argument left
764 if (newArgs.size() == 1)
766 if (*newArgs.begin() == "0")
768 return false;
770 if (*newArgs.begin() == "1")
772 return true;
774 def = makefile->GetDefinition(args[0].c_str());
775 if(cmSystemTools::IsOff(def))
777 return false;
780 else
782 errorString = "Unknown arguments specified";
783 return false;
786 return true;
789 //=========================================================================
790 const char* cmIfCommand::GetVariableOrString(const char* str,
791 const cmMakefile* mf)
793 const char* def = mf->GetDefinition(str);
794 if(!def)
796 def = str;
798 return def;
801 //=========================================================================
802 const char* cmIfCommand::GetVariableOrNumber(const char* str,
803 const cmMakefile* mf)
805 const char* def = mf->GetDefinition(str);
806 if(!def)
808 if (atoi(str))
810 def = str;
813 return def;