summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2013-01-07 19:20:13 (GMT)
committerCMake Topic Stage <kwrobot@kitware.com>2013-01-07 19:20:13 (GMT)
commitdb925e3532719af44022b50d2462ac5b050907bc (patch)
tree3dacf9dd76f0d78df1796ee4a96590ac6b057a88 /Source
parentb5ab3f0707cae7affa14423012997503825a867c (diff)
parent894f52f32d96ae92df0ba3dac2a70fe9c87e818d (diff)
downloadCMake-db925e3532719af44022b50d2462ac5b050907bc.zip
CMake-db925e3532719af44022b50d2462ac5b050907bc.tar.gz
CMake-db925e3532719af44022b50d2462ac5b050907bc.tar.bz2
Merge topic 'interface-includes-defines'
894f52f Handle INTERFACE properties transitively for includes and defines. f5b1980 Populate the ExportedTargets member early in GenerateMainFile c67b812 Make cycles in target properties ignored, not an error. d0f950f Use mapped config properties to evaluate $<CONFIG> 26def17 Make all relevant targets available in the genex context. 0c657dc Add API to populate INTERFACE properties in exported targets. e04f737 Add API to extract target names from a genex string. b0c8f73 Add the TARGET_NAME generator expression. 77475fe Allow generator expressions to require literals. b2f1700 GenEx: Add expressions to specify build- or install-only values
Diffstat (limited to 'Source')
-rw-r--r--Source/cmDocumentGeneratorExpressions.h10
-rw-r--r--Source/cmExportBuildFileGenerator.cxx40
-rw-r--r--Source/cmExportFileGenerator.cxx195
-rw-r--r--Source/cmExportFileGenerator.h19
-rw-r--r--Source/cmExportInstallFileGenerator.cxx42
-rw-r--r--Source/cmGeneratorExpression.cxx108
-rw-r--r--Source/cmGeneratorExpression.h11
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx18
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.h12
-rw-r--r--Source/cmGeneratorExpressionEvaluator.cxx111
-rw-r--r--Source/cmGeneratorExpressionEvaluator.h4
-rw-r--r--Source/cmTarget.cxx108
-rw-r--r--Source/cmTarget.h5
13 files changed, 595 insertions, 88 deletions
diff --git a/Source/cmDocumentGeneratorExpressions.h b/Source/cmDocumentGeneratorExpressions.h
index 445fd0e..b8889ac 100644
--- a/Source/cmDocumentGeneratorExpressions.h
+++ b/Source/cmDocumentGeneratorExpressions.h
@@ -26,6 +26,16 @@
"strings which contain a '>' for example.\n" \
" $<COMMA> = A literal ','. Used to compare " \
"strings which contain a ',' for example.\n" \
+ " $<TARGET_NAME:...> = Marks ... as being the name of a " \
+ "target. This is required if exporting targets to multiple " \
+ "dependent export sets. The '...' must be a literal name of a " \
+ "target- it may not contain generator expressions.\n" \
+ " $<INSTALL_INTERFACE:...> = content of \"...\" when the property " \
+ "is exported using install(EXPORT), and empty otherwise.\n" \
+ " $<BUILD_INTERFACE:...> = content of \"...\" when the property " \
+ "is exported using export(), or when the target is used by another " \
+ "target in the same buildsystem. Expands to the empty string " \
+ "otherwise.\n" \
" $<TARGET_FILE:tgt> = main file (.exe, .so.1.2, .a)\n" \
" $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n" \
" $<TARGET_SONAME_FILE:tgt> = file with soname (.so.3)\n" \
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index cd6a7ab..9533319 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -22,6 +22,7 @@ cmExportBuildFileGenerator::cmExportBuildFileGenerator()
//----------------------------------------------------------------------------
bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{
+ std::vector<cmTarget*> allTargets;
{
std::string expectedTargets;
std::string sep;
@@ -31,20 +32,10 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{
expectedTargets += sep + this->Namespace + (*tei)->GetName();
sep = " ";
- }
-
- this->GenerateExpectedTargetsCode(os, expectedTargets);
- }
-
- // Create all the imported targets.
- for(std::vector<cmTarget*>::const_iterator
- tei = this->Exports->begin();
- tei != this->Exports->end(); ++tei)
- {
cmTarget* te = *tei;
if(this->ExportedTargets.insert(te).second)
{
- this->GenerateImportTargetCode(os, te);
+ allTargets.push_back(te);
}
else
{
@@ -58,6 +49,33 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
}
}
+ this->GenerateExpectedTargetsCode(os, expectedTargets);
+ }
+
+ std::vector<std::string> missingTargets;
+
+ // Create all the imported targets.
+ for(std::vector<cmTarget*>::const_iterator
+ tei = allTargets.begin();
+ tei != allTargets.end(); ++tei)
+ {
+ cmTarget* te = *tei;
+ this->GenerateImportTargetCode(os, te);
+
+ ImportPropertyMap properties;
+
+ this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", te,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", te,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+
+ this->GenerateInterfaceProperties(te, os, properties);
+ }
+
+ this->GenerateMissingTargetsCheckCode(os, missingTargets);
+
// Generate import file content for each configuration.
for(std::vector<std::string>::const_iterator
ci = this->Configurations.begin();
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 3f738cc..2b133be 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -125,6 +125,201 @@ void cmExportFileGenerator::GenerateImportConfig(std::ostream& os,
}
//----------------------------------------------------------------------------
+void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
+ const char *outputName,
+ cmTarget *target,
+ cmGeneratorExpression::PreprocessContext preprocessRule,
+ ImportPropertyMap &properties,
+ std::vector<std::string> &missingTargets)
+{
+ const char *input = target->GetProperty(propName);
+ if (input)
+ {
+ if (!*input)
+ {
+ // Set to empty
+ properties[outputName] = "";
+ return;
+ }
+
+ std::string prepro = cmGeneratorExpression::Preprocess(input,
+ preprocessRule);
+ if (!prepro.empty())
+ {
+ this->ResolveTargetsInGeneratorExpressions(prepro, target,
+ missingTargets);
+ properties[outputName] = prepro;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
+ cmTarget *target,
+ cmGeneratorExpression::PreprocessContext preprocessRule,
+ ImportPropertyMap &properties,
+ std::vector<std::string> &missingTargets)
+{
+ this->PopulateInterfaceProperty(propName, propName, target, preprocessRule,
+ properties, missingTargets);
+}
+
+//----------------------------------------------------------------------------
+void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target,
+ std::ostream& os,
+ const ImportPropertyMap &properties)
+{
+ if (!properties.empty())
+ {
+ std::string targetName = this->Namespace;
+ targetName += target->GetName();
+ os << "SET_TARGET_PROPERTIES(" << targetName << " PROPERTIES\n";
+ for(ImportPropertyMap::const_iterator pi = properties.begin();
+ pi != properties.end(); ++pi)
+ {
+ os << " " << pi->first << " \"" << pi->second << "\"\n";
+ }
+ os << ")\n\n";
+ }
+}
+
+//----------------------------------------------------------------------------
+void
+cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
+ std::string &input,
+ cmTarget* target,
+ std::vector<std::string> &missingTargets)
+{
+ std::string::size_type pos = 0;
+ std::string::size_type lastPos = pos;
+
+ cmMakefile *mf = target->GetMakefile();
+ std::string errorString;
+
+ while((pos = input.find("$<TARGET_PROPERTY:", lastPos)) != input.npos)
+ {
+ std::string::size_type nameStartPos = pos +
+ sizeof("$<TARGET_PROPERTY:") - 1;
+ std::string::size_type closePos = input.find(">", nameStartPos);
+ std::string::size_type commaPos = input.find(",", nameStartPos);
+ std::string::size_type nextOpenPos = input.find("$<", nameStartPos);
+ if (commaPos == input.npos // Implied 'this' target
+ || closePos == input.npos // Imcomplete expression.
+ || closePos < commaPos // Implied 'this' target
+ || nextOpenPos < commaPos) // Non-literal
+ {
+ lastPos = nameStartPos;
+ continue;
+ }
+
+ const std::string targetName = input.substr(nameStartPos,
+ commaPos - nameStartPos);
+
+ pos = nameStartPos; // We're not going to replace the entire expression,
+ // but only the target parameter.
+ if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str()))
+ {
+ if(tgt->IsImported())
+ {
+ pos += targetName.size();
+ }
+ else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
+ {
+ input.replace(pos, targetName.size(),
+ this->Namespace + targetName);
+ pos += this->Namespace.size() + targetName.size();
+ }
+ else
+ {
+ std::string namespacedTarget;
+ this->HandleMissingTarget(namespacedTarget, missingTargets,
+ mf, target, tgt);
+ if (!namespacedTarget.empty())
+ {
+ input.replace(pos, targetName.size(), namespacedTarget);
+ pos += namespacedTarget.size();
+ }
+ }
+ }
+ else
+ {
+ errorString = "$<TARGET_PROPERTY:" + targetName + ",prop> requires "
+ "its first parameter to be a reachable target.";
+ }
+ lastPos = pos;
+ if (!errorString.empty())
+ {
+ break;
+ }
+ }
+ if (!errorString.empty())
+ {
+ mf->IssueMessage(cmake::FATAL_ERROR, errorString);
+ return;
+ }
+
+ pos = 0;
+ lastPos = pos;
+ while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
+ {
+ std::string::size_type nameStartPos = pos + sizeof("$<TARGET_NAME:") - 1;
+ std::string::size_type endPos = input.find(">", nameStartPos);
+ if (endPos == input.npos)
+ {
+ errorString = "$<TARGET_NAME:...> expression incomplete";
+ }
+ const std::string targetName = input.substr(nameStartPos,
+ endPos - nameStartPos);
+ if(targetName.find("$<", lastPos) != input.npos)
+ {
+ errorString = "$<TARGET_NAME:...> requires its parameter to be a "
+ "literal.";
+ }
+ if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str()))
+ {
+ if(tgt->IsImported())
+ {
+ input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
+ targetName);
+ pos += sizeof("$<TARGET_NAME:") + targetName.size();
+ }
+ else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
+ {
+ input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
+ this->Namespace + targetName);
+ pos += sizeof("$<TARGET_NAME:") + targetName.size();
+ }
+ else
+ {
+ std::string namespacedTarget;
+ this->HandleMissingTarget(namespacedTarget, missingTargets,
+ mf, target, tgt);
+ if (!namespacedTarget.empty())
+ {
+ input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
+ namespacedTarget);
+ pos += sizeof("$<TARGET_NAME:") + targetName.size();
+ }
+ }
+ }
+ else
+ {
+ errorString = "$<TARGET_NAME:...> requires its parameter to be a "
+ "reachable target.";
+ }
+ lastPos = pos;
+ if (!errorString.empty())
+ {
+ break;
+ }
+ }
+ if (!errorString.empty())
+ {
+ mf->IssueMessage(cmake::FATAL_ERROR, errorString);
+ }
+}
+
+//----------------------------------------------------------------------------
void
cmExportFileGenerator
::SetImportDetailProperties(const char* config, std::string const& suffix,
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 4a75c52..7c58ad8 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -13,6 +13,7 @@
#define cmExportFileGenerator_h
#include "cmCommand.h"
+#include "cmGeneratorExpression.h"
/** \class cmExportFileGenerator
* \brief Generate a file exporting targets from a build or install tree.
@@ -93,6 +94,17 @@ protected:
cmMakefile* mf,
cmTarget* depender,
cmTarget* dependee) = 0;
+ void PopulateInterfaceProperty(const char *,
+ cmTarget *target,
+ cmGeneratorExpression::PreprocessContext,
+ ImportPropertyMap &properties,
+ std::vector<std::string> &missingTargets);
+ void GenerateInterfaceProperties(cmTarget *target, std::ostream& os,
+ const ImportPropertyMap &properties);
+
+ void ResolveTargetsInGeneratorExpressions(std::string &input,
+ cmTarget* target,
+ std::vector<std::string> &missingTargets);
// The namespace in which the exports are placed in the generated file.
std::string Namespace;
@@ -109,6 +121,13 @@ protected:
// The set of targets included in the export.
std::set<cmTarget*> ExportedTargets;
+
+private:
+ void PopulateInterfaceProperty(const char *, const char *,
+ cmTarget *target,
+ cmGeneratorExpression::PreprocessContext,
+ ImportPropertyMap &properties,
+ std::vector<std::string> &missingTargets);
};
#endif
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 6ba7d9f..bc953c9 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -39,6 +39,7 @@ std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
//----------------------------------------------------------------------------
bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
{
+ std::vector<cmTarget*> allTargets;
{
std::string expectedTargets;
std::string sep;
@@ -48,20 +49,10 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
{
expectedTargets += sep + this->Namespace + (*tei)->Target->GetName();
sep = " ";
- }
-
- this->GenerateExpectedTargetsCode(os, expectedTargets);
- }
-
- // Create all the imported targets.
- for(std::vector<cmTargetExport*>::const_iterator
- tei = this->IEGen->GetExportSet()->GetTargetExports()->begin();
- tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei)
- {
cmTargetExport const* te = *tei;
if(this->ExportedTargets.insert(te->Target).second)
{
- this->GenerateImportTargetCode(os, te->Target);
+ allTargets.push_back(te->Target);
}
else
{
@@ -75,6 +66,35 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
}
}
+ this->GenerateExpectedTargetsCode(os, expectedTargets);
+ }
+
+ std::vector<std::string> missingTargets;
+
+ // Create all the imported targets.
+ for(std::vector<cmTarget*>::const_iterator
+ tei = allTargets.begin();
+ tei != allTargets.end(); ++tei)
+ {
+ cmTarget* te = *tei;
+ this->GenerateImportTargetCode(os, te);
+
+ ImportPropertyMap properties;
+
+ this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES",
+ te,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
+ te,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+
+ this->GenerateInterfaceProperties(te, os, properties);
+ }
+
+ this->GenerateMissingTargetsCheckCode(os, missingTargets);
+
// Now load per-configuration properties for them.
os << "# Load information for each installed configuration.\n"
<< "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 3fd4a5f..6d003e1 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -53,7 +53,22 @@ cmGeneratorExpression::~cmGeneratorExpression()
//----------------------------------------------------------------------------
const char *cmCompiledGeneratorExpression::Evaluate(
cmMakefile* mf, const char* config, bool quiet,
- cmTarget *target,
+ cmTarget *headTarget,
+ cmGeneratorExpressionDAGChecker *dagChecker) const
+{
+ return this->Evaluate(mf,
+ config,
+ quiet,
+ headTarget,
+ headTarget,
+ dagChecker);
+}
+
+//----------------------------------------------------------------------------
+const char *cmCompiledGeneratorExpression::Evaluate(
+ cmMakefile* mf, const char* config, bool quiet,
+ cmTarget *headTarget,
+ cmTarget *currentTarget,
cmGeneratorExpressionDAGChecker *dagChecker) const
{
if (!this->NeedsParsing)
@@ -73,7 +88,8 @@ const char *cmCompiledGeneratorExpression::Evaluate(
context.Config = config;
context.Quiet = quiet;
context.HadError = false;
- context.Target = target;
+ context.HeadTarget = headTarget;
+ context.CurrentTarget = currentTarget ? currentTarget : headTarget;
context.Backtrace = this->Backtrace;
for ( ; it != end; ++it)
@@ -123,15 +139,9 @@ cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression()
}
}
-std::string cmGeneratorExpression::Preprocess(const std::string &input,
- PreprocessContext context)
+//----------------------------------------------------------------------------
+static std::string stripAllGeneratorExpressions(const std::string &input)
{
- if (context != StripAllGeneratorExpressions)
- {
- assert(!"cmGeneratorExpression::Preprocess called with invalid args");
- return std::string();
- }
-
std::string result;
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
@@ -170,3 +180,81 @@ std::string cmGeneratorExpression::Preprocess(const std::string &input,
result += input.substr(lastPos);
return result;
}
+
+//----------------------------------------------------------------------------
+static std::string stripExportInterface(const std::string &input,
+ cmGeneratorExpression::PreprocessContext context)
+{
+ std::string result;
+
+ std::string::size_type pos = 0;
+ std::string::size_type lastPos = pos;
+ while((pos = input.find("$<BUILD_INTERFACE:", lastPos)) != input.npos
+ || (pos = input.find("$<INSTALL_INTERFACE:", lastPos)) != input.npos)
+ {
+ result += input.substr(lastPos, pos - lastPos);
+ const bool gotInstallInterface = input[pos + 2] == 'I';
+ pos += gotInstallInterface ? sizeof("$<INSTALL_INTERFACE:") - 1
+ : sizeof("$<BUILD_INTERFACE:") - 1;
+ 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)
+ {
+ continue;
+ }
+ if(context == cmGeneratorExpression::BuildInterface
+ && !gotInstallInterface)
+ {
+ result += input.substr(pos, c - cStart);
+ }
+ else if(context == cmGeneratorExpression::InstallInterface
+ && gotInstallInterface)
+ {
+ result += input.substr(pos, c - cStart);
+ }
+ 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;
+ }
+ result += input.substr(lastPos);
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+std::string cmGeneratorExpression::Preprocess(const std::string &input,
+ PreprocessContext context)
+{
+ if (context == StripAllGeneratorExpressions)
+ {
+ return stripAllGeneratorExpressions(input);
+ }
+ else if (context == BuildInterface || context == InstallInterface)
+ {
+ return stripExportInterface(input, context);
+ }
+
+ assert(!"cmGeneratorExpression::Preprocess called with invalid args");
+ return std::string();
+}
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index 15e637c..dcdfefb 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -51,7 +51,9 @@ public:
cmsys::auto_ptr<cmCompiledGeneratorExpression> Parse(const char* input);
enum PreprocessContext {
- StripAllGeneratorExpressions
+ StripAllGeneratorExpressions,
+ BuildInterface,
+ InstallInterface
};
static std::string Preprocess(const std::string &input,
@@ -69,8 +71,13 @@ class cmCompiledGeneratorExpression
public:
const char* Evaluate(cmMakefile* mf, const char* config,
bool quiet = false,
- cmTarget *target = 0,
+ cmTarget *headTarget = 0,
+ cmTarget *currentTarget = 0,
cmGeneratorExpressionDAGChecker *dagChecker = 0) const;
+ const char* Evaluate(cmMakefile* mf, const char* config,
+ bool quiet,
+ cmTarget *headTarget,
+ cmGeneratorExpressionDAGChecker *dagChecker) const;
/** Get set of targets found during evaluations. */
std::set<cmTarget*> const& GetTargets() const
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index bfb0ddf..2e5b5ae 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -24,13 +24,14 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
: Parent(parent), Target(target), Property(property),
Content(content), Backtrace(backtrace)
{
- this->IsDAG = this->isDAG();
+ this->CheckResult = this->checkGraph();
}
//----------------------------------------------------------------------------
-bool cmGeneratorExpressionDAGChecker::check() const
+cmGeneratorExpressionDAGChecker::Result
+cmGeneratorExpressionDAGChecker::check() const
{
- return this->IsDAG;
+ return this->CheckResult;
}
//----------------------------------------------------------------------------
@@ -38,7 +39,7 @@ void cmGeneratorExpressionDAGChecker::reportError(
cmGeneratorExpressionContext *context,
const std::string &expr)
{
- if (this->IsDAG)
+ if (this->CheckResult == DAG)
{
return;
}
@@ -57,7 +58,7 @@ void cmGeneratorExpressionDAGChecker::reportError(
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< "Self reference on target \""
- << context->Target->GetName() << "\".\n";
+ << context->HeadTarget->GetName() << "\".\n";
context->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
parent->Backtrace);
@@ -91,16 +92,17 @@ void cmGeneratorExpressionDAGChecker::reportError(
}
//----------------------------------------------------------------------------
-bool cmGeneratorExpressionDAGChecker::isDAG() const
+cmGeneratorExpressionDAGChecker::Result
+cmGeneratorExpressionDAGChecker::checkGraph() const
{
const cmGeneratorExpressionDAGChecker *parent = this->Parent;
while (parent)
{
if (this->Target == parent->Target && this->Property == parent->Property)
{
- return false;
+ return parent->Parent ? CYCLIC_REFERENCE : SELF_REFERENCE;
}
parent = parent->Parent;
}
- return true;
+ return DAG;
}
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index ffc84f8..48f26ed 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -25,12 +25,18 @@ struct cmGeneratorExpressionDAGChecker
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *parent);
- bool check() const;
+ enum Result {
+ DAG,
+ SELF_REFERENCE,
+ CYCLIC_REFERENCE
+ };
+
+ Result check() const;
void reportError(cmGeneratorExpressionContext *context,
const std::string &expr);
private:
- bool isDAG() const;
+ Result checkGraph() const;
private:
const cmGeneratorExpressionDAGChecker * const Parent;
@@ -38,7 +44,7 @@ private:
const std::string Property;
const GeneratorExpressionContent * const Content;
const cmListFileBacktrace Backtrace;
- bool IsDAG;
+ Result CheckResult;
};
#endif
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index d86ae54..82becaf 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -49,6 +49,8 @@ struct cmGeneratorExpressionNode
virtual bool GeneratesContent() const { return true; }
+ virtual bool RequiresLiteralInput() const { return false; }
+
virtual bool AcceptsSingleArbitraryContentParameter() const
{ return false; }
@@ -98,6 +100,12 @@ static const struct OneNode : public cmGeneratorExpressionNode
} oneNode;
//----------------------------------------------------------------------------
+static const struct OneNode buildInterfaceNode;
+
+//----------------------------------------------------------------------------
+static const struct ZeroNode installInterfaceNode;
+
+//----------------------------------------------------------------------------
#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
static const struct OP ## Node : public cmGeneratorExpressionNode \
{ \
@@ -259,11 +267,34 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
return parameters.front().empty() ? "1" : "0";
}
- return cmsysString_strcasecmp(parameters.begin()->c_str(),
- context->Config) == 0 ? "1" : "0";
+ if (cmsysString_strcasecmp(parameters.begin()->c_str(),
+ context->Config) == 0)
+ {
+ return "1";
+ }
+
+ if (context->CurrentTarget
+ && context->CurrentTarget->IsImported())
+ {
+ const char* loc = 0;
+ const char* imp = 0;
+ std::string suffix;
+ return context->CurrentTarget->GetMappedConfig(context->Config,
+ &loc,
+ &imp,
+ suffix) ? "1" : "0";
+ }
+ return "0";
}
} configurationTestNode;
+
+//----------------------------------------------------------------------------
+static const char* targetPropertyTransitiveWhitelist[] = {
+ "INTERFACE_INCLUDE_DIRECTORIES"
+ , "INTERFACE_COMPILE_DEFINITIONS"
+};
+
//----------------------------------------------------------------------------
static const struct TargetPropertyNode : public cmGeneratorExpressionNode
{
@@ -291,7 +322,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
cmsys::RegularExpression propertyNameValidator;
propertyNameValidator.compile("^[A-Za-z0-9_]+$");
- cmTarget* target = context->Target;
+ cmTarget* target = context->HeadTarget;
std::string propertyName = *parameters.begin();
if (!target && parameters.size() == 1)
@@ -372,18 +403,67 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
content,
dagCheckerParent);
- if (!dagChecker.check())
+ switch (dagChecker.check())
{
+ case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.reportError(context, content->GetOriginalExpression());
return std::string();
+ case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
+ // No error. We just skip cyclic references.
+ return std::string();
+ case cmGeneratorExpressionDAGChecker::DAG:
+ break;
}
const char *prop = target->GetProperty(propertyName.c_str());
- return prop ? prop : "";
+ if (!prop)
+ {
+ return std::string();
+ }
+
+ for (size_t i = 0;
+ i < (sizeof(targetPropertyTransitiveWhitelist) /
+ sizeof(*targetPropertyTransitiveWhitelist));
+ ++i)
+ {
+ if (targetPropertyTransitiveWhitelist[i] == propertyName)
+ {
+ cmGeneratorExpression ge(context->Backtrace);
+ return ge.Parse(prop)->Evaluate(context->Makefile,
+ context->Config,
+ context->Quiet,
+ context->HeadTarget,
+ target,
+ &dagChecker);
+ }
+ }
+ return prop;
}
} targetPropertyNode;
//----------------------------------------------------------------------------
+static const struct TargetNameNode : public cmGeneratorExpressionNode
+{
+ TargetNameNode() {}
+
+ virtual bool GeneratesContent() const { return true; }
+
+ virtual bool AcceptsSingleArbitraryContentParameter() const { return true; }
+ virtual bool RequiresLiteralInput() const { return true; }
+
+ std::string Evaluate(const std::vector<std::string> &parameters,
+ cmGeneratorExpressionContext *,
+ const GeneratorExpressionContent *,
+ cmGeneratorExpressionDAGChecker *) const
+ {
+ return parameters.front();
+ }
+
+ virtual int NumExpectedParameters() const { return 1; }
+
+} targetNameNode;
+
+//----------------------------------------------------------------------------
template<bool linker, bool soname>
struct TargetFilesystemArtifactResultCreator
{
@@ -608,6 +688,12 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
return &commaNode;
else if (identifier == "TARGET_PROPERTY")
return &targetPropertyNode;
+ else if (identifier == "TARGET_NAME")
+ return &targetNameNode;
+ else if (identifier == "BUILD_INTERFACE")
+ return &buildInterfaceNode;
+ else if (identifier == "INSTALL_INTERFACE")
+ return &installInterfaceNode;
return 0;
}
@@ -697,6 +783,15 @@ std::string GeneratorExpressionContent::Evaluate(
= pit->end();
for ( ; it != end; ++it)
{
+ if (node->RequiresLiteralInput())
+ {
+ if ((*it)->GetType() != cmGeneratorExpressionEvaluator::Text)
+ {
+ reportError(context, this->GetOriginalExpression(),
+ "$<" + identifier + "> expression requires literal input.");
+ return std::string();
+ }
+ }
result += (*it)->Evaluate(context, dagChecker);
if (context->HadError)
{
@@ -704,6 +799,12 @@ std::string GeneratorExpressionContent::Evaluate(
}
}
}
+ if (node->RequiresLiteralInput())
+ {
+ std::vector<std::string> parameters;
+ parameters.push_back(result);
+ return node->Evaluate(parameters, context, this, dagChecker);
+ }
return result;
}
diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h
index d904b02..59804ff 100644
--- a/Source/cmGeneratorExpressionEvaluator.h
+++ b/Source/cmGeneratorExpressionEvaluator.h
@@ -26,7 +26,9 @@ struct cmGeneratorExpressionContext
std::set<cmTarget*> Targets;
cmMakefile *Makefile;
const char *Config;
- cmTarget *Target;
+ cmTarget *HeadTarget; // The target whose property is being evaluated.
+ cmTarget *CurrentTarget; // The dependent of HeadTarget which appears
+ // directly or indirectly in the property.
bool Quiet;
bool HadError;
};
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 58fab80..ca53a39 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -720,6 +720,28 @@ void cmTarget::DefineProperties(cmake *cm)
"for the named configuration.");
cm->DefineProperty
+ ("INTERFACE_INCLUDE_DIRECTORIES", cmProperty::TARGET,
+ "List of public include directories for a library.",
+ "Targets may populate this property to publish the include directories "
+ "required to compile against the headers for the target. Consuming "
+ "targets can add entries to their own INCLUDE_DIRECTORIES property such "
+ "as $<TARGET_PROPERTY:foo,INTERFACE_INCLUDE_DIRECTORIES> to use the "
+ "include directories specified in the interface of 'foo'."
+ "\n"
+ CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS);
+
+ cm->DefineProperty
+ ("INTERFACE_COMPILE_DEFINITIONS", cmProperty::TARGET,
+ "List of public compile definitions for a library.",
+ "Targets may populate this property to publish the compile definitions "
+ "required to compile against the headers for the target. Consuming "
+ "targets can add entries to their own COMPILE_DEFINITIONS property such "
+ "as $<TARGET_PROPERTY:foo,INTERFACE_COMPILE_DEFINITIONS> to use the "
+ "compile definitions specified in the interface of 'foo'."
+ "\n"
+ CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS);
+
+ cm->DefineProperty
("LINK_INTERFACE_MULTIPLICITY", cmProperty::TARGET,
"Repetition count for STATIC libraries with cyclic dependencies.",
"When linking to a STATIC library target with cyclic dependencies the "
@@ -4365,27 +4387,15 @@ cmTarget::GetImportInfo(const char* config)
return &i->second;
}
-//----------------------------------------------------------------------------
-void cmTarget::ComputeImportInfo(std::string const& desired_config,
- ImportInfo& info)
+bool cmTarget::GetMappedConfig(std::string const& desired_config,
+ const char** loc,
+ const char** imp,
+ std::string& suffix)
{
- // This method finds information about an imported target from its
- // properties. The "IMPORTED_" namespace is reserved for properties
- // defined by the project exporting the target.
-
- // Initialize members.
- info.NoSOName = false;
-
// Track the configuration-specific property suffix.
- std::string suffix = "_";
+ suffix = "_";
suffix += desired_config;
- // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
- // library or an executable with exports.
- bool allowImp = this->HasImportLibrary();
-
- // Look for a mapping from the current project's configuration to
- // the imported project's configuration.
std::vector<std::string> mappedConfigs;
{
std::string mapProp = "MAP_IMPORTED_CONFIG_";
@@ -4396,26 +4406,29 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
}
}
+ // If we needed to find one of the mapped configurations but did not
+ // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
+ // library or an executable with exports.
+ bool allowImp = this->HasImportLibrary();
+
// If a mapping was found, check its configurations.
- const char* loc = 0;
- const char* imp = 0;
for(std::vector<std::string>::const_iterator mci = mappedConfigs.begin();
- !loc && !imp && mci != mappedConfigs.end(); ++mci)
+ !*loc && !*imp && mci != mappedConfigs.end(); ++mci)
{
// Look for this configuration.
std::string mcUpper = cmSystemTools::UpperCase(mci->c_str());
std::string locProp = "IMPORTED_LOCATION_";
locProp += mcUpper;
- loc = this->GetProperty(locProp.c_str());
+ *loc = this->GetProperty(locProp.c_str());
if(allowImp)
{
std::string impProp = "IMPORTED_IMPLIB_";
impProp += mcUpper;
- imp = this->GetProperty(impProp.c_str());
+ *imp = this->GetProperty(impProp.c_str());
}
// If it was found, use it for all properties below.
- if(loc || imp)
+ if(*loc || *imp)
{
suffix = "_";
suffix += mcUpper;
@@ -4425,45 +4438,45 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
// If we needed to find one of the mapped configurations but did not
// then the target is not found. The project does not want any
// other configuration.
- if(!mappedConfigs.empty() && !loc && !imp)
+ if(!mappedConfigs.empty() && !*loc && !*imp)
{
- return;
+ return false;
}
// If we have not yet found it then there are no mapped
// configurations. Look for an exact-match.
- if(!loc && !imp)
+ if(!*loc && !*imp)
{
std::string locProp = "IMPORTED_LOCATION";
locProp += suffix;
- loc = this->GetProperty(locProp.c_str());
+ *loc = this->GetProperty(locProp.c_str());
if(allowImp)
{
std::string impProp = "IMPORTED_IMPLIB";
impProp += suffix;
- imp = this->GetProperty(impProp.c_str());
+ *imp = this->GetProperty(impProp.c_str());
}
}
// If we have not yet found it then there are no mapped
// configurations and no exact match.
- if(!loc && !imp)
+ if(!*loc && !*imp)
{
// The suffix computed above is not useful.
suffix = "";
// Look for a configuration-less location. This may be set by
// manually-written code.
- loc = this->GetProperty("IMPORTED_LOCATION");
+ *loc = this->GetProperty("IMPORTED_LOCATION");
if(allowImp)
{
- imp = this->GetProperty("IMPORTED_IMPLIB");
+ *imp = this->GetProperty("IMPORTED_IMPLIB");
}
}
// If we have not yet found it then the project is willing to try
// any available configuration.
- if(!loc && !imp)
+ if(!*loc && !*imp)
{
std::vector<std::string> availableConfigs;
if(const char* iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS"))
@@ -4472,24 +4485,45 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
}
for(std::vector<std::string>::const_iterator
aci = availableConfigs.begin();
- !loc && !imp && aci != availableConfigs.end(); ++aci)
+ !*loc && !*imp && aci != availableConfigs.end(); ++aci)
{
suffix = "_";
suffix += cmSystemTools::UpperCase(*aci);
std::string locProp = "IMPORTED_LOCATION";
locProp += suffix;
- loc = this->GetProperty(locProp.c_str());
+ *loc = this->GetProperty(locProp.c_str());
if(allowImp)
{
std::string impProp = "IMPORTED_IMPLIB";
impProp += suffix;
- imp = this->GetProperty(impProp.c_str());
+ *imp = this->GetProperty(impProp.c_str());
}
}
}
-
// If we have not yet found it then the target is not available.
- if(!loc && !imp)
+ if(!*loc && !*imp)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+void cmTarget::ComputeImportInfo(std::string const& desired_config,
+ ImportInfo& info)
+{
+ // This method finds information about an imported target from its
+ // properties. The "IMPORTED_" namespace is reserved for properties
+ // defined by the project exporting the target.
+
+ // Initialize members.
+ info.NoSOName = false;
+
+ const char* loc = 0;
+ const char* imp = 0;
+ std::string suffix;
+ if (!this->GetMappedConfig(desired_config, &loc, &imp, suffix))
{
return;
}
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 3f36050..52d5ca6 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -404,6 +404,11 @@ public:
// Get the properties
cmPropertyMap &GetProperties() { return this->Properties; };
+ bool GetMappedConfig(std::string const& desired_config,
+ const char** loc,
+ const char** imp,
+ std::string& suffix);
+
// Define the properties
static void DefineProperties(cmake *cm);