Add the $<LINKED:...> generator expression.

This is both a short form of using a TARGET_DEFINED expression
together with a TARGET_PROPERTY definition, and a way to strip
non-target content from interface properties when exporting.
This commit is contained in:
Stephen Kelly 2013-01-30 23:38:04 +01:00
parent 0fa7f69c0e
commit 0b92602b81
12 changed files with 196 additions and 9 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;
}
@ -324,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;
@ -349,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
{
this->ResolveTargetsInGeneratorExpression(
*li,
target,
target, propName,
missingTargets);
}
input += sep + *li;
@ -361,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;
@ -396,6 +399,57 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
}
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)
@ -491,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

@ -637,6 +637,89 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
} 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::DAG:
break;
}
cmGeneratorExpression ge(context->Backtrace);
return ge.Parse(propContent)->Evaluate(context->Makefile,
context->Config,
context->Quiet,
context->HeadTarget,
target,
&dagChecker);
}
} linkedNode;
//----------------------------------------------------------------------------
template<bool linker, bool soname>
struct TargetFilesystemArtifactResultCreator
@ -874,6 +957,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

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

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