[CMake] Static libraries depending on libraries: only on headers/options/defines

Paul Smith paul at mad-scientist.net
Mon Feb 18 13:01:13 EST 2019


FYI, this function appears to work for me with one caveat: there
appears to be a bug where SYSTEM_INCLUDE_DIRECTORIES are not handled
properly so you can't use this function if you need that property to be
propagated.

I filed https://gitlab.kitware.com/cmake/cmake/issues/18940 for that.

I think I'll submit this as an enhancement request and see what the
CMake devs think of it.  It could be there are issues that I haven't
thought of.


On Sat, 2019-02-16 at 17:46 -0500, Paul Smith wrote:
> I wrote this function.  At first attempt it seems to do what I want but
> I've definitely not completed my work so I may well still find issues
> with it.
> 
> Basically it does everything that target_link_libraries() does (at
> least, it tries to as best as I understand it other than a bunch of
> properties I don't know what they are and don't use) with one caveat:
> it adds libraries to INTERFACE_* but not LINK_LIBRARIES:
> 
> function(static_link_libraries tgt mode)
>     foreach(lib ${ARGN})
>         # Import all the source-level properties as normal
>         foreach(t COMPILE_DEFINITIONS COMPILE_FEATURES COMPILE_OPTIONS
>                   INCLUDE_DIRECTORIES SOURCES SYSTEM_INCLUDE_DIRECTORIES)
>             if(${mode} STREQUAL "PRIVATE" OR ${mode} STREQUAL "PUBLIC")
>                 set_property(TARGET ${tgt} APPEND PROPERTY
>                     ${t} $<TARGET_PROPERTY:${lib},INTERFACE_${t}>)
>             endif()
>             if(${mode} STREQUAL "PUBLIC" OR ${mode} STREQUAL "INTERFACE")
>                 set_property(TARGET ${tgt} APPEND PROPERTY
>                     INTERFACE_${t} $<TARGET_PROPERTY:${lib},INTERFACE_${t}>)
>             endif()
>         endforeach()
>         # Import all the library-level properties as INTERFACE only
>         foreach(t LINK_DEPENDS LINK_DIRECTORIES LINK_OPTIONS)
>             set_property(TARGET ${tgt} APPEND PROPERTY
>                 INTERFACE_${t} $<TARGET_PROPERTY:${lib},INTERFACE_${t}>)
>         endforeach()
>         # Import the library itself as INTERFACE only
>         set_property(TARGET ${tgt} APPEND PROPERTY
>             INTERFACE_LINK_LIBRARIES ${lib})
>     endforeach()
> endfunction()
> 
> 
> 
> On Sat, 2019-02-16 at 23:03 +0100, Andreas Naumann wrote:
> > Hi Paul,
> > 
> > I understand the relationship between libraries as strict, such that you 
> > always build all dependent libraries before.
> > In your use case I thought about splitting the libraries in the actual 
> > target and the interface one.
> > For example, you could create an interface library foo_interface
> > add_library(foo_interface INTERFACE )
> > set the properties and then link foo and bar to this interface library 
> > using target_link_libraries.
> > 
> > But be aware, that now every executable, which links against bar must 
> > manually link against foo. If your project is large, this seems not 
> > really desirable. But I think you could also split the library bar in 
> > two bar_withoutFoo and bar. The library bar_withoutFoo would link 
> > against foo_interface and compile the sources, whereas bar is an 
> > interface library which depends on bar_withoutFoo and foo.
> > The developer could than build bar completely independent from foo and 
> > you could transport the transitive dependencies to the executable.
> > 
> > I don't know if this doubled structure using pure interfaces libraries 
> > and the actual libraries is maintainable.
> > 
> > Hope that helps a bit,
> > Andreas
> > 
> > Am 16.02.19 um 20:20 schrieb Paul Smith:
> > > Hi all;
> > > 
> > > I'm working on modernizing our large complex CMake environment.  It
> > > builds a number of different binaries from an even larger number of
> > > static libraries, and these libraries depend on each other as well, in
> > > that they need to include headers and, sometimes, -D options etc.
> > > 
> > > I've used straightforward target_link_libraries() to declare the
> > > relationship between these libraries; for example:
> > > 
> > >    add_library(foo STATIC ...)
> > >    target_include_directories(foo PUBLIC ...)
> > >    target_compile_definitions(foo PUBLIC ...)
> > >    target_compile_options(foo PUBLIC ...)
> > > 
> > >    add_library(bar STATIC ...)
> > >    target_link_libraries(bar PUBLIC foo)
> > > 
> > >    add_executable(one ...)
> > >    target_link_libraries(one PRIVATE bar)
> > > 
> > > This works, in that everything builds properly but it has a side-effect
> > > we want to avoid.  Because the source tree is large many developers
> > > have a habit of testing compilation of subsets of the code using
> > > something like:
> > > 
> > >    make -jX bar
> > > 
> > > and expect it to just build the static library bar.  Because it's a
> > > static library you don't need to actually build "foo" until link time.
> > > But we do need all the include directories, compile definitions, and
> > > compile options to be inherited from "foo" into "bar".
> > > 
> > > However with the above formulation, building "bar" also forces the
> > > compilation of "foo", which we don't need or want.
> > > 
> > > I've played around with the different values of PUBLIC, PRIVATE, and
> > > INTERFACE but there doesn't seem to be a straightforward way to say,
> > > "take the interface values for includes, definitions, and options, but
> > > don't depend on the generated target".
> > > 
> > > I can write a function to do this myself but this seems like the most
> > > common way someone would want to treat static libraries referencing
> > > other static libraries, so I wondered if I was missing something
> > > that would allow this in a simpler way.
> > > 
> > > Thanks!



More information about the CMake mailing list