1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmIfCommand.cxx,v $
6 Date: $Date: 2008/03/01 02:33:33 $
7 Version: $Revision: 1.84 $
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 bool cmIfFunctionBlocker::
25 IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
26 cmExecutionStatus &inStatus)
28 // Prevent recusion and don't let this blocker block its own
35 // we start by recording all the functions
36 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if"))
40 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
43 // if this is the endif for this if statement, then start executing
44 if (!this->ScopeDepth)
46 // execute the functions for the true parts of the if statement
47 this->Executing = true;
48 cmExecutionStatus status;
50 for(unsigned int c = 0; c < this->Functions.size(); ++c)
52 // keep track of scope depth
53 if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"if"))
57 if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif"))
61 // watch for our state change
62 if (scopeDepth == 0 &&
63 !cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"else"))
65 this->IsBlocking = this->HasRun;
68 else if (scopeDepth == 0 && !cmSystemTools::Strucmp
69 (this->Functions[c].Name.c_str(),"elseif"))
73 this->IsBlocking = true;
77 char* errorString = 0;
79 std::vector<std::string> expandedArguments;
80 mf.ExpandArguments(this->Functions[c].Arguments,
83 cmIfCommand::IsTrue(expandedArguments,&errorString,&mf);
87 std::string err = "had incorrect arguments: ";
89 for(i =0; i < this->Functions[c].Arguments.size(); ++i)
91 err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
92 err += this->Functions[c].Arguments[i].Value;
93 err += (this->Functions[c].Arguments[i].Quoted?"\"":"");
99 cmSystemTools::Error(err.c_str());
100 delete [] errorString;
106 this->IsBlocking = false;
112 // should we execute?
113 else if (!this->IsBlocking)
116 mf.ExecuteCommand(this->Functions[c],status);
117 if (status.GetReturnInvoked())
119 inStatus.SetReturnInvoked(true);
120 mf.RemoveFunctionBlocker(lff);
123 if (status.GetBreakInvoked())
125 inStatus.SetBreakInvoked(true);
126 mf.RemoveFunctionBlocker(lff);
131 mf.RemoveFunctionBlocker(lff);
136 // record the command
137 this->Functions.push_back(lff);
139 // always return true
143 bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
146 if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif"))
148 // if the endif has arguments, then make sure
149 // they match the arguments of the matching if
150 if (lff.Arguments.size() == 0 ||
151 lff.Arguments == this->Args)
160 void cmIfFunctionBlocker::
161 ScopeEnded(cmMakefile &mf)
163 std::string errmsg = "The end of a CMakeLists file was reached with an "
164 "IF statement that was not closed properly.\nWithin the directory: ";
165 errmsg += mf.GetCurrentDirectory();
166 errmsg += "\nThe arguments are: ";
167 for(std::vector<cmListFileArgument>::const_iterator j = this->Args.begin();
168 j != this->Args.end(); ++j)
170 errmsg += (j->Quoted?"\"":"");
172 errmsg += (j->Quoted?"\"":"");
175 cmSystemTools::Message(errmsg.c_str(), "Warning");
179 ::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
182 char* errorString = 0;
184 std::vector<std::string> expandedArguments;
185 this->Makefile->ExpandArguments(args, expandedArguments);
187 cmIfCommand::IsTrue(expandedArguments,&errorString,this->Makefile);
191 std::string err = "had incorrect arguments: ";
193 for(i =0; i < args.size(); ++i)
195 err += (args[i].Quoted?"\"":"");
196 err += args[i].Value;
197 err += (args[i].Quoted?"\"":"");
203 this->SetError(err.c_str());
204 delete [] errorString;
208 cmIfFunctionBlocker *f = new cmIfFunctionBlocker();
209 // if is isn't true block the commands
211 f->IsBlocking = !isTrue;
217 this->Makefile->AddFunctionBlocker(f);
224 void IncrementArguments(std::list<std::string> &newArgs,
225 std::list<std::string>::iterator &argP1,
226 std::list<std::string>::iterator &argP2)
228 if (argP1 != newArgs.end())
232 if (argP1 != newArgs.end())
241 // order of operations,
242 // IS_DIRECTORY EXISTS COMMAND DEFINED
243 // MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL
246 // There is an issue on whether the arguments should be values of references,
247 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
248 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
249 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
250 // take numeric values or variable names. STRLESS and STRGREATER take
251 // variable names but if the variable name is not found it will use the name
252 // directly. AND OR take variables or the values 0 or 1.
255 bool cmIfCommand::IsTrue(const std::vector<std::string> &args,
256 char **errorString, cmMakefile *makefile)
258 // check for the different signatures
261 const char* msg = "Unknown arguments specified";
262 *errorString = new char[strlen(msg) + 1];
263 strcpy(*errorString, msg);
265 // handle empty invocation
268 delete [] *errorString;
273 // store the reduced args in this vector
274 std::list<std::string> newArgs;
278 // copy to the list structure
279 for(i = 0; i < args.size(); ++i)
281 newArgs.push_back(args[i]);
283 std::list<std::string>::iterator argP1;
284 std::list<std::string>::iterator argP2;
286 // now loop through the arguments and see if we can reduce any of them
287 // we do this multiple times. Once for each level of precedence
291 std::list<std::string>::iterator arg = newArgs.begin();
292 while (arg != newArgs.end())
295 IncrementArguments(newArgs,argP1,argP2);
297 if (*arg == "EXISTS" && argP1 != newArgs.end())
299 if(cmSystemTools::FileExists((argP1)->c_str()))
307 newArgs.erase(argP1);
309 IncrementArguments(newArgs,argP1,argP2);
312 // does a directory with this name exist
313 if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end())
315 if(cmSystemTools::FileIsDirectory((argP1)->c_str()))
323 newArgs.erase(argP1);
325 IncrementArguments(newArgs,argP1,argP2);
328 // is the given path an absolute path ?
329 if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end())
331 if(cmSystemTools::FileIsFullPath((argP1)->c_str()))
339 newArgs.erase(argP1);
341 IncrementArguments(newArgs,argP1,argP2);
344 // does a command exist
345 if (*arg == "COMMAND" && argP1 != newArgs.end())
347 if(makefile->CommandExists((argP1)->c_str()))
355 newArgs.erase(argP1);
357 IncrementArguments(newArgs,argP1,argP2);
360 // is a variable defined
361 if (*arg == "DEFINED" && argP1 != newArgs.end())
363 size_t argP1len = argP1->size();
365 if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
366 argP1->operator[](argP1len-1) == '}')
368 std::string env = argP1->substr(4, argP1len-5);
369 bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
373 bdef = makefile->IsDefinitionSet((argP1)->c_str());
383 newArgs.erase(argP1);
385 IncrementArguments(newArgs,argP1,argP2);
394 // now loop through the arguments and see if we can reduce any of them
395 // we do this multiple times. Once for each level of precedence
399 std::list<std::string>::iterator arg = newArgs.begin();
401 while (arg != newArgs.end())
404 IncrementArguments(newArgs,argP1,argP2);
405 if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
406 *(argP1) == "MATCHES")
408 def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
409 const char* rex = (argP2)->c_str();
410 cmStringCommand::ClearMatches(makefile);
411 cmsys::RegularExpression regEntry;
412 if ( !regEntry.compile(rex) )
414 cmOStringStream error;
415 error << "Regular expression \"" << rex << "\" cannot compile";
416 delete [] *errorString;
417 *errorString = new char[error.str().size() + 1];
418 strcpy(*errorString, error.str().c_str());
421 if (regEntry.find(def))
423 cmStringCommand::StoreMatches(makefile, regEntry);
430 newArgs.erase(argP2);
431 newArgs.erase(argP1);
433 IncrementArguments(newArgs,argP1,argP2);
437 if (argP1 != newArgs.end() && *arg == "MATCHES")
440 newArgs.erase(argP1);
442 IncrementArguments(newArgs,argP1,argP2);
446 if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
447 (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
448 *(argP1) == "EQUAL"))
450 def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
451 def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
454 if(sscanf(def, "%lg", &lhs) != 1 ||
455 sscanf(def2, "%lg", &rhs) != 1)
459 else if (*(argP1) == "LESS")
470 else if (*(argP1) == "GREATER")
492 newArgs.erase(argP2);
493 newArgs.erase(argP1);
495 IncrementArguments(newArgs,argP1,argP2);
499 if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
500 (*(argP1) == "STRLESS" ||
501 *(argP1) == "STREQUAL" ||
502 *(argP1) == "STRGREATER"))
504 def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile);
505 def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile);
506 int val = strcmp(def,def2);
508 if (*(argP1) == "STRLESS")
512 else if (*(argP1) == "STRGREATER")
528 newArgs.erase(argP2);
529 newArgs.erase(argP1);
531 IncrementArguments(newArgs,argP1,argP2);
535 // is file A newer than file B
536 if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
537 *(argP1) == "IS_NEWER_THAN")
540 bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
543 if(success==false || fileIsNewer==1 || fileIsNewer==0)
551 newArgs.erase(argP2);
552 newArgs.erase(argP1);
554 IncrementArguments(newArgs,argP1,argP2);
564 // now loop through the arguments and see if we can reduce any of them
565 // we do this multiple times. Once for each level of precedence
569 std::list<std::string>::iterator arg = newArgs.begin();
570 while (arg != newArgs.end())
573 IncrementArguments(newArgs,argP1,argP2);
574 if (argP1 != newArgs.end() && *arg == "NOT")
576 def = cmIfCommand::GetVariableOrNumber((argP1)->c_str(), makefile);
577 if(!cmSystemTools::IsOff(def))
585 newArgs.erase(argP1);
587 IncrementArguments(newArgs,argP1,argP2);
595 // now loop through the arguments and see if we can reduce any of them
596 // we do this multiple times. Once for each level of precedence
600 std::list<std::string>::iterator arg = newArgs.begin();
601 while (arg != newArgs.end())
604 IncrementArguments(newArgs,argP1,argP2);
605 if (argP1 != newArgs.end() && *(argP1) == "AND" &&
606 argP2 != newArgs.end())
608 def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile);
609 def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile);
610 if(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2))
618 newArgs.erase(argP2);
619 newArgs.erase(argP1);
621 IncrementArguments(newArgs,argP1,argP2);
625 if (argP1 != newArgs.end() && *(argP1) == "OR" &&
626 argP2 != newArgs.end())
628 def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile);
629 def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile);
630 if(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2))
638 newArgs.erase(argP2);
639 newArgs.erase(argP1);
641 IncrementArguments(newArgs,argP1,argP2);
650 // now at the end there should only be one argument left
651 if (newArgs.size() == 1)
653 delete [] *errorString;
655 if (*newArgs.begin() == "0")
659 if (*newArgs.begin() == "1")
663 def = makefile->GetDefinition(args[0].c_str());
664 if(cmSystemTools::IsOff(def))
673 const char* cmIfCommand::GetVariableOrString(const char* str,
674 const cmMakefile* mf)
676 const char* def = mf->GetDefinition(str);
684 const char* cmIfCommand::GetVariableOrNumber(const char* str,
685 const cmMakefile* mf)
687 const char* def = mf->GetDefinition(str);