| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "cmIfCommand.h" |
| #include "cmStringCommand.h" |
| |
| #include <stdlib.h> // required for atof |
| #include <list> |
| #include <cmsys/RegularExpression.hxx> |
| |
| |
| static std::string cmIfCommandError( |
| cmMakefile* mf, std::vector<std::string> const& args) |
| { |
| cmLocalGenerator* lg = mf->GetLocalGenerator(); |
| std::string err = "given arguments:\n "; |
| for(std::vector<std::string>::const_iterator i = args.begin(); |
| i != args.end(); ++i) |
| { |
| err += " "; |
| err += lg->EscapeForCMake(i->c_str()); |
| } |
| err += "\n"; |
| return err; |
| } |
| |
| //========================================================================= |
| bool cmIfFunctionBlocker:: |
| IsFunctionBlocked(const cmListFileFunction& lff, |
| cmMakefile &mf, |
| cmExecutionStatus &inStatus) |
| { |
| // we start by recording all the functions |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"if")) |
| { |
| this->ScopeDepth++; |
| } |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) |
| { |
| this->ScopeDepth--; |
| // if this is the endif for this if statement, then start executing |
| if (!this->ScopeDepth) |
| { |
| // Remove the function blocker for this scope or bail. |
| cmsys::auto_ptr<cmFunctionBlocker> |
| fb(mf.RemoveFunctionBlocker(this, lff)); |
| if(!fb.get()) { return false; } |
| |
| // execute the functions for the true parts of the if statement |
| cmExecutionStatus status; |
| int scopeDepth = 0; |
| for(unsigned int c = 0; c < this->Functions.size(); ++c) |
| { |
| // keep track of scope depth |
| if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"if")) |
| { |
| scopeDepth++; |
| } |
| if (!cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"endif")) |
| { |
| scopeDepth--; |
| } |
| // watch for our state change |
| if (scopeDepth == 0 && |
| !cmSystemTools::Strucmp(this->Functions[c].Name.c_str(),"else")) |
| { |
| this->IsBlocking = this->HasRun; |
| this->HasRun = true; |
| } |
| else if (scopeDepth == 0 && !cmSystemTools::Strucmp |
| (this->Functions[c].Name.c_str(),"elseif")) |
| { |
| if (this->HasRun) |
| { |
| this->IsBlocking = true; |
| } |
| else |
| { |
| // Place this call on the call stack. |
| cmMakefileCall stack_manager(&mf, this->Functions[c], status); |
| static_cast<void>(stack_manager); |
| |
| std::string errorString; |
| |
| std::vector<std::string> expandedArguments; |
| mf.ExpandArguments(this->Functions[c].Arguments, |
| expandedArguments); |
| |
| cmake::MessageType messType; |
| bool isTrue = |
| cmIfCommand::IsTrue(expandedArguments, errorString, |
| &mf, messType); |
| |
| if (errorString.size()) |
| { |
| std::string err = cmIfCommandError(&mf, expandedArguments); |
| err += errorString; |
| mf.IssueMessage(messType, err); |
| if (messType == cmake::FATAL_ERROR) |
| { |
| cmSystemTools::SetFatalErrorOccured(); |
| return true; |
| } |
| } |
| |
| if (isTrue) |
| { |
| this->IsBlocking = false; |
| this->HasRun = true; |
| } |
| } |
| } |
| |
| // should we execute? |
| else if (!this->IsBlocking) |
| { |
| status.Clear(); |
| mf.ExecuteCommand(this->Functions[c],status); |
| if (status.GetReturnInvoked()) |
| { |
| inStatus.SetReturnInvoked(true); |
| return true; |
| } |
| if (status.GetBreakInvoked()) |
| { |
| inStatus.SetBreakInvoked(true); |
| return true; |
| } |
| } |
| } |
| return true; |
| } |
| } |
| |
| // record the command |
| this->Functions.push_back(lff); |
| |
| // always return true |
| return true; |
| } |
| |
| //========================================================================= |
| bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, |
| cmMakefile&) |
| { |
| if (!cmSystemTools::Strucmp(lff.Name.c_str(),"endif")) |
| { |
| // if the endif has arguments, then make sure |
| // they match the arguments of the matching if |
| if (lff.Arguments.size() == 0 || |
| lff.Arguments == this->Args) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| //========================================================================= |
| bool cmIfCommand |
| ::InvokeInitialPass(const std::vector<cmListFileArgument>& args, |
| cmExecutionStatus &) |
| { |
| std::string errorString; |
| |
| std::vector<std::string> expandedArguments; |
| this->Makefile->ExpandArguments(args, expandedArguments); |
| |
| cmake::MessageType status; |
| bool isTrue = |
| cmIfCommand::IsTrue(expandedArguments,errorString, |
| this->Makefile, status); |
| |
| if (errorString.size()) |
| { |
| std::string err = cmIfCommandError(this->Makefile, expandedArguments); |
| err += errorString; |
| if (status == cmake::FATAL_ERROR) |
| { |
| this->SetError(err.c_str()); |
| cmSystemTools::SetFatalErrorOccured(); |
| return false; |
| } |
| else |
| { |
| this->Makefile->IssueMessage(status, err); |
| } |
| } |
| |
| cmIfFunctionBlocker *f = new cmIfFunctionBlocker(); |
| // if is isn't true block the commands |
| f->ScopeDepth = 1; |
| f->IsBlocking = !isTrue; |
| if (isTrue) |
| { |
| f->HasRun = true; |
| } |
| f->Args = args; |
| this->Makefile->AddFunctionBlocker(f); |
| |
| return true; |
| } |
| |
| namespace |
| { |
| //========================================================================= |
| bool GetBooleanValue(std::string& arg, cmMakefile* mf) |
| { |
| // Check basic constants. |
| if (arg == "0") |
| { |
| return false; |
| } |
| if (arg == "1") |
| { |
| return true; |
| } |
| |
| // Check named constants. |
| if (cmSystemTools::IsOn(arg.c_str())) |
| { |
| return true; |
| } |
| if (cmSystemTools::IsOff(arg.c_str())) |
| { |
| return false; |
| } |
| |
| // Check for numbers. |
| if(!arg.empty()) |
| { |
| char* end; |
| double d = strtod(arg.c_str(), &end); |
| if(*end == '\0') |
| { |
| // The whole string is a number. Use C conversion to bool. |
| return d? true:false; |
| } |
| } |
| |
| // Check definition. |
| const char* def = mf->GetDefinition(arg.c_str()); |
| return !cmSystemTools::IsOff(def); |
| } |
| |
| //========================================================================= |
| // Boolean value behavior from CMake 2.6.4 and below. |
| bool GetBooleanValueOld(std::string const& arg, cmMakefile* mf, bool one) |
| { |
| if(one) |
| { |
| // Old IsTrue behavior for single argument. |
| if(arg == "0") |
| { return false; } |
| else if(arg == "1") |
| { return true; } |
| else |
| { return !cmSystemTools::IsOff(mf->GetDefinition(arg.c_str())); } |
| } |
| else |
| { |
| // Old GetVariableOrNumber behavior. |
| const char* def = mf->GetDefinition(arg.c_str()); |
| if(!def && atoi(arg.c_str())) |
| { |
| def = arg.c_str(); |
| } |
| return !cmSystemTools::IsOff(def); |
| } |
| } |
| |
| //========================================================================= |
| // returns the resulting boolean value |
| bool GetBooleanValueWithAutoDereference( |
| std::string &newArg, |
| cmMakefile *makefile, |
| std::string &errorString, |
| cmPolicies::PolicyStatus Policy12Status, |
| cmake::MessageType &status, |
| bool oneArg = false) |
| { |
| // Use the policy if it is set. |
| if (Policy12Status == cmPolicies::NEW) |
| { |
| return GetBooleanValue(newArg, makefile); |
| } |
| else if (Policy12Status == cmPolicies::OLD) |
| { |
| return GetBooleanValueOld(newArg, makefile, oneArg); |
| } |
| |
| // Check policy only if old and new results differ. |
| bool newResult = GetBooleanValue(newArg, makefile); |
| bool oldResult = GetBooleanValueOld(newArg, makefile, oneArg); |
| if(newResult != oldResult) |
| { |
| switch(Policy12Status) |
| { |
| case cmPolicies::WARN: |
| { |
| cmPolicies* policies = makefile->GetPolicies(); |
| errorString = "An argument named \"" + newArg |
| + "\" appears in a conditional statement. " |
| + policies->GetPolicyWarning(cmPolicies::CMP0012); |
| status = cmake::AUTHOR_WARNING; |
| } |
| case cmPolicies::OLD: |
| return oldResult; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| { |
| cmPolicies* policies = makefile->GetPolicies(); |
| errorString = "An argument named \"" + newArg |
| + "\" appears in a conditional statement. " |
| + policies->GetRequiredPolicyError(cmPolicies::CMP0012); |
| status = cmake::FATAL_ERROR; |
| } |
| case cmPolicies::NEW: |
| break; |
| } |
| } |
| return newResult; |
| } |
| |
| //========================================================================= |
| 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++; |
| } |
| } |
| } |
| |
| //========================================================================= |
| // helper function to reduce code duplication |
| void HandlePredicate(bool value, int &reducible, |
| std::list<std::string>::iterator &arg, |
| std::list<std::string> &newArgs, |
| std::list<std::string>::iterator &argP1, |
| std::list<std::string>::iterator &argP2) |
| { |
| if(value) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| //========================================================================= |
| // helper function to reduce code duplication |
| void HandleBinaryOp(bool value, int &reducible, |
| std::list<std::string>::iterator &arg, |
| std::list<std::string> &newArgs, |
| std::list<std::string>::iterator &argP1, |
| std::list<std::string>::iterator &argP2) |
| { |
| if(value) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| newArgs.erase(argP2); |
| newArgs.erase(argP1); |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| reducible = 1; |
| } |
| |
| //========================================================================= |
| enum Op { OpLess, OpEqual, OpGreater }; |
| bool HandleVersionCompare(Op op, const char* lhs_str, const char* rhs_str) |
| { |
| // Parse out up to 4 components. |
| unsigned int lhs[4] = {0,0,0,0}; |
| unsigned int rhs[4] = {0,0,0,0}; |
| sscanf(lhs_str, "%u.%u.%u.%u", &lhs[0], &lhs[1], &lhs[2], &lhs[3]); |
| sscanf(rhs_str, "%u.%u.%u.%u", &rhs[0], &rhs[1], &rhs[2], &rhs[3]); |
| |
| // Do component-wise comparison. |
| for(unsigned int i=0; i < 4; ++i) |
| { |
| if(lhs[i] < rhs[i]) |
| { |
| // lhs < rhs, so true if operation is LESS |
| return op == OpLess; |
| } |
| else if(lhs[i] > rhs[i]) |
| { |
| // lhs > rhs, so true if operation is GREATER |
| return op == OpGreater; |
| } |
| } |
| // lhs == rhs, so true if operation is EQUAL |
| return op == OpEqual; |
| } |
| |
| //========================================================================= |
| // level 0 processes parenthetical expressions |
| bool HandleLevel0(std::list<std::string> &newArgs, |
| cmMakefile *makefile, |
| std::string &errorString, |
| cmake::MessageType &status) |
| { |
| int reducible; |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| while (arg != newArgs.end()) |
| { |
| if (*arg == "(") |
| { |
| // search for the closing paren for this opening one |
| std::list<std::string>::iterator argClose; |
| argClose = arg; |
| argClose++; |
| unsigned int depth = 1; |
| while (argClose != newArgs.end() && depth) |
| { |
| if (*argClose == "(") |
| { |
| depth++; |
| } |
| if (*argClose == ")") |
| { |
| depth--; |
| } |
| argClose++; |
| } |
| if (depth) |
| { |
| errorString = "mismatched parenthesis in condition"; |
| status = cmake::FATAL_ERROR; |
| return false; |
| } |
| // store the reduced args in this vector |
| std::vector<std::string> newArgs2; |
| |
| // copy to the list structure |
| std::list<std::string>::iterator argP1 = arg; |
| argP1++; |
| for(; argP1 != argClose; argP1++) |
| { |
| newArgs2.push_back(*argP1); |
| } |
| newArgs2.pop_back(); |
| // now recursively invoke IsTrue to handle the values inside the |
| // parenthetical expression |
| bool value = |
| cmIfCommand::IsTrue(newArgs2, errorString, makefile, status); |
| if(value) |
| { |
| *arg = "1"; |
| } |
| else |
| { |
| *arg = "0"; |
| } |
| argP1 = arg; |
| argP1++; |
| // remove the now evaluated parenthetical expression |
| newArgs.erase(argP1,argClose); |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| return true; |
| } |
| |
| //========================================================================= |
| // level one handles most predicates except for NOT |
| bool HandleLevel1(std::list<std::string> &newArgs, |
| cmMakefile *makefile, |
| std::string &, cmake::MessageType &) |
| { |
| int reducible; |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| std::list<std::string>::iterator argP1; |
| std::list<std::string>::iterator argP2; |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| // does a file exist |
| if (*arg == "EXISTS" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| cmSystemTools::FileExists((argP1)->c_str()), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // does a directory with this name exist |
| if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| cmSystemTools::FileIsDirectory((argP1)->c_str()), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // does a symlink with this name exist |
| if (*arg == "IS_SYMLINK" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| cmSystemTools::FileIsSymlink((argP1)->c_str()), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // is the given path an absolute path ? |
| if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| cmSystemTools::FileIsFullPath((argP1)->c_str()), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // does a command exist |
| if (*arg == "COMMAND" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| makefile->CommandExists((argP1)->c_str()), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // does a policy exist |
| if (*arg == "POLICY" && argP1 != newArgs.end()) |
| { |
| cmPolicies::PolicyID pid; |
| HandlePredicate( |
| makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // does a target exist |
| if (*arg == "TARGET" && argP1 != newArgs.end()) |
| { |
| HandlePredicate( |
| makefile->FindTargetToUse((argP1)->c_str())? true:false, |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| // is a variable defined |
| if (*arg == "DEFINED" && argP1 != newArgs.end()) |
| { |
| size_t argP1len = argP1->size(); |
| bool bdef = false; |
| if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" && |
| argP1->operator[](argP1len-1) == '}') |
| { |
| std::string env = argP1->substr(4, argP1len-5); |
| bdef = cmSystemTools::GetEnv(env.c_str())?true:false; |
| } |
| else |
| { |
| bdef = makefile->IsDefinitionSet((argP1)->c_str()); |
| } |
| HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2); |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| return true; |
| } |
| |
| //========================================================================= |
| // level two handles most binary operations except for AND OR |
| bool HandleLevel2(std::list<std::string> &newArgs, |
| cmMakefile *makefile, |
| std::string &errorString, |
| cmake::MessageType &status) |
| { |
| int reducible; |
| const char *def; |
| const char *def2; |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| std::list<std::string>::iterator argP1; |
| std::list<std::string>::iterator argP2; |
| 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(); |
| cmStringCommand::ClearMatches(makefile); |
| cmsys::RegularExpression regEntry; |
| if ( !regEntry.compile(rex) ) |
| { |
| cmOStringStream error; |
| error << "Regular expression \"" << rex << "\" cannot compile"; |
| errorString = error.str(); |
| status = cmake::FATAL_ERROR; |
| return false; |
| } |
| if (regEntry.find(def)) |
| { |
| cmStringCommand::StoreMatches(makefile, regEntry); |
| *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; |
| bool result; |
| if(sscanf(def, "%lg", &lhs) != 1 || |
| sscanf(def2, "%lg", &rhs) != 1) |
| { |
| result = false; |
| } |
| else if (*(argP1) == "LESS") |
| { |
| result = (lhs < rhs); |
| } |
| else if (*(argP1) == "GREATER") |
| { |
| result = (lhs > rhs); |
| } |
| else |
| { |
| result = (lhs == rhs); |
| } |
| HandleBinaryOp(result, |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| |
| 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); |
| bool result; |
| if (*(argP1) == "STRLESS") |
| { |
| result = (val < 0); |
| } |
| else if (*(argP1) == "STRGREATER") |
| { |
| result = (val > 0); |
| } |
| else // strequal |
| { |
| result = (val == 0); |
| } |
| HandleBinaryOp(result, |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| |
| if (argP1 != newArgs.end() && argP2 != newArgs.end() && |
| (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" || |
| *(argP1) == "VERSION_EQUAL")) |
| { |
| def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); |
| def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); |
| Op op = OpEqual; |
| if(*argP1 == "VERSION_LESS") |
| { |
| op = OpLess; |
| } |
| else if(*argP1 == "VERSION_GREATER") |
| { |
| op = OpGreater; |
| } |
| bool result = HandleVersionCompare(op, def, def2); |
| HandleBinaryOp(result, |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| |
| // 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); |
| HandleBinaryOp( |
| (success==false || fileIsNewer==1 || fileIsNewer==0), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| |
| ++arg; |
| } |
| } |
| while (reducible); |
| return true; |
| } |
| |
| //========================================================================= |
| // level 3 handles NOT |
| bool HandleLevel3(std::list<std::string> &newArgs, |
| cmMakefile *makefile, |
| std::string &errorString, |
| cmPolicies::PolicyStatus Policy12Status, |
| cmake::MessageType &status) |
| { |
| int reducible; |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| std::list<std::string>::iterator argP1; |
| std::list<std::string>::iterator argP2; |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| if (argP1 != newArgs.end() && *arg == "NOT") |
| { |
| bool rhs = GetBooleanValueWithAutoDereference(*argP1, makefile, |
| errorString, |
| Policy12Status, |
| status); |
| HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2); |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| return true; |
| } |
| |
| //========================================================================= |
| // level 4 handles AND OR |
| bool HandleLevel4(std::list<std::string> &newArgs, |
| cmMakefile *makefile, |
| std::string &errorString, |
| cmPolicies::PolicyStatus Policy12Status, |
| cmake::MessageType &status) |
| { |
| int reducible; |
| bool lhs; |
| bool rhs; |
| do |
| { |
| reducible = 0; |
| std::list<std::string>::iterator arg = newArgs.begin(); |
| std::list<std::string>::iterator argP1; |
| std::list<std::string>::iterator argP2; |
| while (arg != newArgs.end()) |
| { |
| argP1 = arg; |
| IncrementArguments(newArgs,argP1,argP2); |
| if (argP1 != newArgs.end() && *(argP1) == "AND" && |
| argP2 != newArgs.end()) |
| { |
| lhs = GetBooleanValueWithAutoDereference(*arg, makefile, |
| errorString, |
| Policy12Status, |
| status); |
| rhs = GetBooleanValueWithAutoDereference(*argP2, makefile, |
| errorString, |
| Policy12Status, |
| status); |
| HandleBinaryOp((lhs && rhs), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| |
| if (argP1 != newArgs.end() && *(argP1) == "OR" && |
| argP2 != newArgs.end()) |
| { |
| lhs = GetBooleanValueWithAutoDereference(*arg, makefile, |
| errorString, |
| Policy12Status, |
| status); |
| rhs = GetBooleanValueWithAutoDereference(*argP2, makefile, |
| errorString, |
| Policy12Status, |
| status); |
| HandleBinaryOp((lhs || rhs), |
| reducible, arg, newArgs, argP1, argP2); |
| } |
| ++arg; |
| } |
| } |
| while (reducible); |
| return true; |
| } |
| } |
| |
| |
| //========================================================================= |
| // order of operations, |
| // 1. ( ) -- parenthetical groups |
| // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates |
| // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops |
| // 4. NOT |
| // 5. 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, |
| std::string &errorString, cmMakefile *makefile, |
| cmake::MessageType &status) |
| { |
| errorString = ""; |
| |
| // handle empty invocation |
| if (args.size() < 1) |
| { |
| return false; |
| } |
| |
| // store the reduced args in this vector |
| std::list<std::string> newArgs; |
| |
| // copy to the list structure |
| for(unsigned int i = 0; i < args.size(); ++i) |
| { |
| newArgs.push_back(args[i]); |
| } |
| |
| // 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 |
| // parens |
| if (!HandleLevel0(newArgs, makefile, errorString, status)) |
| { |
| return false; |
| } |
| //predicates |
| if (!HandleLevel1(newArgs, makefile, errorString, status)) |
| { |
| return false; |
| } |
| // binary ops |
| if (!HandleLevel2(newArgs, makefile, errorString, status)) |
| { |
| return false; |
| } |
| |
| // used to store the value of policy CMP0012 for performance |
| cmPolicies::PolicyStatus Policy12Status = |
| makefile->GetPolicyStatus(cmPolicies::CMP0012); |
| |
| // NOT |
| if (!HandleLevel3(newArgs, makefile, errorString, |
| Policy12Status, status)) |
| { |
| return false; |
| } |
| // AND OR |
| if (!HandleLevel4(newArgs, makefile, errorString, |
| Policy12Status, status)) |
| { |
| return false; |
| } |
| |
| // now at the end there should only be one argument left |
| if (newArgs.size() != 1) |
| { |
| errorString = "Unknown arguments specified"; |
| return false; |
| } |
| |
| return GetBooleanValueWithAutoDereference(*(newArgs.begin()), |
| makefile, |
| errorString, |
| Policy12Status, |
| status, true); |
| } |
| |
| //========================================================================= |
| const char* cmIfCommand::GetVariableOrString(const char* str, |
| const cmMakefile* mf) |
| { |
| const char* def = mf->GetDefinition(str); |
| if(!def) |
| { |
| def = str; |
| } |
| return def; |
| } |