Merge topic 'generator-expression-refactor'

91011bd cmGeneratorExpression: Port users to two-stage processing
f1eacf0 cmGeneratorExpression: Re-write for multi-stage evaluation
This commit is contained in:
Brad King 2012-09-19 08:28:16 -04:00 committed by CMake Topic Stage
commit d407dcdbc8
21 changed files with 1395 additions and 236 deletions

View File

@ -183,6 +183,12 @@ set(SRCS
cmFileTimeComparison.cxx
cmFileTimeComparison.h
cmGeneratedFileStream.cxx
cmGeneratorExpressionEvaluator.cxx
cmGeneratorExpressionEvaluator.h
cmGeneratorExpressionLexer.cxx
cmGeneratorExpressionLexer.h
cmGeneratorExpressionParser.cxx
cmGeneratorExpressionParser.h
cmGeneratorExpression.cxx
cmGeneratorExpression.h
cmGeneratorTarget.cxx

View File

@ -21,7 +21,7 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(
cmCustomCommand const& cc, const char* config, cmMakefile* mf):
CC(cc), Config(config), Makefile(mf), LG(mf->GetLocalGenerator()),
OldStyle(cc.GetEscapeOldStyle()), MakeVars(cc.GetEscapeAllowMakeVars()),
GE(new cmGeneratorExpression(mf, config, cc.GetBacktrace()))
GE(new cmGeneratorExpression(cc.GetBacktrace()))
{
}
@ -47,7 +47,7 @@ std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const
{
return target->GetLocation(this->Config);
}
return this->GE->Process(argv0);
return this->GE->Parse(argv0).Evaluate(this->Makefile, this->Config);
}
//----------------------------------------------------------------------------
@ -58,7 +58,8 @@ cmCustomCommandGenerator
cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c];
for(unsigned int j=1;j < commandLine.size(); ++j)
{
std::string arg = this->GE->Process(commandLine[j]);
std::string arg = this->GE->Parse(commandLine[j]).Evaluate(this->Makefile,
this->Config);
cmd += " ";
if(this->OldStyle)
{

View File

@ -16,233 +16,112 @@
#include <cmsys/String.h>
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorExpressionLexer.h"
#include "cmGeneratorExpressionParser.h"
//----------------------------------------------------------------------------
cmGeneratorExpression::cmGeneratorExpression(
cmMakefile* mf, const char* config,
cmListFileBacktrace const& backtrace, bool quiet):
Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet)
cmListFileBacktrace const& backtrace):
Backtrace(backtrace), CompiledExpression(0)
{
this->TargetInfo.compile("^\\$<TARGET"
"(|_SONAME|_LINKER)" // File with what purpose?
"_FILE(|_NAME|_DIR):" // Filename component.
"([A-Za-z0-9_.-]+)" // Target name.
">$");
this->TestConfig.compile("^\\$<CONFIG:([A-Za-z0-9_]*)>$");
}
//----------------------------------------------------------------------------
const char* cmGeneratorExpression::Process(std::string const& input)
const cmCompiledGeneratorExpression &
cmGeneratorExpression::Parse(std::string const& input)
{
return this->Process(input.c_str());
return this->Parse(input.c_str());
}
//----------------------------------------------------------------------------
const char* cmGeneratorExpression::Process(const char* input)
const cmCompiledGeneratorExpression &
cmGeneratorExpression::Parse(const char* input)
{
this->Data.clear();
cmGeneratorExpressionLexer l;
std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(input);
bool needsParsing = l.GetSawGeneratorExpression();
std::vector<cmGeneratorExpressionEvaluator*> evaluators;
// We construct and evaluate expressions directly in the output
// buffer. Each expression is replaced by its own output value
// after evaluation. A stack of barriers records the starting
// indices of open (pending) expressions.
for(const char* c = input; *c; ++c)
if (needsParsing)
{
if(c[0] == '$' && c[1] == '<')
{
this->Barriers.push(this->Data.size());
this->Data.push_back('$');
this->Data.push_back('<');
c += 1;
}
else if(c[0] == '>' && !this->Barriers.empty())
{
this->Data.push_back('>');
if(!this->Evaluate()) { break; }
this->Barriers.pop();
}
else
{
this->Data.push_back(c[0]);
}
cmGeneratorExpressionParser p(tokens);
p.Parse(evaluators);
}
// Return a null-terminated output value.
this->Data.push_back('\0');
return &*this->Data.begin();
delete this->CompiledExpression;
this->CompiledExpression = new cmCompiledGeneratorExpression(
this->Backtrace,
evaluators,
input,
needsParsing);
return *this->CompiledExpression;
}
cmGeneratorExpression::~cmGeneratorExpression()
{
delete this->CompiledExpression;
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::Evaluate()
const char *cmCompiledGeneratorExpression::Evaluate(
cmMakefile* mf, const char* config, bool quiet) const
{
// The top-most barrier points at the beginning of the expression.
size_t barrier = this->Barriers.top();
// Construct a null-terminated representation of the expression.
this->Data.push_back('\0');
const char* expr = &*(this->Data.begin()+barrier);
// Evaluate the expression.
std::string result;
if(this->Evaluate(expr, result))
if (!this->NeedsParsing)
{
// Success. Replace the expression with its evaluation result.
this->Data.erase(this->Data.begin()+barrier, this->Data.end());
this->Data.insert(this->Data.end(), result.begin(), result.end());
return true;
return this->Input;
}
else if(!this->Quiet)
this->Output = "";
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
= this->Evaluators.begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
= this->Evaluators.end();
cmGeneratorExpressionContext context;
context.Makefile = mf;
context.Config = config;
context.Quiet = quiet;
context.HadError = false;
context.Backtrace = this->Backtrace;
for ( ; it != end; ++it)
{
// Failure. Report the error message.
cmOStringStream e;
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< result;
this->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
this->Backtrace);
return false;
this->Output += (*it)->Evaluate(&context);
if (context.HadError)
{
this->Output = "";
break;
}
}
return true;
this->Targets = context.Targets;
// TODO: Return a std::string from here instead?
return this->Output.c_str();
}
cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
cmListFileBacktrace const& backtrace,
const std::vector<cmGeneratorExpressionEvaluator*> &evaluators,
const char *input, bool needsParsing)
: Backtrace(backtrace), Evaluators(evaluators), Input(input),
NeedsParsing(needsParsing)
{
}
//----------------------------------------------------------------------------
static bool cmGeneratorExpressionBool(const char* c, std::string& result,
const char* name,
const char* a, const char* b)
cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression()
{
result = a;
while((c[0] == '0' || c[0] == '1') && (c[1] == ',' || c[1] == '>'))
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
= this->Evaluators.begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
= this->Evaluators.end();
for ( ; it != end; ++it)
{
if(c[0] == b[0]) { result = b; }
c += 2;
delete *it;
}
if(c[0])
{
result = name;
result += " requires one or more comma-separated '0' or '1' values.";
return false;
}
return true;
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
{
if(this->TargetInfo.find(expr))
{
if(!this->EvaluateTargetInfo(result))
{
return false;
}
}
else if(strcmp(expr, "$<CONFIGURATION>") == 0)
{
result = this->Config? this->Config : "";
}
else if(strncmp(expr, "$<0:",4) == 0)
{
result = "";
}
else if(strncmp(expr, "$<1:",4) == 0)
{
result = std::string(expr+4, strlen(expr)-5);
}
else if(strncmp(expr, "$<NOT:",6) == 0)
{
const char* c = expr+6;
if((c[0] != '0' && c[0] != '1') || c[1] != '>' || c[2])
{
result = "NOT requires exactly one '0' or '1' value.";
return false;
}
result = c[0] == '1'? "0" : "1";
}
else if(strncmp(expr, "$<AND:",6) == 0)
{
return cmGeneratorExpressionBool(expr+6, result, "AND", "1", "0");
}
else if(strncmp(expr, "$<OR:",5) == 0)
{
return cmGeneratorExpressionBool(expr+5, result, "OR", "0", "1");
}
else if(this->TestConfig.find(expr))
{
result = cmsysString_strcasecmp(this->TestConfig.match(1).c_str(),
this->Config? this->Config:"") == 0
? "1":"0";
}
else
{
result = "Expression syntax not recognized.";
return false;
}
return true;
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::EvaluateTargetInfo(std::string& result)
{
// Lookup the referenced target.
std::string name = this->TargetInfo.match(3);
cmTarget* target = this->Makefile->FindTargetToUse(name.c_str());
if(!target)
{
result = "No target \"" + name + "\"";
return false;
}
if(target->GetType() >= cmTarget::UTILITY &&
target->GetType() != cmTarget::UNKNOWN_LIBRARY)
{
result = "Target \"" + name + "\" is not an executable or library.";
return false;
}
this->Targets.insert(target);
// Lookup the target file with the given purpose.
std::string purpose = this->TargetInfo.match(1);
if(purpose == "")
{
// The target implementation file (.so.1.2, .dll, .exe, .a).
result = target->GetFullPath(this->Config, false, true);
}
else if(purpose == "_LINKER")
{
// The file used to link to the target (.so, .lib, .a).
if(!target->IsLinkable())
{
result = ("TARGET_LINKER_FILE is allowed only for libraries and "
"executables with ENABLE_EXPORTS.");
return false;
}
result = target->GetFullPath(this->Config, target->HasImportLibrary());
}
else if(purpose == "_SONAME")
{
// The target soname file (.so.1).
if(target->IsDLLPlatform())
{
result = "TARGET_SONAME_FILE is not allowed for DLL target platforms.";
return false;
}
if(target->GetType() != cmTarget::SHARED_LIBRARY)
{
result = "TARGET_SONAME_FILE is allowed only for SHARED libraries.";
return false;
}
result = target->GetDirectory(this->Config);
result += "/";
result += target->GetSOName(this->Config);
}
// Extract the requested portion of the full path.
std::string part = this->TargetInfo.match(2);
if(part == "_NAME")
{
result = cmSystemTools::GetFilenameName(result);
}
else if(part == "_DIR")
{
result = cmSystemTools::GetFilenamePath(result);
}
return true;
}

View File

@ -19,6 +19,10 @@ class cmTarget;
class cmMakefile;
class cmListFileBacktrace;
struct cmGeneratorExpressionEvaluator;
class cmCompiledGeneratorExpression;
/** \class cmGeneratorExpression
* \brief Evaluate generate-time query expression syntax.
*
@ -31,29 +35,48 @@ class cmListFileBacktrace;
class cmGeneratorExpression
{
public:
/** Construct with an evaluation context and configuration. */
cmGeneratorExpression(cmMakefile* mf, const char* config,
cmListFileBacktrace const& backtrace,
bool quiet = false);
/** Construct. */
cmGeneratorExpression(cmListFileBacktrace const& backtrace);
~cmGeneratorExpression();
/** Evaluate generator expressions in a string. */
const char* Process(std::string const& input);
const char* Process(const char* input);
const cmCompiledGeneratorExpression& Parse(std::string const& input);
const cmCompiledGeneratorExpression& Parse(const char* input);
private:
cmGeneratorExpression(const cmGeneratorExpression &);
void operator=(const cmGeneratorExpression &);
cmListFileBacktrace const& Backtrace;
cmCompiledGeneratorExpression *CompiledExpression;
};
class cmCompiledGeneratorExpression
{
public:
const char* Evaluate(cmMakefile* mf, const char* config,
bool quiet = false) const;
/** Get set of targets found during evaluations. */
std::set<cmTarget*> const& GetTargets() const
{ return this->Targets; }
~cmCompiledGeneratorExpression();
private:
cmMakefile* Makefile;
const char* Config;
cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
const std::vector<cmGeneratorExpressionEvaluator*> &evaluators,
const char *input, bool needsParsing);
friend class cmGeneratorExpression;
cmCompiledGeneratorExpression(const cmCompiledGeneratorExpression &);
void operator=(const cmCompiledGeneratorExpression &);
cmListFileBacktrace const& Backtrace;
bool Quiet;
std::vector<char> Data;
std::stack<size_t> Barriers;
cmsys::RegularExpression TargetInfo;
cmsys::RegularExpression TestConfig;
std::set<cmTarget*> Targets;
bool Evaluate();
bool Evaluate(const char* expr, std::string& result);
bool EvaluateTargetInfo(std::string& result);
const std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
const char* const Input;
const bool NeedsParsing;
mutable std::set<cmTarget*> Targets;
mutable std::string Output;
};

View File

@ -0,0 +1,568 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 "cmMakefile.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorExpressionParser.h"
//----------------------------------------------------------------------------
static void reportError(cmGeneratorExpressionContext *context,
const std::string &expr, const std::string &result)
{
context->HadError = true;
if (context->Quiet)
{
return;
}
cmOStringStream e;
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< result;
context->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
context->Backtrace);
}
//----------------------------------------------------------------------------
struct cmGeneratorExpressionNode
{
virtual ~cmGeneratorExpressionNode() {}
virtual bool GeneratesContent() const { return true; }
virtual bool AcceptsSingleArbitraryContentParameter() const
{ return false; }
virtual int NumExpectedParameters() const { return 1; }
virtual std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content
) const = 0;
};
//----------------------------------------------------------------------------
static const struct ZeroNode : public cmGeneratorExpressionNode
{
ZeroNode() {}
virtual bool GeneratesContent() const { return false; }
std::string Evaluate(const std::vector<std::string> &,
cmGeneratorExpressionContext *,
const GeneratorExpressionContent *) const
{
// Unreachable
return std::string();
}
} zeroNode;
//----------------------------------------------------------------------------
static const struct OneNode : public cmGeneratorExpressionNode
{
OneNode() {}
virtual bool AcceptsSingleArbitraryContentParameter() const { return true; }
std::string Evaluate(const std::vector<std::string> &,
cmGeneratorExpressionContext *,
const GeneratorExpressionContent *) const
{
// Unreachable
return std::string();
}
} oneNode;
//----------------------------------------------------------------------------
#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
static const struct OP ## Node : public cmGeneratorExpressionNode \
{ \
OP ## Node () {} \
/* We let -1 carry the meaning 'at least one' */ \
virtual int NumExpectedParameters() const { return -1; } \
\
std::string Evaluate(const std::vector<std::string> &parameters, \
cmGeneratorExpressionContext *context, \
const GeneratorExpressionContent *content) const \
{ \
std::vector<std::string>::const_iterator it = parameters.begin(); \
const std::vector<std::string>::const_iterator end = parameters.end(); \
for ( ; it != end; ++it) \
{ \
if (*it == #FAILURE_VALUE) \
{ \
return #FAILURE_VALUE; \
} \
else if (*it != #SUCCESS_VALUE) \
{ \
reportError(context, content->GetOriginalExpression(), \
"Parameters to $<" #OP "> must resolve to either '0' or '1'."); \
return std::string(); \
} \
} \
return #SUCCESS_VALUE; \
} \
} OPNAME;
BOOLEAN_OP_NODE(andNode, AND, 1, 0)
BOOLEAN_OP_NODE(orNode, OR, 0, 1)
#undef BOOLEAN_OP_NODE
//----------------------------------------------------------------------------
static const struct NotNode : public cmGeneratorExpressionNode
{
NotNode() {}
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content) const
{
if (*parameters.begin() != "0" && *parameters.begin() != "1")
{
reportError(context, content->GetOriginalExpression(),
"$<NOT> parameter must resolve to exactly one '0' or '1' value.");
return std::string();
}
return *parameters.begin() == "0" ? "1" : "0";
}
} notNode;
//----------------------------------------------------------------------------
static const struct ConfigurationNode : public cmGeneratorExpressionNode
{
ConfigurationNode() {}
virtual int NumExpectedParameters() const { return 0; }
std::string Evaluate(const std::vector<std::string> &,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *) const
{
return context->Config ? context->Config : "";
}
} configurationNode;
//----------------------------------------------------------------------------
static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
{
ConfigurationTestNode() {}
virtual int NumExpectedParameters() const { return 1; }
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content) const
{
if (!context->Config)
{
return std::string();
}
cmsys::RegularExpression configValidator;
configValidator.compile("^[A-Za-z0-9_]*$");
if (!configValidator.find(parameters.begin()->c_str()))
{
reportError(context, content->GetOriginalExpression(),
"Expression syntax not recognized.");
return std::string();
}
return *parameters.begin() == context->Config ? "1" : "0";
}
} configurationTestNode;
//----------------------------------------------------------------------------
template<bool linker, bool soname>
struct TargetFilesystemArtifactResultCreator
{
static std::string Create(cmTarget* target,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content);
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultCreator<false, true>
{
static std::string Create(cmTarget* target,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content)
{
// The target soname file (.so.1).
if(target->IsDLLPlatform())
{
::reportError(context, content->GetOriginalExpression(),
"TARGET_SONAME_FILE is not allowed "
"for DLL target platforms.");
return std::string();
}
if(target->GetType() != cmTarget::SHARED_LIBRARY)
{
::reportError(context, content->GetOriginalExpression(),
"TARGET_SONAME_FILE is allowed only for "
"SHARED libraries.");
return std::string();
}
std::string result = target->GetDirectory(context->Config);
result += "/";
result += target->GetSOName(context->Config);
return result;
}
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultCreator<true, false>
{
static std::string Create(cmTarget* target,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content)
{
// The file used to link to the target (.so, .lib, .a).
if(!target->IsLinkable())
{
::reportError(context, content->GetOriginalExpression(),
"TARGET_LINKER_FILE is allowed only for libraries and "
"executables with ENABLE_EXPORTS.");
return std::string();
}
return target->GetFullPath(context->Config,
target->HasImportLibrary());
}
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultCreator<false, false>
{
static std::string Create(cmTarget* target,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *)
{
return target->GetFullPath(context->Config, false, true);
}
};
//----------------------------------------------------------------------------
template<bool dirQual, bool nameQual>
struct TargetFilesystemArtifactResultGetter
{
static std::string Get(const std::string &result);
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultGetter<false, true>
{
static std::string Get(const std::string &result)
{ return cmSystemTools::GetFilenameName(result); }
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultGetter<true, false>
{
static std::string Get(const std::string &result)
{ return cmSystemTools::GetFilenamePath(result); }
};
//----------------------------------------------------------------------------
template<>
struct TargetFilesystemArtifactResultGetter<false, false>
{
static std::string Get(const std::string &result)
{ return result; }
};
//----------------------------------------------------------------------------
template<bool linker, bool soname, bool dirQual, bool nameQual>
struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
{
TargetFilesystemArtifact() {}
virtual int NumExpectedParameters() const { return 1; }
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content) const
{
// Lookup the referenced target.
std::string name = *parameters.begin();
cmsys::RegularExpression targetValidator;
targetValidator.compile("^[A-Za-z0-9_]+$");
if (!targetValidator.find(name.c_str()))
{
::reportError(context, content->GetOriginalExpression(),
"Expression syntax not recognized.");
return std::string();
}
cmTarget* target = context->Makefile->FindTargetToUse(name.c_str());
if(!target)
{
::reportError(context, content->GetOriginalExpression(),
"No target \"" + name + "\"");
return std::string();
}
if(target->GetType() >= cmTarget::UTILITY &&
target->GetType() != cmTarget::UNKNOWN_LIBRARY)
{
::reportError(context, content->GetOriginalExpression(),
"Target \"" + name + "\" is not an executable or library.");
return std::string();
}
context->Targets.insert(target);
std::string result =
TargetFilesystemArtifactResultCreator<linker, soname>::Create(
target,
context,
content);
if (context->HadError)
{
return std::string();
}
return
TargetFilesystemArtifactResultGetter<dirQual, nameQual>::Get(result);
}
};
//----------------------------------------------------------------------------
static const
TargetFilesystemArtifact<false, false, false, false> targetFileNode;
static const
TargetFilesystemArtifact<true, false, false, false> targetLinkerFileNode;
static const
TargetFilesystemArtifact<false, true, false, false> targetSoNameFileNode;
static const
TargetFilesystemArtifact<false, false, false, true> targetFileNameNode;
static const
TargetFilesystemArtifact<true, false, false, true> targetLinkerFileNameNode;
static const
TargetFilesystemArtifact<false, true, false, true> targetSoNameFileNameNode;
static const
TargetFilesystemArtifact<false, false, true, false> targetFileDirNode;
static const
TargetFilesystemArtifact<true, false, true, false> targetLinkerFileDirNode;
static const
TargetFilesystemArtifact<false, true, true, false> targetSoNameFileDirNode;
//----------------------------------------------------------------------------
static const
cmGeneratorExpressionNode* GetNode(const std::string &identifier)
{
if (identifier == "0")
return &zeroNode;
if (identifier == "1")
return &oneNode;
if (identifier == "AND")
return &andNode;
if (identifier == "OR")
return &orNode;
if (identifier == "NOT")
return &notNode;
else if (identifier == "CONFIGURATION")
return &configurationNode;
else if (identifier == "CONFIG")
return &configurationTestNode;
else if (identifier == "TARGET_FILE")
return &targetFileNode;
else if (identifier == "TARGET_LINKER_FILE")
return &targetLinkerFileNode;
else if (identifier == "TARGET_SONAME_FILE")
return &targetSoNameFileNode;
else if (identifier == "TARGET_FILE_NAME")
return &targetFileNameNode;
else if (identifier == "TARGET_LINKER_FILE_NAME")
return &targetLinkerFileNameNode;
else if (identifier == "TARGET_SONAME_FILE_NAME")
return &targetSoNameFileNameNode;
else if (identifier == "TARGET_FILE_DIR")
return &targetFileDirNode;
else if (identifier == "TARGET_LINKER_FILE_DIR")
return &targetLinkerFileDirNode;
else if (identifier == "TARGET_SONAME_FILE_DIR")
return &targetSoNameFileDirNode;
return 0;
}
//----------------------------------------------------------------------------
GeneratorExpressionContent::GeneratorExpressionContent(
const char *startContent,
unsigned int length)
: StartContent(startContent), ContentLength(length)
{
}
//----------------------------------------------------------------------------
std::string GeneratorExpressionContent::GetOriginalExpression() const
{
return std::string(this->StartContent, this->ContentLength);
}
//----------------------------------------------------------------------------
std::string GeneratorExpressionContent::Evaluate(
cmGeneratorExpressionContext *context) const
{
std::string identifier;
{
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
= this->IdentifierChildren.begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
= this->IdentifierChildren.end();
for ( ; it != end; ++it)
{
identifier += (*it)->Evaluate(context);
if (context->HadError)
{
return std::string();
}
}
}
const cmGeneratorExpressionNode *node = GetNode(identifier);
if (!node)
{
reportError(context, this->GetOriginalExpression(),
"Expression did not evaluate to a known generator expression");
return std::string();
}
if (!node->GeneratesContent())
{
return std::string();
}
if (node->AcceptsSingleArbitraryContentParameter())
{
std::string result;
std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
pit = this->ParamChildren.begin();
const
std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
pend = this->ParamChildren.end();
for ( ; pit != pend; ++pit)
{
if (!result.empty())
{
result += ",";
}
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
= pit->begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
= pit->end();
for ( ; it != end; ++it)
{
result += (*it)->Evaluate(context);
if (context->HadError)
{
return std::string();
}
}
}
return result;
}
std::vector<std::string> parameters;
{
std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
pit = this->ParamChildren.begin();
const
std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
pend = this->ParamChildren.end();
for ( ; pit != pend; ++pit)
{
std::string parameter;
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it =
pit->begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end =
pit->end();
for ( ; it != end; ++it)
{
parameter += (*it)->Evaluate(context);
if (context->HadError)
{
return std::string();
}
}
parameters.push_back(parameter);
}
}
int numExpected = node->NumExpectedParameters();
if ((numExpected != -1 && (unsigned int)numExpected != parameters.size()))
{
if (numExpected == 0)
{
reportError(context, this->GetOriginalExpression(),
"$<" + identifier + "> expression requires no parameters.");
}
else if (numExpected == 1)
{
reportError(context, this->GetOriginalExpression(),
"$<" + identifier + "> expression requires "
"exactly one parameter.");
}
else
{
cmOStringStream e;
e << "$<" + identifier + "> expression requires "
<< numExpected
<< " comma separated parameters, but got "
<< parameters.size() << " instead.";
reportError(context, this->GetOriginalExpression(), e.str());
}
return std::string();
}
if (numExpected == -1 && parameters.empty())
{
reportError(context, this->GetOriginalExpression(), "$<" + identifier
+ "> expression requires at least one parameter.");
return std::string();
}
return node->Evaluate(parameters, context, this);
}
//----------------------------------------------------------------------------
static void deleteAll(const std::vector<cmGeneratorExpressionEvaluator*> &c)
{
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
= c.begin();
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
= c.end();
for ( ; it != end; ++it)
{
delete *it;
}
}
//----------------------------------------------------------------------------
GeneratorExpressionContent::~GeneratorExpressionContent()
{
deleteAll(this->IdentifierChildren);
typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
typedef std::vector<cmGeneratorExpressionToken> TokenVector;
std::vector<EvaluatorVector>::const_iterator pit =
this->ParamChildren.begin();
const std::vector<EvaluatorVector>::const_iterator pend =
this->ParamChildren.end();
for ( ; pit != pend; ++pit)
{
deleteAll(*pit);
}
}

View File

@ -0,0 +1,118 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 cmGeneratorExpressionEvaluator_h
#define cmGeneratorExpressionEvaluator_h
#include <vector>
#include <string>
//----------------------------------------------------------------------------
struct cmGeneratorExpressionContext
{
cmListFileBacktrace Backtrace;
std::set<cmTarget*> Targets;
cmMakefile *Makefile;
const char *Config;
cmTarget *Target;
bool Quiet;
bool HadError;
};
//----------------------------------------------------------------------------
struct cmGeneratorExpressionEvaluator
{
cmGeneratorExpressionEvaluator() {}
virtual ~cmGeneratorExpressionEvaluator() {}
enum Type
{
Text,
Generator
};
virtual Type GetType() const = 0;
virtual std::string Evaluate(cmGeneratorExpressionContext *context
) const = 0;
private:
cmGeneratorExpressionEvaluator(const cmGeneratorExpressionEvaluator &);
void operator=(const cmGeneratorExpressionEvaluator &);
};
struct TextContent : public cmGeneratorExpressionEvaluator
{
TextContent(const char *start, unsigned int length)
: Content(start), Length(length)
{
}
std::string Evaluate(cmGeneratorExpressionContext *) const
{
return std::string(this->Content, this->Length);
}
Type GetType() const
{
return cmGeneratorExpressionEvaluator::Text;
}
void Extend(unsigned int length)
{
this->Length += length;
}
unsigned int GetLength()
{
return this->Length;
}
private:
const char *Content;
unsigned int Length;
};
//----------------------------------------------------------------------------
struct GeneratorExpressionContent : public cmGeneratorExpressionEvaluator
{
GeneratorExpressionContent(const char *startContent, unsigned int length);
void SetIdentifier(std::vector<cmGeneratorExpressionEvaluator*> identifier)
{
this->IdentifierChildren = identifier;
}
void SetParameters(
std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters)
{
this->ParamChildren = parameters;
}
Type GetType() const
{
return cmGeneratorExpressionEvaluator::Generator;
}
std::string Evaluate(cmGeneratorExpressionContext *context) const;
std::string GetOriginalExpression() const;
~GeneratorExpressionContent();
private:
std::vector<cmGeneratorExpressionEvaluator*> IdentifierChildren;
std::vector<std::vector<cmGeneratorExpressionEvaluator*> > ParamChildren;
const char *StartContent;
unsigned int ContentLength;
};
#endif

View File

@ -0,0 +1,85 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 "cmGeneratorExpressionLexer.h"
//----------------------------------------------------------------------------
cmGeneratorExpressionLexer::cmGeneratorExpressionLexer()
: SawBeginExpression(false), SawGeneratorExpression(false)
{
}
//----------------------------------------------------------------------------
static void InsertText(const char *upto, const char *c,
std::vector<cmGeneratorExpressionToken> &result)
{
if (upto != c)
{
result.push_back(cmGeneratorExpressionToken(
cmGeneratorExpressionToken::Text, upto, c - upto));
}
}
//----------------------------------------------------------------------------
std::vector<cmGeneratorExpressionToken>
cmGeneratorExpressionLexer::Tokenize(const char *input)
{
std::vector<cmGeneratorExpressionToken> result;
if (!input)
return result;
const char *c = input;
const char *upto = c;
for ( ; *c; ++c)
{
if(c[0] == '$' && c[1] == '<')
{
InsertText(upto, c, result);
upto = c;
result.push_back(cmGeneratorExpressionToken(
cmGeneratorExpressionToken::BeginExpression, upto, 2));
upto = c + 2;
++c;
SawBeginExpression = true;
}
else if(c[0] == '>')
{
InsertText(upto, c, result);
upto = c;
result.push_back(cmGeneratorExpressionToken(
cmGeneratorExpressionToken::EndExpression, upto, 1));
upto = c + 1;
SawGeneratorExpression = SawBeginExpression;
}
else if(c[0] == ':')
{
InsertText(upto, c, result);
upto = c;
result.push_back(cmGeneratorExpressionToken(
cmGeneratorExpressionToken::ColonSeparator, upto, 1));
upto = c + 1;
}
else if(c[0] == ',')
{
InsertText(upto, c, result);
upto = c;
result.push_back(cmGeneratorExpressionToken(
cmGeneratorExpressionToken::CommaSeparator, upto, 1));
upto = c + 1;
}
}
InsertText(upto, c, result);
return result;
}

View File

@ -0,0 +1,58 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 cmGeneratorExpressionLexer_h
#define cmGeneratorExpressionLexer_h
#include "cmStandardIncludes.h"
#include <vector>
//----------------------------------------------------------------------------
struct cmGeneratorExpressionToken
{
cmGeneratorExpressionToken(unsigned type, const char *c, unsigned l)
: TokenType(type), Content(c), Length(l)
{
}
enum {
Text,
BeginExpression,
EndExpression,
ColonSeparator,
CommaSeparator
};
unsigned TokenType;
const char *Content;
unsigned Length;
};
/** \class cmGeneratorExpressionLexer
*
*/
class cmGeneratorExpressionLexer
{
public:
cmGeneratorExpressionLexer();
std::vector<cmGeneratorExpressionToken> Tokenize(const char *input);
bool GetSawGeneratorExpression() const
{
return this->SawGeneratorExpression;
}
private:
bool SawBeginExpression;
bool SawGeneratorExpression;
};
#endif

View File

@ -0,0 +1,235 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 "cmGeneratorExpressionParser.h"
#include "cmGeneratorExpressionEvaluator.h"
//----------------------------------------------------------------------------
cmGeneratorExpressionParser::cmGeneratorExpressionParser(
const std::vector<cmGeneratorExpressionToken> &tokens)
: Tokens(tokens), NestingLevel(0)
{
}
//----------------------------------------------------------------------------
void cmGeneratorExpressionParser::Parse(
std::vector<cmGeneratorExpressionEvaluator*> &result)
{
it = this->Tokens.begin();
while (this->it != this->Tokens.end())
{
this->ParseContent(result);
}
}
//----------------------------------------------------------------------------
static void extendText(std::vector<cmGeneratorExpressionEvaluator*> &result,
std::vector<cmGeneratorExpressionToken>::const_iterator it)
{
if (result.size() > 0
&& (*(result.end() - 1))->GetType()
== cmGeneratorExpressionEvaluator::Text)
{
TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
textContent->Extend(it->Length);
}
else
{
TextContent *textContent = new TextContent(it->Content, it->Length);
result.push_back(textContent);
}
}
//----------------------------------------------------------------------------
static void extendResult(std::vector<cmGeneratorExpressionEvaluator*> &result,
const std::vector<cmGeneratorExpressionEvaluator*> &contents)
{
if (result.size() > 0
&& (*(result.end() - 1))->GetType()
== cmGeneratorExpressionEvaluator::Text
&& (*contents.begin())->GetType()
== cmGeneratorExpressionEvaluator::Text)
{
TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
textContent->Extend(
static_cast<TextContent*>(*contents.begin())->GetLength());
delete *contents.begin();
result.insert(result.end(), contents.begin() + 1, contents.end());
} else {
result.insert(result.end(), contents.begin(), contents.end());
}
}
//----------------------------------------------------------------------------
void cmGeneratorExpressionParser::ParseGeneratorExpression(
std::vector<cmGeneratorExpressionEvaluator*> &result)
{
unsigned int nestedLevel = this->NestingLevel;
++this->NestingLevel;
std::vector<cmGeneratorExpressionToken>::const_iterator startToken
= this->it - 1;
std::vector<cmGeneratorExpressionEvaluator*> identifier;
while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression
&& this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator)
{
this->ParseContent(identifier);
if (this->it == this->Tokens.end())
{
break;
}
}
if (identifier.empty())
{
// ERROR
}
if (this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
{
GeneratorExpressionContent *content = new GeneratorExpressionContent(
startToken->Content, this->it->Content
- startToken->Content
+ this->it->Length);
++this->it;
--this->NestingLevel;
content->SetIdentifier(identifier);
result.push_back(content);
return;
}
std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters;
std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator>
commaTokens;
std::vector<cmGeneratorExpressionToken>::const_iterator colonToken;
if (this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)
{
colonToken = this->it;
parameters.resize(parameters.size() + 1);
++this->it;
while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression)
{
this->ParseContent(*(parameters.end() - 1));
if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator)
{
commaTokens.push_back(this->it);
parameters.resize(parameters.size() + 1);
++this->it;
}
if (this->it == this->Tokens.end())
{
break;
}
}
if(this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
{
--this->NestingLevel;
++this->it;
}
if (parameters.empty())
{
// ERROR
}
}
if (nestedLevel != this->NestingLevel)
{
// There was a '$<' in the text, but no corresponding '>'. Rebuild to
// treat the '$<' as having been plain text, along with the
// corresponding : and , tokens that might have been found.
extendText(result, startToken);
extendResult(result, identifier);
if (!parameters.empty())
{
extendText(result, colonToken);
typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
typedef std::vector<cmGeneratorExpressionToken> TokenVector;
std::vector<EvaluatorVector>::const_iterator pit = parameters.begin();
const std::vector<EvaluatorVector>::const_iterator pend =
parameters.end();
std::vector<TokenVector::const_iterator>::const_iterator commaIt =
commaTokens.begin();
for ( ; pit != pend; ++pit, ++commaIt)
{
extendResult(result, *pit);
if (commaIt != commaTokens.end())
{
extendText(result, *commaIt);
}
}
}
return;
}
int contentLength = ((this->it - 1)->Content
- startToken->Content)
+ (this->it - 1)->Length;
GeneratorExpressionContent *content = new GeneratorExpressionContent(
startToken->Content, contentLength);
content->SetIdentifier(identifier);
content->SetParameters(parameters);
result.push_back(content);
}
//----------------------------------------------------------------------------
void cmGeneratorExpressionParser::ParseContent(
std::vector<cmGeneratorExpressionEvaluator*> &result)
{
switch(this->it->TokenType)
{
case cmGeneratorExpressionToken::Text:
{
if (this->NestingLevel == 0)
{
if (result.size() > 0
&& (*(result.end() - 1))->GetType()
== cmGeneratorExpressionEvaluator::Text)
{
// A comma in 'plain text' could have split text that should
// otherwise be continuous. Extend the last text content instead of
// creating a new one.
TextContent *textContent =
static_cast<TextContent*>(*(result.end() - 1));
textContent->Extend(this->it->Length);
++this->it;
return;
}
}
cmGeneratorExpressionEvaluator* n = new TextContent(this->it->Content,
this->it->Length);
result.push_back(n);
++this->it;
return ;
}
case cmGeneratorExpressionToken::BeginExpression:
++this->it;
this->ParseGeneratorExpression(result);
return;
case cmGeneratorExpressionToken::EndExpression:
case cmGeneratorExpressionToken::ColonSeparator:
case cmGeneratorExpressionToken::CommaSeparator:
if (this->NestingLevel == 0)
{
extendText(result, this->it);
}
else
{
// TODO: Unreachable. Assert?
}
++this->it;
return;
}
// Unreachable. Assert?
}

View File

@ -0,0 +1,45 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2012 Stephen Kelly <steveire@gmail.com>
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 cmGeneratorExpressionParser_h
#define cmGeneratorExpressionParser_h
#include "cmGeneratorExpressionLexer.h"
#include <set>
#include <vector>
#include "cmListFileCache.h"
class cmMakefile;
class cmTarget;
struct cmGeneratorExpressionEvaluator;
//----------------------------------------------------------------------------
struct cmGeneratorExpressionParser
{
cmGeneratorExpressionParser(
const std::vector<cmGeneratorExpressionToken> &tokens);
void Parse(std::vector<cmGeneratorExpressionEvaluator*> &result);
private:
void ParseContent(std::vector<cmGeneratorExpressionEvaluator*> &);
void ParseGeneratorExpression(
std::vector<cmGeneratorExpressionEvaluator*> &);
private:
std::vector<cmGeneratorExpressionToken>::const_iterator it;
const std::vector<cmGeneratorExpressionToken> Tokens;
unsigned int NestingLevel;
};
#endif

View File

@ -1623,7 +1623,11 @@ cmTargetTraceDependencies
{
// Transform command names that reference targets built in this
// project to corresponding target-level dependencies.
cmGeneratorExpression ge(this->Makefile, 0, cc.GetBacktrace(), true);
cmGeneratorExpression ge(cc.GetBacktrace());
// Add target-level dependencies referenced by generator expressions.
std::set<cmTarget*> targets;
for(cmCustomCommandLines::const_iterator cit = cc.GetCommandLines().begin();
cit != cc.GetCommandLines().end(); ++cit)
{
@ -1645,12 +1649,17 @@ cmTargetTraceDependencies
for(cmCustomCommandLine::const_iterator cli = cit->begin();
cli != cit->end(); ++cli)
{
ge.Process(*cli);
const cmCompiledGeneratorExpression &cge = ge.Parse(*cli);
cge.Evaluate(this->Makefile, 0, true);
std::set<cmTarget*> geTargets = cge.GetTargets();
for(std::set<cmTarget*>::const_iterator it = geTargets.begin();
it != geTargets.end(); ++it)
{
targets.insert(*it);
}
}
}
// Add target-level dependencies referenced by generator expressions.
std::set<cmTarget*> targets = ge.GetTargets();
for(std::set<cmTarget*>::iterator ti = targets.begin();
ti != targets.end(); ++ti)
{

View File

@ -91,8 +91,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
this->TestGenerated = true;
// Set up generator expression evaluation context.
cmMakefile* mf = this->Test->GetMakefile();
cmGeneratorExpression ge(mf, config, this->Test->GetBacktrace());
cmGeneratorExpression ge(this->Test->GetBacktrace());
// Start the test command.
os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
@ -103,6 +102,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
// Check whether the command executable is a target whose name is to
// be translated.
std::string exe = command[0];
cmMakefile* mf = this->Test->GetMakefile();
cmTarget* target = mf->FindTargetToUse(exe.c_str());
if(target && target->GetType() == cmTarget::EXECUTABLE)
{
@ -112,7 +112,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
else
{
// Use the command name given.
exe = ge.Process(exe.c_str());
exe = ge.Parse(exe.c_str()).Evaluate(mf, config);
cmSystemTools::ConvertToUnixSlashes(exe);
}
@ -122,7 +122,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
for(std::vector<std::string>::const_iterator ci = command.begin()+1;
ci != command.end(); ++ci)
{
os << " " << lg->EscapeForCMake(ge.Process(*ci));
os << " " << lg->EscapeForCMake(ge.Parse(*ci).Evaluate(mf, config));
}
// Finish the test command.

View File

@ -1,9 +1,18 @@
CMake Error at BadAND.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<AND>
\$<AND> expression requires at least one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadAND.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<AND:>
AND requires one or more comma-separated '0' or '1' values.
Parameters to \$<AND> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
@ -12,6 +21,24 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\):
\$<AND:,>
AND requires one or more comma-separated '0' or '1' values.
Parameters to \$<AND> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadAND.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<AND:01>
Parameters to \$<AND> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadAND.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<AND:nothing>
Parameters to \$<AND> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -1,4 +1,7 @@
add_custom_target(check ALL COMMAND check
$<AND>
$<AND:>
$<AND:,>
$<AND:01>
$<AND:nothing>
VERBATIM)

View File

@ -1,8 +1,44 @@
CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<CONFIG>
\$<CONFIG> expression requires exactly one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<CONFIG:.>
Expression syntax not recognized.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<CONFIG:Foo,Bar>
\$<CONFIG> expression requires exactly one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<CONFIG:Foo-Bar>
Expression syntax not recognized.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<CONFIG:Foo-Nested>
Expression syntax not recognized.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -1,3 +1,7 @@
add_custom_target(check ALL COMMAND check
$<CONFIG>
$<CONFIG:.>
$<CONFIG:Foo,Bar>
$<CONFIG:Foo-Bar>
$<$<CONFIG:Foo-Nested>:foo>
VERBATIM)

View File

@ -1,9 +1,17 @@
CMake Error at BadNOT.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<NOT>
\$<NOT> expression requires exactly one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<NOT:>
NOT requires exactly one '0' or '1' value.
\$<NOT> parameter must resolve to exactly one '0' or '1' value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
@ -12,7 +20,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
\$<NOT:,>
NOT requires exactly one '0' or '1' value.
\$<NOT> parameter must resolve to exactly one '0' or '1' value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
@ -21,6 +29,24 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
\$<NOT:0,1>
NOT requires exactly one '0' or '1' value.
\$<NOT> expression requires exactly one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadNOT.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<NOT:01>
\$<NOT> parameter must resolve to exactly one '0' or '1' value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadNOT.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<NOT:nothing>
\$<NOT> parameter must resolve to exactly one '0' or '1' value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -1,5 +1,8 @@
add_custom_target(check ALL COMMAND check
$<NOT>
$<NOT:>
$<NOT:,>
$<NOT:0,1>
$<NOT:01>
$<NOT:nothing>
VERBATIM)

View File

@ -1,9 +1,18 @@
CMake Error at BadOR.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<OR>
\$<OR> expression requires at least one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadOR.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<OR:>
OR requires one or more comma-separated '0' or '1' values.
Parameters to \$<OR> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
@ -12,6 +21,24 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\):
\$<OR:,>
OR requires one or more comma-separated '0' or '1' values.
Parameters to \$<OR> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadOR.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<OR:01>
Parameters to \$<OR> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
CMake Error at BadOR.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<OR:nothing>
Parameters to \$<OR> must resolve to either '0' or '1'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -1,4 +1,7 @@
add_custom_target(check ALL COMMAND check
$<OR>
$<OR:>
$<OR:,>
$<OR:01>
$<OR:nothing>
VERBATIM)

View File

@ -202,6 +202,9 @@ CMAKE_CXX_SOURCES="\
cmInstallDirectoryGenerator \
cmGeneratedFileStream \
cmGeneratorTarget \
cmGeneratorExpressionEvaluator \
cmGeneratorExpressionLexer \
cmGeneratorExpressionParser \
cmGeneratorExpression \
cmGlobalGenerator \
cmLocalGenerator \