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

Paul Smith paul at mad-scientist.net
Sat Feb 16 17:46:20 EST 2019


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