[CMake] transitive linking with separate projects

Michael Hertling mhertling at online.de
Tue Mar 6 10:46:02 EST 2012


On 03/06/2012 02:47 PM, Alexander Dahl wrote:
> Hei hei, 
> 
> we faced a build problem with transitive linking of separate projects
> where I can't find the right solution on my own. I hope someone can give
> me a hint. I prepared a test case with two libraries libfoo and libbar
> and an application baz. libfoo is on his own, libbar calls a function
> from libfoo and baz calls the function from libbar calling libfoo. So
> the dependencies are like this:
> 
> baz -> libbar -> libfoo
> 
> baz doesn't need to know of libfoo because it just calls libbar, so I
> thought.
> 
> Now the projects are separated and both libraries come with cmake
> package configuration files. For linking libfoo in libbar I do the
> following:
> 
> find_package(FOO)
> target_link_libraries(BAR_SRC foo-shared)
> 
> foo-shared is the target libfoo exports via cmake package
> configuration. This works and ldd shows libbar is correctly linked
> against libfoo.
> 
> Now when compiling baz I more or less do the same:
> 
> find_package(BAR)
> target_link_libraries(BAZ_SRC bar-shared)
> 
> However building baz fails with the following error:
> 
> % make
> [100%] Building C object src/CMakeFiles/baz.dir/baz.c.o
> Linking C executable baz
> /usr/bin/ld: cannot find -lfoo-shared
> collect2: ld returned 1 exit status
> make[2]: *** [src/baz] Fehler 1
> make[1]: *** [src/CMakeFiles/baz.dir/all] Fehler 2
> make: *** [all] Fehler 2
> 
> It seems like cmake tries to link against libfoo here but does not know
> anything about it. If I add find_package(FOO) to baz obviously the
> target is imported from libfoo cmake package files. The question is, if
> I know nothing about the requirements of libbar and want to avoid adding
> find_package statements for those requirements to baz, how would I do
> this?
> 
> I put all the code on GitHub, so if someone maybe could have a look?
> 
> https://github.com/LeSpocky/libfoo
> https://github.com/LeSpocky/libbar
> https://github.com/LeSpocky/baz
> 
> Greets
> Alex

If you run "grep foo -r <libbar-install-dir>/lib/cmake/bar", you will
see only one line which informs the user of bar-config.cmake that the
bar-shared target has a prerequisite name foo-shared, but there is no
more information. For this reason, it's passed as -lfoo-shared to the
linker. You need to include foo-targets.cmake in bar-config.cmake in
order to make the necessary information available, e.g. by

# libbar/bar-config.cmake.in:
INCLUDE(@FOO_CONFIG@)
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_prefix "${_dir}/../../.." ABSOLUTE)
include("${_dir}/@PROJECT_NAME at -targets.cmake")
set(BAR_INCLUDE_DIRS "${_prefix}/include/@PROJECT_NAME@")

or possibly better:

# libbar/bar-config.cmake.in:
FIND_PACKAGE(FOO PATHS @FOO_DIR@ NO_DEFAULT_PATH)
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_prefix "${_dir}/../../.." ABSOLUTE)
include("${_dir}/@PROJECT_NAME at -targets.cmake")
set(BAR_INCLUDE_DIRS "${_prefix}/include/@PROJECT_NAME@")

This will make the user of bar-config.cmake include the same foo-
config.cmake and, thus, foo-targets.cmake that bar's CMakeLists.txt
has included, too. See also FIND_PACKAGE()'s NAMES / CONFIGS clauses.

BTW, find modules / config files should provide a *_LIBRARIES variable
even if they use imported targets, e.g.: SET(FOO_LIBRARIES foo-shared)

Regards,

Michael

PS: The baz project on GitHub only contains a README.


More information about the CMake mailing list