diff options
-rw-r--r-- | Source/cmBootstrapCommands2.cxx | 1 | ||||
-rw-r--r-- | Source/cmConditionEvaluator.cxx | 692 | ||||
-rw-r--r-- | Source/cmConditionEvaluator.h | 90 | ||||
-rw-r--r-- | Source/cmIfCommand.cxx | 713 | ||||
-rw-r--r-- | Source/cmIfCommand.h | 12 | ||||
-rw-r--r-- | Source/cmWhileCommand.cxx | 15 |
6 files changed, 803 insertions, 720 deletions
diff --git a/Source/cmBootstrapCommands2.cxx b/Source/cmBootstrapCommands2.cxx index be72526..91c189f 100644 --- a/Source/cmBootstrapCommands2.cxx +++ b/Source/cmBootstrapCommands2.cxx @@ -14,6 +14,7 @@ // This is sort of a boot strapping approach since you would // like to have CMake to build CMake. #include "cmCommands.h" +#include "cmConditionEvaluator.cxx" #include "cmGeneratorExpressionEvaluationFile.cxx" #include "cmGetCMakePropertyCommand.cxx" #include "cmGetDirectoryPropertyCommand.cxx" diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx new file mode 100644 index 0000000..869d3c5 --- /dev/null +++ b/Source/cmConditionEvaluator.cxx @@ -0,0 +1,692 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 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 "cmConditionEvaluator.h" + +cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile): + Makefile(makefile), + Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012)) +{ + +} + +//========================================================================= +// 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 cmConditionEvaluator::IsTrue( + const std::vector<std::string> &args, + std::string &errorString, + cmake::MessageType &status) +{ + errorString = ""; + + // handle empty invocation + if (args.size() < 1) + { + return false; + } + + // store the reduced args in this vector + cmArgumentList 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 (!this->HandleLevel0(newArgs, errorString, status)) + { + return false; + } + //predicates + if (!this->HandleLevel1(newArgs, errorString, status)) + { + return false; + } + // binary ops + if (!this->HandleLevel2(newArgs, errorString, status)) + { + return false; + } + + // NOT + if (!this->HandleLevel3(newArgs, errorString, status)) + { + return false; + } + // AND OR + if (!this->HandleLevel4(newArgs, errorString, 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 this->GetBooleanValueWithAutoDereference(*(newArgs.begin()), + errorString, status, true); +} + +//========================================================================= +const char* cmConditionEvaluator::GetVariableOrString( + const std::string& str) const +{ + const char* def = this->Makefile.GetDefinition(str); + + if(!def) + { + def = str.c_str(); + } + + return def; +} + +//========================================================================= +bool cmConditionEvaluator::GetBooleanValue( + std::string& arg) const +{ + // 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 = this->Makefile.GetDefinition(arg); + return !cmSystemTools::IsOff(def); +} + +//========================================================================= +// Boolean value behavior from CMake 2.6.4 and below. +bool cmConditionEvaluator::GetBooleanValueOld( + std::string const& arg, bool one) const +{ + if(one) + { + // Old IsTrue behavior for single argument. + if(arg == "0") + { return false; } + else if(arg == "1") + { return true; } + else + { return !cmSystemTools::IsOff(this->Makefile.GetDefinition(arg)); } + } + else + { + // Old GetVariableOrNumber behavior. + const char* def = this->Makefile.GetDefinition(arg); + if(!def && atoi(arg.c_str())) + { + def = arg.c_str(); + } + return !cmSystemTools::IsOff(def); + } +} + +//========================================================================= +// returns the resulting boolean value +bool cmConditionEvaluator::GetBooleanValueWithAutoDereference( + std::string &newArg, + std::string &errorString, + cmake::MessageType &status, + bool oneArg) const +{ + // Use the policy if it is set. + if (this->Policy12Status == cmPolicies::NEW) + { + return GetBooleanValue(newArg); + } + else if (this->Policy12Status == cmPolicies::OLD) + { + return GetBooleanValueOld(newArg, oneArg); + } + + // Check policy only if old and new results differ. + bool newResult = this->GetBooleanValue(newArg); + bool oldResult = this->GetBooleanValueOld(newArg, oneArg); + if(newResult != oldResult) + { + switch(this->Policy12Status) + { + case cmPolicies::WARN: + { + cmPolicies* policies = this->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 = this->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 cmConditionEvaluator::IncrementArguments(cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2) const +{ + if (argP1 != newArgs.end()) + { + argP1++; + argP2 = argP1; + if (argP1 != newArgs.end()) + { + argP2++; + } + } +} + +//========================================================================= +// helper function to reduce code duplication +void cmConditionEvaluator::HandlePredicate(bool value, int &reducible, + cmArgumentList::iterator &arg, + cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2) const +{ + if(value) + { + *arg = "1"; + } + else + { + *arg = "0"; + } + newArgs.erase(argP1); + argP1 = arg; + this->IncrementArguments(newArgs,argP1,argP2); + reducible = 1; +} + +//========================================================================= +// helper function to reduce code duplication +void cmConditionEvaluator::HandleBinaryOp(bool value, int &reducible, + cmArgumentList::iterator &arg, + cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2) +{ + if(value) + { + *arg = "1"; + } + else + { + *arg = "0"; + } + newArgs.erase(argP2); + newArgs.erase(argP1); + argP1 = arg; + this->IncrementArguments(newArgs,argP1,argP2); + reducible = 1; +} + +//========================================================================= +// level 0 processes parenthetical expressions +bool cmConditionEvaluator::HandleLevel0(cmArgumentList &newArgs, + 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 = + this->IsTrue(newArgs2, errorString, 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 cmConditionEvaluator::HandleLevel1(cmArgumentList &newArgs, + 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; + this->IncrementArguments(newArgs,argP1,argP2); + // does a file exist + if (*arg == "EXISTS" && argP1 != newArgs.end()) + { + this->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()) + { + this->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()) + { + this->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()) + { + this->HandlePredicate( + cmSystemTools::FileIsFullPath((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); + } + // does a command exist + if (*arg == "COMMAND" && argP1 != newArgs.end()) + { + this->HandlePredicate( + this->Makefile.CommandExists((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); + } + // does a policy exist + if (*arg == "POLICY" && argP1 != newArgs.end()) + { + cmPolicies::PolicyID pid; + this->HandlePredicate( + this->Makefile.GetPolicies()->GetPolicyID((argP1)->c_str(), pid), + reducible, arg, newArgs, argP1, argP2); + } + // does a target exist + if (*arg == "TARGET" && argP1 != newArgs.end()) + { + this->HandlePredicate( + this->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 = this->Makefile.IsDefinitionSet(*(argP1)); + } + this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2); + } + ++arg; + } + } + while (reducible); + return true; +} + +//========================================================================= +// level two handles most binary operations except for AND OR +bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs, + 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; + this->IncrementArguments(newArgs,argP1,argP2); + if (argP1 != newArgs.end() && argP2 != newArgs.end() && + *(argP1) == "MATCHES") + { + def = this->GetVariableOrString(*arg); + const char* rex = (argP2)->c_str(); + this->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)) + { + this->Makefile.StoreMatches(regEntry); + *arg = "1"; + } + else + { + *arg = "0"; + } + newArgs.erase(argP2); + newArgs.erase(argP1); + argP1 = arg; + this->IncrementArguments(newArgs,argP1,argP2); + reducible = 1; + } + + if (argP1 != newArgs.end() && *arg == "MATCHES") + { + *arg = "0"; + newArgs.erase(argP1); + argP1 = arg; + this->IncrementArguments(newArgs,argP1,argP2); + reducible = 1; + } + + if (argP1 != newArgs.end() && argP2 != newArgs.end() && + (*(argP1) == "LESS" || *(argP1) == "GREATER" || + *(argP1) == "EQUAL")) + { + def = this->GetVariableOrString(*arg); + def2 = this->GetVariableOrString(*argP2); + 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 = this->GetVariableOrString(*arg); + def2 = this->GetVariableOrString(*argP2); + 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); + } + this->HandleBinaryOp(result, + reducible, arg, newArgs, argP1, argP2); + } + + if (argP1 != newArgs.end() && argP2 != newArgs.end() && + (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" || + *(argP1) == "VERSION_EQUAL")) + { + def = this->GetVariableOrString(*arg); + def2 = this->GetVariableOrString(*argP2); + 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); + this->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); + this->HandleBinaryOp( + (success==false || fileIsNewer==1 || fileIsNewer==0), + reducible, arg, newArgs, argP1, argP2); + } + + ++arg; + } + } + while (reducible); + return true; +} + +//========================================================================= +// level 3 handles NOT +bool cmConditionEvaluator::HandleLevel3(cmArgumentList &newArgs, + std::string &errorString, + 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 = this->GetBooleanValueWithAutoDereference(*argP1, + errorString, + status); + this->HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2); + } + ++arg; + } + } + while (reducible); + return true; +} + +//========================================================================= +// level 4 handles AND OR +bool cmConditionEvaluator::HandleLevel4(cmArgumentList &newArgs, + std::string &errorString, + 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 = this->GetBooleanValueWithAutoDereference(*arg, + errorString, + status); + rhs = this->GetBooleanValueWithAutoDereference(*argP2, + errorString, + status); + this->HandleBinaryOp((lhs && rhs), + reducible, arg, newArgs, argP1, argP2); + } + + if (argP1 != newArgs.end() && *(argP1) == "OR" && + argP2 != newArgs.end()) + { + lhs = this->GetBooleanValueWithAutoDereference(*arg, + errorString, + status); + rhs = this->GetBooleanValueWithAutoDereference(*argP2, + errorString, + status); + this->HandleBinaryOp((lhs || rhs), + reducible, arg, newArgs, argP1, argP2); + } + ++arg; + } + } + while (reducible); + return true; +} diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h new file mode 100644 index 0000000..9076988 --- /dev/null +++ b/Source/cmConditionEvaluator.h @@ -0,0 +1,90 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 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. +============================================================================*/ +#ifndef cmConditionEvaluator_h +#define cmConditionEvaluator_h + +#include "cmCommand.h" + +class cmConditionEvaluator +{ +public: + typedef std::list<std::string> cmArgumentList; + + cmConditionEvaluator(cmMakefile& makefile); + + // this is a shared function for both If and Else to determine if the + // arguments were valid, and if so, was the response true. If there is + // an error, the errorString will be set. + bool IsTrue(const std::vector<std::string> &args, + std::string &errorString, + cmake::MessageType &status); + +private: + const char* GetVariableOrString( + const std::string& argument) const; + + bool IsKeyword(std::string const& keyword, + std::string& argument) const; + + bool GetBooleanValue( + std::string& arg) const; + + bool GetBooleanValueOld( + std::string const& arg, bool one) const; + + bool GetBooleanValueWithAutoDereference( + std::string &newArg, + std::string &errorString, + cmake::MessageType &status, + bool oneArg = false) const; + + void IncrementArguments( + cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2) const; + + void HandlePredicate(bool value, int &reducible, + cmArgumentList::iterator &arg, + cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2) const; + + void HandleBinaryOp(bool value, int &reducible, + cmArgumentList::iterator &arg, + cmArgumentList &newArgs, + cmArgumentList::iterator &argP1, + cmArgumentList::iterator &argP2); + + bool HandleLevel0(cmArgumentList &newArgs, + std::string &errorString, + cmake::MessageType &status); + + bool HandleLevel1(cmArgumentList &newArgs, + std::string &, cmake::MessageType &); + + bool HandleLevel2(cmArgumentList &newArgs, + std::string &errorString, + cmake::MessageType &status); + + bool HandleLevel3(cmArgumentList &newArgs, + std::string &errorString, + cmake::MessageType &status); + + bool HandleLevel4(cmArgumentList &newArgs, + std::string &errorString, + cmake::MessageType &status); + + cmMakefile& Makefile; + cmPolicies::PolicyStatus Policy12Status; +}; + +#endif diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 1141b01..25ee25a 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; -} diff --git a/Source/cmIfCommand.h b/Source/cmIfCommand.h index 814c052..ea2a68f 100644 --- a/Source/cmIfCommand.h +++ b/Source/cmIfCommand.h @@ -70,18 +70,6 @@ public: */ virtual bool IsScriptable() const { return true; } - // this is a shared function for both If and Else to determine if the - // arguments were valid, and if so, was the response true. If there is - // an error, the errorString will be set. - static bool IsTrue(const std::vector<std::string> &args, - std::string &errorString, cmMakefile *mf, - cmake::MessageType &status); - - // Get a definition from the makefile. If it doesn't exist, - // return the original string. - static const char* GetVariableOrString(const std::string& str, - const cmMakefile* mf); - cmTypeMacro(cmIfCommand, cmCommand); }; diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index 7d2eead..5565b8a 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -10,7 +10,7 @@ See the License for more information. ============================================================================*/ #include "cmWhileCommand.h" -#include "cmIfCommand.h" +#include "cmConditionEvaluator.h" bool cmWhileFunctionBlocker:: IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, @@ -37,9 +37,11 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, std::vector<std::string> expandedArguments; mf.ExpandArguments(this->Args, expandedArguments); cmake::MessageType messageType; - bool isTrue = - cmIfCommand::IsTrue(expandedArguments,errorString, - &mf, messageType); + + cmConditionEvaluator conditionEvaluator(mf); + + bool isTrue = conditionEvaluator.IsTrue( + expandedArguments, errorString, messageType); while (isTrue) { @@ -86,9 +88,8 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, } expandedArguments.clear(); mf.ExpandArguments(this->Args, expandedArguments); - isTrue = - cmIfCommand::IsTrue(expandedArguments,errorString, - &mf, messageType); + isTrue = conditionEvaluator.IsTrue( + expandedArguments, errorString, messageType); } return true; } |