[CMake] Transitive linking

Andreas Pakulat apaku at gmx.de
Sat Nov 19 05:54:54 EST 2011


On 19.11.11 00:02:22, James Bigler wrote:
> On Fri, Nov 18, 2011 at 5:51 PM, Michael Hertling <mhertling at online.de>wrote:
> 
> > On 11/18/2011 10:03 PM, James Bigler wrote:
> > > 2011/11/18 Alexander Neundorf <a.neundorf-work at gmx.net>
> > >
> > >> On Friday 18 November 2011, James Bigler wrote:
> > >>> I thought CMake knew how to not drag all the dependent libraries once
> > you
> > >>> linked an executable module.
> > >>>
> > >>> add_library(A STATIC a.cpp)
> > >>> add_library(B SHARED b.cpp)
> > >>> target_link_libraries(B A)
> > >>> add_library(C SHARED c.cpp)
> > >>> target_link_libraries(C B)
> > >>>
> > >>> add_executable(run run.cpp)
> > >>> target_link_libraries(run C)
> > >>>
> > >>> At this point I'm seeing that C links against B and A when I think it
> > >>> should only link against B since A shouldn't be needed to link against
> > B.
> > >>> In addition when compiling run, it links against B and A.
> > >>>
> > >>> /usr/bin/c++   -dynamiclib -Wl,-headerpad_max_install_names   -o
> > >> libC.dylib
> > >>> -install_name
> > /Users/jbigler/tmp/code/cmake/translinking/build/libC.dylib
> > >>> CMakeFiles/C.dir/c.cpp.o libB.dylib libA.a
> > >>> /usr/bin/c++    -Wl,-search_paths_first
> > -Wl,-headerpad_max_install_names
> > >>> CMakeFiles/run.dir/run.cpp.o  -o run  libC.dylib libB.dylib libA.a
> > >>>
> > >>> Is this the expected behavior?
> > >>
> > >> Yes.
> > >> If you want to limit this, use target_link_libraries( C
> > >> LINK_INTERFACE_LIBRARIES ... ), with this you can specify the
> > transitively
> > >> linked libraries when linking against C.
> > >>
> > >> Alex
> > >>
> > >
> > > OK, so propagating the libraries is the default behavior.  I tried to use
> > > LINK_INTERFACE_LIBRARIES, but it only seemed to work for static
> > libraries:
> > >
> > > add_library(A STATIC a.cpp)
> > > add_library(B SHARED b.cpp)
> > > target_link_libraries(B A)
> > > target_link_libraries(B LINK_INTERFACE_LIBRARIES)
> > > add_library(C SHARED c.cpp)
> > > target_link_libraries(C B)
> > > target_link_libraries(C LINK_INTERFACE_LIBRARIES)
> > >
> > > add_executable(run run.cpp)
> > > target_link_libraries(run C)
> > >
> > > During build:
> > >
> > > Build B (this is fine)
> > > /usr/bin/c++   -dynamiclib -o libB.dylib CMakeFiles/B.dir/b.cpp.o libA.a
> > > Build C (this is fine too, no A in the list)
> > > /usr/bin/c++   -dynamiclib -o libC.dylib CMakeFiles/C.dir/c.cpp.o
> > libB.dylib
> > > Build run (this is weird, it linked both B and C)
> > > /usr/bin/c++    CMakeFiles/run.dir/run.cpp.o  -o run  libC.dylib
> > libB.dylib
> > >
> > > Did I specify something wrong or does this property only consider static
> > > libraries?
> >
> > On *nix, I can't confirm this:
> >
> > CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
> > PROJECT(TRANSLINK CXX)
> > SET(CMAKE_VERBOSE_MAKEFILE ON)
> > FILE(WRITE ${CMAKE_BINARY_DIR}/a.cxx "void a(){}\n")
> > ADD_LIBRARY(A STATIC a.cxx)
> > FILE(WRITE ${CMAKE_BINARY_DIR}/b.cxx "void b(){}\n")
> > ADD_LIBRARY(B SHARED b.cxx)
> > TARGET_LINK_LIBRARIES(B A)
> > TARGET_LINK_LIBRARIES(B LINK_INTERFACE_LIBRARIES)
> > FILE(WRITE ${CMAKE_BINARY_DIR}/c.cxx "void c(){}\n")
> > ADD_LIBRARY(C SHARED c.cxx)
> > TARGET_LINK_LIBRARIES(C B)
> > TARGET_LINK_LIBRARIES(C LINK_INTERFACE_LIBRARIES)
> > FILE(WRITE ${CMAKE_BINARY_DIR}/main.cxx "int main(){}\n")
> > ADD_EXECUTABLE(main main.cxx)
> > TARGET_LINK_LIBRARIES(main C)
> >
> > The main target's link command line reads:
> >
> > .../c++ .../main.cxx.o -o main ... libC.so ...  # No libB.so!
> >
> > Do you actually get different results with the above-noted project?
> >
> > In general, CMake's transitive handling of target dependencies causes
> > no harm since no library is loaded unnecessarily, although there may be
> > libraries which are specified unnecessarily. E.g., main would be linked
> > against libB.so without immediately referring to the latter, i.e. it is
> > formally overlinked, but whether libB.so is loaded on behalf of main or
> > on behalf of libC.so does not matter, and overlinking w.r.t. a static
> > library is not possible. Do you have particular requirements why you
> > want to reduce a target's references to the immediate dependencies?
> >
> > Regards,
> >
> > Michael
> > --
> >
> > Powered by www.kitware.com
> >
> > Visit other Kitware open-source projects at
> > http://www.kitware.com/opensource/opensource.html
> >
> > Please keep messages on-topic and check the CMake FAQ at:
> > http://www.cmake.org/Wiki/CMake_FAQ
> >
> > Follow this link to subscribe/unsubscribe:
> > http://www.cmake.org/mailman/listinfo/cmake
> >
> 
> I tried your script, and it seems to exhibit the same behavior as my script:
> 
> /usr/bin/c++    -Wl,-search_paths_first -Wl,-headerpad_max_install_names
> CMakeFiles/main.dir/main.cxx.o  -o main  libC.dylib libB.dylib
> 
> Perhaps this is some Mac specific behavior that isn't showing up on other
> systems.
> 
> The main reason I wanted it was to prevent build failures.  I specified a
> library path (-L) in one subdirectory to correctly link in the set of
> external static libraries.  CMake tried to drag those libraries on to the
> "next" project which didn't have the library path and link failed.  This is
> why I started to look into why those static libraries were linked in the
> first place.  As far as I'm concerned once you link an executable module,
> you don't need its dependencies anymore.  Those dependencies should be
> satisfied within the library itself.  I'm not exactly sure why CMake
> implements this behavior by default, since it seems to go against the
> philosophy of shared library linkage.

Well, CMake cannot determine automatically wether a shared library that
links to a static library exposes some of that static libraries API.
Hence it assumes 'worst' case and drags those static libs into the next
target. The problem here is that linking of static libraries into a
shared lib or executable only carries over the symbols that the
sharedlib/executable actually use. For shared libs it could be that the
shared lib again exposes symbols from the static library in its own API.
Now the next project sees that it can use this class from the shared lib
and might hence assume that the shared lib contains all symbols that
originated from the static library - its not visible anymore where the
symbols came from. In such a case linking of the next project can fail
if the static libs are not dragged into it, because the shared library
will not contain all symbols from the static library (usually).

Of course if the shared lib uses the static library only internally
without exposing its API to the outside, or if it can make sure to use
all symbols that the static library exposes then dragging the static lib
further to the next project is not necessary. But that is something that
only the developers of the shared lib can know and hence need to tell
CMake by setting the link-interface.

Andreas



More information about the CMake mailing list