diff options
author | Ken Martin <ken.martin@kitware.com> | 2008-06-26 17:01:35 (GMT) |
---|---|---|
committer | Ken Martin <ken.martin@kitware.com> | 2008-06-26 17:01:35 (GMT) |
commit | 19e891532a915d1fa4fcf4cdba6254d28c2adc6c (patch) | |
tree | 8f128805f9f8e6bcef1bee2a10acf9e4b6e4bc83 /Source/cmIfCommand.cxx | |
parent | d8e05b43a1fc0370aa3ac84f4209b1b942809b89 (diff) | |
download | CMake-19e891532a915d1fa4fcf4cdba6254d28c2adc6c.zip CMake-19e891532a915d1fa4fcf4cdba6254d28c2adc6c.tar.gz CMake-19e891532a915d1fa4fcf4cdba6254d28c2adc6c.tar.bz2 |
ENH: support parenthesis as arguments and in conditionals feature request #6191
Diffstat (limited to 'Source/cmIfCommand.cxx')
-rw-r--r-- | Source/cmIfCommand.cxx | 499 |
1 files changed, 277 insertions, 222 deletions
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 517348c..0baf142 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -21,8 +21,10 @@ #include <list> #include <cmsys/RegularExpression.hxx> +//========================================================================= bool cmIfFunctionBlocker:: -IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, +IsFunctionBlocked(const cmListFileFunction& lff, + cmMakefile &mf, cmExecutionStatus &inStatus) { // Prevent recusion and don't let this blocker block its own @@ -140,6 +142,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, return true; } +//========================================================================= bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile&) { @@ -157,8 +160,8 @@ bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, return false; } -void cmIfFunctionBlocker:: -ScopeEnded(cmMakefile &mf) +//========================================================================= +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: "; @@ -175,6 +178,7 @@ ScopeEnded(cmMakefile &mf) cmSystemTools::Message(errmsg.c_str(), "Warning"); } +//========================================================================= bool cmIfCommand ::InvokeInitialPass(const std::vector<cmListFileArgument>& args, cmExecutionStatus &) @@ -221,6 +225,7 @@ bool cmIfCommand namespace { +//========================================================================= void IncrementArguments(std::list<std::string> &newArgs, std::list<std::string>::iterator &argP1, std::list<std::string>::iterator &argP2) @@ -235,68 +240,108 @@ namespace } } } -} - - -// 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, 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); + //========================================================================= + // 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; + } - // handle empty invocation - if (args.size() < 1) - { - delete [] *errorString; - *errorString = 0; - return false; - } + //========================================================================= + // 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; + } - // store the reduced args in this vector - std::list<std::string> newArgs; + //========================================================================= + // level 0 processes parenthetical expressions + bool HandleLevel0(std::list<std::string> &newArgs, + cmMakefile *makefile, + char **errorString) + { 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 (*arg == "(") { - if(cmSystemTools::FileExists((argP1)->c_str())) + // 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) + { + cmOStringStream error; + error << "mismatched parenthesis in condition"; + delete [] *errorString; + *errorString = new char[error.str().size() + 1]; + strcpy(*errorString, error.str().c_str()); + 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); + if(value) { *arg = "1"; } @@ -304,75 +349,70 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, { *arg = "0"; } - newArgs.erase(argP1); argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + 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, + char **) + { + 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()) { - if(cmSystemTools::FileIsDirectory((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + cmSystemTools::FileIsDirectory((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // is the given path an absolute path ? if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end()) { - if(cmSystemTools::FileIsFullPath((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + cmSystemTools::FileIsFullPath((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // 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; + HandlePredicate( + makefile->CommandExists((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // does a policy exist if (*arg == "POLICY" && argP1 != newArgs.end()) { cmPolicies::PolicyID pid; - if(makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid)) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid), + reducible, arg, newArgs, argP1, argP2); } // is a variable defined if (*arg == "DEFINED" && argP1 != newArgs.end()) @@ -389,32 +429,30 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, { bdef = makefile->IsDefinitionSet((argP1)->c_str()); } - if(bdef) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - - // 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 + //========================================================================= + // level two handles most binary operations except for AND OR + bool HandleLevel2(std::list<std::string> &newArgs, + cmMakefile *makefile, + char **errorString) + { + 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; @@ -468,49 +506,26 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); double lhs; double rhs; + bool result; if(sscanf(def, "%lg", &lhs) != 1 || sscanf(def2, "%lg", &rhs) != 1) { - *arg = "0"; + result = false; } else if (*(argP1) == "LESS") { - if(lhs < rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs < rhs); } else if (*(argP1) == "GREATER") { - if(lhs > rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs > rhs); } else { - if(lhs == rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs == rhs); } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp(result, + reducible, arg, newArgs, argP1, argP2); } if (argP1 != newArgs.end() && argP2 != newArgs.end() && @@ -521,7 +536,7 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); int val = strcmp(def,def2); - int result; + bool result; if (*(argP1) == "STRLESS") { result = (val < 0); @@ -534,19 +549,8 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, { result = (val == 0); } - if(result) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp(result, + reducible, arg, newArgs, argP1, argP2); } // is file A newer than file B @@ -557,33 +561,32 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, 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; + HandleBinaryOp( + (success==false || fileIsNewer==1 || fileIsNewer==0), + reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - - // 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 + //========================================================================= + // level 3 handles NOT + bool HandleLevel3(std::list<std::string> &newArgs, + cmMakefile *makefile, + char **) + { + int reducible; + const char *def; 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; @@ -591,30 +594,31 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, 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; + HandlePredicate(cmSystemTools::IsOff(def), + reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - // 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 + //========================================================================= + // level 4 handles AND OR + bool HandleLevel4(std::list<std::string> &newArgs, + cmMakefile *makefile, + char **) + { + 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; @@ -624,19 +628,9 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, { 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; + HandleBinaryOp( + !(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2)), + reducible, arg, newArgs, argP1, argP2); } if (argP1 != newArgs.end() && *(argP1) == "OR" && @@ -644,25 +638,84 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, { 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; + HandleBinaryOp( + !(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)), + 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, + char **errorString, cmMakefile *makefile) +{ + // check for the different signatures + const char *def; + 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; + } + + // 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 + if (!HandleLevel0(newArgs, makefile, errorString)) // parens + { + return false; + } + if (!HandleLevel1(newArgs, makefile, errorString)) //predicates + { + return false; + } + if (!HandleLevel2(newArgs, makefile, errorString)) // binary ops + { + return false; + } + if (!HandleLevel3(newArgs, makefile, errorString)) // NOT + { + return false; + } + if (!HandleLevel4(newArgs, makefile, errorString)) // AND OR + { + return false; + } // now at the end there should only be one argument left if (newArgs.size() == 1) @@ -687,6 +740,7 @@ bool cmIfCommand::IsTrue(const std::vector<std::string> &args, return true; } +//========================================================================= const char* cmIfCommand::GetVariableOrString(const char* str, const cmMakefile* mf) { @@ -698,6 +752,7 @@ const char* cmIfCommand::GetVariableOrString(const char* str, return def; } +//========================================================================= const char* cmIfCommand::GetVariableOrNumber(const char* str, const cmMakefile* mf) { |