Linked library generator expressions not evaluated "recursively"
Dear All,
Sorry for opening a ticket on this, I was trying to send a question to cmake@cmake.org, but my e-mail bounced. Even though I'm subscribed to the list. (But not sure anymore with which account of mine...)
Let me paste here what I wanted to send there:
I started playing around with "object libraries" recently, as we're considering to re-organise our codebase to make use of such constructs. (Creating larger libraries as part of our software than what we have now seems to bring a non-negligible amount of performance improvements with itself.)
Our software is split up into many packages. (Small parts of the code that each do some well defined task(s).) Libraries built as part of these "packages" depend on each other in quite complicated ways, which, as long as we're using shared libraries during the build, can be handled relatively easily in our configuration.
Now, I would like to create a setup where:
- A bunch of packages each declare one object library of their own, setting all properties on these object libraries that make them "usable" without having to know internal details about those libraries. (You'll see in a moment what I mean.)
- These object libraries would be linked together into a small number of large shared libraries. By just declaring which object libraries I want to include into a shared library, without having to know what upstream shared/static/etc. libraries are needed by the object libraries that I'm trying to link together.
Unfortunately as far as I can see there's no built-in way for doing this sort of a thing in CMake currently. :-( So I thought I would just try to introduce a custom property on my object libraries, which describes what libraries the object libraries depend on. Like:
set_property( TARGET Library1 PROPERTY LIBRARIES_USED ${UUID_LIBRARY} )
This actually (sort of) works. I can build an executable from this dummy library like:
add_executable( TestApp main.cxx $<TARGET_OBJECTS:Library1> )
target_link_libraries( TestApp PRIVATE $<TARGET_PROPERTY:Library1,LIBRARIES_USED> )
But... I was hoping to be able to use a configuration one order of magnitude more complicated. Where object libraries could inherit each others' dependencies. As I started, at the moment our "packages" depend on each other in quite complicated ways. I was hoping that I could keep this dependency structure (at least for the moment). But, as it turns out, generator expressions are not evaluated recursively during the build system generation. :-(
Take the following configuration:
add_library( Library1 OBJECT getUUID.h getUUID.cxx )
find_path( UUID_INCLUDE_DIR uuid/uuid.h )
find_library( UUID_LIBRARY uuid )
target_include_directories( Library1 PRIVATE ${UUID_INCLUDE_DIR} )
if( UUID_LIBRARY )
set_property( TARGET Library1 PROPERTY LIBRARIES_USED ${UUID_LIBRARY} )
endif()
add_library( Library2 OBJECT getUniqueIdentifier.h getUniqueIdentifier.cxx )
set_property( TARGET Library2 PROPERTY LIBRARIES_USED $<TARGET_PROPERTY:Library1,LIBRARIES_USED> )
add_executable( TestApp main.cxx $<TARGET_OBJECTS:Library1> $<TARGET_OBJECTS:Library2> )
target_link_libraries( TestApp PRIVATE $<TARGET_PROPERTY:Library1,LIBRARIES_USED> $<TARGET_PROPERTY:Library2,LIBRARIES_USED> )
In this setup the first generator expression on the application is evaluated just fine. But the second one evaluates to a generator expression itself. Hence I get:
[ 75%] Linking CXX executable TestApp
clang: error: no such file or directory: '$<TARGET_PROPERTY:Library1,LIBRARIES_USED>'
make[2]: *** [TestApp] Error 1
make[1]: *** [CMakeFiles/TestApp.dir/all] Error 2
make: *** [all] Error 2
I guess my question is: Is this by design? Would it not be possible to handle variables, that can swallow generator expressions, in a "recursive" manner?
The reason that I'm bumping into this issue is that in our software I'd like to keep the "packages" as independent as possible. I'd love it if one package wouldn't have to know exactly what type all of the libraries are that it is using. So, when setting up the library linking, I tried to use expressions like:
# Add non-object-libraries to the list of libraries to link against:
list( APPEND _libs
$<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:${lib},TYPE>,OBJECT_LIBRARY>>:${lib}> )
if( ARG_OBJECT )
# If we are building an object library, inherit all of the include
# directory settings from all of the "linked" libraries:
list( APPEND _includes
$<TARGET_PROPERTY:${lib},INTERFACE_INCLUDE_DIRECTORIES> )
else()
# For "normal" libraries, collect the include directory settings
# from all of the object libraries that are being linked into the
# library:
list( APPEND _includes
$<$<STREQUAL:$<TARGET_PROPERTY:${lib},TYPE>,OBJECT_LIBRARY>:$<TARGET_PROPERTY:${lib},INTERFACE_INCLUDE_DIRECTORIES>> )
# And collect all hand-set library dependencies:
list( APPEND _libs $<TARGET_PROPERTY:${lib},LIBRARIES_USED> )
endif()
Which seemed like an elegant idea at first. But, because of the issue outlined in my "e-mail", it doesn't work.
Cheers, Attila