Преглед изворни кода

If: Introduce policy CMP0054 - don't dereference quoted variables in if()

Nils Gladitz пре 11 година
родитељ
комит
188a1f236e
42 измењених фајлова са 846 додато и 93 уклоњено
  1. 6 0
      Help/command/if.rst
  2. 1 0
      Help/manual/cmake-policies.7.rst
  3. 18 0
      Help/policy/CMP0054.rst
  4. 6 0
      Help/release/dev/if-sanity.rst
  5. 1 0
      Source/cmBootstrapCommands2.cxx
  6. 157 79
      Source/cmConditionEvaluator.cxx
  7. 13 7
      Source/cmConditionEvaluator.h
  8. 51 0
      Source/cmExpandedCommandArgument.cxx
  9. 45 0
      Source/cmExpandedCommandArgument.h
  10. 5 5
      Source/cmIfCommand.cxx
  11. 4 0
      Source/cmIfCommand.h
  12. 61 0
      Source/cmMakefile.cxx
  13. 34 1
      Source/cmMakefile.h
  14. 5 0
      Source/cmPolicies.cxx
  15. 2 0
      Source/cmPolicies.h
  16. 1 1
      Source/cmWhileCommand.cxx
  17. 1 0
      Tests/RunCMake/CMP0054/CMP0054-NEW-stderr.txt
  18. 47 0
      Tests/RunCMake/CMP0054/CMP0054-NEW.cmake
  19. 1 0
      Tests/RunCMake/CMP0054/CMP0054-OLD-stderr.txt
  20. 47 0
      Tests/RunCMake/CMP0054/CMP0054-OLD.cmake
  21. 11 0
      Tests/RunCMake/CMP0054/CMP0054-WARN-stderr.txt
  22. 5 0
      Tests/RunCMake/CMP0054/CMP0054-WARN.cmake
  23. 24 0
      Tests/RunCMake/CMP0054/CMP0054-duplicate-warnings-stderr.txt
  24. 12 0
      Tests/RunCMake/CMP0054/CMP0054-duplicate-warnings.cmake
  25. 1 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-NEW-result.txt
  26. 8 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-NEW-stderr.txt
  27. 25 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-NEW.cmake
  28. 1 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-OLD-stderr.txt
  29. 9 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-OLD.cmake
  30. 12 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-WARN-stderr.txt
  31. 3 0
      Tests/RunCMake/CMP0054/CMP0054-keywords-WARN.cmake
  32. 1 0
      Tests/RunCMake/CMP0054/CMP0054-policy-command-scope-stderr.txt
  33. 53 0
      Tests/RunCMake/CMP0054/CMP0054-policy-command-scope.cmake
  34. 1 0
      Tests/RunCMake/CMP0054/CMP0054-policy-foreach-scope-stderr.txt
  35. 49 0
      Tests/RunCMake/CMP0054/CMP0054-policy-foreach-scope.cmake
  36. 1 0
      Tests/RunCMake/CMP0054/CMP0054-policy-nested-if-stderr.txt
  37. 41 0
      Tests/RunCMake/CMP0054/CMP0054-policy-nested-if.cmake
  38. 1 0
      Tests/RunCMake/CMP0054/CMP0054-policy-while-scope-stderr.txt
  39. 65 0
      Tests/RunCMake/CMP0054/CMP0054-policy-while-scope.cmake
  40. 3 0
      Tests/RunCMake/CMP0054/CMakeLists.txt
  41. 13 0
      Tests/RunCMake/CMP0054/RunCMakeTest.cmake
  42. 1 0
      Tests/RunCMake/CMakeLists.txt

+ 6 - 0
Help/command/if.rst

@@ -199,3 +199,9 @@ above-documented signature accepts ``<variable|string>``:
 * The left and right hand arguments to ``AND`` and ``OR`` are independently
 * The left and right hand arguments to ``AND`` and ``OR`` are independently
   tested to see if they are boolean constants, if so they are used as
   tested to see if they are boolean constants, if so they are used as
   such, otherwise they are assumed to be variables and are dereferenced.
   such, otherwise they are assumed to be variables and are dereferenced.
+
+To prevent ambiguity, potential variable or keyword names can be
+specified in a :ref:`Quoted Argument` or a :ref:`Bracket Argument`.
+A quoted or bracketed variable or keyword will be interpreted as a
+string and not dereferenced or interpreted.
+See policy :policy:`CMP0054`.

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -105,3 +105,4 @@ All Policies
    /policy/CMP0051
    /policy/CMP0051
    /policy/CMP0052
    /policy/CMP0052
    /policy/CMP0053
    /policy/CMP0053
+   /policy/CMP0054

+ 18 - 0
Help/policy/CMP0054.rst

@@ -0,0 +1,18 @@
+CMP0054
+-------
+
+Only interpret :command:`if` arguments as variables or keywords when unquoted.
+
+CMake 3.1 and above no longer dereference variables or interpret keywords
+in an :command:`if` command argument when it is a :ref:`Quoted Argument`
+or a :ref:`Bracket Argument`.
+
+The ``OLD`` behavior for this policy is to dereference variables and
+interpret keywords even if they are quoted or bracketed.
+The ``NEW`` behavior is to not dereference variables or interpret keywords
+that have been quoted or bracketed.
+
+This policy was introduced in CMake version 3.1.
+CMake version |release| warns when the policy is not set and uses
+``OLD`` behavior.  Use the :command:`cmake_policy` command to set
+it to ``OLD`` or ``NEW`` explicitly.

+ 6 - 0
Help/release/dev/if-sanity.rst

@@ -0,0 +1,6 @@
+if-sanity
+---------
+
+* The :command:`if` command no longer automatically dereferences
+  variables named in quoted or bracket arguments.  See policy
+  :policy:`CMP0054`.

+ 1 - 0
Source/cmBootstrapCommands2.cxx

@@ -15,6 +15,7 @@
 // like to have CMake to build CMake.
 // like to have CMake to build CMake.
 #include "cmCommands.h"
 #include "cmCommands.h"
 #include "cmConditionEvaluator.cxx"
 #include "cmConditionEvaluator.cxx"
+#include "cmExpandedCommandArgument.cxx"
 #include "cmGeneratorExpressionEvaluationFile.cxx"
 #include "cmGeneratorExpressionEvaluationFile.cxx"
 #include "cmGetCMakePropertyCommand.cxx"
 #include "cmGetCMakePropertyCommand.cxx"
 #include "cmGetDirectoryPropertyCommand.cxx"
 #include "cmGetDirectoryPropertyCommand.cxx"

+ 157 - 79
Source/cmConditionEvaluator.cxx

@@ -14,7 +14,8 @@
 
 
 cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile):
 cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile):
   Makefile(makefile),
   Makefile(makefile),
-  Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012))
+  Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012)),
+  Policy54Status(makefile.GetPolicyStatus(cmPolicies::CMP0054))
 {
 {
 
 
 }
 }
@@ -36,7 +37,7 @@ cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile):
 // directly. AND OR take variables or the values 0 or 1.
 // directly. AND OR take variables or the values 0 or 1.
 
 
 bool cmConditionEvaluator::IsTrue(
 bool cmConditionEvaluator::IsTrue(
-  const std::vector<std::string> &args,
+  const std::vector<cmExpandedCommandArgument> &args,
   std::string &errorString,
   std::string &errorString,
   cmake::MessageType &status)
   cmake::MessageType &status)
 {
 {
@@ -98,23 +99,94 @@ bool cmConditionEvaluator::IsTrue(
     errorString, status, true);
     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 char* cmConditionEvaluator::GetVariableOrString(
-    const std::string& str) const
+    const cmExpandedCommandArgument& argument) const
 {
 {
-  const char* def = this->Makefile.GetDefinition(str);
+  const char* def = this->GetDefinitionIfUnquoted(argument);
 
 
   if(!def)
   if(!def)
     {
     {
-    def = str.c_str();
+    def = argument.c_str();
     }
     }
 
 
   return def;
   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(
 bool cmConditionEvaluator::GetBooleanValue(
-  std::string& arg) const
+  cmExpandedCommandArgument& arg) const
 {
 {
   // Check basic constants.
   // Check basic constants.
   if (arg == "0")
   if (arg == "0")
@@ -149,14 +221,14 @@ bool cmConditionEvaluator::GetBooleanValue(
     }
     }
 
 
   // Check definition.
   // Check definition.
-  const char* def = this->Makefile.GetDefinition(arg);
+  const char* def = this->GetDefinitionIfUnquoted(arg);
   return !cmSystemTools::IsOff(def);
   return !cmSystemTools::IsOff(def);
 }
 }
 
 
 //=========================================================================
 //=========================================================================
 // Boolean value behavior from CMake 2.6.4 and below.
 // Boolean value behavior from CMake 2.6.4 and below.
 bool cmConditionEvaluator::GetBooleanValueOld(
 bool cmConditionEvaluator::GetBooleanValueOld(
-  std::string const& arg, bool one) const
+  cmExpandedCommandArgument const& arg, bool one) const
 {
 {
   if(one)
   if(one)
     {
     {
@@ -166,12 +238,15 @@ bool cmConditionEvaluator::GetBooleanValueOld(
     else if(arg == "1")
     else if(arg == "1")
       { return true; }
       { return true; }
     else
     else
-      { return !cmSystemTools::IsOff(this->Makefile.GetDefinition(arg)); }
+      {
+      const char* def = this->GetDefinitionIfUnquoted(arg);
+      return !cmSystemTools::IsOff(def);
+      }
     }
     }
   else
   else
     {
     {
     // Old GetVariableOrNumber behavior.
     // Old GetVariableOrNumber behavior.
-    const char* def = this->Makefile.GetDefinition(arg);
+    const char* def = this->GetDefinitionIfUnquoted(arg);
     if(!def && atoi(arg.c_str()))
     if(!def && atoi(arg.c_str()))
       {
       {
       def = arg.c_str();
       def = arg.c_str();
@@ -183,7 +258,7 @@ bool cmConditionEvaluator::GetBooleanValueOld(
 //=========================================================================
 //=========================================================================
 // returns the resulting boolean value
 // returns the resulting boolean value
 bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
 bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
-  std::string &newArg,
+  cmExpandedCommandArgument &newArg,
   std::string &errorString,
   std::string &errorString,
   cmake::MessageType &status,
   cmake::MessageType &status,
   bool oneArg) const
   bool oneArg) const
@@ -208,7 +283,7 @@ bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
       case cmPolicies::WARN:
       case cmPolicies::WARN:
         {
         {
         cmPolicies* policies = this->Makefile.GetPolicies();
         cmPolicies* policies = this->Makefile.GetPolicies();
-        errorString = "An argument named \"" + newArg
+        errorString = "An argument named \"" + newArg.GetValue()
           + "\" appears in a conditional statement.  "
           + "\" appears in a conditional statement.  "
           + policies->GetPolicyWarning(cmPolicies::CMP0012);
           + policies->GetPolicyWarning(cmPolicies::CMP0012);
         status = cmake::AUTHOR_WARNING;
         status = cmake::AUTHOR_WARNING;
@@ -219,7 +294,7 @@ bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
       case cmPolicies::REQUIRED_ALWAYS:
       case cmPolicies::REQUIRED_ALWAYS:
         {
         {
         cmPolicies* policies = this->Makefile.GetPolicies();
         cmPolicies* policies = this->Makefile.GetPolicies();
-        errorString = "An argument named \"" + newArg
+        errorString = "An argument named \"" + newArg.GetValue()
           + "\" appears in a conditional statement.  "
           + "\" appears in a conditional statement.  "
           + policies->GetRequiredPolicyError(cmPolicies::CMP0012);
           + policies->GetRequiredPolicyError(cmPolicies::CMP0012);
         status = cmake::FATAL_ERROR;
         status = cmake::FATAL_ERROR;
@@ -257,11 +332,11 @@ void cmConditionEvaluator::HandlePredicate(bool value, int &reducible,
 {
 {
   if(value)
   if(value)
     {
     {
-    *arg = "1";
+    *arg = cmExpandedCommandArgument("1", true);
     }
     }
   else
   else
     {
     {
-    *arg = "0";
+    *arg = cmExpandedCommandArgument("0", true);
     }
     }
   newArgs.erase(argP1);
   newArgs.erase(argP1);
   argP1 = arg;
   argP1 = arg;
@@ -279,11 +354,11 @@ void cmConditionEvaluator::HandleBinaryOp(bool value, int &reducible,
 {
 {
   if(value)
   if(value)
     {
     {
-    *arg = "1";
+    *arg = cmExpandedCommandArgument("1", true);
     }
     }
   else
   else
     {
     {
-    *arg = "0";
+    *arg = cmExpandedCommandArgument("0", true);
     }
     }
   newArgs.erase(argP2);
   newArgs.erase(argP2);
   newArgs.erase(argP1);
   newArgs.erase(argP1);
@@ -302,23 +377,23 @@ bool cmConditionEvaluator::HandleLevel0(cmArgumentList &newArgs,
   do
   do
     {
     {
     reducible = 0;
     reducible = 0;
-    std::list<std::string>::iterator arg = newArgs.begin();
+    cmArgumentList::iterator arg = newArgs.begin();
     while (arg != newArgs.end())
     while (arg != newArgs.end())
       {
       {
-      if (*arg == "(")
+      if (IsKeyword("(", *arg))
         {
         {
         // search for the closing paren for this opening one
         // search for the closing paren for this opening one
-        std::list<std::string>::iterator argClose;
+        cmArgumentList::iterator argClose;
         argClose = arg;
         argClose = arg;
         argClose++;
         argClose++;
         unsigned int depth = 1;
         unsigned int depth = 1;
         while (argClose != newArgs.end() && depth)
         while (argClose != newArgs.end() && depth)
           {
           {
-          if (*argClose == "(")
+          if (this->IsKeyword("(", *argClose))
             {
             {
               depth++;
               depth++;
             }
             }
-          if (*argClose == ")")
+          if (this->IsKeyword(")", *argClose))
             {
             {
               depth--;
               depth--;
             }
             }
@@ -331,10 +406,10 @@ bool cmConditionEvaluator::HandleLevel0(cmArgumentList &newArgs,
           return false;
           return false;
           }
           }
         // store the reduced args in this vector
         // store the reduced args in this vector
-        std::vector<std::string> newArgs2;
+        std::vector<cmExpandedCommandArgument> newArgs2;
 
 
         // copy to the list structure
         // copy to the list structure
-        std::list<std::string>::iterator argP1 = arg;
+        cmArgumentList::iterator argP1 = arg;
         argP1++;
         argP1++;
         for(; argP1 != argClose; argP1++)
         for(; argP1 != argClose; argP1++)
           {
           {
@@ -347,11 +422,11 @@ bool cmConditionEvaluator::HandleLevel0(cmArgumentList &newArgs,
           this->IsTrue(newArgs2, errorString, status);
           this->IsTrue(newArgs2, errorString, status);
         if(value)
         if(value)
           {
           {
-          *arg = "1";
+          *arg = cmExpandedCommandArgument("1", true);
           }
           }
         else
         else
           {
           {
-          *arg = "0";
+          *arg = cmExpandedCommandArgument("0", true);
           }
           }
         argP1 = arg;
         argP1 = arg;
         argP1++;
         argP1++;
@@ -374,77 +449,78 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList &newArgs,
   do
   do
     {
     {
     reducible = 0;
     reducible = 0;
-    std::list<std::string>::iterator arg = newArgs.begin();
-    std::list<std::string>::iterator argP1;
-    std::list<std::string>::iterator argP2;
+    cmArgumentList::iterator arg = newArgs.begin();
+    cmArgumentList::iterator argP1;
+    cmArgumentList::iterator argP2;
     while (arg != newArgs.end())
     while (arg != newArgs.end())
       {
       {
       argP1 = arg;
       argP1 = arg;
       this->IncrementArguments(newArgs,argP1,argP2);
       this->IncrementArguments(newArgs,argP1,argP2);
       // does a file exist
       // does a file exist
-      if (*arg == "EXISTS" && argP1  != newArgs.end())
+      if (this->IsKeyword("EXISTS", *arg) && argP1  != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          cmSystemTools::FileExists((argP1)->c_str()),
+          cmSystemTools::FileExists(argP1->c_str()),
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // does a directory with this name exist
       // does a directory with this name exist
-      if (*arg == "IS_DIRECTORY" && argP1  != newArgs.end())
+      if (this->IsKeyword("IS_DIRECTORY", *arg) && argP1  != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          cmSystemTools::FileIsDirectory((argP1)->c_str()),
+          cmSystemTools::FileIsDirectory(argP1->c_str()),
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // does a symlink with this name exist
       // does a symlink with this name exist
-      if (*arg == "IS_SYMLINK" && argP1  != newArgs.end())
+      if (this->IsKeyword("IS_SYMLINK", *arg) && argP1  != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          cmSystemTools::FileIsSymlink((argP1)->c_str()),
+          cmSystemTools::FileIsSymlink(argP1->c_str()),
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // is the given path an absolute path ?
       // is the given path an absolute path ?
-      if (*arg == "IS_ABSOLUTE" && argP1  != newArgs.end())
+      if (this->IsKeyword("IS_ABSOLUTE", *arg) && argP1  != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          cmSystemTools::FileIsFullPath((argP1)->c_str()),
+          cmSystemTools::FileIsFullPath(argP1->c_str()),
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // does a command exist
       // does a command exist
-      if (*arg == "COMMAND" && argP1  != newArgs.end())
+      if (this->IsKeyword("COMMAND", *arg) && argP1  != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          this->Makefile.CommandExists((argP1)->c_str()),
+          this->Makefile.CommandExists(argP1->c_str()),
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // does a policy exist
       // does a policy exist
-      if (*arg == "POLICY" && argP1 != newArgs.end())
+      if (this->IsKeyword("POLICY", *arg) && argP1 != newArgs.end())
         {
         {
         cmPolicies::PolicyID pid;
         cmPolicies::PolicyID pid;
         this->HandlePredicate(
         this->HandlePredicate(
-          this->Makefile.GetPolicies()->GetPolicyID((argP1)->c_str(), pid),
-          reducible, arg, newArgs, argP1, argP2);
+          this->Makefile.GetPolicies()->GetPolicyID(
+            argP1->c_str(), pid),
+            reducible, arg, newArgs, argP1, argP2);
         }
         }
       // does a target exist
       // does a target exist
-      if (*arg == "TARGET" && argP1 != newArgs.end())
+      if (this->IsKeyword("TARGET", *arg) && argP1 != newArgs.end())
         {
         {
         this->HandlePredicate(
         this->HandlePredicate(
-          this->Makefile.FindTargetToUse(*argP1)?true:false,
+          this->Makefile.FindTargetToUse(argP1->GetValue())?true:false,
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
       // is a variable defined
       // is a variable defined
-      if (*arg == "DEFINED" && argP1  != newArgs.end())
+      if (this->IsKeyword("DEFINED", *arg) && argP1  != newArgs.end())
         {
         {
-        size_t argP1len = argP1->size();
+        size_t argP1len = argP1->GetValue().size();
         bool bdef = false;
         bool bdef = false;
-        if(argP1len > 4 && argP1->substr(0, 4) == "ENV{" &&
-           argP1->operator[](argP1len-1) == '}')
+        if(argP1len > 4 && argP1->GetValue().substr(0, 4) == "ENV{" &&
+           argP1->GetValue().operator[](argP1len-1) == '}')
           {
           {
-          std::string env = argP1->substr(4, argP1len-5);
+          std::string env = argP1->GetValue().substr(4, argP1len-5);
           bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
           bdef = cmSystemTools::GetEnv(env.c_str())?true:false;
           }
           }
         else
         else
           {
           {
-          bdef = this->Makefile.IsDefinitionSet(*(argP1));
+          bdef = this->Makefile.IsDefinitionSet(argP1->GetValue());
           }
           }
         this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
         this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
         }
         }
@@ -467,18 +543,18 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
   do
   do
     {
     {
     reducible = 0;
     reducible = 0;
-    std::list<std::string>::iterator arg = newArgs.begin();
-    std::list<std::string>::iterator argP1;
-    std::list<std::string>::iterator argP2;
+    cmArgumentList::iterator arg = newArgs.begin();
+    cmArgumentList::iterator argP1;
+    cmArgumentList::iterator argP2;
     while (arg != newArgs.end())
     while (arg != newArgs.end())
       {
       {
       argP1 = arg;
       argP1 = arg;
       this->IncrementArguments(newArgs,argP1,argP2);
       this->IncrementArguments(newArgs,argP1,argP2);
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-        *(argP1) == "MATCHES")
+        IsKeyword("MATCHES", *argP1))
         {
         {
         def = this->GetVariableOrString(*arg);
         def = this->GetVariableOrString(*arg);
-        const char* rex = (argP2)->c_str();
+        const char* rex = argP2->c_str();
         this->Makefile.ClearMatches();
         this->Makefile.ClearMatches();
         cmsys::RegularExpression regEntry;
         cmsys::RegularExpression regEntry;
         if ( !regEntry.compile(rex) )
         if ( !regEntry.compile(rex) )
@@ -492,11 +568,11 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
         if (regEntry.find(def))
         if (regEntry.find(def))
           {
           {
           this->Makefile.StoreMatches(regEntry);
           this->Makefile.StoreMatches(regEntry);
-          *arg = "1";
+          *arg = cmExpandedCommandArgument("1", true);
           }
           }
         else
         else
           {
           {
-          *arg = "0";
+          *arg = cmExpandedCommandArgument("0", true);
           }
           }
         newArgs.erase(argP2);
         newArgs.erase(argP2);
         newArgs.erase(argP1);
         newArgs.erase(argP1);
@@ -505,9 +581,9 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
         reducible = 1;
         reducible = 1;
         }
         }
 
 
-      if (argP1 != newArgs.end() && *arg == "MATCHES")
+      if (argP1 != newArgs.end() && this->IsKeyword("MATCHES", *arg))
         {
         {
-        *arg = "0";
+        *arg = cmExpandedCommandArgument("0", true);
         newArgs.erase(argP1);
         newArgs.erase(argP1);
         argP1 = arg;
         argP1 = arg;
         this->IncrementArguments(newArgs,argP1,argP2);
         this->IncrementArguments(newArgs,argP1,argP2);
@@ -515,8 +591,9 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
         }
         }
 
 
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-        (*(argP1) == "LESS" || *(argP1) == "GREATER" ||
-         *(argP1) == "EQUAL"))
+        (this->IsKeyword("LESS", *argP1) ||
+         this->IsKeyword("GREATER", *argP1) ||
+         this->IsKeyword("EQUAL", *argP1)))
         {
         {
         def = this->GetVariableOrString(*arg);
         def = this->GetVariableOrString(*arg);
         def2 = this->GetVariableOrString(*argP2);
         def2 = this->GetVariableOrString(*argP2);
@@ -540,14 +617,14 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
           {
           {
           result = (lhs == rhs);
           result = (lhs == rhs);
           }
           }
-        HandleBinaryOp(result,
+        this->HandleBinaryOp(result,
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
 
 
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-        (*(argP1) == "STRLESS" ||
-         *(argP1) == "STREQUAL" ||
-         *(argP1) == "STRGREATER"))
+        (this->IsKeyword("STRLESS", *argP1) ||
+         this->IsKeyword("STREQUAL", *argP1) ||
+         this->IsKeyword("STRGREATER", *argP1)))
         {
         {
         def = this->GetVariableOrString(*arg);
         def = this->GetVariableOrString(*arg);
         def2 = this->GetVariableOrString(*argP2);
         def2 = this->GetVariableOrString(*argP2);
@@ -570,8 +647,9 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
         }
         }
 
 
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-        (*(argP1) == "VERSION_LESS" || *(argP1) == "VERSION_GREATER" ||
-         *(argP1) == "VERSION_EQUAL"))
+        (this->IsKeyword("VERSION_LESS", *argP1) ||
+         this->IsKeyword("VERSION_GREATER", *argP1) ||
+         this->IsKeyword("VERSION_EQUAL", *argP1)))
         {
         {
         def = this->GetVariableOrString(*arg);
         def = this->GetVariableOrString(*arg);
         def2 = this->GetVariableOrString(*argP2);
         def2 = this->GetVariableOrString(*argP2);
@@ -591,11 +669,11 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
 
 
       // is file A newer than file B
       // is file A newer than file B
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          *(argP1) == "IS_NEWER_THAN")
+          this->IsKeyword("IS_NEWER_THAN", *argP1))
         {
         {
         int fileIsNewer=0;
         int fileIsNewer=0;
-        bool success=cmSystemTools::FileTimeCompare(arg->c_str(),
-            (argP2)->c_str(),
+        bool success=cmSystemTools::FileTimeCompare(arg->GetValue(),
+            (argP2)->GetValue(),
             &fileIsNewer);
             &fileIsNewer);
         this->HandleBinaryOp(
         this->HandleBinaryOp(
           (success==false || fileIsNewer==1 || fileIsNewer==0),
           (success==false || fileIsNewer==1 || fileIsNewer==0),
@@ -619,14 +697,14 @@ bool cmConditionEvaluator::HandleLevel3(cmArgumentList &newArgs,
   do
   do
     {
     {
     reducible = 0;
     reducible = 0;
-    std::list<std::string>::iterator arg = newArgs.begin();
-    std::list<std::string>::iterator argP1;
-    std::list<std::string>::iterator argP2;
+    cmArgumentList::iterator arg = newArgs.begin();
+    cmArgumentList::iterator argP1;
+    cmArgumentList::iterator argP2;
     while (arg != newArgs.end())
     while (arg != newArgs.end())
       {
       {
       argP1 = arg;
       argP1 = arg;
       IncrementArguments(newArgs,argP1,argP2);
       IncrementArguments(newArgs,argP1,argP2);
-      if (argP1 != newArgs.end() && *arg == "NOT")
+      if (argP1 != newArgs.end() && IsKeyword("NOT", *arg))
         {
         {
         bool rhs = this->GetBooleanValueWithAutoDereference(*argP1,
         bool rhs = this->GetBooleanValueWithAutoDereference(*argP1,
                                                       errorString,
                                                       errorString,
@@ -652,14 +730,14 @@ bool cmConditionEvaluator::HandleLevel4(cmArgumentList &newArgs,
   do
   do
     {
     {
     reducible = 0;
     reducible = 0;
-    std::list<std::string>::iterator arg = newArgs.begin();
-    std::list<std::string>::iterator argP1;
-    std::list<std::string>::iterator argP2;
+    cmArgumentList::iterator arg = newArgs.begin();
+    cmArgumentList::iterator argP1;
+    cmArgumentList::iterator argP2;
     while (arg != newArgs.end())
     while (arg != newArgs.end())
       {
       {
       argP1 = arg;
       argP1 = arg;
       IncrementArguments(newArgs,argP1,argP2);
       IncrementArguments(newArgs,argP1,argP2);
-      if (argP1 != newArgs.end() && *(argP1) == "AND" &&
+      if (argP1 != newArgs.end() && IsKeyword("AND", *argP1) &&
         argP2 != newArgs.end())
         argP2 != newArgs.end())
         {
         {
         lhs = this->GetBooleanValueWithAutoDereference(*arg,
         lhs = this->GetBooleanValueWithAutoDereference(*arg,
@@ -672,7 +750,7 @@ bool cmConditionEvaluator::HandleLevel4(cmArgumentList &newArgs,
           reducible, arg, newArgs, argP1, argP2);
           reducible, arg, newArgs, argP1, argP2);
         }
         }
 
 
-      if (argP1 != newArgs.end() && *(argP1) == "OR" &&
+      if (argP1 != newArgs.end() && this->IsKeyword("OR", *argP1) &&
         argP2 != newArgs.end())
         argP2 != newArgs.end())
         {
         {
         lhs = this->GetBooleanValueWithAutoDereference(*arg,
         lhs = this->GetBooleanValueWithAutoDereference(*arg,

+ 13 - 7
Source/cmConditionEvaluator.h

@@ -13,36 +13,41 @@
 #define cmConditionEvaluator_h
 #define cmConditionEvaluator_h
 
 
 #include "cmCommand.h"
 #include "cmCommand.h"
+#include "cmExpandedCommandArgument.h"
 
 
 class cmConditionEvaluator
 class cmConditionEvaluator
 {
 {
 public:
 public:
-  typedef std::list<std::string> cmArgumentList;
+  typedef std::list<cmExpandedCommandArgument> cmArgumentList;
 
 
   cmConditionEvaluator(cmMakefile& makefile);
   cmConditionEvaluator(cmMakefile& makefile);
 
 
   // this is a shared function for both If and Else to determine if the
   // 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
   // arguments were valid, and if so, was the response true. If there is
   // an error, the errorString will be set.
   // an error, the errorString will be set.
-  bool IsTrue(const std::vector<std::string> &args,
+  bool IsTrue(const std::vector<cmExpandedCommandArgument> &args,
       std::string &errorString,
       std::string &errorString,
       cmake::MessageType &status);
       cmake::MessageType &status);
 
 
 private:
 private:
+  // Filter the given variable definition based on policy CMP0054.
+  const char* GetDefinitionIfUnquoted(
+      const cmExpandedCommandArgument& argument) const;
+
   const char* GetVariableOrString(
   const char* GetVariableOrString(
-      const std::string& argument) const;
+      const cmExpandedCommandArgument& argument) const;
 
 
   bool IsKeyword(std::string const& keyword,
   bool IsKeyword(std::string const& keyword,
-    std::string& argument) const;
+    cmExpandedCommandArgument& argument) const;
 
 
   bool GetBooleanValue(
   bool GetBooleanValue(
-    std::string& arg) const;
+    cmExpandedCommandArgument& arg) const;
 
 
   bool GetBooleanValueOld(
   bool GetBooleanValueOld(
-    std::string const& arg, bool one) const;
+    cmExpandedCommandArgument const& arg, bool one) const;
 
 
   bool GetBooleanValueWithAutoDereference(
   bool GetBooleanValueWithAutoDereference(
-    std::string &newArg,
+    cmExpandedCommandArgument &newArg,
     std::string &errorString,
     std::string &errorString,
     cmake::MessageType &status,
     cmake::MessageType &status,
     bool oneArg = false) const;
     bool oneArg = false) const;
@@ -85,6 +90,7 @@ private:
 
 
   cmMakefile& Makefile;
   cmMakefile& Makefile;
   cmPolicies::PolicyStatus Policy12Status;
   cmPolicies::PolicyStatus Policy12Status;
+  cmPolicies::PolicyStatus Policy54Status;
 };
 };
 
 
 #endif
 #endif

+ 51 - 0
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();
+}

+ 45 - 0
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

+ 5 - 5
Source/cmIfCommand.cxx

@@ -20,15 +20,15 @@
 
 
 
 
 static std::string cmIfCommandError(
 static std::string cmIfCommandError(
-  cmMakefile* mf, std::vector<std::string> const& args)
+  cmMakefile* mf, std::vector<cmExpandedCommandArgument> const& args)
 {
 {
   cmLocalGenerator* lg = mf->GetLocalGenerator();
   cmLocalGenerator* lg = mf->GetLocalGenerator();
   std::string err = "given arguments:\n ";
   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)
       i != args.end(); ++i)
     {
     {
     err += " ";
     err += " ";
-    err += lg->EscapeForCMake(*i);
+    err += lg->EscapeForCMake(i->GetValue());
     }
     }
   err += "\n";
   err += "\n";
   return err;
   return err;
@@ -105,7 +105,7 @@ IsFunctionBlocked(const cmListFileFunction& lff,
 
 
             std::string errorString;
             std::string errorString;
 
 
-            std::vector<std::string> expandedArguments;
+            std::vector<cmExpandedCommandArgument> expandedArguments;
             mf.ExpandArguments(this->Functions[c].Arguments,
             mf.ExpandArguments(this->Functions[c].Arguments,
                                expandedArguments);
                                expandedArguments);
 
 
@@ -189,7 +189,7 @@ bool cmIfCommand
 {
 {
   std::string errorString;
   std::string errorString;
 
 
-  std::vector<std::string> expandedArguments;
+  std::vector<cmExpandedCommandArgument> expandedArguments;
   this->Makefile->ExpandArguments(args, expandedArguments);
   this->Makefile->ExpandArguments(args, expandedArguments);
 
 
   cmake::MessageType status;
   cmake::MessageType status;

+ 4 - 0
Source/cmIfCommand.h

@@ -70,6 +70,10 @@ public:
    */
    */
   virtual bool IsScriptable() const { return true; }
   virtual bool IsScriptable() const { return true; }
 
 
+  // Filter the given variable definition based on policy CMP0054.
+  static const char* GetDefinitionIfUnquoted(
+    const cmMakefile* mf, cmExpandedCommandArgument const& argument);
+
   cmTypeMacro(cmIfCommand, cmCommand);
   cmTypeMacro(cmIfCommand, cmCommand);
 };
 };
 
 

+ 61 - 0
Source/cmMakefile.cxx

@@ -3292,6 +3292,7 @@ void cmMakefile::PopFunctionBlockerBarrier(bool reportError)
   this->FunctionBlockerBarriers.pop_back();
   this->FunctionBlockerBarriers.pop_back();
 }
 }
 
 
+//----------------------------------------------------------------------------
 bool cmMakefile::ExpandArguments(
 bool cmMakefile::ExpandArguments(
   std::vector<cmListFileArgument> const& inArgs,
   std::vector<cmListFileArgument> const& inArgs,
   std::vector<std::string>& outArgs) const
   std::vector<std::string>& outArgs) const
@@ -3327,6 +3328,47 @@ bool cmMakefile::ExpandArguments(
   return !cmSystemTools::GetFatalErrorOccured();
   return !cmSystemTools::GetFatalErrorOccured();
 }
 }
 
 
+//----------------------------------------------------------------------------
+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)
 void cmMakefile::AddFunctionBlocker(cmFunctionBlocker* fb)
 {
 {
@@ -4938,12 +4980,14 @@ void cmMakefile::PopPolicyBarrier(bool reportError)
   this->PolicyBarriers.pop_back();
   this->PolicyBarriers.pop_back();
 }
 }
 
 
+//----------------------------------------------------------------------------
 bool cmMakefile::SetPolicyVersion(const char *version)
 bool cmMakefile::SetPolicyVersion(const char *version)
 {
 {
   return this->GetCMakeInstance()->GetPolicies()->
   return this->GetCMakeInstance()->GetPolicies()->
     ApplyPolicyVersion(this,version);
     ApplyPolicyVersion(this,version);
 }
 }
 
 
+//----------------------------------------------------------------------------
 cmPolicies *cmMakefile::GetPolicies() const
 cmPolicies *cmMakefile::GetPolicies() const
 {
 {
   if (!this->GetCMakeInstance())
   if (!this->GetCMakeInstance())
@@ -4953,6 +4997,23 @@ cmPolicies *cmMakefile::GetPolicies() const
   return this->GetCMakeInstance()->GetPolicies();
   return this->GetCMakeInstance()->GetPolicies();
 }
 }
 
 
+//----------------------------------------------------------------------------
+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)
 void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm)
 {
 {

+ 34 - 1
Source/cmMakefile.h

@@ -21,6 +21,7 @@
 #include "cmTarget.h"
 #include "cmTarget.h"
 #include "cmNewLineStyle.h"
 #include "cmNewLineStyle.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
+#include "cmExpandedCommandArgument.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #if defined(CMAKE_BUILD_WITH_CMAKE)
@@ -375,7 +376,35 @@ public:
   /**
   /**
     * Get the Policies Instance
     * 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.
    * Add an auxiliary directory to the build.
@@ -770,6 +799,10 @@ public:
    */
    */
   bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs,
   bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs,
                        std::vector<std::string>& outArgs) const;
                        std::vector<std::string>& outArgs) const;
+
+  bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs,
+                       std::vector<cmExpandedCommandArgument>& outArgs) const;
+
   /**
   /**
    * Get the instance
    * Get the instance
    */
    */

+ 5 - 0
Source/cmPolicies.cxx

@@ -359,6 +359,11 @@ cmPolicies::cmPolicies()
     CMP0053, "CMP0053",
     CMP0053, "CMP0053",
     "Simplify variable reference and escape sequence evaluation.",
     "Simplify variable reference and escape sequence evaluation.",
     3,1,0, cmPolicies::WARN);
     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()
 cmPolicies::~cmPolicies()

+ 2 - 0
Source/cmPolicies.h

@@ -109,6 +109,8 @@ public:
     /// INTERFACE_INCLUDE_DIRECTORIES
     /// INTERFACE_INCLUDE_DIRECTORIES
 
 
     CMP0053, ///< Simplify variable reference and escape sequence evaluation
     CMP0053, ///< Simplify variable reference and escape sequence evaluation
+    CMP0054, ///< Only interpret if() arguments as variables
+    /// or keywords when unquoted.
 
 
     /** \brief Always the last entry.
     /** \brief Always the last entry.
      *
      *

+ 1 - 1
Source/cmWhileCommand.cxx

@@ -34,7 +34,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
 
 
       std::string errorString;
       std::string errorString;
 
 
-      std::vector<std::string> expandedArguments;
+      std::vector<cmExpandedCommandArgument> expandedArguments;
       mf.ExpandArguments(this->Args, expandedArguments);
       mf.ExpandArguments(this->Args, expandedArguments);
       cmake::MessageType messageType;
       cmake::MessageType messageType;
 
 

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-NEW-stderr.txt

@@ -0,0 +1 @@
+$^

+ 47 - 0
Tests/RunCMake/CMP0054/CMP0054-NEW.cmake

@@ -0,0 +1,47 @@
+cmake_policy(SET CMP0054 NEW)
+
+set(FOO "BAR")
+set(BAZ "ZZZ")
+set(MYTRUE ON)
+set(MYNUMBER 3)
+set(MYVERSION 3.0)
+
+function(assert_unquoted PREFIX FIRST)
+  string(REPLACE ";" " " ARGN_SP "${ARGN}")
+  if(${PREFIX} ${FIRST} ${ARGN})
+    message(FATAL_ERROR "Assertion failed [${PREFIX} ${FIRST} ${ARGN_SP}]")
+  endif()
+endfunction()
+
+function(assert_quoted PREFIX FIRST)
+  string(REPLACE ";" " " ARGN_SP "${ARGN}")
+  if(${PREFIX} "${FIRST}" ${ARGN})
+    message(FATAL_ERROR "Assertion failed [${PREFIX} \"${FIRST}\" ${ARGN_SP}]")
+  endif()
+endfunction()
+
+function(assert FIRST)
+  assert_unquoted(NOT ${FIRST} ${ARGN})
+  assert_quoted("" ${FIRST} ${ARGN})
+endfunction()
+
+assert(MYTRUE)
+
+assert(FOO MATCHES "^BAR$")
+
+assert(MYNUMBER LESS 4)
+assert(MYNUMBER GREATER 2)
+assert(MYNUMBER EQUAL 3)
+
+assert(FOO STRLESS CCC)
+assert(BAZ STRGREATER CCC)
+assert(FOO STREQUAL BAR)
+
+assert_unquoted(NOT MYVERSION VERSION_LESS 3.1)
+assert_unquoted("" MYVERSION VERSION_LESS 2.9)
+
+assert_quoted(NOT MYVERSION VERSION_LESS 2.9)
+assert_quoted(NOT MYVERSION VERSION_LESS 3.1)
+
+assert(MYVERSION VERSION_GREATER 2.9)
+assert(MYVERSION VERSION_EQUAL 3.0)

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-OLD-stderr.txt

@@ -0,0 +1 @@
+$^

+ 47 - 0
Tests/RunCMake/CMP0054/CMP0054-OLD.cmake

@@ -0,0 +1,47 @@
+cmake_policy(SET CMP0054 OLD)
+
+set(FOO "BAR")
+set(BAZ "ZZZ")
+set(MYTRUE ON)
+set(MYNUMBER 3)
+set(MYVERSION 3.0)
+
+function(assert_unquoted PREFIX FIRST)
+  string(REPLACE ";" " " ARGN_SP "${ARGN}")
+  if(${PREFIX} ${FIRST} ${ARGN})
+    message(FATAL_ERROR "Assertion failed [${PREFIX} ${FIRST} ${ARGN_SP}]")
+  endif()
+endfunction()
+
+function(assert_quoted PREFIX FIRST)
+  string(REPLACE ";" " " ARGN_SP "${ARGN}")
+  if(${PREFIX} "${FIRST}" ${ARGN})
+    message(FATAL_ERROR "Assertion failed [${PREFIX} \"${FIRST}\" ${ARGN_SP}]")
+  endif()
+endfunction()
+
+function(assert FIRST)
+  assert_unquoted(NOT ${FIRST} ${ARGN})
+  assert_quoted(NOT ${FIRST} ${ARGN})
+endfunction()
+
+assert(MYTRUE)
+
+assert(FOO MATCHES "^BAR$")
+
+assert(MYNUMBER LESS 4)
+assert(MYNUMBER GREATER 2)
+assert(MYNUMBER EQUAL 3)
+
+assert(FOO STRLESS CCC)
+assert(BAZ STRGREATER CCC)
+assert(FOO STREQUAL BAR)
+
+assert_unquoted(NOT MYVERSION VERSION_LESS 3.1)
+assert_unquoted("" MYVERSION VERSION_LESS 2.9)
+
+assert_quoted("" MYVERSION VERSION_LESS 2.9)
+assert_quoted(NOT MYVERSION VERSION_LESS 3.1)
+
+assert(MYVERSION VERSION_GREATER 2.9)
+assert(MYVERSION VERSION_EQUAL 3.0)

+ 11 - 0
Tests/RunCMake/CMP0054/CMP0054-WARN-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at CMP0054-WARN.cmake:3 \(if\):
+  Policy CMP0054 is not set: Only interpret if\(\) arguments as variables or
+  keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
+  details.  Use the cmake_policy command to set the policy and suppress this
+  warning.
+
+  Quoted variables like "FOO" will no longer be dereferenced when the policy
+  is set to NEW.  Since the policy is not set the OLD behavior will be used.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 5 - 0
Tests/RunCMake/CMP0054/CMP0054-WARN.cmake

@@ -0,0 +1,5 @@
+set(FOO "BAR")
+
+if(NOT "FOO" STREQUAL "BAR")
+  message(FATAL_ERROR "The given literals should match")
+endif()

+ 24 - 0
Tests/RunCMake/CMP0054/CMP0054-duplicate-warnings-stderr.txt

@@ -0,0 +1,24 @@
+CMake Warning \(dev\) at CMP0054-duplicate-warnings.cmake:4 \(if\):
+  Policy CMP0054 is not set: Only interpret if\(\) arguments as variables or
+  keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
+  details.  Use the cmake_policy command to set the policy and suppress this
+  warning.
+
+  Quoted variables like "FOO" will no longer be dereferenced when the policy
+  is set to NEW.  Since the policy is not set the OLD behavior will be used.
+Call Stack \(most recent call first\):
+  CMP0054-duplicate-warnings.cmake:8 \(generate_warning\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at CMP0054-duplicate-warnings.cmake:11 \(if\):
+  Policy CMP0054 is not set: Only interpret if\(\) arguments as variables or
+  keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
+  details.  Use the cmake_policy command to set the policy and suppress this
+  warning.
+
+  Quoted variables like "FOO" will no longer be dereferenced when the policy
+  is set to NEW.  Since the policy is not set the OLD behavior will be used.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 12 - 0
Tests/RunCMake/CMP0054/CMP0054-duplicate-warnings.cmake

@@ -0,0 +1,12 @@
+set(FOO "BAR")
+
+function(generate_warning)
+  if("FOO" STREQUAL "BAR")
+  endif()
+endfunction()
+
+generate_warning()
+generate_warning()
+
+if("FOO" STREQUAL "BAR")
+endif()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-NEW-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-NEW-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at CMP0054-keywords-NEW.cmake:23 \(if\):
+  if given arguments:
+
+    "NOT" "1"
+
+  Unknown arguments specified
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 25 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-NEW.cmake

@@ -0,0 +1,25 @@
+cmake_policy(SET CMP0054 NEW)
+
+function(assert KEYWORD)
+  if("${KEYWORD}" STREQUAL "${KEYWORD}")
+  else()
+    message(FATAL_ERROR
+      "Assertion failed [\"${KEYWORD}\" STREQUAL \"${KEYWORD}\"]")
+  endif()
+endfunction()
+
+assert("NOT")
+assert("COMMAND")
+assert("POLICY")
+assert("TARGET")
+assert("EXISTS")
+assert("IS_DIRECTORY")
+assert("IS_SYMLINK")
+assert("IS_ABSOLUTE")
+assert("DEFINED")
+assert("(")
+assert(")")
+
+if("NOT" 1)
+  message(FATAL_ERROR "[\"NOT\" 1] evaluated true")
+endif()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-OLD-stderr.txt

@@ -0,0 +1 @@
+$^

+ 9 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-OLD.cmake

@@ -0,0 +1,9 @@
+cmake_policy(SET CMP0054 OLD)
+
+if(NOT 1)
+  message(FATAL_ERROR "[NOT 1] evaluated true")
+endif()
+
+if("NOT" 1)
+  message(FATAL_ERROR "[\"NOT\" 1] evaluated true")
+endif()

+ 12 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-WARN-stderr.txt

@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at CMP0054-keywords-WARN.cmake:1 \(if\):
+  Policy CMP0054 is not set: Only interpret if\(\) arguments as variables or
+  keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
+  details.  Use the cmake_policy command to set the policy and suppress this
+  warning.
+
+  Quoted keywords like "NOT" 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.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 3 - 0
Tests/RunCMake/CMP0054/CMP0054-keywords-WARN.cmake

@@ -0,0 +1,3 @@
+if("NOT" 1)
+  message(FATAL_ERROR "[\"NOT\" 1] evaluated true")
+endif()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-command-scope-stderr.txt

@@ -0,0 +1 @@
+$^

+ 53 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-command-scope.cmake

@@ -0,0 +1,53 @@
+set(FOO BAR)
+
+cmake_policy(SET CMP0054 NEW)
+
+function(function_defined_new_called_old)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+endfunction()
+
+macro(macro_defined_new_called_old)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+endmacro()
+
+cmake_policy(SET CMP0054 OLD)
+
+function_defined_new_called_old()
+macro_defined_new_called_old()
+
+function(function_defined_old_called_new)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+endfunction()
+
+macro(macro_defined_old_called_new)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+endmacro()
+
+cmake_policy(SET CMP0054 NEW)
+
+function_defined_old_called_new()
+macro_defined_old_called_new()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-foreach-scope-stderr.txt

@@ -0,0 +1 @@
+$^

+ 49 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-foreach-scope.cmake

@@ -0,0 +1,49 @@
+set(FOO BAR)
+
+cmake_policy(SET CMP0054 NEW)
+
+foreach(loop_var arg1 arg2)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+
+  cmake_policy(SET CMP0054 OLD)
+
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+
+  cmake_policy(SET CMP0054 NEW)
+endforeach()
+
+cmake_policy(SET CMP0054 OLD)
+
+foreach(loop_var arg1 arg2)
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+
+  cmake_policy(SET CMP0054 NEW)
+
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+
+  cmake_policy(SET CMP0054 OLD)
+endforeach()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-nested-if-stderr.txt

@@ -0,0 +1 @@
+$^

+ 41 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-nested-if.cmake

@@ -0,0 +1,41 @@
+set(FOO BAR)
+
+cmake_policy(SET CMP0054 NEW)
+
+if("FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The strings should not match")
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+
+  cmake_policy(SET CMP0054 OLD)
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+endif()
+
+if("FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The strings should not match")
+endif()
+
+cmake_policy(SET CMP0054 OLD)
+
+if(NOT "FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The quoted variable should match the string")
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+
+  cmake_policy(SET CMP0054 NEW)
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+endif()
+
+if(NOT "FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The quoted variable should match the string")
+endif()

+ 1 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-while-scope-stderr.txt

@@ -0,0 +1 @@
+$^

+ 65 - 0
Tests/RunCMake/CMP0054/CMP0054-policy-while-scope.cmake

@@ -0,0 +1,65 @@
+set(FOO BAR)
+
+set(LOOP_VAR "")
+
+cmake_policy(SET CMP0054 NEW)
+
+while(NOT LOOP_VAR STREQUAL "xx")
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+
+  cmake_policy(SET CMP0054 OLD)
+
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+
+  cmake_policy(SET CMP0054 NEW)
+
+  set(LOOP_VAR "${LOOP_VAR}x")
+endwhile()
+
+while("FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The strings should not match")
+endwhile()
+
+set(LOOP_VAR "")
+
+cmake_policy(SET CMP0054 OLD)
+
+while(NOT LOOP_VAR STREQUAL "xx")
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if(NOT "FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The quoted variable should match the string")
+  endif()
+
+  cmake_policy(SET CMP0054 NEW)
+
+  if(NOT FOO STREQUAL BAR)
+    message(FATAL_ERROR "The variable should match the string")
+  endif()
+
+  if("FOO" STREQUAL BAR)
+    message(FATAL_ERROR "The strings should not match")
+  endif()
+
+  cmake_policy(SET CMP0054 OLD)
+
+  set(LOOP_VAR "${LOOP_VAR}x")
+endwhile()
+
+if(NOT "FOO" STREQUAL BAR)
+  message(FATAL_ERROR "The quoted variable should match the string")
+endif()

+ 3 - 0
Tests/RunCMake/CMP0054/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 13 - 0
Tests/RunCMake/CMP0054/RunCMakeTest.cmake

@@ -0,0 +1,13 @@
+include(RunCMake)
+
+run_cmake(CMP0054-OLD)
+run_cmake(CMP0054-NEW)
+run_cmake(CMP0054-WARN)
+run_cmake(CMP0054-keywords-NEW)
+run_cmake(CMP0054-keywords-OLD)
+run_cmake(CMP0054-keywords-WARN)
+run_cmake(CMP0054-duplicate-warnings)
+run_cmake(CMP0054-policy-command-scope)
+run_cmake(CMP0054-policy-foreach-scope)
+run_cmake(CMP0054-policy-while-scope)
+run_cmake(CMP0054-policy-nested-if)

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -47,6 +47,7 @@ add_RunCMake_test(CMP0049)
 add_RunCMake_test(CMP0050)
 add_RunCMake_test(CMP0050)
 add_RunCMake_test(CMP0051)
 add_RunCMake_test(CMP0051)
 add_RunCMake_test(CMP0053)
 add_RunCMake_test(CMP0053)
+add_RunCMake_test(CMP0054)
 add_RunCMake_test(CTest)
 add_RunCMake_test(CTest)
 if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles")
 if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles")
   add_RunCMake_test(CompilerChange)
   add_RunCMake_test(CompilerChange)