summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2014-09-15 14:27:36 (GMT)
committerCMake Topic Stage <kwrobot@kitware.com>2014-09-15 14:27:36 (GMT)
commit9edf6903e62f84a567db3938bb8fec3f19789545 (patch)
tree889b2078804ab5f74e8e76a9e4000b9dc27479d5 /Source
parent78884b6effcb5406eb64365d6cf4b43c085f56c3 (diff)
parent858d5a0b3e52dbae635fac5a6944fba23a362f5b (diff)
downloadCMake-9edf6903e62f84a567db3938bb8fec3f19789545.zip
CMake-9edf6903e62f84a567db3938bb8fec3f19789545.tar.gz
CMake-9edf6903e62f84a567db3938bb8fec3f19789545.tar.bz2
Merge topic 'if-sanity'
858d5a0b Fix if() checks of CMAKE_SYSTEM_NAME on Cygwin e177e7af FPHSA: Avoid if() dereferencing of quoted variable 425acc52 cmcurl: Use if(DEFINED) to simplify conditions cede5cbd libarchive: Avoid depending on if() to dereference a quoted variable 2d97178b FindGTK2: Avoid depending on if() to dereference a quoted variable 0b12815d Modules/Test*.cmake: Use if(DEFINED) to simplify conditions 188a1f23 If: Introduce policy CMP0054 - don't dereference quoted variables in if() b900c1cc If: Extract cmConditionEvaluator from if() implementation
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmBootstrapCommands2.cxx2
-rw-r--r--Source/cmConditionEvaluator.cxx770
-rw-r--r--Source/cmConditionEvaluator.h96
-rw-r--r--Source/cmExpandedCommandArgument.cxx51
-rw-r--r--Source/cmExpandedCommandArgument.h45
-rw-r--r--Source/cmIfCommand.cxx723
-rw-r--r--Source/cmIfCommand.h14
-rw-r--r--Source/cmMakefile.cxx61
-rw-r--r--Source/cmMakefile.h35
-rw-r--r--Source/cmPolicies.cxx5
-rw-r--r--Source/cmPolicies.h2
-rw-r--r--Source/cmWhileCommand.cxx17
13 files changed, 1096 insertions, 727 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index c3f77f4..f9405b3 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -451,7 +451,7 @@ if (WIN32)
endif ()
# Watcom support
-if(WIN32 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
set_property(SOURCE cmake.cxx APPEND PROPERTY COMPILE_DEFINITIONS CMAKE_USE_WMAKE)
list(APPEND SRCS
cmGlobalWatcomWMakeGenerator.cxx
diff --git a/Source/cmBootstrapCommands2.cxx b/Source/cmBootstrapCommands2.cxx
index be72526..5675295 100644
--- a/Source/cmBootstrapCommands2.cxx
+++ b/Source/cmBootstrapCommands2.cxx
@@ -14,6 +14,8 @@
// 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 "cmExpandedCommandArgument.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..aba26de
--- /dev/null
+++ b/Source/cmConditionEvaluator.cxx
@@ -0,0 +1,770 @@
+/*============================================================================
+ 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)),
+ Policy54Status(makefile.GetPolicyStatus(cmPolicies::CMP0054))
+{
+
+}
+
+//=========================================================================
+// 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<cmExpandedCommandArgument> &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::GetDefinitionIfUnquoted(
+ cmExpandedCommandArgument const& argument) const
+{
+ if((this->Policy54Status != cmPolicies::WARN &&
+ this->Policy54Status != cmPolicies::OLD) &&
+ argument.WasQuoted())
+ {
+ return 0;
+ }
+
+ const char* def = this->Makefile.GetDefinition(argument.GetValue());
+
+ if(def && argument.WasQuoted() && this->Policy54Status == cmPolicies::WARN)
+ {
+ bool hasBeenReported = this->Makefile.HasCMP0054AlreadyBeenReported(
+ this->Makefile.GetBacktrace()[0]);
+
+ if(!hasBeenReported)
+ {
+ cmOStringStream e;
+ e << (this->Makefile.GetPolicies()->GetPolicyWarning(
+ cmPolicies::CMP0054)) << "\n";
+ e << "Quoted variables like \"" << argument.GetValue() <<
+ "\" will no longer be dereferenced "
+ "when the policy is set to NEW. "
+ "Since the policy is not set the OLD behavior will be used.";
+
+ this->Makefile.IssueMessage(cmake::AUTHOR_WARNING, e.str());
+ }
+ }
+
+ return def;
+}
+
+//=========================================================================
+const char* cmConditionEvaluator::GetVariableOrString(
+ const cmExpandedCommandArgument& argument) const
+{
+ const char* def = this->GetDefinitionIfUnquoted(argument);
+
+ if(!def)
+ {
+ def = argument.c_str();
+ }
+
+ return def;
+}
+
+//=========================================================================
+bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
+ cmExpandedCommandArgument& argument) const
+{
+ if((this->Policy54Status != cmPolicies::WARN &&
+ this->Policy54Status != cmPolicies::OLD) &&
+ argument.WasQuoted())
+ {
+ return false;
+ }
+
+ bool isKeyword = argument.GetValue() == keyword;
+
+ if(isKeyword && argument.WasQuoted() &&
+ this->Policy54Status == cmPolicies::WARN)
+ {
+ bool hasBeenReported = this->Makefile.HasCMP0054AlreadyBeenReported(
+ this->Makefile.GetBacktrace()[0]);
+
+ if(!hasBeenReported)
+ {
+ cmOStringStream e;
+ e << (this->Makefile.GetPolicies()->GetPolicyWarning(
+ cmPolicies::CMP0054)) << "\n";
+ e << "Quoted keywords like \"" << argument.GetValue() <<
+ "\" will no longer be interpreted as keywords "
+ "when the policy is set to NEW. "
+ "Since the policy is not set the OLD behavior will be used.";
+
+ this->Makefile.IssueMessage(cmake::AUTHOR_WARNING, e.str());
+ }
+ }
+
+ return isKeyword;
+}
+
+//=========================================================================
+bool cmConditionEvaluator::GetBooleanValue(
+ cmExpandedCommandArgument& 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->GetDefinitionIfUnquoted(arg);
+ return !cmSystemTools::IsOff(def);
+}
+
+//=========================================================================
+// Boolean value behavior from CMake 2.6.4 and below.
+bool cmConditionEvaluator::GetBooleanValueOld(
+ cmExpandedCommandArgument 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
+ {
+ const char* def = this->GetDefinitionIfUnquoted(arg);
+ return !cmSystemTools::IsOff(def);
+ }
+ }
+ else
+ {
+ // Old GetVariableOrNumber behavior.
+ const char* def = this->GetDefinitionIfUnquoted(arg);
+ if(!def && atoi(arg.c_str()))
+ {
+ def = arg.c_str();
+ }
+ return !cmSystemTools::IsOff(def);
+ }
+}
+
+//=========================================================================
+// returns the resulting boolean value
+bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
+ cmExpandedCommandArgument &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.GetValue()
+ + "\" 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.GetValue()
+ + "\" 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 = cmExpandedCommandArgument("1", true);
+ }
+ else
+ {
+ *arg = cmExpandedCommandArgument("0", true);
+ }
+ 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 = cmExpandedCommandArgument("1", true);
+ }
+ else
+ {
+ *arg = cmExpandedCommandArgument("0", true);
+ }
+ 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;
+ cmArgumentList::iterator arg = newArgs.begin();
+ while (arg != newArgs.end())
+ {
+ if (IsKeyword("(", *arg))
+ {
+ // search for the closing paren for this opening one
+ cmArgumentList::iterator argClose;
+ argClose = arg;
+ argClose++;
+ unsigned int depth = 1;
+ while (argClose != newArgs.end() && depth)
+ {
+ if (this->IsKeyword("(", *argClose))
+ {
+ depth++;
+ }
+ if (this->IsKeyword(")", *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<cmExpandedCommandArgument> newArgs2;
+
+ // copy to the list structure
+ cmArgumentList::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 = cmExpandedCommandArgument("1", true);
+ }
+ else
+ {
+ *arg = cmExpandedCommandArgument("0", true);
+ }
+ 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;
+ cmArgumentList::iterator arg = newArgs.begin();
+ cmArgumentList::iterator argP1;
+ cmArgumentList::iterator argP2;
+ while (arg != newArgs.end())
+ {
+ argP1 = arg;
+ this->IncrementArguments(newArgs,argP1,argP2);
+ // does a file exist
+ if (this->IsKeyword("EXISTS", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ cmSystemTools::FileExists(argP1->c_str()),
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // does a directory with this name exist
+ if (this->IsKeyword("IS_DIRECTORY", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ cmSystemTools::FileIsDirectory(argP1->c_str()),
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // does a symlink with this name exist
+ if (this->IsKeyword("IS_SYMLINK", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ cmSystemTools::FileIsSymlink(argP1->c_str()),
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // is the given path an absolute path ?
+ if (this->IsKeyword("IS_ABSOLUTE", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ cmSystemTools::FileIsFullPath(argP1->c_str()),
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // does a command exist
+ if (this->IsKeyword("COMMAND", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ this->Makefile.CommandExists(argP1->c_str()),
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // does a policy exist
+ if (this->IsKeyword("POLICY", *arg) && 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 (this->IsKeyword("TARGET", *arg) && argP1 != newArgs.end())
+ {
+ this->HandlePredicate(
+ this->Makefile.FindTargetToUse(argP1->GetValue())?true:false,
+ reducible, arg, newArgs, argP1, argP2);
+ }
+ // is a variable defined
+ if (this->IsKeyword("DEFINED", *arg) && argP1 != newArgs.end())
+ {
+ size_t argP1len = argP1->GetValue().size();
+ bool bdef = false;
+ if(argP1len > 4 && argP1->GetValue().substr(0, 4) == "ENV{" &&
+ argP1->GetValue().operator[](argP1len-1) == '}')
+ {
+ std::string env = argP1->GetValue().substr(4, argP1len-5);
+ bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
+ }
+ else
+ {
+ bdef = this->Makefile.IsDefinitionSet(argP1->GetValue());
+ }
+ 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;
+ cmArgumentList::iterator arg = newArgs.begin();
+ cmArgumentList::iterator argP1;
+ cmArgumentList::iterator argP2;
+ while (arg != newArgs.end())
+ {
+ argP1 = arg;
+ this->IncrementArguments(newArgs,argP1,argP2);
+ if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
+ IsKeyword("MATCHES", *argP1))
+ {
+ 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 = cmExpandedCommandArgument("1", true);
+ }
+ else
+ {
+ *arg = cmExpandedCommandArgument("0", true);
+ }
+ newArgs.erase(argP2);
+ newArgs.erase(argP1);
+ argP1 = arg;
+ this->IncrementArguments(newArgs,argP1,argP2);
+ reducible = 1;
+ }
+
+ if (argP1 != newArgs.end() && this->IsKeyword("MATCHES", *arg))
+ {
+ *arg = cmExpandedCommandArgument("0", true);
+ newArgs.erase(argP1);
+ argP1 = arg;
+ this->IncrementArguments(newArgs,argP1,argP2);
+ reducible = 1;
+ }
+
+ if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
+ (this->IsKeyword("LESS", *argP1) ||
+ this->IsKeyword("GREATER", *argP1) ||
+ this->IsKeyword("EQUAL", *argP1)))
+ {
+ 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);
+ }
+ this->HandleBinaryOp(result,
+ reducible, arg, newArgs, argP1, argP2);
+ }
+
+ if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
+ (this->IsKeyword("STRLESS", *argP1) ||
+ this->IsKeyword("STREQUAL", *argP1) ||
+ this->IsKeyword("STRGREATER", *argP1)))
+ {
+ 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() &&
+ (this->IsKeyword("VERSION_LESS", *argP1) ||
+ this->IsKeyword("VERSION_GREATER", *argP1) ||
+ this->IsKeyword("VERSION_EQUAL", *argP1)))
+ {
+ 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() &&
+ this->IsKeyword("IS_NEWER_THAN", *argP1))
+ {
+ int fileIsNewer=0;
+ bool success=cmSystemTools::FileTimeCompare(arg->GetValue(),
+ (argP2)->GetValue(),
+ &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;
+ cmArgumentList::iterator arg = newArgs.begin();
+ cmArgumentList::iterator argP1;
+ cmArgumentList::iterator argP2;
+ while (arg != newArgs.end())
+ {
+ argP1 = arg;
+ IncrementArguments(newArgs,argP1,argP2);
+ if (argP1 != newArgs.end() && IsKeyword("NOT", *arg))
+ {
+ 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;
+ cmArgumentList::iterator arg = newArgs.begin();
+ cmArgumentList::iterator argP1;
+ cmArgumentList::iterator argP2;
+ while (arg != newArgs.end())
+ {
+ argP1 = arg;
+ IncrementArguments(newArgs,argP1,argP2);
+ if (argP1 != newArgs.end() && IsKeyword("AND", *argP1) &&
+ 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() && this->IsKeyword("OR", *argP1) &&
+ 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..01624f9
--- /dev/null
+++ b/Source/cmConditionEvaluator.h
@@ -0,0 +1,96 @@
+/*============================================================================
+ 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"
+#include "cmExpandedCommandArgument.h"
+
+class cmConditionEvaluator
+{
+public:
+ typedef std::list<cmExpandedCommandArgument> 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<cmExpandedCommandArgument> &args,
+ std::string &errorString,
+ cmake::MessageType &status);
+
+private:
+ // Filter the given variable definition based on policy CMP0054.
+ const char* GetDefinitionIfUnquoted(
+ const cmExpandedCommandArgument& argument) const;
+
+ const char* GetVariableOrString(
+ const cmExpandedCommandArgument& argument) const;
+
+ bool IsKeyword(std::string const& keyword,
+ cmExpandedCommandArgument& argument) const;
+
+ bool GetBooleanValue(
+ cmExpandedCommandArgument& arg) const;
+
+ bool GetBooleanValueOld(
+ cmExpandedCommandArgument const& arg, bool one) const;
+
+ bool GetBooleanValueWithAutoDereference(
+ cmExpandedCommandArgument &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;
+ cmPolicies::PolicyStatus Policy54Status;
+};
+
+#endif
diff --git a/Source/cmExpandedCommandArgument.cxx b/Source/cmExpandedCommandArgument.cxx
new file mode 100644
index 0000000..4477cf5
--- /dev/null
+++ b/Source/cmExpandedCommandArgument.cxx
@@ -0,0 +1,51 @@
+/*============================================================================
+ 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 "cmExpandedCommandArgument.h"
+
+cmExpandedCommandArgument::cmExpandedCommandArgument():
+ Quoted(false)
+{
+
+}
+
+cmExpandedCommandArgument::cmExpandedCommandArgument(
+ std::string const& value, bool quoted):
+ Value(value), Quoted(quoted)
+{
+
+}
+
+std::string const& cmExpandedCommandArgument::GetValue() const
+{
+ return this->Value;
+}
+
+bool cmExpandedCommandArgument::WasQuoted() const
+{
+ return this->Quoted;
+}
+
+bool cmExpandedCommandArgument::operator== (std::string const& value) const
+{
+ return this->Value == value;
+}
+
+bool cmExpandedCommandArgument::empty() const
+{
+ return this->Value.empty();
+}
+
+const char* cmExpandedCommandArgument::c_str() const
+{
+ return this->Value.c_str();
+}
diff --git a/Source/cmExpandedCommandArgument.h b/Source/cmExpandedCommandArgument.h
new file mode 100644
index 0000000..f4e1517
--- /dev/null
+++ b/Source/cmExpandedCommandArgument.h
@@ -0,0 +1,45 @@
+/*============================================================================
+ 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 cmExpandedCommandArgument_h
+#define cmExpandedCommandArgument_h
+
+#include "cmStandardIncludes.h"
+
+/** \class cmExpandedCommandArgument
+ * \brief Represents an expanded command argument
+ *
+ * cmCommandArgument stores a string representing an expanded
+ * command argument and context information.
+ */
+
+class cmExpandedCommandArgument
+{
+public:
+ cmExpandedCommandArgument();
+ cmExpandedCommandArgument(std::string const& value, bool quoted);
+
+ std::string const& GetValue() const;
+
+ bool WasQuoted() const;
+
+ bool operator== (std::string const& value) const;
+
+ bool empty() const;
+
+ const char* c_str() const;
+
+private:
+ std::string Value;
+ bool Quoted;
+};
+
+#endif
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx
index 1141b01..f728c15 100644
--- a/Source/cmIfCommand.cxx
+++ b/Source/cmIfCommand.cxx
@@ -12,21 +12,23 @@
#include "cmIfCommand.h"
#include "cmStringCommand.h"
+#include "cmConditionEvaluator.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)
+ cmMakefile* mf, std::vector<cmExpandedCommandArgument> const& args)
{
cmLocalGenerator* lg = mf->GetLocalGenerator();
std::string err = "given arguments:\n ";
- for(std::vector<std::string>::const_iterator i = args.begin();
+ for(std::vector<cmExpandedCommandArgument>::const_iterator i = args.begin();
i != args.end(); ++i)
{
err += " ";
- err += lg->EscapeForCMake(*i);
+ err += lg->EscapeForCMake(i->GetValue());
}
err += "\n";
return err;
@@ -103,14 +105,16 @@ IsFunctionBlocked(const cmListFileFunction& lff,
std::string errorString;
- std::vector<std::string> expandedArguments;
+ std::vector<cmExpandedCommandArgument> expandedArguments;
mf.ExpandArguments(this->Functions[c].Arguments,
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())
{
@@ -185,13 +189,15 @@ bool cmIfCommand
{
std::string errorString;
- std::vector<std::string> expandedArguments;
+ std::vector<cmExpandedCommandArgument> expandedArguments;
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..689efce 100644
--- a/Source/cmIfCommand.h
+++ b/Source/cmIfCommand.h
@@ -70,17 +70,9 @@ 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);
+ // Filter the given variable definition based on policy CMP0054.
+ static const char* GetDefinitionIfUnquoted(
+ const cmMakefile* mf, cmExpandedCommandArgument const& argument);
cmTypeMacro(cmIfCommand, cmCommand);
};
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index efdaeec..8806205 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3292,6 +3292,7 @@ void cmMakefile::PopFunctionBlockerBarrier(bool reportError)
this->FunctionBlockerBarriers.pop_back();
}
+//----------------------------------------------------------------------------
bool cmMakefile::ExpandArguments(
std::vector<cmListFileArgument> const& inArgs,
std::vector<std::string>& outArgs) const
@@ -3328,6 +3329,47 @@ bool cmMakefile::ExpandArguments(
}
//----------------------------------------------------------------------------
+bool cmMakefile::ExpandArguments(
+ std::vector<cmListFileArgument> const& inArgs,
+ std::vector<cmExpandedCommandArgument>& outArgs) const
+{
+ std::vector<cmListFileArgument>::const_iterator i;
+ std::string value;
+ outArgs.reserve(inArgs.size());
+ for(i = inArgs.begin(); i != inArgs.end(); ++i)
+ {
+ // No expansion in a bracket argument.
+ if(i->Delim == cmListFileArgument::Bracket)
+ {
+ outArgs.push_back(cmExpandedCommandArgument(i->Value, true));
+ continue;
+ }
+ // Expand the variables in the argument.
+ value = i->Value;
+ this->ExpandVariablesInString(value, false, false, false,
+ i->FilePath, i->Line,
+ false, false);
+
+ // If the argument is quoted, it should be one argument.
+ // Otherwise, it may be a list of arguments.
+ if(i->Delim == cmListFileArgument::Quoted)
+ {
+ outArgs.push_back(cmExpandedCommandArgument(value, true));
+ }
+ else
+ {
+ std::vector<std::string> stringArgs;
+ cmSystemTools::ExpandListArgument(value, stringArgs);
+ for(size_t j = 0; j < stringArgs.size(); ++j)
+ {
+ outArgs.push_back(cmExpandedCommandArgument(stringArgs[j], false));
+ }
+ }
+ }
+ return !cmSystemTools::GetFatalErrorOccured();
+}
+
+//----------------------------------------------------------------------------
void cmMakefile::AddFunctionBlocker(cmFunctionBlocker* fb)
{
if(!this->CallStack.empty())
@@ -4939,12 +4981,14 @@ void cmMakefile::PopPolicyBarrier(bool reportError)
this->PolicyBarriers.pop_back();
}
+//----------------------------------------------------------------------------
bool cmMakefile::SetPolicyVersion(const char *version)
{
return this->GetCMakeInstance()->GetPolicies()->
ApplyPolicyVersion(this,version);
}
+//----------------------------------------------------------------------------
cmPolicies *cmMakefile::GetPolicies() const
{
if (!this->GetCMakeInstance())
@@ -4955,6 +4999,23 @@ cmPolicies *cmMakefile::GetPolicies() const
}
//----------------------------------------------------------------------------
+bool cmMakefile::HasCMP0054AlreadyBeenReported(
+ cmListFileContext context) const
+{
+ cmCMP0054Id id(context);
+
+ bool alreadyReported =
+ this->CMP0054ReportedIds.find(id) != this->CMP0054ReportedIds.end();
+
+ if(!alreadyReported)
+ {
+ this->CMP0054ReportedIds.insert(id);
+ }
+
+ return alreadyReported;
+}
+
+//----------------------------------------------------------------------------
void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm)
{
/* Record the setting of every policy. */
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index d728a62..164290a 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -21,6 +21,7 @@
#include "cmTarget.h"
#include "cmNewLineStyle.h"
#include "cmGeneratorTarget.h"
+#include "cmExpandedCommandArgument.h"
#include "cmake.h"
#if defined(CMAKE_BUILD_WITH_CMAKE)
@@ -375,7 +376,35 @@ public:
/**
* Get the Policies Instance
*/
- cmPolicies *GetPolicies() const;
+ cmPolicies *GetPolicies() const;
+
+ struct cmCMP0054Id
+ {
+ cmCMP0054Id(cmListFileContext const& context):
+ Context(context)
+ {
+
+ }
+
+ bool operator< (cmCMP0054Id const& id) const
+ {
+ if(this->Context.FilePath != id.Context.FilePath)
+ return this->Context.FilePath < id.Context.FilePath;
+
+ return this->Context.Line < id.Context.Line;
+ }
+
+ cmListFileContext Context;
+ };
+
+ mutable std::set<cmCMP0054Id> CMP0054ReportedIds;
+
+ /**
+ * Determine if the given context, name pair has already been reported
+ * in context of CMP0054.
+ */
+ bool HasCMP0054AlreadyBeenReported(
+ cmListFileContext context) const;
/**
* Add an auxiliary directory to the build.
@@ -770,6 +799,10 @@ public:
*/
bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs,
std::vector<std::string>& outArgs) const;
+
+ bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs,
+ std::vector<cmExpandedCommandArgument>& outArgs) const;
+
/**
* Get the instance
*/
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index 693945d..a420f59 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -359,6 +359,11 @@ cmPolicies::cmPolicies()
CMP0053, "CMP0053",
"Simplify variable reference and escape sequence evaluation.",
3,1,0, cmPolicies::WARN);
+
+ this->DefinePolicy(
+ CMP0054, "CMP0054",
+ "Only interpret if() arguments as variables or keywords when unquoted.",
+ 3,1,0, cmPolicies::WARN);
}
cmPolicies::~cmPolicies()
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 5d69d14..7c73da8 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -109,6 +109,8 @@ public:
/// INTERFACE_INCLUDE_DIRECTORIES
CMP0053, ///< Simplify variable reference and escape sequence evaluation
+ CMP0054, ///< Only interpret if() arguments as variables
+ /// or keywords when unquoted.
/** \brief Always the last entry.
*
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index 7d2eead..851c4cb 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,
@@ -34,12 +34,14 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
std::string errorString;
- std::vector<std::string> expandedArguments;
+ std::vector<cmExpandedCommandArgument> 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;
}