Allow generator expressions in LINK_INTERFACE_LIBRARIES.

The Config and IMPORTED_ variants may also contain generator
expressions.

If 'the implementation is the interface', then the result of
evaluating the expressions at generate time is used to populate
the IMPORTED_LINK_INTERFACE_LIBRARIES property.

1) In the case of non-static libraries, this is fine because the
  user still has the option to populate the LINK_INTERFACE_LIBRARIES
  with generator expressions if that is what is wanted.

2) In the case of static libraries, this prevents a footgun,
  enforcing that the interface and the implementation are really
  the same.

  Otherwise, the LINK_LIBRARIES could contain a generator
  expression which is evaluated with a different context at build
  time, and when used as an imported target. That would mean that the
  result of evaluating the INTERFACE_LINK_LIBRARIES property for
  a static library would not necessarily be the 'link implementation'.

  For example:

    add_library(libone STATIC libone.cpp)
    add_library(libtwo STATIC libtwo.cpp)
    add_library(libthree STATIC libthree.cpp)

    target_link_libraries(libtwo
      $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:libone>)
    target_link_libraries(libthree libtwo)

  If the LINK_LIBRARIES content was simply copied to the
  IMPORTED_LINK_INTERFACE_LIBRARIES, then libthree links to libone, but
  executables linking to libthree will not link to libone.

3) As the 'implementation is the interface' concept is to be
  deprecated in the future anyway, this should be fine.
This commit is contained in:
Stephen Kelly 2013-01-04 13:36:18 +01:00
parent 94aeaf72c7
commit 77d2646784
14 changed files with 221 additions and 17 deletions

View File

@ -359,9 +359,47 @@ cmExportFileGenerator
{
return;
}
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LIBRARIES",
iface->Libraries, properties, missingTargets);
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;
}
}
//----------------------------------------------------------------------------

View File

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

View File

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

View File

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

View File

@ -0,0 +1,13 @@
#include "depD.h"
int DepD::foo()
{
return 0;
}
DepA DepD::getA()
{
DepA a;
return a;
}

View File

@ -0,0 +1,11 @@
#include "depd_export.h"
#include "depA.h"
struct DEPD_EXPORT DepD
{
int foo();
DepA getA();
};

View File

@ -0,0 +1,10 @@
#include "depD.h"
int main(int argc, char **argv)
{
DepD d;
DepA a = d.getA();
return d.foo() + a.foo();
}

View File

@ -158,6 +158,36 @@ set_property(TARGET testLibRequired APPEND PROPERTY
$<INSTALL_INTERFACE:InstallOnly_DEFINE>
)
include(GenerateExportHeader)
add_library(testSharedLibRequired SHARED testSharedLibRequired.cpp)
generate_export_header(testSharedLibRequired)
set_property(TARGET testSharedLibRequired APPEND PROPERTY
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}"
)
set_property(TARGET testSharedLibRequired APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}"
)
add_library(testSharedLibDepends SHARED testSharedLibDepends.cpp)
set_property(TARGET testSharedLibDepends APPEND PROPERTY
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}"
)
generate_export_header(testSharedLibDepends)
set_property(TARGET testSharedLibDepends APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testSharedLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET testSharedLibDepends APPEND PROPERTY
LINK_INTERFACE_LIBRARIES
$<1:$<TARGET_NAME:testSharedLibRequired>>
)
# LINK_PRIVATE because the LINK_INTERFACE_LIBRARIES is specified above.
target_link_libraries(testSharedLibDepends LINK_PRIVATE testSharedLibRequired)
install(TARGETS testLibRequired
testLibIncludeRequired1
testLibIncludeRequired2
@ -165,10 +195,11 @@ install(TARGETS testLibRequired
testLibIncludeRequired4
testLibIncludeRequired5
testLibIncludeRequired6
testSharedLibRequired
EXPORT RequiredExp DESTINATION lib )
install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired)
install(TARGETS testLibDepends EXPORT DependsExp DESTINATION lib )
install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib )
install(EXPORT DependsExp FILE testLibDependsConfig.cmake DESTINATION lib/cmake/testLibDepends)

View File

@ -0,0 +1,8 @@
#include "testSharedLibDepends.h"
int TestSharedLibDepends::foo()
{
TestSharedLibRequired req;
return req.foo();
}

View File

@ -0,0 +1,14 @@
#ifndef TESTSHAREDLIBDEPENDS_H
#define TESTSHAREDLIBDEPENDS_H
#include "testsharedlibdepends_export.h"
#include "testSharedLibRequired.h"
struct TESTSHAREDLIBDEPENDS_EXPORT TestSharedLibDepends
{
int foo();
};
#endif

View File

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

View File

@ -0,0 +1,12 @@
#ifndef TESTSHAREDLIBREQUIRED_H
#define TESTSHAREDLIBREQUIRED_H
#include "testsharedlibrequired_export.h"
struct TESTSHAREDLIBREQUIRED_EXPORT TestSharedLibRequired
{
int foo();
};
#endif

View File

@ -167,3 +167,14 @@ set_property(TARGET deps_iface APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
)
add_executable(deps_shared_iface deps_shared_iface.cpp)
target_link_libraries(deps_shared_iface testSharedLibDepends)
set_property(TARGET deps_shared_iface APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS>
)
set_property(TARGET deps_shared_iface APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
)

View File

@ -0,0 +1,11 @@
#include "testSharedLibDepends.h"
int main(int,char **)
{
TestSharedLibDepends dep;
TestSharedLibRequired req;
return dep.foo() + req.foo();
}