| /*========================================================================= |
| |
| Program: CMake - Cross-Platform Makefile Generator |
| Module: $RCSfile$ |
| Language: C++ |
| Date: $Date$ |
| Version: $Revision$ |
| |
| Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. |
| See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even |
| the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| PURPOSE. See the above copyright notices for more information. |
| |
| =========================================================================*/ |
| #include "cmIfCommand.h" |
| #include <stdlib.h> // required for atof |
| #include <list> |
| #include <cmsys/RegularExpression.hxx> |
| |
| bool cmIfFunctionBlocker:: |
| IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) |
| { |
| // always let if statements through |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if")) |
| { |
| return false; |
| } |
| |
| // watch for our ELSE or ENDIF |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else") || |
| !cmSystemTools::Strucmp(lff.Name.c_str(),"elseif") || |
| !cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) |
| { |
| // if it was an else statement then we should change state |
| // and block this Else Command |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"else")) |
| { |
| this->IsBlocking = this->HasRun; |
| return true; |
| } |
| // if it was an elseif statement then we should check state |
| // and possibly block this Else Command |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"elseif")) |
| { |
| if (!this->HasRun) |
| { |
| char* errorString = 0; |
| |
| std::vector<std::string> expandedArguments; |
| mf.ExpandArguments(lff.Arguments, expandedArguments); |
| bool isTrue = |
| cmIfCommand::IsTrue(expandedArguments,&errorString,&mf); |
| |
| if (errorString) |
| { |
| std::string err = "had incorrect arguments: "; |
| unsigned int i; |
| for(i =0; i < lff.Arguments.size(); ++i) |
| { |
| err += (lff.Arguments[i].Quoted?"\"":""); |
| err += lff.Arguments[i].Value; |
| err += (lff.Arguments[i].Quoted?"\"":""); |
| err += " "; |
| } |
| err += "("; |
| err += errorString; |
| err += ")."; |
| cmSystemTools::Error(err.c_str()); |
| delete [] errorString; |
| return false; |
| } |
| |
| if (isTrue) |
| { |
| this->IsBlocking = false; |
| this->HasRun = true; |
| return true; |
| } |
| } |
| this->IsBlocking = true; |
| return true; |
| } |
| // otherwise it must be an ENDIF statement, in that case remove the |
| // function blocker |
| mf.RemoveFunctionBlocker(lff); |
| return true; |
| } |
| |
| return this->IsBlocking; |
| } |
| |
| bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, |
| cmMakefile& mf) |
| { |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) |
| { |
| if (mf.IsOn("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS") |
| || lff.Arguments == this->Args) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void cmIfFunctionBlocker:: |
| ScopeEnded(cmMakefile &mf) |
| { |
| std::string errmsg = "The end of a CMakeLists file was reached with an " |
| "IF statement that was not closed properly.\nWithin the directory: "; |
| errmsg += mf.GetCurrentDirectory(); |
| errmsg += "\nThe arguments are: "; |
| for(std::vector<cmListFileArgument>::const_iterator j = this->Args.begin(); |
| j != this->Args.end(); ++j) |
| { |
| errmsg += (j->Quoted?"\"":""); |
| errmsg += j->Value; |
| errmsg += (j->Quoted?"\"":""); |
| errmsg += " "; |
| } |
| cmSystemTools::Message(errmsg.c_str(), "Warning"); |
| } |
| |
| bool cmIfCommand |
| ::InvokeInitialPass(const std::vector<cmListFileArgument>& args) |
| { |
| char* errorString = 0; |
| |
| std::vector<std::string> expandedArguments; |
| this->Makefile->ExpandArguments(args, expandedArguments); |
| bool isTrue = |
| cmIfCommand::IsTrue(expandedArguments,&errorString,this->Makefile); |
| |
| if (errorString) |
| { |
| std::string err = "had incorrect arguments: "; |
| unsigned int i; |
| for(i =0; i < args.size(); ++i) |
| { |
| err += (args[i].Quoted?"\"":""); |
| err += args[i].Value; |
| err += (args[i].Quoted?"\"":""); |
| err += " "; |
| } |
| err += "("; |
| err += errorString; |
| err += ")."; |
| this->SetError(err.c_str()); |
| delete [] errorString; |
| return false; |
| } |
| |
| cmIfFunctionBlocker *f = new cmIfFunctionBlocker(); |
| // if is isn't true block the commands |
| f->IsBlocking = !isTrue; |
| if (isTrue) |
| { |
| f->HasRun = true; |
| } |
| f->Args = args; |
| this->Makefile->AddFunctionBlocker(f); |
| |
| return true; |
| } |
| |
| namespace |
| { |
| void IncrementArguments(std::list<std::string> &newArgs, |
| std::list<std::string>::iterator &argP1, |
| std::list<std::string>::iterator &argP2) |
| { |
| if (argP1 != newArgs.end()) |
| { |
| argP1++; |
| argP2 = argP1; |
| if (argP1 != newArgs.end()) |
| { |
| argP2++; |
| } |
| } |
| } |
| } |
| |
| |
| // order of operations, |
| // IS_DIRECTORY EXISTS COMMAND DEFINED |
| // MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL |
| // AND OR |
| // |
| // There is an issue on whether the arguments should be values of references, |
| // for example IF (FOO AND BAR) should that compare the strings FOO and BAR |
| // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY |
| // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can |
| // take numeric values or variable names. STRLESS and STRGREATER take |
| // variable names but if the variable name is not found it will use the name |
| // directly. AND OR take variables or the values 0 or 1. |
| |
| |
| bool cmIfCommand::IsTrue(const std::vector<std::string> &args, |
| char **errorString, const cmMakefile *makefile) |
| { |
| // check for the different signatures |
| const char *def; |
| const char *def2; |
| const char* msg = "Unknown arguments specified"; |
| *errorString = new char[strlen(msg) + 1]; |
| strcpy(*errorString, msg); |
| |
| // handle empty invocation |
| if (args.size() < 1) |
| { |
| delete [] *errorString; |
| *errorString = 0; |
| return false; |
| } |
| |
| // this is a super ugly hack. Basically old versiosn of VTK and ITK have a |
| // bad test to check for more recent versions of CMake in the |
| // CMakeLists.txt file for libtiff. So when we reved CMake up to 2.0 the |
| // test started failing because the minor version went to zero this causes |
| // the test to pass |
| if (args.size() == 3 && |
| (makefile->GetDefinition("VTKTIFF_SOURCE_DIR") || |
| makefile->GetDefinition("ITKTIFF_SOURCE_DIR")) && |
| args[0] == "CMAKE_MINOR_VERSION" && |
| args[1] == "MATCHES") |
| { |
| delete [] *errorString; |
| *errorString = 0; |
| return true; |
| } |
| |
| |
| // store the reduced args in this vector |
| std::list<std::string> newArgs; |
| int reducible; |
| unsigned int i; |
| |
| // copy to the list structure |
| for(i = 0; i < args.size(); ++i) |
| { |
| newArgs.push_back(args[i]); |
| } |
| std::list<std::string>::iterator argP1; |
| std::list<std::string>::iterator argP2; |
| |
| // now loop through the arguments and see if we can reduce any of them |
| // we do this multiple times. Once for each level of precedence |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| // does a file exist |
| if (*arg == "EXISTS" && argP1 != newArgs.end()) |
| { |
| if(cmSystemTools::FileExists((argP1)->c_str())) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| // does a directory with this name exist |
| if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end()) |
| { |
| if(cmSystemTools::FileIsDirectory((argP1)->c_str())) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| // does a command exist |
| if (*arg == "COMMAND" && argP1 != newArgs.end()) |
| { |
| if(makefile->CommandExists((argP1)->c_str())) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| // is a variable defined |
| if (*arg == "DEFINED" && argP1 != newArgs.end()) |
| { |
| size_t argP1len = argP1->size(); |
| if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" && |
| argP1->operator[](argP1len-1) == '}') |
| { |
| std::string env = argP1->substr(4, argP1len-5); |
| def = cmSystemTools::GetEnv(env.c_str()); |
| } |
| else |
| { |
| def = makefile->GetDefinition((argP1)->c_str()); |
| } |
| if(def) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| |
| |
| // now loop through the arguments and see if we can reduce any of them |
| // we do this multiple times. Once for each level of precedence |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| if (argP1 != newArgs.end() && argP2 != newArgs.end() && |
| *(argP1) == "MATCHES") |
| { |
| def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); |
| const char* rex = (argP2)->c_str(); |
| cmsys::RegularExpression regEntry; |
| if ( !regEntry.compile(rex) ) |
| { |
| cmOStringStream error; |
| error << "Regular expression \"" << rex << "\" cannot compile"; |
| delete [] *errorString; |
| *errorString = new char[error.str().size() + 1]; |
| strcpy(*errorString, error.str().c_str()); |
| return false; |
| } |
| if (regEntry.find(def)) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| if (argP1 != newArgs.end() && *arg == "MATCHES") |
| { |
| *arg = "0"; |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| if (argP1 != newArgs.end() && argP2 != newArgs.end() && |
| (*(argP1) == "LESS" || *(argP1) == "GREATER" || |
| *(argP1) == "EQUAL")) |
| { |
| def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); |
| def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); |
| double lhs; |
| double rhs; |
| if(sscanf(def, "%lg", &lhs) != 1 || |
| sscanf(def2, "%lg", &rhs) != 1) |
| { |
| *arg = "0"; |
| } |
| else if (*(argP1) == "LESS") |
| { |
| if(lhs < rhs) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| } |
| else if (*(argP1) == "GREATER") |
| { |
| if(lhs > rhs) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| } |
| else |
| { |
| if(lhs == rhs) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| if (argP1 != newArgs.end() && argP2 != newArgs.end() && |
| (*(argP1) == "STRLESS" || |
| *(argP1) == "STREQUAL" || |
| *(argP1) == "STRGREATER")) |
| { |
| def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); |
| def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); |
| int val = strcmp(def,def2); |
| int result; |
| if (*(argP1) == "STRLESS") |
| { |
| result = (val < 0); |
| } |
| else if (*(argP1) == "STRGREATER") |
| { |
| result = (val > 0); |
| } |
| else // strequal |
| { |
| result = (val == 0); |
| } |
| if(result) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| // is file A newer than file B |
| if (argP1 != newArgs.end() && argP2 != newArgs.end() && |
| *(argP1) == "IS_NEWER_THAN") |
| { |
| int fileIsNewer=0; |
| bool success=cmSystemTools::FileTimeCompare(arg->c_str(), |
| (argP2)->c_str(), |
| &fileIsNewer); |
| if(success==false || fileIsNewer==1 || fileIsNewer==0) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| ++arg; |
| } |
| } |
| while (reducible); |
| |
| |
| // now loop through the arguments and see if we can reduce any of them |
| // we do this multiple times. Once for each level of precedence |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| if (argP1 != newArgs.end() && *arg == "NOT") |
| { |
| def = cmIfCommand::GetVariableOrNumber((argP1)->c_str(), makefile); |
| if(!cmSystemTools::IsOff(def)) |
| { |
| *arg = "0"; |
| } |
| else |
| { |
| *arg = "1"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| |
| // now loop through the arguments and see if we can reduce any of them |
| // we do this multiple times. Once for each level of precedence |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| if (argP1 != newArgs.end() && *(argP1) == "AND" && |
| argP2 != newArgs.end()) |
| { |
| def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile); |
| def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile); |
| if(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2)) |
| { |
| *arg = "0"; |
| } |
| else |
| { |
| *arg = "1"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| if (argP1 != newArgs.end() && *(argP1) == "OR" && |
| argP2 != newArgs.end()) |
| { |
| def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile); |
| def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile); |
| if(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)) |
| { |
| *arg = "0"; |
| } |
| else |
| { |
| *arg = "1"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| ++arg; |
| } |
| } |
| while (reducible); |
| |
| // now at the end there should only be one argument left |
| if (newArgs.size() == 1) |
| { |
| delete [] *errorString; |
| *errorString = 0; |
| if (*newArgs.begin() == "0") |
| { |
| return false; |
| } |
| if (*newArgs.begin() == "1") |
| { |
| return true; |
| } |
| def = makefile->GetDefinition(args[0].c_str()); |
| if(cmSystemTools::IsOff(def)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| const char* cmIfCommand::GetVariableOrString(const char* str, |
| const cmMakefile* mf) |
| { |
| const char* def = mf->GetDefinition(str); |
| if(!def) |
| { |
| def = str; |
| } |
| return def; |
| } |
| |
| const char* cmIfCommand::GetVariableOrNumber(const char* str, |
| const cmMakefile* mf) |
| { |
| const char* def = mf->GetDefinition(str); |
| if(!def) |
| { |
| if (atoi(str)) |
| { |
| def = str; |
| } |
| } |
| return def; |
| } |