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

View File

@ -51,6 +51,14 @@
"on the target tgt.\n" \
"Note that tgt is not added as a dependency of the target this " \
"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 " \
"the 'head' target was created, else '0'. If the policy was not " \
"set, the warning message for the policy will be emitted. This " \

View File

@ -25,6 +25,8 @@
#include <cmsys/auto_ptr.hxx>
#include "assert.h"
//----------------------------------------------------------------------------
cmExportFileGenerator::cmExportFileGenerator()
{
@ -160,7 +162,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
preprocessRule);
if (!prepro.empty())
{
this->ResolveTargetsInGeneratorExpressions(prepro, target,
this->ResolveTargetsInGeneratorExpressions(prepro, target, propName,
missingTargets);
properties[outputName] = prepro;
}
@ -264,15 +266,16 @@ void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target,
{
if (!properties.empty())
{
os << "if(NOT ${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES)\n";
std::string targetName = this->Namespace;
targetName += target->GetName();
os << "set_target_properties(" << targetName << " PROPERTIES\n";
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 << " " << pi->first << " \"" << pi->second << "\"\n";
}
os << ")\n\n";
os << " )\nendif()\n\n";
}
}
@ -323,13 +326,14 @@ static bool isGeneratorExpression(const std::string &lib)
void
cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
std::string &input,
cmTarget* target,
cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets,
FreeTargetsReplace replace)
{
if (replace == NoReplaceFreeTargets)
{
this->ResolveTargetsInGeneratorExpression(input, target, missingTargets);
this->ResolveTargetsInGeneratorExpression(input, target, propName,
missingTargets);
return;
}
std::vector<std::string> parts;
@ -348,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
{
this->ResolveTargetsInGeneratorExpression(
*li,
target,
target, propName,
missingTargets);
}
input += sep + *li;
@ -360,7 +364,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
void
cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
std::string &input,
cmTarget* target,
cmTarget* target, const char *propName,
std::vector<std::string> &missingTargets)
{
std::string::size_type pos = 0;
@ -391,10 +395,61 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
{
input.replace(nameStartPos, commaPos - nameStartPos, targetName);
}
lastPos = pos + targetName.size();
lastPos = nameStartPos + targetName.size() + 1;
}
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;
lastPos = pos;
while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
@ -490,7 +545,7 @@ cmExportFileGenerator
preprocessRule);
if (!prepro.empty())
{
this->ResolveTargetsInGeneratorExpressions(prepro, target,
this->ResolveTargetsInGeneratorExpressions(prepro, target, 0,
missingTargets,
ReplaceFreeTargets);
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;

View File

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

View File

@ -376,6 +376,26 @@ void cmFindPackageCommand::GenerateDocumentation()
"The package configuration file may set <package>_FOUND to false "
"to tell find_package that component requirements are not satisfied."
"\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 "
"NO_POLICY_SCOPE option."
;

View File

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

View File

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

View File

@ -24,7 +24,33 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
: Parent(parent), Target(target), Property(property),
Content(content), Backtrace(backtrace)
{
const cmGeneratorExpressionDAGChecker *top = this;
const cmGeneratorExpressionDAGChecker *p = this->Parent;
while (p)
{
top = p;
p = p->Parent;
}
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, "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 );
}

View File

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

View File

@ -238,6 +238,7 @@ static const struct ConfigurationNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent *,
cmGeneratorExpressionDAGChecker *) const
{
context->HadContextSensitiveCondition = true;
return context->Config ? context->Config : "";
}
} configurationNode;
@ -262,6 +263,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
"Expression syntax not recognized.");
return std::string();
}
context->HadContextSensitiveCondition = true;
if (!context->Config)
{
return parameters.front().empty() ? "1" : "0";
@ -435,6 +437,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
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;
}
@ -452,12 +457,14 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
}
if (propertyName == "POSITION_INDEPENDENT_CODE")
{
context->HadContextSensitiveCondition = true;
return target->GetLinkInterfaceDependentBoolProperty(
"POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0";
}
if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
context->Config))
{
context->HadContextSensitiveCondition = true;
return target->GetLinkInterfaceDependentBoolProperty(
propertyName,
context->Config) ? "1" : "0";
@ -465,9 +472,12 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (target->IsLinkInterfaceDependentStringProperty(propertyName,
context->Config))
{
return target->GetLinkInterfaceDependentStringProperty(
context->HadContextSensitiveCondition = true;
const char *propContent =
target->GetLinkInterfaceDependentStringProperty(
propertyName,
context->Config);
return propContent ? propContent : "";
}
return std::string();
@ -481,12 +491,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (targetPropertyTransitiveWhitelist[i] == propertyName)
{
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->Quiet,
context->HeadTarget,
target,
&dagChecker);
if (cge->GetHadContextSensitiveCondition())
{
context->HadContextSensitiveCondition = true;
}
return result;
}
}
return prop;
@ -580,6 +597,9 @@ static const struct TargetPolicyNode : public cmGeneratorExpressionNode
"be used with add_custom_command.");
return std::string();
}
context->HadContextSensitiveCondition = true;
for (size_t i = 0;
i < (sizeof(targetPolicyWhitelist) /
sizeof(*targetPolicyWhitelist));
@ -619,19 +639,114 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
{
InstallPrefixNode() {}
virtual bool GeneratesContent() const { return false; }
virtual bool GeneratesContent() const { return true; }
virtual int NumExpectedParameters() const { return 0; }
std::string Evaluate(const std::vector<std::string> &,
cmGeneratorExpressionContext *,
const GeneratorExpressionContent *,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *) const
{
reportError(context, content->GetOriginalExpression(),
"INSTALL_PREFIX is a marker for install(EXPORT) only. It "
"should never be evaluated.");
return std::string();
}
} 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>
struct TargetFilesystemArtifactResultCreator
@ -869,6 +984,8 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
return &targetDefinedNode;
else if (identifier == "INSTALL_PREFIX")
return &installPrefixNode;
else if (identifier == "LINKED")
return &linkedNode;
return 0;
}

View File

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

View File

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

View File

@ -514,6 +514,9 @@ public:
const char *GetLinkInterfaceDependentStringProperty(const std::string &p,
const char *config);
std::string GetDebugGeneratorExpressions(const std::string &value,
cmTarget::LinkLibraryType llt);
private:
/**
* A list of direct dependencies. Use in conjunction with DependencyMap.
@ -659,9 +662,6 @@ private:
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
// always be set.
cmMakefile* Makefile;

View File

@ -249,11 +249,52 @@ cmTargetLinkLibrariesCommand
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
cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
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.
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.
std::vector<std::string> const& debugConfigs =
this->Makefile->GetCMakeInstance()->GetDebugConfigs();

View File

@ -97,6 +97,15 @@ public:
"Calls to other signatures of this command may set the property "
"making any libraries linked exclusively by this signature private."
"\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"
" [[debug|optimized|general] <lib>] ...)\n"
"The LINK_INTERFACE_LIBRARIES mode appends the libraries "

View File

@ -16,9 +16,15 @@ add_executable(consumer
"${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
PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS>
$<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED>
$<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED>
$<LINKED:linked>
-DDASH_D_DEFINE
)

View File

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

View File

@ -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(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
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
)
@ -42,7 +45,13 @@ add_executable(consumer
"${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
PRIVATE $<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES>
PRIVATE
$<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES>
$<LINKED:linked>
relative_dir
)

View File

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

View File

@ -62,10 +62,6 @@ assert_property(targetA LINK_INTERFACE_LIBRARIES "")
add_subdirectory(subdir)
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)
@ -87,3 +83,24 @@ set_property(TARGET depD APPEND PROPERTY
add_executable(targetB targetB.cpp)
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)

View File

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

View File

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

View File

@ -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();
}

View File

@ -48,10 +48,22 @@ target_compile_definitions(CompatibleInterface
add_library(iface2 SHARED iface2.cpp)
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
# properties defined only in the interface - they should be implicitly zero
set_property(TARGET iface2
APPEND PROPERTY
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>
)

View File

@ -90,23 +90,7 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3)
# Test exporting dependent libraries into different exports
add_library(testLibRequired testLibRequired.c)
add_library(testLibDepends testLibDepends.c)
set_property(TARGET testLibDepends APPEND PROPERTY
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)
target_link_libraries(testLibDepends LINK_PUBLIC testLibRequired)
macro(add_include_lib _libName)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n")
@ -234,11 +218,30 @@ install(TARGETS testLibRequired
testLibIncludeRequired6
testSharedLibRequired
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(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(

View File

@ -5,8 +5,8 @@ include(${Import_BINARY_DIR}/../Export/ExportBuildTree.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:
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredConfig.cmake)
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsConfig.cmake)
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredTargets.cmake)
include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsTargets.cmake)
# Try referencing an executable imported from the install tree.
add_custom_command(
@ -159,18 +159,11 @@ endif()
add_executable(deps_iface deps_iface.c)
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)
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
PRIVATE
$<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS>
$<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
$<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
$<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>
@ -200,13 +193,8 @@ endif()
add_executable(deps_shared_iface2 deps_shared_iface.cpp)
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
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:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
$<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>

View File

@ -17,3 +17,8 @@ add_executable(imp_testTransExe1 imp_testTransExe1.c)
target_link_libraries(imp_testTransExe1 imp_lib1)
add_executable(imp_testTransExe1b imp_testTransExe1.c)
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)

View File

@ -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)

View File

@ -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})

View File

@ -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})

View File

@ -89,7 +89,6 @@ add_custom_target(check-part2 ALL
-Dtest_install_interface=$<INSTALL_INTERFACE:install>
-Dtest_target_name_1=$<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
COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)"
VERBATIM

View File

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

View File

@ -82,3 +82,44 @@ add_custom_target(test_custom_target
$<TARGET_PROPERTY:TargetIncludeDirectories,COMPILE_DEFINITIONS>
WORKING_DIRECTORY
"${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>
)

View File

@ -12,29 +12,10 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(Qt4Targets WIN32 main.cpp)
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 (TARGET Qt4::QAxServer)
add_executable(activeqtexe WIN32 activeqtexe.cpp)
set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON)
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()

View File

@ -0,0 +1 @@
1

View File

@ -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\)

View File

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

View File

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

View File

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

View File

@ -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>")

View File

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