Commit 77d26467 authored by Stephen Kelly's avatar Stephen Kelly

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.
parent 94aeaf72
......@@ -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;
}
}
//----------------------------------------------------------------------------
......
......@@ -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();
};
#include "depD.h"
int main(int argc, char **argv)
{
DepD d;
DepA a = d.getA();
return d.foo() + a.foo();
}
......@@ -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)
......
#include "testSharedLibDepends.h"
int TestSharedLibDepends::foo()
{
TestSharedLibRequired req;
return req.foo();
}
#ifndef TESTSHAREDLIBDEPENDS_H
#define TESTSHAREDLIBDEPENDS_H
#include "testsharedlibdepends_export.h"
#include "testSharedLibRequired.h"
struct TESTSHAREDLIBDEPENDS_EXPORT TestSharedLibDepends
{
int foo();
};
#endif
#include "testSharedLibRequired.h"
int TestSharedLibRequired::foo()
{
return 0;
}
#ifndef TESTSHAREDLIBREQUIRED_H
#define TESTSHAREDLIBREQUIRED_H
#include "testsharedlibrequired_export.h"
struct TESTSHAREDLIBREQUIRED_EXPORT TestSharedLibRequired
{
int foo();
};
#endif
......@@ -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>
)
#include "testSharedLibDepends.h"
int main(int,char **)
{
TestSharedLibDepends dep;
TestSharedLibRequired req;
return dep.foo() + req.foo();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment