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

This commit is contained in:
Nils Gladitz 2014-09-04 20:21:28 +02:00
parent b900c1ccaa
commit 188a1f236e
42 changed files with 849 additions and 96 deletions

View File

@ -199,3 +199,9 @@ above-documented signature accepts ``<variable|string>``:
* 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
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`.

View File

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

18
Help/policy/CMP0054.rst Normal file
View File

@ -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.

View File

@ -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`.

View File

@ -15,6 +15,7 @@
// 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"

View File

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

View File

@ -13,36 +13,41 @@
#define cmConditionEvaluator_h
#include "cmCommand.h"
#include "cmExpandedCommandArgument.h"
class cmConditionEvaluator
{
public:
typedef std::list<std::string> cmArgumentList;
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<std::string> &args,
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 std::string& argument) const;
const cmExpandedCommandArgument& argument) const;
bool IsKeyword(std::string const& keyword,
std::string& argument) const;
cmExpandedCommandArgument& argument) const;
bool GetBooleanValue(
std::string& arg) const;
cmExpandedCommandArgument& arg) const;
bool GetBooleanValueOld(
std::string const& arg, bool one) const;
cmExpandedCommandArgument const& arg, bool one) const;
bool GetBooleanValueWithAutoDereference(
std::string &newArg,
cmExpandedCommandArgument &newArg,
std::string &errorString,
cmake::MessageType &status,
bool oneArg = false) const;
@ -85,6 +90,7 @@ private:
cmMakefile& Makefile;
cmPolicies::PolicyStatus Policy12Status;
cmPolicies::PolicyStatus Policy54Status;
};
#endif

View File

@ -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();
}

View File

@ -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

View File

@ -20,15 +20,15 @@
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;
@ -105,7 +105,7 @@ IsFunctionBlocked(const cmListFileFunction& lff,
std::string errorString;
std::vector<std::string> expandedArguments;
std::vector<cmExpandedCommandArgument> expandedArguments;
mf.ExpandArguments(this->Functions[c].Arguments,
expandedArguments);
@ -189,7 +189,7 @@ bool cmIfCommand
{
std::string errorString;
std::vector<std::string> expandedArguments;
std::vector<cmExpandedCommandArgument> expandedArguments;
this->Makefile->ExpandArguments(args, expandedArguments);
cmake::MessageType status;

View File

@ -70,6 +70,10 @@ public:
*/
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);
};

View File

@ -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
@ -3327,6 +3328,47 @@ bool cmMakefile::ExpandArguments(
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)
{
@ -4938,12 +4980,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())
@ -4953,6 +4997,23 @@ cmPolicies *cmMakefile::GetPolicies() const
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)
{

View File

@ -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
*/

View File

@ -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()

View File

@ -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.
*

View File

@ -34,7 +34,7 @@ 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;

View File

@ -0,0 +1 @@
$^

View File

@ -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)

View File

@ -0,0 +1 @@
$^

View File

@ -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)

View File

@ -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.

View File

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

View File

@ -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.

View File

@ -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()

View File

@ -0,0 +1 @@
1

View File

@ -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\)

View File

@ -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()

View File

@ -0,0 +1 @@
$^

View File

@ -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()

View File

@ -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.

View File

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

View File

@ -0,0 +1 @@
$^

View File

@ -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()

View File

@ -0,0 +1 @@
$^

View File

@ -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()

View File

@ -0,0 +1 @@
$^

View File

@ -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()

View File

@ -0,0 +1 @@
$^

View File

@ -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()

View File

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

View File

@ -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)

View File

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