ENH: check in almost building VMS stuff with VMSBuild directory since the bootstrap...
[cmake.git] / Source / cmIfCommand.cxx
blob5884f1f3e041f24f82766fe8c6d00618cf2258b4
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
5 Language: C++
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
21 #include <list>
22 #include <cmsys/RegularExpression.hxx>
24 //=========================================================================
25 bool cmIfFunctionBlocker::
26 IsFunctionBlocked(const cmListFileFunction& lff,
27 cmMakefile &mf,
28 cmExecutionStatus &inStatus)
30 // we start by recording all the functions
31 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
33 this->ScopeDepth++;
35 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
37 this->ScopeDepth--;
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;
48 int scopeDepth = 0;
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"))
54 scopeDepth++;
56 if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
58 scopeDepth--;
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;
65 this->HasRun = true;
67 else if (scopeDepth == 0 && !cmSystemTools::Strucmp
68 (this->Functions[c].Name.c_str(),"elseif"))
70 if (this->HasRun)
72 this->IsBlocking = true;
74 else
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,
84 expandedArguments);
85 bool isTrue =
86 cmIfCommand::IsTrue(expandedArguments,errorString,&mf);
88 if (errorString.size())
90 std::string err = "had incorrect arguments: ";
91 unsigned int i;
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?"\"":"");
97 err += " ";
99 err += "(";
100 err += errorString;
101 err += ").";
102 mf.IssueMessage(cmake::FATAL_ERROR, err);
103 cmSystemTools::SetFatalErrorOccured();
104 return true;
107 if (isTrue)
109 this->IsBlocking = false;
110 this->HasRun = true;
115 // should we execute?
116 else if (!this->IsBlocking)
118 status.Clear();
119 mf.ExecuteCommand(this->Functions[c],status);
120 if (status.GetReturnInvoked())
122 inStatus.SetReturnInvoked(true);
123 return true;
125 if (status.GetBreakInvoked())
127 inStatus.SetBreakInvoked(true);
128 return true;
132 return true;
136 // record the command
137 this->Functions.push_back(lff);
139 // always return true
140 return true;
143 //=========================================================================
144 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
145 cmMakefile&)
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)
154 return true;
158 return false;
161 //=========================================================================
162 bool cmIfCommand
163 ::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
164 cmExecutionStatus &)
166 std::string errorString;
168 std::vector<std::string> expandedArguments;
169 this->Makefile->ExpandArguments(args, expandedArguments);
170 bool isTrue =
171 cmIfCommand::IsTrue(expandedArguments,errorString,this->Makefile);
173 if (errorString.size())
175 std::string err = "had incorrect arguments: ";
176 unsigned int i;
177 for(i =0; i < args.size(); ++i)
179 err += (args[i].Quoted?"\"":"");
180 err += args[i].Value;
181 err += (args[i].Quoted?"\"":"");
182 err += " ";
184 err += "(";
185 err += errorString;
186 err += ").";
187 this->SetError(err.c_str());
188 cmSystemTools::SetFatalErrorOccured();
189 return false;
192 cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
193 // if is isn't true block the commands
194 f->ScopeDepth = 1;
195 f->IsBlocking = !isTrue;
196 if (isTrue)
198 f->HasRun = true;
200 f->Args = args;
201 this->Makefile->AddFunctionBlocker(f);
203 return true;
206 namespace
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())
215 argP1++;
216 argP2 = argP1;
217 if (argP1 != newArgs.end())
219 argP2++;
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)
232 if(value)
234 *arg = "1";
236 else
238 *arg = "0";
240 newArgs.erase(argP1);
241 argP1 = arg;
242 IncrementArguments(newArgs,argP1,argP2);
243 reducible = 1;
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)
254 if(value)
256 *arg = "1";
258 else
260 *arg = "0";
262 newArgs.erase(argP2);
263 newArgs.erase(argP1);
264 argP1 = arg;
265 IncrementArguments(newArgs,argP1,argP2);
266 reducible = 1;
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)
282 if(lhs[i] < rhs[i])
284 // lhs < rhs, so true if operation is LESS
285 return op == OpLess;
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)
303 int reducible;
306 reducible = 0;
307 std::list<std::string>::iterator arg = newArgs.begin();
308 while (arg != newArgs.end())
310 if (*arg == "(")
312 // search for the closing paren for this opening one
313 std::list<std::string>::iterator argClose;
314 argClose = arg;
315 argClose++;
316 unsigned int depth = 1;
317 while (argClose != newArgs.end() && depth)
319 if (*argClose == "(")
321 depth++;
323 if (*argClose == ")")
325 depth--;
327 argClose++;
329 if (depth)
331 errorString = "mismatched parenthesis in condition";
332 return false;
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;
339 argP1++;
340 for(; argP1 != argClose; argP1++)
342 newArgs2.push_back(*argP1);
344 newArgs2.pop_back();
345 // now recursively invoke IsTrue to handle the values inside the
346 // parenthetical expression
347 bool value =
348 cmIfCommand::IsTrue(newArgs2, errorString, makefile);
349 if(value)
351 *arg = "1";
353 else
355 *arg = "0";
357 argP1 = arg;
358 argP1++;
359 // remove the now evaluated parenthetical expression
360 newArgs.erase(argP1,argClose);
362 ++arg;
365 while (reducible);
366 return true;
369 //=========================================================================
370 // level one handles most predicates except for NOT
371 bool HandleLevel1(std::list<std::string> &newArgs,
372 cmMakefile *makefile,
373 std::string &)
375 int reducible;
378 reducible = 0;
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())
384 argP1 = arg;
385 IncrementArguments(newArgs,argP1,argP2);
386 // does a file exist
387 if (*arg == "EXISTS" && argP1 != newArgs.end())
389 HandlePredicate(
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())
396 HandlePredicate(
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())
403 HandlePredicate(
404 cmSystemTools::FileIsFullPath((argP1)->c_str()),
405 reducible, arg, newArgs, argP1, argP2);
407 // does a command exist
408 if (*arg == "COMMAND" && argP1 != newArgs.end())
410 HandlePredicate(
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;
418 HandlePredicate(
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())
425 HandlePredicate(
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();
433 bool bdef = false;
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;
440 else
442 bdef = makefile->IsDefinitionSet((argP1)->c_str());
444 HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
446 ++arg;
449 while (reducible);
450 return true;
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)
459 int reducible;
460 const char *def;
461 const char *def2;
464 reducible = 0;
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())
470 argP1 = arg;
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();
484 return false;
486 if (regEntry.find(def))
488 cmStringCommand::StoreMatches(makefile, regEntry);
489 *arg = "1";
491 else
493 *arg = "0";
495 newArgs.erase(argP2);
496 newArgs.erase(argP1);
497 argP1 = arg;
498 IncrementArguments(newArgs,argP1,argP2);
499 reducible = 1;
502 if (argP1 != newArgs.end() && *arg == "MATCHES")
504 *arg = "0";
505 newArgs.erase(argP1);
506 argP1 = arg;
507 IncrementArguments(newArgs,argP1,argP2);
508 reducible = 1;
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);
517 double lhs;
518 double rhs;
519 bool result;
520 if(sscanf(def, "%lg", &lhs) != 1 ||
521 sscanf(def2, "%lg", &rhs) != 1)
523 result = false;
525 else if (*(argP1) == "LESS")
527 result = (lhs < rhs);
529 else if (*(argP1) == "GREATER")
531 result = (lhs > rhs);
533 else
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);
549 bool result;
550 if (*(argP1) == "STRLESS")
552 result = (val < 0);
554 else if (*(argP1) == "STRGREATER")
556 result = (val > 0);
558 else // strequal
560 result = (val == 0);
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);
572 Op op = OpEqual;
573 if(*argP1 == "VERSION_LESS")
575 op = OpLess;
577 else if(*argP1 == "VERSION_GREATER")
579 op = OpGreater;
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")
590 int fileIsNewer=0;
591 bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
592 (argP2)->c_str(),
593 &fileIsNewer);
594 HandleBinaryOp(
595 (success==false || fileIsNewer==1 || fileIsNewer==0),
596 reducible, arg, newArgs, argP1, argP2);
599 ++arg;
602 while (reducible);
603 return true;
606 //=========================================================================
607 // level 3 handles NOT
608 bool HandleLevel3(std::list<std::string> &newArgs,
609 cmMakefile *makefile,
610 std::string &)
612 int reducible;
613 const char *def;
616 reducible = 0;
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())
622 argP1 = arg;
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);
630 ++arg;
633 while (reducible);
634 return true;
637 //=========================================================================
638 // level 4 handles AND OR
639 bool HandleLevel4(std::list<std::string> &newArgs,
640 cmMakefile *makefile,
641 std::string &)
643 int reducible;
644 const char *def;
645 const char *def2;
648 reducible = 0;
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())
654 argP1 = arg;
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);
661 HandleBinaryOp(
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);
671 HandleBinaryOp(
672 !(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)),
673 reducible, arg, newArgs, argP1, argP2);
675 ++arg;
678 while (reducible);
679 return true;
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
689 // 4. NOT
690 // 5. AND OR
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)
704 const char *def;
705 errorString = "";
707 // handle empty invocation
708 if (args.size() < 1)
710 return false;
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
726 return false;
728 if (!HandleLevel1(newArgs, makefile, errorString)) //predicates
730 return false;
732 if (!HandleLevel2(newArgs, makefile, errorString)) // binary ops
734 return false;
736 if (!HandleLevel3(newArgs, makefile, errorString)) // NOT
738 return false;
740 if (!HandleLevel4(newArgs, makefile, errorString)) // AND OR
742 return false;
745 // now at the end there should only be one argument left
746 if (newArgs.size() == 1)
748 if (*newArgs.begin() == "0")
750 return false;
752 if (*newArgs.begin() == "1")
754 return true;
756 def = makefile->GetDefinition(args[0].c_str());
757 if(cmSystemTools::IsOff(def))
759 return false;
762 else
764 errorString = "Unknown arguments specified";
765 return false;
768 return true;
771 //=========================================================================
772 const char* cmIfCommand::GetVariableOrString(const char* str,
773 const cmMakefile* mf)
775 const char* def = mf->GetDefinition(str);
776 if(!def)
778 def = str;
780 return def;
783 //=========================================================================
784 const char* cmIfCommand::GetVariableOrNumber(const char* str,
785 const cmMakefile* mf)
787 const char* def = mf->GetDefinition(str);
788 if(!def)
790 if (atoi(str))
792 def = str;
795 return def;