Merge topic 'tll-includes-defines'

e48d842 Cache context-independent includes on evaluation.
089fe1c Optimize genex evaluation for includes and defines.
179f495 find_package: Reword <package>_NO_INTERFACES documentation
e7b579b Test workaround of bad interface include directories from depends.
77cecb7 Add includes and compile definitions with target_link_libraries.
0b92602 Add the $<LINKED:...> generator expression.
0fa7f69 Add API to check if we're reading a includes or defines property.
2c3654c Add a way to exclude INTERFACE properties from exported targets.
d4297d5 Export targets to a targets file, not a Config file.
df4d2b2 Make it an error for INSTALL_PREFIX to be evaluated.
7ceeba9 Advance more when preprocessing exported strings.
30268b4 Handle reading empty properties defined by the link interface.
This commit is contained in:
Brad King 2013-02-05 14:46:43 -05:00 committed by CMake Topic Stage
commit ec85306025
41 changed files with 633 additions and 94 deletions

@ -51,6 +51,14 @@
"on the target tgt.\n" \ "on the target tgt.\n" \
"Note that tgt is not added as a dependency of the target this " \ "Note that tgt is not added as a dependency of the target this " \
"expression is evaluated on.\n" \ "expression is evaluated on.\n" \
" $<LINKED:item> = An empty string if item is not a " \
"target. If item is a target then the " \
"INTERFACE_INCLUDE_DIRECTORIES or INTERFACE_COMPILE_DEFINITIONS " \
"content is read from the target. " \
"This generator expression can only be used in evaluation of the " \
"INCLUDE_DIRECTORIES or COMPILE_DEFINITIONS property. Note that " \
"this expression is for internal use and may be changed or removed " \
"in the future.\n" \
" $<TARGET_POLICY:pol> = '1' if the policy was NEW when " \ " $<TARGET_POLICY:pol> = '1' if the policy was NEW when " \
"the 'head' target was created, else '0'. If the policy was not " \ "the 'head' target was created, else '0'. If the policy was not " \
"set, the warning message for the policy will be emitted. This " \ "set, the warning message for the policy will be emitted. This " \

@ -25,6 +25,8 @@
#include <cmsys/auto_ptr.hxx> #include <cmsys/auto_ptr.hxx>
#include "assert.h"
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmExportFileGenerator::cmExportFileGenerator() cmExportFileGenerator::cmExportFileGenerator()
{ {
@ -160,7 +162,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
preprocessRule); preprocessRule);
if (!prepro.empty()) if (!prepro.empty())
{ {
this->ResolveTargetsInGeneratorExpressions(prepro, target, this->ResolveTargetsInGeneratorExpressions(prepro, target, propName,
missingTargets); missingTargets);
properties[outputName] = prepro; properties[outputName] = prepro;
} }
@ -264,15 +266,16 @@ void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target,
{ {
if (!properties.empty()) if (!properties.empty())
{ {
os << "if(NOT ${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES)\n";
std::string targetName = this->Namespace; std::string targetName = this->Namespace;
targetName += target->GetName(); targetName += target->GetName();
os << "set_target_properties(" << targetName << " PROPERTIES\n"; os << " set_target_properties(" << targetName << " PROPERTIES\n";
for(ImportPropertyMap::const_iterator pi = properties.begin(); for(ImportPropertyMap::const_iterator pi = properties.begin();
pi != properties.end(); ++pi) pi != properties.end(); ++pi)
{ {
os << " " << pi->first << " \"" << pi->second << "\"\n"; os << " " << pi->first << " \"" << pi->second << "\"\n";
} }
os << ")\n\n"; os << " )\nendif()\n\n";
} }
} }
@ -323,13 +326,14 @@ static bool isGeneratorExpression(const std::string &lib)
void void
cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
std::string &input, std::string &input,
cmTarget* target, cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets, std::vector<std::string> &missingTargets,
FreeTargetsReplace replace) FreeTargetsReplace replace)
{ {
if (replace == NoReplaceFreeTargets) if (replace == NoReplaceFreeTargets)
{ {
this->ResolveTargetsInGeneratorExpression(input, target, missingTargets); this->ResolveTargetsInGeneratorExpression(input, target, propName,
missingTargets);
return; return;
} }
std::vector<std::string> parts; std::vector<std::string> parts;
@ -348,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
{ {
this->ResolveTargetsInGeneratorExpression( this->ResolveTargetsInGeneratorExpression(
*li, *li,
target, target, propName,
missingTargets); missingTargets);
} }
input += sep + *li; input += sep + *li;
@ -360,7 +364,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
void void
cmExportFileGenerator::ResolveTargetsInGeneratorExpression( cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
std::string &input, std::string &input,
cmTarget* target, cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets) std::vector<std::string> &missingTargets)
{ {
std::string::size_type pos = 0; std::string::size_type pos = 0;
@ -391,10 +395,61 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
{ {
input.replace(nameStartPos, commaPos - nameStartPos, targetName); input.replace(nameStartPos, commaPos - nameStartPos, targetName);
} }
lastPos = pos + targetName.size(); lastPos = nameStartPos + targetName.size() + 1;
} }
std::string errorString; std::string errorString;
pos = 0;
lastPos = pos;
while((pos = input.find("$<LINKED:", lastPos)) != input.npos)
{
std::string::size_type nameStartPos = pos + sizeof("$<LINKED:") - 1;
std::string::size_type endPos = input.find(">", nameStartPos);
if (endPos == input.npos)
{
errorString = "$<LINKED:...> expression incomplete";
break;
}
std::string targetName = input.substr(nameStartPos,
endPos - nameStartPos);
if(targetName.find("$<") != input.npos)
{
errorString = "$<LINKED:...> requires its parameter to be a "
"literal.";
break;
}
if (this->AddTargetNamespace(targetName, target, missingTargets))
{
assert(propName); // The link libraries strings will
// never contain $<LINKED>
std::string replacement = "$<TARGET_PROPERTY:"
+ targetName + "," + propName;
input.replace(pos, endPos - pos, replacement);
lastPos = pos + replacement.size() + 1;
}
else
{
if (pos != 0)
{
if (input[pos - 1] == ';')
{
--pos;
}
}
else if (input[endPos + 1] == ';')
{
++endPos;
}
input.replace(pos, endPos - pos + 1, "");
lastPos = pos;
}
}
if (!errorString.empty())
{
mf->IssueMessage(cmake::FATAL_ERROR, errorString);
return;
}
pos = 0; pos = 0;
lastPos = pos; lastPos = pos;
while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos) while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
@ -490,7 +545,7 @@ cmExportFileGenerator
preprocessRule); preprocessRule);
if (!prepro.empty()) if (!prepro.empty())
{ {
this->ResolveTargetsInGeneratorExpressions(prepro, target, this->ResolveTargetsInGeneratorExpressions(prepro, target, 0,
missingTargets, missingTargets,
ReplaceFreeTargets); ReplaceFreeTargets);
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro; properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;

@ -119,7 +119,7 @@ protected:
}; };
void ResolveTargetsInGeneratorExpressions(std::string &input, void ResolveTargetsInGeneratorExpressions(std::string &input,
cmTarget* target, cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets, std::vector<std::string> &missingTargets,
FreeTargetsReplace replace = NoReplaceFreeTargets); FreeTargetsReplace replace = NoReplaceFreeTargets);
@ -150,7 +150,7 @@ private:
std::vector<std::string> &missingTargets); std::vector<std::string> &missingTargets);
void ResolveTargetsInGeneratorExpression(std::string &input, void ResolveTargetsInGeneratorExpression(std::string &input,
cmTarget* target, cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets); std::vector<std::string> &missingTargets);
virtual void ReplaceInstallPrefix(std::string &input); virtual void ReplaceInstallPrefix(std::string &input);

@ -376,6 +376,26 @@ void cmFindPackageCommand::GenerateDocumentation()
"The package configuration file may set <package>_FOUND to false " "The package configuration file may set <package>_FOUND to false "
"to tell find_package that component requirements are not satisfied." "to tell find_package that component requirements are not satisfied."
"\n" "\n"
"A package configuration file may include() a <package>Targets.cmake "
"file, created by install(EXPORT) in the upstream source, to import "
"targets into the downstream consumer. "
"When a new version of the upstream adds INTERFACE properties not "
"present in a previous version it can change behavior for existing "
"downstreams. "
"In order to remain source compatible the upstream package configuration "
"file may set <package>_NO_INTERFACES to disable INTERFACE properties. "
"For example, code of the form:\n"
" if(<package>_FIND_VERSION VERSION_LESS <new-version>\n"
" AND NOT <package>_INTERFACES)\n"
" set(<package>_NO_INTERFACES 1)\n"
" endif()\n"
" include(\"${CMAKE_CURRENT_LIST_DIR}/<package>Targets.cmake\")\n"
"tells <package>Targets.cmake not to provide the INTERFACE properties "
"unless the downstream requests at least <new-version> or sets "
"<package>_INTERFACES to explicitly request them. "
"This allows consumers to decide when to enable the new interfaces when "
"upgrading."
"\n"
"See the cmake_policy() command documentation for discussion of the " "See the cmake_policy() command documentation for discussion of the "
"NO_POLICY_SCOPE option." "NO_POLICY_SCOPE option."
; ;

@ -88,6 +88,7 @@ const char *cmCompiledGeneratorExpression::Evaluate(
context.Config = config; context.Config = config;
context.Quiet = quiet; context.Quiet = quiet;
context.HadError = false; context.HadError = false;
context.HadContextSensitiveCondition = false;
context.HeadTarget = headTarget; context.HeadTarget = headTarget;
context.CurrentTarget = currentTarget ? currentTarget : headTarget; context.CurrentTarget = currentTarget ? currentTarget : headTarget;
context.Backtrace = this->Backtrace; context.Backtrace = this->Backtrace;
@ -109,6 +110,10 @@ const char *cmCompiledGeneratorExpression::Evaluate(
break; break;
} }
} }
if (!context.HadError)
{
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
}
this->Targets = context.Targets; this->Targets = context.Targets;
// TODO: Return a std::string from here instead? // TODO: Return a std::string from here instead?
@ -118,7 +123,8 @@ const char *cmCompiledGeneratorExpression::Evaluate(
cmCompiledGeneratorExpression::cmCompiledGeneratorExpression( cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
cmListFileBacktrace const& backtrace, cmListFileBacktrace const& backtrace,
const char *input) const char *input)
: Backtrace(backtrace), Input(input ? input : "") : Backtrace(backtrace), Input(input ? input : ""),
HadContextSensitiveCondition(false)
{ {
cmGeneratorExpressionLexer l; cmGeneratorExpressionLexer l;
std::vector<cmGeneratorExpressionToken> tokens = std::vector<cmGeneratorExpressionToken> tokens =

@ -100,6 +100,10 @@ public:
{ {
return this->Backtrace; return this->Backtrace;
} }
bool GetHadContextSensitiveCondition() const
{
return this->HadContextSensitiveCondition;
}
private: private:
cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace, cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
@ -118,6 +122,7 @@ private:
mutable std::set<cmTarget*> Targets; mutable std::set<cmTarget*> Targets;
mutable std::map<cmStdString, cmStdString> SeenTargetProperties; mutable std::map<cmStdString, cmStdString> SeenTargetProperties;
mutable std::string Output; mutable std::string Output;
mutable bool HadContextSensitiveCondition;
}; };
#endif #endif

@ -24,7 +24,33 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
: Parent(parent), Target(target), Property(property), : Parent(parent), Target(target), Property(property),
Content(content), Backtrace(backtrace) Content(content), Backtrace(backtrace)
{ {
const cmGeneratorExpressionDAGChecker *top = this;
const cmGeneratorExpressionDAGChecker *p = this->Parent;
while (p)
{
top = p;
p = p->Parent;
}
this->CheckResult = this->checkGraph(); this->CheckResult = this->checkGraph();
if (CheckResult == DAG && (top->Property == "INCLUDE_DIRECTORIES"
|| top->Property == "COMPILE_DEFINITIONS") )
{
std::map<cmStdString, std::set<cmStdString> >::const_iterator it
= top->Seen.find(target);
if (it != top->Seen.end())
{
const std::set<cmStdString> &propSet = it->second;
const std::set<cmStdString>::const_iterator i = propSet.find(property);
if (i != propSet.end())
{
this->CheckResult = ALREADY_SEEN;
return;
}
}
const_cast<cmGeneratorExpressionDAGChecker *>(top)
->Seen[target].insert(property);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -125,3 +151,19 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries()
|| strncmp(prop, "LINK_INTERFACE_LIBRARIES_", 26) == 0 || strncmp(prop, "LINK_INTERFACE_LIBRARIES_", 26) == 0
|| strncmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_", 35) == 0); || strncmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_", 35) == 0);
} }
//----------------------------------------------------------------------------
bool cmGeneratorExpressionDAGChecker::EvaluatingIncludeDirectories()
{
const char *prop = this->Property.c_str();
return (strcmp(prop, "INCLUDE_DIRECTORIES") == 0
|| strcmp(prop, "INTERFACE_INCLUDE_DIRECTORIES") == 0 );
}
//----------------------------------------------------------------------------
bool cmGeneratorExpressionDAGChecker::EvaluatingCompileDefinitions()
{
const char *prop = this->Property.c_str();
return (strcmp(prop, "COMPILE_DEFINITIONS") == 0
|| strcmp(prop, "INTERFACE_COMPILE_DEFINITIONS") == 0 );
}

@ -28,7 +28,8 @@ struct cmGeneratorExpressionDAGChecker
enum Result { enum Result {
DAG, DAG,
SELF_REFERENCE, SELF_REFERENCE,
CYCLIC_REFERENCE CYCLIC_REFERENCE,
ALREADY_SEEN
}; };
Result check() const; Result check() const;
@ -37,6 +38,8 @@ struct cmGeneratorExpressionDAGChecker
const std::string &expr); const std::string &expr);
bool EvaluatingLinkLibraries(); bool EvaluatingLinkLibraries();
bool EvaluatingIncludeDirectories();
bool EvaluatingCompileDefinitions();
private: private:
Result checkGraph() const; Result checkGraph() const;
@ -45,6 +48,7 @@ private:
const cmGeneratorExpressionDAGChecker * const Parent; const cmGeneratorExpressionDAGChecker * const Parent;
const std::string Target; const std::string Target;
const std::string Property; const std::string Property;
std::map<cmStdString, std::set<cmStdString> > Seen;
const GeneratorExpressionContent * const Content; const GeneratorExpressionContent * const Content;
const cmListFileBacktrace Backtrace; const cmListFileBacktrace Backtrace;
Result CheckResult; Result CheckResult;

@ -238,6 +238,7 @@ static const struct ConfigurationNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent *, const GeneratorExpressionContent *,
cmGeneratorExpressionDAGChecker *) const cmGeneratorExpressionDAGChecker *) const
{ {
context->HadContextSensitiveCondition = true;
return context->Config ? context->Config : ""; return context->Config ? context->Config : "";
} }
} configurationNode; } configurationNode;
@ -262,6 +263,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
"Expression syntax not recognized."); "Expression syntax not recognized.");
return std::string(); return std::string();
} }
context->HadContextSensitiveCondition = true;
if (!context->Config) if (!context->Config)
{ {
return parameters.front().empty() ? "1" : "0"; return parameters.front().empty() ? "1" : "0";
@ -435,6 +437,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
// No error. We just skip cyclic references. // No error. We just skip cyclic references.
return std::string(); return std::string();
case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
// No error. We're not going to find anything new here.
return std::string();
case cmGeneratorExpressionDAGChecker::DAG: case cmGeneratorExpressionDAGChecker::DAG:
break; break;
} }
@ -452,12 +457,14 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
} }
if (propertyName == "POSITION_INDEPENDENT_CODE") if (propertyName == "POSITION_INDEPENDENT_CODE")
{ {
context->HadContextSensitiveCondition = true;
return target->GetLinkInterfaceDependentBoolProperty( return target->GetLinkInterfaceDependentBoolProperty(
"POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0"; "POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0";
} }
if (target->IsLinkInterfaceDependentBoolProperty(propertyName, if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
context->Config)) context->Config))
{ {
context->HadContextSensitiveCondition = true;
return target->GetLinkInterfaceDependentBoolProperty( return target->GetLinkInterfaceDependentBoolProperty(
propertyName, propertyName,
context->Config) ? "1" : "0"; context->Config) ? "1" : "0";
@ -465,9 +472,12 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (target->IsLinkInterfaceDependentStringProperty(propertyName, if (target->IsLinkInterfaceDependentStringProperty(propertyName,
context->Config)) context->Config))
{ {
return target->GetLinkInterfaceDependentStringProperty( context->HadContextSensitiveCondition = true;
const char *propContent =
target->GetLinkInterfaceDependentStringProperty(
propertyName, propertyName,
context->Config); context->Config);
return propContent ? propContent : "";
} }
return std::string(); return std::string();
@ -481,12 +491,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (targetPropertyTransitiveWhitelist[i] == propertyName) if (targetPropertyTransitiveWhitelist[i] == propertyName)
{ {
cmGeneratorExpression ge(context->Backtrace); cmGeneratorExpression ge(context->Backtrace);
return ge.Parse(prop)->Evaluate(context->Makefile, cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
std::string result = cge->Evaluate(context->Makefile,
context->Config, context->Config,
context->Quiet, context->Quiet,
context->HeadTarget, context->HeadTarget,
target, target,
&dagChecker); &dagChecker);
if (cge->GetHadContextSensitiveCondition())
{
context->HadContextSensitiveCondition = true;
}
return result;
} }
} }
return prop; return prop;
@ -580,6 +597,9 @@ static const struct TargetPolicyNode : public cmGeneratorExpressionNode
"be used with add_custom_command."); "be used with add_custom_command.");
return std::string(); return std::string();
} }
context->HadContextSensitiveCondition = true;
for (size_t i = 0; for (size_t i = 0;
i < (sizeof(targetPolicyWhitelist) / i < (sizeof(targetPolicyWhitelist) /
sizeof(*targetPolicyWhitelist)); sizeof(*targetPolicyWhitelist));
@ -619,19 +639,114 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
{ {
InstallPrefixNode() {} InstallPrefixNode() {}
virtual bool GeneratesContent() const { return false; } virtual bool GeneratesContent() const { return true; }
virtual int NumExpectedParameters() const { return 0; } virtual int NumExpectedParameters() const { return 0; }
std::string Evaluate(const std::vector<std::string> &, std::string Evaluate(const std::vector<std::string> &,
cmGeneratorExpressionContext *, cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *, const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *) const cmGeneratorExpressionDAGChecker *) const
{ {
reportError(context, content->GetOriginalExpression(),
"INSTALL_PREFIX is a marker for install(EXPORT) only. It "
"should never be evaluated.");
return std::string(); return std::string();
} }
} installPrefixNode; } installPrefixNode;
//----------------------------------------------------------------------------
static const struct LinkedNode : public cmGeneratorExpressionNode
{
LinkedNode() {}
virtual bool GeneratesContent() const { return true; }
virtual int NumExpectedParameters() const { return 1; }
virtual bool RequiresLiteralInput() const { return true; }
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *dagChecker) const
{
if (dagChecker->EvaluatingIncludeDirectories())
{
return this->GetInterfaceProperty(parameters.front(),
"INCLUDE_DIRECTORIES",
context, content, dagChecker);
}
if (dagChecker->EvaluatingCompileDefinitions())
{
return this->GetInterfaceProperty(parameters.front(),
"COMPILE_DEFINITIONS",
context, content, dagChecker);
}
reportError(context, content->GetOriginalExpression(),
"$<LINKED:...> may only be used in INCLUDE_DIRECTORIES and "
"COMPILE_DEFINITIONS properties.");
return std::string();
}
private:
std::string GetInterfaceProperty(const std::string &item,
const std::string &prop,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *dagCheckerParent) const
{
cmTarget *target = context->CurrentTarget
->GetMakefile()->FindTargetToUse(item.c_str());
if (!target)
{
return std::string();
}
std::string propertyName = "INTERFACE_" + prop;
const char *propContent = target->GetProperty(propertyName.c_str());
if (!propContent)
{
return std::string();
}
cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace,
target->GetName(),
propertyName,
content,
dagCheckerParent);
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::ALREADY_SEEN:
// No error. We're not going to find anything new here.
return std::string();
case cmGeneratorExpressionDAGChecker::DAG:
break;
}
cmGeneratorExpression ge(context->Backtrace);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(propContent);
std::string result = cge->Evaluate(context->Makefile,
context->Config,
context->Quiet,
context->HeadTarget,
target,
&dagChecker);
if (cge->GetHadContextSensitiveCondition())
{
context->HadContextSensitiveCondition = true;
}
return result;
}
} linkedNode;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
template<bool linker, bool soname> template<bool linker, bool soname>
struct TargetFilesystemArtifactResultCreator struct TargetFilesystemArtifactResultCreator
@ -869,6 +984,8 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
return &targetDefinedNode; return &targetDefinedNode;
else if (identifier == "INSTALL_PREFIX") else if (identifier == "INSTALL_PREFIX")
return &installPrefixNode; return &installPrefixNode;
else if (identifier == "LINKED")
return &linkedNode;
return 0; return 0;
} }

@ -32,6 +32,7 @@ struct cmGeneratorExpressionContext
// directly or indirectly in the property. // directly or indirectly in the property.
bool Quiet; bool Quiet;
bool HadError; bool HadError;
bool HadContextSensitiveCondition;
}; };
struct cmGeneratorExpressionDAGChecker; struct cmGeneratorExpressionDAGChecker;

@ -134,6 +134,7 @@ public:
: ge(cge) : ge(cge)
{} {}
const cmsys::auto_ptr<cmCompiledGeneratorExpression> ge; const cmsys::auto_ptr<cmCompiledGeneratorExpression> ge;
std::vector<std::string> CachedIncludes;
}; };
std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries; std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries;
}; };
@ -2778,22 +2779,36 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
end = this->Internal->IncludeDirectoriesEntries.end(); end = this->Internal->IncludeDirectoriesEntries.end();
it != end; ++it) it != end; ++it)
{ {
std::vector<std::string> entryIncludes;
cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile, bool testIsOff = true;
config, bool cacheIncludes = false;
false, std::vector<std::string> entryIncludes = (*it)->CachedIncludes;
this, if(!entryIncludes.empty())
&dagChecker), {
entryIncludes); testIsOff = false;
}
else
{
cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile,
config,
false,
this,
&dagChecker),
entryIncludes);
if (!(*it)->ge->GetHadContextSensitiveCondition())
{
cacheIncludes = true;
}
}
std::string usedIncludes; std::string usedIncludes;
for(std::vector<std::string>::const_iterator for(std::vector<std::string>::iterator
li = entryIncludes.begin(); li != entryIncludes.end(); ++li) li = entryIncludes.begin(); li != entryIncludes.end(); ++li)
{ {
std::string inc = *li; if (testIsOff && !cmSystemTools::IsOff(li->c_str()))
if (!cmSystemTools::IsOff(inc.c_str()))
{ {
cmSystemTools::ConvertToUnixSlashes(inc); cmSystemTools::ConvertToUnixSlashes(*li);
} }
std::string inc = *li;
if(uniqueIncludes.insert(inc).second) if(uniqueIncludes.insert(inc).second)
{ {
@ -2804,6 +2819,10 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
} }
} }
} }
if (cacheIncludes)
{
(*it)->CachedIncludes = entryIncludes;
}
if (!usedIncludes.empty()) if (!usedIncludes.empty())
{ {
this->Makefile->GetCMakeInstance()->IssueMessage(cmake::LOG, this->Makefile->GetCMakeInstance()->IssueMessage(cmake::LOG,

@ -514,6 +514,9 @@ public:
const char *GetLinkInterfaceDependentStringProperty(const std::string &p, const char *GetLinkInterfaceDependentStringProperty(const std::string &p,
const char *config); const char *config);
std::string GetDebugGeneratorExpressions(const std::string &value,
cmTarget::LinkLibraryType llt);
private: private:
/** /**
* A list of direct dependencies. Use in conjunction with DependencyMap. * A list of direct dependencies. Use in conjunction with DependencyMap.
@ -659,9 +662,6 @@ private:
void ProcessSourceExpression(std::string const& expr); void ProcessSourceExpression(std::string const& expr);
std::string GetDebugGeneratorExpressions(const std::string &value,
cmTarget::LinkLibraryType llt);
// The cmMakefile instance that owns this target. This should // The cmMakefile instance that owns this target. This should
// always be set. // always be set.
cmMakefile* Makefile; cmMakefile* Makefile;

@ -249,11 +249,52 @@ cmTargetLinkLibrariesCommand
this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
} }
//----------------------------------------------------------------------------
static std::string compileProperty(cmTarget *tgt, const std::string &lib,
bool isGenex,
const std::string &property,
cmTarget::LinkLibraryType llt)
{
std::string value = !isGenex ? "$<LINKED:" + lib + ">"
: "$<$<TARGET_DEFINED:" + lib + ">:" +
"$<TARGET_PROPERTY:" + lib +
",INTERFACE_" + property + ">"
">";
return tgt->GetDebugGeneratorExpressions(value, llt);
}
//----------------------------------------------------------------------------
static bool isGeneratorExpression(const std::string &lib)
{
const std::string::size_type openpos = lib.find("$<");
return (openpos != std::string::npos)
&& (lib.find(">", openpos) != std::string::npos);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib, cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
cmTarget::LinkLibraryType llt) cmTarget::LinkLibraryType llt)
{ {
const bool isGenex = isGeneratorExpression(lib);
cmsys::RegularExpression targetNameValidator;
targetNameValidator.compile("^[A-Za-z0-9_.:-]+$");
const bool potentialTargetName = targetNameValidator.find(lib);
if (potentialTargetName || isGenex)
{
this->Target->AppendProperty("INCLUDE_DIRECTORIES",
compileProperty(this->Target, lib,
isGenex,
"INCLUDE_DIRECTORIES", llt).c_str());
this->Target->AppendProperty("COMPILE_DEFINITIONS",
compileProperty(this->Target, lib,
isGenex,
"COMPILE_DEFINITIONS", llt).c_str());
}
// Handle normal case first. // Handle normal case first.
if(this->CurrentProcessingState != ProcessingLinkInterface) if(this->CurrentProcessingState != ProcessingLinkInterface)
{ {
@ -266,6 +307,18 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
} }
} }
if (potentialTargetName || isGenex)
{
this->Target->AppendProperty("INTERFACE_COMPILE_DEFINITIONS",
compileProperty(this->Target, lib,
isGenex,
"COMPILE_DEFINITIONS", llt).c_str());
this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES",
compileProperty(this->Target, lib,
isGenex,
"INCLUDE_DIRECTORIES", llt).c_str());
}
// Get the list of configurations considered to be DEBUG. // Get the list of configurations considered to be DEBUG.
std::vector<std::string> const& debugConfigs = std::vector<std::string> const& debugConfigs =
this->Makefile->GetCMakeInstance()->GetDebugConfigs(); this->Makefile->GetCMakeInstance()->GetDebugConfigs();

@ -97,6 +97,15 @@ public:
"Calls to other signatures of this command may set the property " "Calls to other signatures of this command may set the property "
"making any libraries linked exclusively by this signature private." "making any libraries linked exclusively by this signature private."
"\n" "\n"
"Target usage requirements are also consumed by this command. If the "
"<target> is linked to another target which has "
"a populated INTERFACE_INCLUDE_DIRECTORIES, the content of it is "
"appended to the INCLUDE_DIRECTORIES of <target>. Similarly, the "
"INTERFACE_COMPILE_DEFINITONS of a dependee are added to the "
"COMPILE_DEFINITONS of <target>, and the "
"INTERFACE_POSITION_INDEPENDENT_CODE property is used to determine the "
"POSITION_INDEPENDENT_CODE property of <target>."
"\n"
" target_link_libraries(<target> LINK_INTERFACE_LIBRARIES\n" " target_link_libraries(<target> LINK_INTERFACE_LIBRARIES\n"
" [[debug|optimized|general] <lib>] ...)\n" " [[debug|optimized|general] <lib>] ...)\n"
"The LINK_INTERFACE_LIBRARIES mode appends the libraries " "The LINK_INTERFACE_LIBRARIES mode appends the libraries "

@ -16,9 +16,15 @@ add_executable(consumer
"${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
) )
add_library(linked UNKNOWN IMPORTED)
set_property(TARGET linked PROPERTY
INTERFACE_COMPILE_DEFINITIONS "MY_LINKED_DEFINE")
target_compile_definitions(consumer target_compile_definitions(consumer
PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS> PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS>
$<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED> $<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED>
$<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED> $<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED>
$<LINKED:linked>
-DDASH_D_DEFINE -DDASH_D_DEFINE
) )

@ -23,4 +23,8 @@
#error Expected DASH_D_DEFINE #error Expected DASH_D_DEFINE
#endif #endif
#ifndef MY_LINKED_DEFINE
#error Expected MY_LINKED_DEFINE
#endif
int main() { return 0; } int main() { return 0; }

@ -17,6 +17,9 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/poison/common.h" "#error Should not be i
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cure") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cure")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cure/common.h" "#define CURE_DEFINE\n") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cure/common.h" "#define CURE_DEFINE\n")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude/linkedinclude.h" "#define LINKEDINCLUDE_DEFINE\n")
add_executable(target_include_directories add_executable(target_include_directories
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
) )
@ -42,7 +45,13 @@ add_executable(consumer
"${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
) )
add_library(linked UNKNOWN IMPORTED)
set_property(TARGET linked PROPERTY
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude")
target_include_directories(consumer target_include_directories(consumer
PRIVATE $<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES> PRIVATE
$<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES>
$<LINKED:linked>
relative_dir relative_dir
) )

@ -3,6 +3,7 @@
#include "publicinclude.h" #include "publicinclude.h"
#include "interfaceinclude.h" #include "interfaceinclude.h"
#include "relative_dir.h" #include "relative_dir.h"
#include "linkedinclude.h"
#ifdef PRIVATEINCLUDE_DEFINE #ifdef PRIVATEINCLUDE_DEFINE
#error Unexpected PRIVATEINCLUDE_DEFINE #error Unexpected PRIVATEINCLUDE_DEFINE
@ -24,4 +25,8 @@
#error Expected RELATIVE_DIR_DEFINE #error Expected RELATIVE_DIR_DEFINE
#endif #endif
#ifndef LINKEDINCLUDE_DEFINE
#error Expected LINKEDINCLUDE_DEFINE
#endif
int main() { return 0; } int main() { return 0; }

@ -62,10 +62,6 @@ assert_property(targetA LINK_INTERFACE_LIBRARIES "")
add_subdirectory(subdir) add_subdirectory(subdir)
target_link_libraries(targetA subdirlib) target_link_libraries(targetA subdirlib)
set_property(TARGET targetA APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:subdirlib,INTERFACE_INCLUDE_DIRECTORIES>
)
target_link_libraries(targetA depB depC) target_link_libraries(targetA depB depC)
@ -87,3 +83,24 @@ set_property(TARGET depD APPEND PROPERTY
add_executable(targetB targetB.cpp) add_executable(targetB targetB.cpp)
target_link_libraries(targetB depD) target_link_libraries(targetB depD)
macro(create_header _name)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_name}")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_name}/${_name}.h" "//${_name}.h\n")
endmacro()
create_header(foo)
create_header(bar)
add_library(depG SHARED depG.cpp)
generate_export_header(depG)
target_include_directories(depG INTERFACE
"${CMAKE_CURRENT_BINARY_DIR}/foo"
"${CMAKE_CURRENT_BINARY_DIR}/bar"
)
target_compile_definitions(depG INTERFACE
TEST_DEF
)
add_executable(targetC targetC.cpp)
target_link_libraries(targetC depG)

@ -0,0 +1,7 @@
#include "depG.h"
int DepG::foo()
{
return 0;
}

@ -0,0 +1,7 @@
#include "depg_export.h"
struct DEPG_EXPORT DepG
{
int foo();
};

@ -0,0 +1,16 @@
#include "depG.h"
#include "foo.h"
#include "bar.h"
#ifndef TEST_DEF
#error Expected TEST_DEF definition
#endif
int main(int argc, char **argv)
{
DepG g;
return g.foo();
}

@ -48,10 +48,22 @@ target_compile_definitions(CompatibleInterface
add_library(iface2 SHARED iface2.cpp) add_library(iface2 SHARED iface2.cpp)
generate_export_header(iface2) generate_export_header(iface2)
set_property(TARGET iface2 APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING
Iface2_PROP
)
# For the LINK_LIBRARIES and related properties, we should not evaluate # For the LINK_LIBRARIES and related properties, we should not evaluate
# properties defined only in the interface - they should be implicitly zero # properties defined only in the interface - they should be implicitly zero
set_property(TARGET iface2 set_property(TARGET iface2
APPEND PROPERTY APPEND PROPERTY
LINK_INTERFACE_LIBRARIES $<$<BOOL:$<TARGET_PROPERTY:BOOL_PROP4>>:nonexistant> LINK_INTERFACE_LIBRARIES $<$<BOOL:$<TARGET_PROPERTY:BOOL_PROP4>>:nonexistant>
) )
target_link_libraries(CompatibleInterface iface2) target_link_libraries(CompatibleInterface iface2
$<$<BOOL:$<TARGET_PROPERTY:Iface2_PROP>>:nonexistant>
)
# Test that this does not segfault:
target_compile_definitions(CompatibleInterface
PRIVATE
$<$<BOOL:$<TARGET_PROPERTY:Iface2_PROP>>:SOME_DEFINE>
)

@ -90,23 +90,7 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3)
# Test exporting dependent libraries into different exports # Test exporting dependent libraries into different exports
add_library(testLibRequired testLibRequired.c) add_library(testLibRequired testLibRequired.c)
add_library(testLibDepends testLibDepends.c) add_library(testLibDepends testLibDepends.c)
set_property(TARGET testLibDepends APPEND PROPERTY target_link_libraries(testLibDepends LINK_PUBLIC testLibRequired)
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET testLibDepends APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
)
set_property(TARGET testLibDepends APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET testLibDepends APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
)
target_link_libraries(testLibDepends testLibRequired)
macro(add_include_lib _libName) macro(add_include_lib _libName)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n")
@ -234,11 +218,30 @@ install(TARGETS testLibRequired
testLibIncludeRequired6 testLibIncludeRequired6
testSharedLibRequired testSharedLibRequired
EXPORT RequiredExp DESTINATION lib ) EXPORT RequiredExp DESTINATION lib )
install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired) install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredTargets.cmake DESTINATION lib/cmake/testLibRequired)
install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib ) install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib )
install(EXPORT DependsExp FILE testLibDependsConfig.cmake DESTINATION lib/cmake/testLibDepends) install(EXPORT DependsExp FILE testLibDependsTargets.cmake DESTINATION lib/cmake/testLibDepends)
file(WRITE
"${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake"
"
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION VERSION_LESS 2.3 AND NOT \${CMAKE_FIND_PACKAGE_NAME}_INTERFACES)
set(\${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES 1)
endif()
include(\"\${CMAKE_CURRENT_LIST_DIR}/testLibRequiredTargets.cmake\")
set(\${CMAKE_FIND_PACKAGE_NAME}_INCLUDE_DIRS \"${CMAKE_CURRENT_BINARY_DIR}\" \"${CMAKE_CURRENT_SOURCE_DIR}\" )
"
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file( testLibRequiredConfigVersion.cmake VERSION 2.5 COMPATIBILITY AnyNewerVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfigVersion.cmake"
DESTINATION lib/cmake/testLibRequired
)
# Install and export from install tree. # Install and export from install tree.
install( install(

@ -5,8 +5,8 @@ include(${Import_BINARY_DIR}/../Export/ExportBuildTree.cmake)
include(${CMAKE_INSTALL_PREFIX}/lib/exp/exp.cmake) include(${CMAKE_INSTALL_PREFIX}/lib/exp/exp.cmake)
# Import two exports, where the Depends one depends on an exported target from the Required one: # Import two exports, where the Depends one depends on an exported target from the Required one:
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredConfig.cmake) include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredTargets.cmake)
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsConfig.cmake) include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsTargets.cmake)
# Try referencing an executable imported from the install tree. # Try referencing an executable imported from the install tree.
add_custom_command( add_custom_command(
@ -159,18 +159,11 @@ endif()
add_executable(deps_iface deps_iface.c) add_executable(deps_iface deps_iface.c)
target_link_libraries(deps_iface testLibDepends) target_link_libraries(deps_iface testLibDepends)
target_include_directories(deps_iface PRIVATE $<TARGET_PROPERTY:testLibDepends,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_definitions(deps_iface PRIVATE $<TARGET_PROPERTY:testLibDepends,INTERFACE_COMPILE_DEFINITIONS>)
add_executable(deps_shared_iface deps_shared_iface.cpp) add_executable(deps_shared_iface deps_shared_iface.cpp)
target_link_libraries(deps_shared_iface testSharedLibDepends) target_link_libraries(deps_shared_iface testSharedLibDepends)
target_include_directories(deps_shared_iface
PRIVATE
$<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
)
target_compile_definitions(deps_shared_iface target_compile_definitions(deps_shared_iface
PRIVATE PRIVATE
$<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS>
$<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON> $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
$<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON> $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
$<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH> $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>
@ -200,13 +193,8 @@ endif()
add_executable(deps_shared_iface2 deps_shared_iface.cpp) add_executable(deps_shared_iface2 deps_shared_iface.cpp)
target_link_libraries(deps_shared_iface2 bld_testSharedLibDepends bld_subdirlib) target_link_libraries(deps_shared_iface2 bld_testSharedLibDepends bld_subdirlib)
target_include_directories(deps_shared_iface2
PRIVATE
$<TARGET_PROPERTY:bld_testSharedLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:bld_subdirlib,INTERFACE_INCLUDE_DIRECTORIES>
)
target_compile_definitions(deps_shared_iface2 target_compile_definitions(deps_shared_iface2
PRIVATE $<TARGET_PROPERTY:bld_testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS> TEST_SUBDIR_LIB PRIVATE TEST_SUBDIR_LIB
$<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON> $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
$<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON> $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
$<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH> $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>

@ -17,3 +17,8 @@ add_executable(imp_testTransExe1 imp_testTransExe1.c)
target_link_libraries(imp_testTransExe1 imp_lib1) target_link_libraries(imp_testTransExe1 imp_lib1)
add_executable(imp_testTransExe1b imp_testTransExe1.c) add_executable(imp_testTransExe1b imp_testTransExe1.c)
target_link_libraries(imp_testTransExe1b imp_lib1b) target_link_libraries(imp_testTransExe1b imp_lib1b)
# Test package INTERFACE controls
add_subdirectory(package_old_old)
add_subdirectory(package_new_old)
add_subdirectory(package_new_new)

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 2.8)
find_package(testLibRequired 2.5 REQUIRED)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
"
#include \"testSharedLibRequired.h\"
int main(int argc, char **argv)
{
TestSharedLibRequired req;
return req.foo();
}
"
)
get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
if (NOT prop)
message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty")
endif()
add_executable(new_new_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
target_link_libraries(new_new_test Req::testSharedLibRequired)

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 2.8)
find_package(testLibRequired 2.5 REQUIRED)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
"
#include \"testSharedLibRequired.h\"
int main(int argc, char **argv)
{
TestSharedLibRequired req;
return req.foo();
}
"
)
get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
if ("${prop}" STREQUAL "")
message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty")
endif()
add_executable(new_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
target_link_libraries(new_old_test Req::testSharedLibRequired)
include_directories(${testLibRequired_INCLUDE_DIRS})

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 2.8)
find_package(testLibRequired 2.1 REQUIRED)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
"
#include \"testSharedLibRequired.h\"
int main(int argc, char **argv)
{
TestSharedLibRequired req;
return req.foo();
}
"
)
get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
if (prop)
message(SEND_ERROR "Interface of Req::testSharedLibRequired should be empty, but is ${prop}")
endif()
add_executable(old_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
target_link_libraries(old_old_test Req::testSharedLibRequired)
include_directories(${testLibRequired_INCLUDE_DIRS})

@ -89,7 +89,6 @@ add_custom_target(check-part2 ALL
-Dtest_install_interface=$<INSTALL_INTERFACE:install> -Dtest_install_interface=$<INSTALL_INTERFACE:install>
-Dtest_target_name_1=$<TARGET_NAME:tgt,ok> -Dtest_target_name_1=$<TARGET_NAME:tgt,ok>
-Dtest_target_name_2=$<TARGET_NAME:tgt:ok> -Dtest_target_name_2=$<TARGET_NAME:tgt:ok>
-Dtest_install_prefix=$<INSTALL_PREFIX>
-P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)" COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)"
VERBATIM VERBATIM

@ -26,4 +26,3 @@ check(test_build_interface "build")
check(test_install_interface "") check(test_install_interface "")
check(test_target_name_1 "tgt,ok") check(test_target_name_1 "tgt,ok")
check(test_target_name_2 "tgt:ok") check(test_target_name_2 "tgt:ok")
check(test_install_prefix "")

@ -82,3 +82,44 @@ add_custom_target(test_custom_target
$<TARGET_PROPERTY:TargetIncludeDirectories,COMPILE_DEFINITIONS> $<TARGET_PROPERTY:TargetIncludeDirectories,COMPILE_DEFINITIONS>
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}") "${CMAKE_CURRENT_SOURCE_DIR}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bad")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bad/common.h" "#error Should not be included\n")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/good")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/good/common.h" "#include \"othergood.h\"\n")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/othergood")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/othergood/othergood.h" "// No error\n")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp" "// No content \n")
add_library(libothergood "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp")
set_property(TARGET libothergood APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/othergood"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp" "// No content \n")
add_library(libgood "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp")
set_property(TARGET libgood APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_BINARY_DIR}/good;$<TARGET_PROPERTY:libothergood,INTERFACE_INCLUDE_DIRECTORIES>"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp" "// No content \n")
add_library(libbad "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp")
set_property(TARGET libbad APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/bad"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp" "#include \"common.h\"\n")
add_library(lib5 "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp")
# Assuming the link order must be:
target_link_libraries(lib5 libbad libgood)
# Oops!.
# As include directory order and link order are the same when using target_link_libraries, we have to
# get the libgood includes in before the libbad includes.
# We do that with this command:
target_include_directories(lib5
BEFORE PRIVATE $<LINKED:libgood>
)

@ -12,29 +12,10 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(Qt4Targets WIN32 main.cpp) add_executable(Qt4Targets WIN32 main.cpp)
target_link_libraries(Qt4Targets Qt4::QtGui) target_link_libraries(Qt4Targets Qt4::QtGui)
set_property(TARGET Qt4Targets APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET Qt4Targets APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_COMPILE_DEFINITIONS>
)
if (WIN32) if (WIN32)
if (TARGET Qt4::QAxServer) if (TARGET Qt4::QAxServer)
add_executable(activeqtexe WIN32 activeqtexe.cpp) add_executable(activeqtexe WIN32 activeqtexe.cpp)
set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON) set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON)
target_link_libraries(activeqtexe Qt4::QAxServer Qt4::QtGui) target_link_libraries(activeqtexe Qt4::QAxServer Qt4::QtGui)
set_property(TARGET activeqtexe APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:Qt4::QAxServer,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET activeqtexe APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:Qt4::QAxServer,INTERFACE_COMPILE_DEFINITIONS>
$<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_COMPILE_DEFINITIONS>
)
endif() endif()
endif() endif()

@ -0,0 +1,9 @@
CMake Error at BadInstallPrefix.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<INSTALL_PREFIX>
INSTALL_PREFIX is a marker for install\(EXPORT\) only. It should never be
evaluated.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

@ -0,0 +1,3 @@
add_custom_target(check ALL COMMAND check
$<INSTALL_PREFIX>/include
VERBATIM)

@ -7,3 +7,4 @@ run_cmake(BadNOT)
run_cmake(BadStrEqual) run_cmake(BadStrEqual)
run_cmake(BadZero) run_cmake(BadZero)
run_cmake(BadTargetName) run_cmake(BadTargetName)
run_cmake(BadInstallPrefix)

@ -0,0 +1,7 @@
CMake Error:
Error evaluating generator expression:
\$<LINKED:something>
\$<LINKED:...> may only be used in INCLUDE_DIRECTORIES and
COMPILE_DEFINITIONS properties.$

@ -0,0 +1,7 @@
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
"int main(int, char **) { return 0; }\n")
add_executable(TargetPropertyGeneratorExpressions
"${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
target_link_libraries(TargetPropertyGeneratorExpressions "$<LINKED:something>")

@ -15,3 +15,4 @@ run_cmake(BadInvalidName5)
run_cmake(BadInvalidName6) run_cmake(BadInvalidName6)
run_cmake(BadInvalidName7) run_cmake(BadInvalidName7)
run_cmake(BadInvalidName8) run_cmake(BadInvalidName8)
run_cmake(BadLinked)