From 77d26467848fd689fbbffbaa302d041aa2365b15 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 4 Jan 2013 13:36:18 +0100 Subject: [PATCH] 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 $<$,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. --- Source/cmExportFileGenerator.cxx | 44 ++++++++++++++-- Source/cmTarget.cxx | 50 ++++++++++++++----- Source/cmTarget.h | 4 +- .../target_link_libraries/CMakeLists.txt | 10 ++++ .../target_link_libraries/depD.cpp | 13 +++++ .../target_link_libraries/depD.h | 11 ++++ .../target_link_libraries/targetB.cpp | 10 ++++ Tests/ExportImport/Export/CMakeLists.txt | 33 +++++++++++- .../Export/testSharedLibDepends.cpp | 8 +++ .../Export/testSharedLibDepends.h | 14 ++++++ .../Export/testSharedLibRequired.cpp | 7 +++ .../Export/testSharedLibRequired.h | 12 +++++ Tests/ExportImport/Import/A/CMakeLists.txt | 11 ++++ .../Import/A/deps_shared_iface.cpp | 11 ++++ 14 files changed, 221 insertions(+), 17 deletions(-) create mode 100644 Tests/CMakeCommands/target_link_libraries/depD.cpp create mode 100644 Tests/CMakeCommands/target_link_libraries/depD.h create mode 100644 Tests/CMakeCommands/target_link_libraries/targetB.cpp create mode 100644 Tests/ExportImport/Export/testSharedLibDepends.cpp create mode 100644 Tests/ExportImport/Export/testSharedLibDepends.h create mode 100644 Tests/ExportImport/Export/testSharedLibRequired.cpp create mode 100644 Tests/ExportImport/Export/testSharedLibRequired.h create mode 100644 Tests/ExportImport/Import/A/deps_shared_iface.cpp diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 5e5f45509..94e24c7a0 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -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; + } } //---------------------------------------------------------------------------- diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 2cfb1bfd5..9b50b8e52 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -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) diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 1188a6a6a..b4d053d84 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -258,7 +258,9 @@ public: // Needed only for OLD behavior of CMP0003. std::vector WrongConfigLibraries; - LinkInterface(): Multiplicity(0) {} + bool ImplementationIsInterface; + + LinkInterface(): Multiplicity(0), ImplementationIsInterface(false) {} }; /** Get the link interface for the given configuration. Returns 0 diff --git a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt index 746d30c27..1551c5055 100644 --- a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt +++ b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt @@ -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 + $<$,EXECUTABLE>:depA> +) + +add_executable(targetB targetB.cpp) +target_link_libraries(targetB depD) diff --git a/Tests/CMakeCommands/target_link_libraries/depD.cpp b/Tests/CMakeCommands/target_link_libraries/depD.cpp new file mode 100644 index 000000000..b02c76cf3 --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/depD.cpp @@ -0,0 +1,13 @@ + +#include "depD.h" + +int DepD::foo() +{ + return 0; +} + +DepA DepD::getA() +{ + DepA a; + return a; +} diff --git a/Tests/CMakeCommands/target_link_libraries/depD.h b/Tests/CMakeCommands/target_link_libraries/depD.h new file mode 100644 index 000000000..d24ff5f7c --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/depD.h @@ -0,0 +1,11 @@ + +#include "depd_export.h" + +#include "depA.h" + +struct DEPD_EXPORT DepD +{ + int foo(); + + DepA getA(); +}; diff --git a/Tests/CMakeCommands/target_link_libraries/targetB.cpp b/Tests/CMakeCommands/target_link_libraries/targetB.cpp new file mode 100644 index 000000000..063d63a32 --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/targetB.cpp @@ -0,0 +1,10 @@ + +#include "depD.h" + +int main(int argc, char **argv) +{ + DepD d; + DepA a = d.getA(); + + return d.foo() + a.foo(); +} diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 569845a71..779d88958 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -158,6 +158,36 @@ set_property(TARGET testLibRequired APPEND PROPERTY $ ) +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 + $ +) +set_property(TARGET testSharedLibDepends APPEND PROPERTY + LINK_INTERFACE_LIBRARIES + $<1:$> +) + +# 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) diff --git a/Tests/ExportImport/Export/testSharedLibDepends.cpp b/Tests/ExportImport/Export/testSharedLibDepends.cpp new file mode 100644 index 000000000..e27920725 --- /dev/null +++ b/Tests/ExportImport/Export/testSharedLibDepends.cpp @@ -0,0 +1,8 @@ + +#include "testSharedLibDepends.h" + +int TestSharedLibDepends::foo() +{ + TestSharedLibRequired req; + return req.foo(); +} diff --git a/Tests/ExportImport/Export/testSharedLibDepends.h b/Tests/ExportImport/Export/testSharedLibDepends.h new file mode 100644 index 000000000..b93143a5c --- /dev/null +++ b/Tests/ExportImport/Export/testSharedLibDepends.h @@ -0,0 +1,14 @@ + +#ifndef TESTSHAREDLIBDEPENDS_H +#define TESTSHAREDLIBDEPENDS_H + +#include "testsharedlibdepends_export.h" + +#include "testSharedLibRequired.h" + +struct TESTSHAREDLIBDEPENDS_EXPORT TestSharedLibDepends +{ + int foo(); +}; + +#endif diff --git a/Tests/ExportImport/Export/testSharedLibRequired.cpp b/Tests/ExportImport/Export/testSharedLibRequired.cpp new file mode 100644 index 000000000..1ac34aaef --- /dev/null +++ b/Tests/ExportImport/Export/testSharedLibRequired.cpp @@ -0,0 +1,7 @@ + +#include "testSharedLibRequired.h" + +int TestSharedLibRequired::foo() +{ + return 0; +} diff --git a/Tests/ExportImport/Export/testSharedLibRequired.h b/Tests/ExportImport/Export/testSharedLibRequired.h new file mode 100644 index 000000000..edaddd485 --- /dev/null +++ b/Tests/ExportImport/Export/testSharedLibRequired.h @@ -0,0 +1,12 @@ + +#ifndef TESTSHAREDLIBREQUIRED_H +#define TESTSHAREDLIBREQUIRED_H + +#include "testsharedlibrequired_export.h" + +struct TESTSHAREDLIBREQUIRED_EXPORT TestSharedLibRequired +{ + int foo(); +}; + +#endif diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 56cfea031..abb2ab01d 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -167,3 +167,14 @@ set_property(TARGET deps_iface APPEND PROPERTY 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 + $ +) +set_property(TARGET deps_shared_iface APPEND PROPERTY + INCLUDE_DIRECTORIES + $ +) diff --git a/Tests/ExportImport/Import/A/deps_shared_iface.cpp b/Tests/ExportImport/Import/A/deps_shared_iface.cpp new file mode 100644 index 000000000..4f7eb234a --- /dev/null +++ b/Tests/ExportImport/Import/A/deps_shared_iface.cpp @@ -0,0 +1,11 @@ + + +#include "testSharedLibDepends.h" + +int main(int,char **) +{ + TestSharedLibDepends dep; + TestSharedLibRequired req; + + return dep.foo() + req.foo(); +}