Commit c0c8ef85 authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'LINK_INTERFACE_LIBRARIES-genex'

77d26467 Allow generator expressions in LINK_INTERFACE_LIBRARIES.
94aeaf72 Split LINK_INTERFACE_LIBRARIES export handling into dedicated method.
a3aedb81 Split the generator expression before extracting targets.
b6036d10 Extract the AddTargetNamespace method.
cb1afbf4 Don't pass a position when determining if a target name is a literal.
f99196dc Add cmGeneratorExpression::Split() API.
parents ffe93315 77d26467
......@@ -107,6 +107,10 @@ cmExportBuildFileGenerator
std::vector<std::string> missingTargets;
this->SetImportDetailProperties(config, suffix,
target, properties, missingTargets);
this->SetImportLinkInterface(config, suffix,
cmGeneratorExpression::BuildInterface,
target, properties, missingTargets);
// TOOD: PUBLIC_HEADER_LOCATION
// This should wait until the build feature propagation stuff
......
......@@ -183,9 +183,89 @@ void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target,
}
}
//----------------------------------------------------------------------------
bool
cmExportFileGenerator::AddTargetNamespace(std::string &input,
cmTarget* target,
std::vector<std::string> &missingTargets)
{
cmMakefile *mf = target->GetMakefile();
cmTarget *tgt = mf->FindTargetToUse(input.c_str());
if (!tgt)
{
return false;
}
if(tgt->IsImported())
{
return true;
}
if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
{
input = this->Namespace + input;
}
else
{
std::string namespacedTarget;
this->HandleMissingTarget(namespacedTarget, missingTargets,
mf, target, tgt);
if (!namespacedTarget.empty())
{
input = namespacedTarget;
}
}
return true;
}
//----------------------------------------------------------------------------
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
cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
std::string &input,
cmTarget* target,
std::vector<std::string> &missingTargets,
FreeTargetsReplace replace)
{
if (replace == NoReplaceFreeTargets)
{
this->ResolveTargetsInGeneratorExpression(input, target, missingTargets);
return;
}
std::vector<std::string> parts;
cmGeneratorExpression::Split(input, parts);
std::string sep;
input = "";
for(std::vector<std::string>::iterator li = parts.begin();
li != parts.end(); ++li)
{
if (!isGeneratorExpression(*li))
{
this->AddTargetNamespace(*li, target, missingTargets);
}
else
{
this->ResolveTargetsInGeneratorExpression(
*li,
target,
missingTargets);
}
input += sep + *li;
sep = ";";
}
}
//----------------------------------------------------------------------------
void
cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
std::string &input,
cmTarget* target,
std::vector<std::string> &missingTargets)
......@@ -212,45 +292,17 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
continue;
}
const std::string targetName = input.substr(nameStartPos,
std::string targetName = input.substr(nameStartPos,
commaPos - nameStartPos);
pos = nameStartPos; // We're not going to replace the entire expression,
// but only the target parameter.
if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str()))
{
if(tgt->IsImported())
{
pos += targetName.size();
}
else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
{
input.replace(pos, targetName.size(),
this->Namespace + targetName);
pos += this->Namespace.size() + targetName.size();
}
else
{
std::string namespacedTarget;
this->HandleMissingTarget(namespacedTarget, missingTargets,
mf, target, tgt);
if (!namespacedTarget.empty())
{
input.replace(pos, targetName.size(), namespacedTarget);
pos += namespacedTarget.size();
}
}
}
else
if (!this->AddTargetNamespace(targetName, target, missingTargets))
{
errorString = "$<TARGET_PROPERTY:" + targetName + ",prop> requires "
"its first parameter to be a reachable target.";
}
lastPos = pos;
if (!errorString.empty())
{
break;
}
input.replace(nameStartPos, commaPos - nameStartPos, targetName);
lastPos = pos + targetName.size();
}
if (!errorString.empty())
{
......@@ -267,51 +319,24 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
if (endPos == input.npos)
{
errorString = "$<TARGET_NAME:...> expression incomplete";
break;
}
const std::string targetName = input.substr(nameStartPos,
std::string targetName = input.substr(nameStartPos,
endPos - nameStartPos);
if(targetName.find("$<", lastPos) != input.npos)
if(targetName.find("$<") != input.npos)
{
errorString = "$<TARGET_NAME:...> requires its parameter to be a "
"literal.";
break;
}
if (cmTarget *tgt = mf->FindTargetToUse(targetName.c_str()))
{
if(tgt->IsImported())
{
input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
targetName);
pos += sizeof("$<TARGET_NAME:") + targetName.size();
}
else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
{
input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
this->Namespace + targetName);
pos += sizeof("$<TARGET_NAME:") + targetName.size();
}
else
{
std::string namespacedTarget;
this->HandleMissingTarget(namespacedTarget, missingTargets,
mf, target, tgt);
if (!namespacedTarget.empty())
{
input.replace(pos, sizeof("$<TARGET_NAME:") + targetName.size(),
namespacedTarget);
pos += sizeof("$<TARGET_NAME:") + targetName.size();
}
}
}
else
if (!this->AddTargetNamespace(targetName, target, missingTargets))
{
errorString = "$<TARGET_NAME:...> requires its parameter to be a "
"reachable target.";
}
lastPos = pos;
if (!errorString.empty())
{
break;
}
input.replace(pos, endPos - pos + 1, targetName);
lastPos = endPos;
}
if (!errorString.empty())
{
......@@ -319,6 +344,64 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
}
}
//----------------------------------------------------------------------------
void
cmExportFileGenerator
::SetImportLinkInterface(const char* config, std::string const& suffix,
cmGeneratorExpression::PreprocessContext preprocessRule,
cmTarget* target, ImportPropertyMap& properties,
std::vector<std::string>& missingTargets)
{
// Add the transitive link dependencies for this configuration.
cmTarget::LinkInterface const* iface = target->GetLinkInterface(config,
target);
if (!iface)
{
return;
}
if (iface->ImplementationIsInterface)
{
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LIBRARIES",
iface->Libraries, properties, missingTargets);
return;
}
const char *propContent;
if (const char *prop_suffixed = target->GetProperty(
("LINK_INTERFACE_LIBRARIES" + suffix).c_str()))
{
propContent = prop_suffixed;
}
else if (const char *prop = target->GetProperty(
"LINK_INTERFACE_LIBRARIES"))
{
propContent = prop;
}
else
{
return;
}
if (!*propContent)
{
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = "";
return;
}
std::string prepro = cmGeneratorExpression::Preprocess(propContent,
preprocessRule);
if (!prepro.empty())
{
this->ResolveTargetsInGeneratorExpressions(prepro, target,
missingTargets,
ReplaceFreeTargets);
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
}
}
//----------------------------------------------------------------------------
void
cmExportFileGenerator
......@@ -363,9 +446,7 @@ cmExportFileGenerator
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LANGUAGES",
iface->Languages, properties, missingTargets);
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LIBRARIES",
iface->Libraries, properties, missingTargets);
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_DEPENDENT_LIBRARIES",
iface->SharedDeps, properties, missingTargets);
......@@ -397,9 +478,6 @@ cmExportFileGenerator
return;
}
// Get the makefile in which to lookup target information.
cmMakefile* mf = target->GetMakefile();
// Construct the property value.
std::string link_libs;
const char* sep = "";
......@@ -410,33 +488,9 @@ cmExportFileGenerator
link_libs += sep;
sep = ";";
// Append this entry.
if(cmTarget* tgt = mf->FindTargetToUse(li->c_str()))
{
// This is a target.
if(tgt->IsImported())
{
// The target is imported (and therefore is not in the
// export). Append the raw name.
link_libs += *li;
}
else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
{
// The target is in the export. Append it with the export
// namespace.
link_libs += this->Namespace;
link_libs += *li;
}
else
{
this->HandleMissingTarget(link_libs, missingTargets, mf, target, tgt);
}
}
else
{
// Append the raw name.
link_libs += *li;
}
std::string temp = *li;
this->AddTargetNamespace(temp, target, missingTargets);
link_libs += temp;
}
// Store the property.
......
......@@ -102,9 +102,20 @@ protected:
void GenerateInterfaceProperties(cmTarget *target, std::ostream& os,
const ImportPropertyMap &properties);
void SetImportLinkInterface(const char* config, std::string const& suffix,
cmGeneratorExpression::PreprocessContext preprocessRule,
cmTarget* target, ImportPropertyMap& properties,
std::vector<std::string>& missingTargets);
enum FreeTargetsReplace {
ReplaceFreeTargets,
NoReplaceFreeTargets
};
void ResolveTargetsInGeneratorExpressions(std::string &input,
cmTarget* target,
std::vector<std::string> &missingTargets);
cmTarget* target,
std::vector<std::string> &missingTargets,
FreeTargetsReplace replace = NoReplaceFreeTargets);
// The namespace in which the exports are placed in the generated file.
std::string Namespace;
......@@ -128,6 +139,13 @@ private:
cmGeneratorExpression::PreprocessContext,
ImportPropertyMap &properties,
std::vector<std::string> &missingTargets);
bool AddTargetNamespace(std::string &input, cmTarget* target,
std::vector<std::string> &missingTargets);
void ResolveTargetsInGeneratorExpression(std::string &input,
cmTarget* target,
std::vector<std::string> &missingTargets);
};
#endif
......@@ -229,6 +229,10 @@ cmExportInstallFileGenerator
this->SetImportDetailProperties(config, suffix,
te->Target, properties, missingTargets);
this->SetImportLinkInterface(config, suffix,
cmGeneratorExpression::InstallInterface,
te->Target, properties, missingTargets);
// TOOD: PUBLIC_HEADER_LOCATION
// This should wait until the build feature propagation stuff
// is done. Then this can be a propagated include directory.
......
......@@ -250,6 +250,67 @@ static std::string stripExportInterface(const std::string &input,
return result;
}
//----------------------------------------------------------------------------
void cmGeneratorExpression::Split(const std::string &input,
std::vector<std::string> &output)
{
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
while((pos = input.find("$<", lastPos)) != input.npos)
{
std::string part = input.substr(lastPos, pos - lastPos);
std::string preGenex;
if (!part.empty())
{
std::string::size_type startPos = input.rfind(";", pos);
if (startPos != pos - 1 && startPos >= lastPos)
{
part = input.substr(lastPos, startPos - lastPos);
preGenex = input.substr(startPos + 1, pos - startPos - 1);
}
cmSystemTools::ExpandListArgument(part.c_str(), output);
}
pos += 2;
int nestingLevel = 1;
const char *c = input.c_str() + pos;
const char * const cStart = c;
for ( ; *c; ++c)
{
if(c[0] == '$' && c[1] == '<')
{
++nestingLevel;
++c;
continue;
}
if(c[0] == '>')
{
--nestingLevel;
if (nestingLevel == 0)
{
break;
}
}
}
for ( ; *c; ++c)
{
// Capture the part after the genex and before the next ';'
if(c[0] == ';')
{
--c;
break;
}
}
const std::string::size_type traversed = (c - cStart) + 1;
output.push_back(preGenex + "$<" + input.substr(pos, traversed));
pos += traversed;
lastPos = pos;
}
if (lastPos < input.size())
{
cmSystemTools::ExpandListArgument(input.substr(lastPos), output);
}
}
//----------------------------------------------------------------------------
std::string cmGeneratorExpression::Preprocess(const std::string &input,
PreprocessContext context)
......
......@@ -59,6 +59,9 @@ public:
static std::string Preprocess(const std::string &input,
PreprocessContext context);
static void Split(const std::string &input,
std::vector<std::string> &output);
private:
cmGeneratorExpression(const cmGeneratorExpression &);
void operator=(const cmGeneratorExpression &);
......
......@@ -4896,16 +4896,30 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
{
std::string linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
linkProp += suffix;
if(const char* config_libs = this->GetProperty(linkProp.c_str()))
const char *propertyLibs = this->GetProperty(linkProp.c_str());
if(!propertyLibs)
{
cmSystemTools::ExpandListArgument(config_libs,
info.LinkInterface.Libraries);
linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
propertyLibs = this->GetProperty(linkProp.c_str());
}
else if(const char* libs =
this->GetProperty("IMPORTED_LINK_INTERFACE_LIBRARIES"))
if(propertyLibs)
{
cmSystemTools::ExpandListArgument(libs,
info.LinkInterface.Libraries);
cmListFileBacktrace lfbt;
cmGeneratorExpression ge(lfbt);
cmGeneratorExpressionDAGChecker dagChecker(lfbt,
this->GetName(),
linkProp, 0, 0);
cmSystemTools::ExpandListArgument(ge.Parse(propertyLibs)
->Evaluate(this->Makefile,
desired_config.c_str(),
false,
headTarget,
this,
&dagChecker),
info.LinkInterface.Libraries);
}
}
......@@ -5019,18 +5033,20 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
// An explicit list of interface libraries may be set for shared
// libraries and executables that export symbols.
const char* explicitLibraries = 0;
std::string linkIfaceProp;
if(this->GetType() == cmTarget::SHARED_LIBRARY ||
this->IsExecutableWithExports())
{
// Lookup the per-configuration property.
std::string propName = "LINK_INTERFACE_LIBRARIES";
propName += suffix;
explicitLibraries = this->GetProperty(propName.c_str());
linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
linkIfaceProp += suffix;
explicitLibraries = this->GetProperty(linkIfaceProp.c_str());
// If not set, try the generic property.
if(!explicitLibraries)
{
explicitLibraries = this->GetProperty("LINK_INTERFACE_LIBRARIES");
linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
explicitLibraries = this->GetProperty(linkIfaceProp.c_str());
}
}
......@@ -5048,7 +5064,16 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
if(explicitLibraries)
{
// The interface libraries have been explicitly set.
cmSystemTools::ExpandListArgument(explicitLibraries, iface.Libraries);
cmListFileBacktrace lfbt;
cmGeneratorExpression ge(lfbt);
cmGeneratorExpressionDAGChecker dagChecker(lfbt, this->GetName(),
linkIfaceProp, 0, 0);
cmSystemTools::ExpandListArgument(ge.Parse(explicitLibraries)->Evaluate(
this->Makefile,
config,
false,
headTarget,
this, &dagChecker), iface.Libraries);
if(this->GetType() == cmTarget::SHARED_LIBRARY)
{
......@@ -5091,6 +5116,7 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
// The link implementation is the default link interface.
LinkImplementation const* impl = this->GetLinkImplementation(config,
headTarget);
iface.ImplementationIsInterface = true;
iface.Libraries = impl->Libraries;
iface.WrongConfigLibraries = impl->WrongConfigLibraries;
if(this->GetType() == cmTarget::STATIC_LIBRARY)
......
......@@ -258,7 +258,9 @@ public:
// Needed only for OLD behavior of CMP0003.
std::vector<std::string> WrongConfigLibraries;
LinkInterface(): Multiplicity(0) {}
bool ImplementationIsInterface;
LinkInterface(): Multiplicity(0), ImplementationIsInterface(false) {}
};
/** Get the link interface for the given configuration. Returns 0
......
......@@ -80,3 +80,13 @@ assert_property(targetA LINK_INTERFACE_LIBRARIES "")
add_library(depIfaceOnly SHARED EXCLUDE_FROM_ALL depIfaceOnly.cpp)
generate_export_header(depIfaceOnly)
set_property(TARGET depB APPEND PROPERTY LINK_INTERFACE_LIBRARIES depIfaceOnly)
add_library(depD SHARED depD.cpp)
generate_export_header(depD)
set_property(TARGET depD APPEND PROPERTY
LINK_INTERFACE_LIBRARIES
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:depA>
)
add_executable(targetB targetB.cpp)
target_link_libraries(targetB depD)
#include "depD.h"
int DepD::foo()
{
return 0;
}
DepA DepD::getA()
{
DepA a;
return a;
}
#include "depd_export.h"
#include "depA.h"
struct DEPD_EXPORT DepD
{
int foo();
DepA getA();
};