diff options
Diffstat (limited to 'Source/cmIfCommand.cxx')
-rw-r--r-- | Source/cmIfCommand.cxx | 713 |
1 files changed, 12 insertions, 701 deletions
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 1141b01b42..25ee25a04a 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -12,6 +12,8 @@ #include "cmIfCommand.h" #include "cmStringCommand.h" +#include "cmConditionEvaluator.h" + #include <stdlib.h> // required for atof #include <list> #include <cmsys/RegularExpression.hxx> @@ -108,9 +110,11 @@ IsFunctionBlocked(const cmListFileFunction& lff, expandedArguments); cmake::MessageType messType; - bool isTrue = - cmIfCommand::IsTrue(expandedArguments, errorString, - &mf, messType); + + cmConditionEvaluator conditionEvaluator(mf); + + bool isTrue = conditionEvaluator.IsTrue( + expandedArguments, errorString, messType); if (errorString.size()) { @@ -189,9 +193,11 @@ bool cmIfCommand this->Makefile->ExpandArguments(args, expandedArguments); cmake::MessageType status; - bool isTrue = - cmIfCommand::IsTrue(expandedArguments,errorString, - this->Makefile, status); + + cmConditionEvaluator conditionEvaluator(*(this->Makefile)); + + bool isTrue = conditionEvaluator.IsTrue( + expandedArguments, errorString, status); if (errorString.size()) { @@ -222,698 +228,3 @@ bool cmIfCommand 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); - 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)); } - } - else - { - // Old GetVariableOrNumber behavior. - const char* def = mf->GetDefinition(arg); - 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; - } - - //========================================================================= - // 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)?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)); - } - 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, makefile); - const char* rex = (argP2)->c_str(); - makefile->ClearMatches(); - 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)) - { - makefile->StoreMatches(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, makefile); - def2 = cmIfCommand::GetVariableOrString(*argP2, 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, makefile); - def2 = cmIfCommand::GetVariableOrString(*argP2, 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, makefile); - def2 = cmIfCommand::GetVariableOrString(*argP2, makefile); - cmSystemTools::CompareOp op = cmSystemTools::OP_EQUAL; - if(*argP1 == "VERSION_LESS") - { - op = cmSystemTools::OP_LESS; - } - else if(*argP1 == "VERSION_GREATER") - { - op = cmSystemTools::OP_GREATER; - } - bool result = cmSystemTools::VersionCompare(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"; - status = cmake::FATAL_ERROR; - return false; - } - - return GetBooleanValueWithAutoDereference(*(newArgs.begin()), - makefile, - errorString, - Policy12Status, - status, true); -} - -//========================================================================= -const char* cmIfCommand::GetVariableOrString(const std::string& str, - const cmMakefile* mf) -{ - const char* def = mf->GetDefinition(str); - if(!def) - { - def = str.c_str(); - } - return def; -} |