[CMake] Private dependencies of static libraries exported as targets

Ivan Shapovalov intelfx at intelfx.name
Mon Aug 15 21:32:33 EDT 2016


Hello,

So I'm trying to learn how to do CMake in a modern way. If I understand
correctly, the "modern way" is to use IMPORTED targets.

Preamble
--------

I have a project which builds a library `foo`. This library can be
built as static or shared, depending on value of BUILD_SHARED_LIBS. My
CMakeLists.txt does not perform any special handling of either case.

It links to a library `bar`, which is an implementation detail, i. e.
clients of `foo` do not need to know that I use `bar`. Hence I make it
a PRIVATE dependency (let's assume that authors of Bar also try to do
modern CMake and package their library using IMPORTED targets):

# somewhere in BarConfig.cmake
add_library(bar STATIC IMPORTED ...)
set_target_properties(bar PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES ${_IMPORT_PREFIX}/include
  IMPORTED_LOCATION ${_IMPORT_PREFIX}/lib/libfoo.a)

# somewhere in my CMakeLists.txt
find_package(Bar)

add_library(foo ... EXPORT FooTargets)
target_include_directories(foo ...)
target_link_libraries(foo PRIVATE bar)

install(TARGETS foo DESTINATION lib)
install(EXPORT FooTargets DESTINATION lib/cmake/Foo)

# this file does find_package(Bar) and includes FooTargets.cmake
configure_file(FooConfig.cmake.in FooConfig.cmake @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake DESTINATION lib/cmake/Foo)

Problem
-------

If library `foo` is built as shared, then everything is fine because:

- if library `bar` is built as static, then its code is simply added
  to library `foo` at link time => everything OK

- if library `bar` is built as shared, then the runtime linker will
  recursively load it when library `foo` is loaded => everything OK

But, if library `foo` is built as static, then its clients suddenly
become required to link to `bar` as well, and this information is not
recorded anywhere!

Possible Solution #1
--------------------

I could wrap `target_link_libraries()` into a function which says
PRIVATE or PUBLIC dependent on whether we are building a SHARED or
STATIC library respectively:

function(foo_target_link_private_library target library)
  if(BUILD_SHARED_LIBS)
    target_link_libraries(${target} PRIVATE ${library})
  else
    target_link_libraries(${target} PUBLIC ${library})
endfunction()

But this feels like a hack and adds include path bloat: the clients of
`foo` will never need `bar`'s include pathes, even if `bar` is static.

I could imagine creating fake targets like `bar_for_static` and copying
there `bar`'s IMPORTED_LOCATION and INTERFACE_LINK_FLAGS but not
INTERFACE_INCLUDE_DIRECTORIES (and then link to `bar_for_static` in
`foo`'s interface), but this is even more gross.

Is there anything less hackish or more idiomatic?

Thanks,
--
Ivan Shapovalov / intelfx /
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part
URL: <http://public.kitware.com/pipermail/cmake/attachments/20160816/871cbb9a/attachment.sig>


More information about the CMake mailing list