summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
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);