mirror of
https://github.com/reactos/CMake.git
synced 2025-01-08 12:10:29 +00:00
fc1c7cf85d
The storage of a pointer means that the ownership and lifetime are externally determined, which is harder to reason about. It also imposes API constraints, requiring APIs to return references to backtraces. This pointer storage was introduced in commit v3.1.0-rc1~425^2~3 (genex: remove the need for backtraces, 2014-05-23). As backtraces are now cheap to copy around, just do that instead.
477 lines
14 KiB
C++
477 lines
14 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 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 "cmGeneratorExpression.h"
|
|
|
|
#include "cmMakefile.h"
|
|
#include "cmTarget.h"
|
|
#include "assert.h"
|
|
#include "cmAlgorithms.h"
|
|
|
|
#include "cmGeneratorExpressionEvaluator.h"
|
|
#include "cmGeneratorExpressionLexer.h"
|
|
#include "cmGeneratorExpressionParser.h"
|
|
#include "cmGeneratorExpressionDAGChecker.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmGeneratorExpression::cmGeneratorExpression(
|
|
const cmListFileBacktrace& backtrace):
|
|
Backtrace(backtrace)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmsys::auto_ptr<cmCompiledGeneratorExpression>
|
|
cmGeneratorExpression::Parse(std::string const& input)
|
|
{
|
|
return cmsys::auto_ptr<cmCompiledGeneratorExpression>(
|
|
new cmCompiledGeneratorExpression(this->Backtrace, input));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmsys::auto_ptr<cmCompiledGeneratorExpression>
|
|
cmGeneratorExpression::Parse(const char* input)
|
|
{
|
|
return this->Parse(std::string(input ? input : ""));
|
|
}
|
|
|
|
cmGeneratorExpression::~cmGeneratorExpression()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const char *cmCompiledGeneratorExpression::Evaluate(
|
|
cmMakefile* mf, const std::string& config, bool quiet,
|
|
cmTarget const* headTarget,
|
|
cmGeneratorExpressionDAGChecker *dagChecker,
|
|
std::string const& language) const
|
|
{
|
|
return this->Evaluate(mf,
|
|
config,
|
|
quiet,
|
|
headTarget,
|
|
headTarget,
|
|
dagChecker,
|
|
language);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const char *cmCompiledGeneratorExpression::Evaluate(
|
|
cmMakefile* mf, const std::string& config, bool quiet,
|
|
cmTarget const* headTarget,
|
|
cmTarget const* currentTarget,
|
|
cmGeneratorExpressionDAGChecker *dagChecker,
|
|
std::string const& language) const
|
|
{
|
|
cmGeneratorExpressionContext context(mf, config, quiet, headTarget,
|
|
currentTarget ? currentTarget : headTarget,
|
|
this->EvaluateForBuildsystem,
|
|
this->Backtrace, language);
|
|
|
|
return this->EvaluateWithContext(context, dagChecker);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const char* cmCompiledGeneratorExpression::EvaluateWithContext(
|
|
cmGeneratorExpressionContext& context,
|
|
cmGeneratorExpressionDAGChecker *dagChecker) const
|
|
{
|
|
if (!this->NeedsEvaluation)
|
|
{
|
|
return this->Input.c_str();
|
|
}
|
|
|
|
this->Output = "";
|
|
|
|
std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
|
|
= this->Evaluators.begin();
|
|
const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
|
|
= this->Evaluators.end();
|
|
|
|
for ( ; it != end; ++it)
|
|
{
|
|
this->Output += (*it)->Evaluate(&context, dagChecker);
|
|
|
|
this->SeenTargetProperties.insert(context.SeenTargetProperties.begin(),
|
|
context.SeenTargetProperties.end());
|
|
if (context.HadError)
|
|
{
|
|
this->Output = "";
|
|
break;
|
|
}
|
|
}
|
|
|
|
this->MaxLanguageStandard = context.MaxLanguageStandard;
|
|
|
|
if (!context.HadError)
|
|
{
|
|
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
|
|
this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
|
|
this->SourceSensitiveTargets = context.SourceSensitiveTargets;
|
|
}
|
|
|
|
this->DependTargets = context.DependTargets;
|
|
this->AllTargetsSeen = context.AllTargets;
|
|
// TODO: Return a std::string from here instead?
|
|
return this->Output.c_str();
|
|
}
|
|
|
|
cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
|
|
cmListFileBacktrace const& backtrace,
|
|
const std::string& input)
|
|
: Backtrace(backtrace), Input(input),
|
|
HadContextSensitiveCondition(false),
|
|
HadHeadSensitiveCondition(false),
|
|
EvaluateForBuildsystem(false)
|
|
{
|
|
cmGeneratorExpressionLexer l;
|
|
std::vector<cmGeneratorExpressionToken> tokens =
|
|
l.Tokenize(this->Input);
|
|
this->NeedsEvaluation = l.GetSawGeneratorExpression();
|
|
|
|
if (this->NeedsEvaluation)
|
|
{
|
|
cmGeneratorExpressionParser p(tokens);
|
|
p.Parse(this->Evaluators);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression()
|
|
{
|
|
cmDeleteAll(this->Evaluators);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string cmGeneratorExpression::StripEmptyListElements(
|
|
const std::string &input)
|
|
{
|
|
if (input.find(';') == input.npos)
|
|
{
|
|
return input;
|
|
}
|
|
std::string result;
|
|
result.reserve(input.size());
|
|
|
|
const char *c = input.c_str();
|
|
const char *last = c;
|
|
bool skipSemiColons = true;
|
|
for ( ; *c; ++c)
|
|
{
|
|
if(*c == ';')
|
|
{
|
|
if(skipSemiColons)
|
|
{
|
|
result.append(last, c - last);
|
|
last = c + 1;
|
|
}
|
|
skipSemiColons = true;
|
|
}
|
|
else
|
|
{
|
|
skipSemiColons = false;
|
|
}
|
|
}
|
|
result.append(last);
|
|
|
|
if (!result.empty() && *(result.end() - 1) == ';')
|
|
{
|
|
result.resize(result.size() - 1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static std::string stripAllGeneratorExpressions(const std::string &input)
|
|
{
|
|
std::string result;
|
|
std::string::size_type pos = 0;
|
|
std::string::size_type lastPos = pos;
|
|
int nestingLevel = 0;
|
|
while((pos = input.find("$<", lastPos)) != input.npos)
|
|
{
|
|
result += input.substr(lastPos, pos - lastPos);
|
|
pos += 2;
|
|
nestingLevel = 1;
|
|
const char *c = input.c_str() + pos;
|
|
const char * const cStart = c;
|
|
for ( ; *c; ++c)
|
|
{
|
|
if(c[0] == '$' && c[1] == '<')
|
|
{
|
|
++nestingLevel;
|
|
++c;
|
|
continue;
|
|
}
|
|
if(c[0] == '>')
|
|
{
|
|
--nestingLevel;
|
|
if (nestingLevel == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const std::string::size_type traversed = (c - cStart) + 1;
|
|
if (!*c)
|
|
{
|
|
result += "$<" + input.substr(pos, traversed);
|
|
}
|
|
pos += traversed;
|
|
lastPos = pos;
|
|
}
|
|
if (nestingLevel == 0)
|
|
{
|
|
result += input.substr(lastPos);
|
|
}
|
|
return cmGeneratorExpression::StripEmptyListElements(result);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static void prefixItems(const std::string &content, std::string &result,
|
|
const std::string &prefix)
|
|
{
|
|
std::vector<std::string> entries;
|
|
cmGeneratorExpression::Split(content, entries);
|
|
const char *sep = "";
|
|
for(std::vector<std::string>::const_iterator ei = entries.begin();
|
|
ei != entries.end(); ++ei)
|
|
{
|
|
result += sep;
|
|
sep = ";";
|
|
if (!cmSystemTools::FileIsFullPath(ei->c_str())
|
|
&& cmGeneratorExpression::Find(*ei) != 0)
|
|
{
|
|
result += prefix;
|
|
}
|
|
result += *ei;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static std::string stripExportInterface(const std::string &input,
|
|
cmGeneratorExpression::PreprocessContext context,
|
|
bool resolveRelative)
|
|
{
|
|
std::string result;
|
|
|
|
int nestingLevel = 0;
|
|
std::string::size_type pos = 0;
|
|
std::string::size_type lastPos = pos;
|
|
while (true)
|
|
{
|
|
std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
|
|
std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
|
|
|
|
if (bPos == std::string::npos && iPos == std::string::npos)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (bPos == std::string::npos)
|
|
{
|
|
pos = iPos;
|
|
}
|
|
else if (iPos == std::string::npos)
|
|
{
|
|
pos = bPos;
|
|
}
|
|
else
|
|
{
|
|
pos = (bPos < iPos) ? bPos : iPos;
|
|
}
|
|
|
|
result += input.substr(lastPos, pos - lastPos);
|
|
const bool gotInstallInterface = input[pos + 2] == 'I';
|
|
pos += gotInstallInterface ? sizeof("$<INSTALL_INTERFACE:") - 1
|
|
: sizeof("$<BUILD_INTERFACE:") - 1;
|
|
nestingLevel = 1;
|
|
const char *c = input.c_str() + pos;
|
|
const char * const cStart = c;
|
|
for ( ; *c; ++c)
|
|
{
|
|
if(c[0] == '$' && c[1] == '<')
|
|
{
|
|
++nestingLevel;
|
|
++c;
|
|
continue;
|
|
}
|
|
if(c[0] == '>')
|
|
{
|
|
--nestingLevel;
|
|
if (nestingLevel != 0)
|
|
{
|
|
continue;
|
|
}
|
|
if(context == cmGeneratorExpression::BuildInterface
|
|
&& !gotInstallInterface)
|
|
{
|
|
result += input.substr(pos, c - cStart);
|
|
}
|
|
else if(context == cmGeneratorExpression::InstallInterface
|
|
&& gotInstallInterface)
|
|
{
|
|
const std::string content = input.substr(pos, c - cStart);
|
|
if (resolveRelative)
|
|
{
|
|
prefixItems(content, result, "${_IMPORT_PREFIX}/");
|
|
}
|
|
else
|
|
{
|
|
result += content;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
const std::string::size_type traversed = (c - cStart) + 1;
|
|
if (!*c)
|
|
{
|
|
result += std::string(gotInstallInterface ? "$<INSTALL_INTERFACE:"
|
|
: "$<BUILD_INTERFACE:")
|
|
+ input.substr(pos, traversed);
|
|
}
|
|
pos += traversed;
|
|
lastPos = pos;
|
|
}
|
|
if (nestingLevel == 0)
|
|
{
|
|
result += input.substr(lastPos);
|
|
}
|
|
|
|
return cmGeneratorExpression::StripEmptyListElements(result);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmGeneratorExpression::Split(const std::string &input,
|
|
std::vector<std::string> &output)
|
|
{
|
|
std::string::size_type pos = 0;
|
|
std::string::size_type lastPos = pos;
|
|
while((pos = input.find("$<", lastPos)) != input.npos)
|
|
{
|
|
std::string part = input.substr(lastPos, pos - lastPos);
|
|
std::string preGenex;
|
|
if (!part.empty())
|
|
{
|
|
std::string::size_type startPos = input.rfind(";", pos);
|
|
if (startPos == std::string::npos)
|
|
{
|
|
preGenex = part;
|
|
part = "";
|
|
}
|
|
else if (startPos != pos - 1 && startPos >= lastPos)
|
|
{
|
|
part = input.substr(lastPos, startPos - lastPos);
|
|
preGenex = input.substr(startPos + 1, pos - startPos - 1);
|
|
}
|
|
if(!part.empty())
|
|
{
|
|
cmSystemTools::ExpandListArgument(part, output);
|
|
}
|
|
}
|
|
pos += 2;
|
|
int nestingLevel = 1;
|
|
const char *c = input.c_str() + pos;
|
|
const char * const cStart = c;
|
|
for ( ; *c; ++c)
|
|
{
|
|
if(c[0] == '$' && c[1] == '<')
|
|
{
|
|
++nestingLevel;
|
|
++c;
|
|
continue;
|
|
}
|
|
if(c[0] == '>')
|
|
{
|
|
--nestingLevel;
|
|
if (nestingLevel == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for ( ; *c; ++c)
|
|
{
|
|
// Capture the part after the genex and before the next ';'
|
|
if(c[0] == ';')
|
|
{
|
|
--c;
|
|
break;
|
|
}
|
|
}
|
|
const std::string::size_type traversed = (c - cStart) + 1;
|
|
output.push_back(preGenex + "$<" + input.substr(pos, traversed));
|
|
pos += traversed;
|
|
lastPos = pos;
|
|
}
|
|
if (lastPos < input.size())
|
|
{
|
|
cmSystemTools::ExpandListArgument(input.substr(lastPos), output);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string cmGeneratorExpression::Preprocess(const std::string &input,
|
|
PreprocessContext context,
|
|
bool resolveRelative)
|
|
{
|
|
if (context == StripAllGeneratorExpressions)
|
|
{
|
|
return stripAllGeneratorExpressions(input);
|
|
}
|
|
else if (context == BuildInterface || context == InstallInterface)
|
|
{
|
|
return stripExportInterface(input, context, resolveRelative);
|
|
}
|
|
|
|
assert(0 && "cmGeneratorExpression::Preprocess called with invalid args");
|
|
return std::string();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string::size_type cmGeneratorExpression::Find(const std::string &input)
|
|
{
|
|
const std::string::size_type openpos = input.find("$<");
|
|
if (openpos != std::string::npos
|
|
&& input.find(">", openpos) != std::string::npos)
|
|
{
|
|
return openpos;
|
|
}
|
|
return std::string::npos;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmGeneratorExpression::IsValidTargetName(const std::string &input)
|
|
{
|
|
// The ':' is supported to allow use with IMPORTED targets. At least
|
|
// Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
|
|
static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
|
|
|
|
return targetNameValidator.find(input);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
cmCompiledGeneratorExpression::GetMaxLanguageStandard(cmTarget const* tgt,
|
|
std::map<std::string, std::string>& mapping)
|
|
{
|
|
typedef std::map<cmTarget const*,
|
|
std::map<std::string, std::string> > MapType;
|
|
MapType::const_iterator it = this->MaxLanguageStandard.find(tgt);
|
|
if (it != this->MaxLanguageStandard.end())
|
|
{
|
|
mapping = it->second;
|
|
}
|
|
}
|