From 8e756d2b9bb84b182a8402e71fb62d7a28d0a9c6 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 12 Jan 2012 10:22:00 -0500 Subject: [PATCH] Tolerate cycles in shared library link interfaces (#12647) Since commit 183b9509 (Follow all dependencies of shared library private dependencies, 2011-12-14) we honor LINK_INTERFACE_LIBRARIES when following dependent shared libraries. The link interface properties may form a cycle if set incorrectly by a project. Furthermore, the property LINK_DEPENDENT_LIBRARIES may form a cycle if set incorrectly by hand (though CMake should never generate one). In either case, do not follow the cycle forever when following the dependent shared library closure. We only need to add dependency edges to the constraint graph once. Add "LinkInterfaceLoop" test to cover this case. --- Source/cmComputeLinkDepends.cxx | 22 ++++++++++++--- Source/cmComputeLinkDepends.h | 4 +++ Tests/CMakeOnly/CMakeLists.txt | 3 +++ .../LinkInterfaceLoop/CMakeLists.txt | 27 +++++++++++++++++++ Tests/CMakeOnly/LinkInterfaceLoop/lib.c | 1 + Tests/CMakeOnly/LinkInterfaceLoop/main.c | 1 + 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt create mode 100644 Tests/CMakeOnly/LinkInterfaceLoop/lib.c create mode 100644 Tests/CMakeOnly/LinkInterfaceLoop/main.c diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 802cfcf70..9a075bd12 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -358,7 +358,7 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe) this->AddLinkEntries(depender_index, iface->Libraries); // Handle dependent shared libraries. - this->QueueSharedDependencies(depender_index, iface->SharedDeps); + this->FollowSharedDeps(depender_index, iface); // Support for CMP0003. for(std::vector::const_iterator @@ -376,6 +376,23 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe) } } +//---------------------------------------------------------------------------- +void +cmComputeLinkDepends +::FollowSharedDeps(int depender_index, cmTarget::LinkInterface const* iface, + bool follow_interface) +{ + // Follow dependencies if we have not followed them already. + if(this->SharedDepFollowed.insert(depender_index).second) + { + if(follow_interface) + { + this->QueueSharedDependencies(depender_index, iface->Libraries); + } + this->QueueSharedDependencies(depender_index, iface->SharedDeps); + } +} + //---------------------------------------------------------------------------- void cmComputeLinkDepends @@ -430,8 +447,7 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep) entry.Target->GetLinkInterface(this->Config)) { // Follow public and private dependencies transitively. - this->QueueSharedDependencies(index, iface->Libraries); - this->QueueSharedDependencies(index, iface->SharedDeps); + this->FollowSharedDeps(index, iface, true); } } } diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index e196e00ca..80a04541f 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -105,6 +105,10 @@ private: int DependerIndex; }; std::queue SharedDepQueue; + std::set SharedDepFollowed; + void FollowSharedDeps(int depender_index, + cmTarget::LinkInterface const* iface, + bool follow_interface = false); void QueueSharedDependencies(int depender_index, std::vector const& deps); void HandleSharedDependency(SharedDepEntry const& dep); diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt index d32e7be24..e4ba53aa8 100644 --- a/Tests/CMakeOnly/CMakeLists.txt +++ b/Tests/CMakeOnly/CMakeLists.txt @@ -7,3 +7,6 @@ macro(add_CMakeOnly_test test) -P ${CMAKE_CURRENT_BINARY_DIR}/Test.cmake ) endmacro() + +add_CMakeOnly_test(LinkInterfaceLoop) +set_property(TEST CMakeOnly.LinkInterfaceLoop PROPERTY TIMEOUT 90) diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt new file mode 100644 index 000000000..56e449afd --- /dev/null +++ b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required (VERSION 2.8) +project(LinkInterfaceLoop C) + +# Add a shared library that incorrectly names itself as a +# dependency, thus forming a cycle. +add_library(A SHARED IMPORTED) +set_target_properties(A PROPERTIES + IMPORTED_LINK_DEPENDENT_LIBRARIES A + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/dirA/A" + ) + +# Add a shared library that incorrectly names itself in +# its link interface, thus forming a cycle. +add_library(B SHARED IMPORTED) +set_target_properties(B PROPERTIES + IMPORTED_LINK_INTERFACE_LIBRARIES B + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/dirB/B" + ) + +# Add a shared library with an empty link interface +# that depends on two shared libraries. +add_library(C SHARED lib.c) +set_property(TARGET C PROPERTY LINK_INTERFACE_LIBRARIES "") +target_link_libraries(C B A) + +add_executable(main main.c) +target_link_libraries(main C) diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/lib.c b/Tests/CMakeOnly/LinkInterfaceLoop/lib.c new file mode 100644 index 000000000..fede1d61f --- /dev/null +++ b/Tests/CMakeOnly/LinkInterfaceLoop/lib.c @@ -0,0 +1 @@ +int lib(void) { return 0; } diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/main.c b/Tests/CMakeOnly/LinkInterfaceLoop/main.c new file mode 100644 index 000000000..78f2de106 --- /dev/null +++ b/Tests/CMakeOnly/LinkInterfaceLoop/main.c @@ -0,0 +1 @@ +int main(void) { return 0; }