[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