29

Here is snippet from make CMakeLists.txt:

add_library(foo-object OBJECT src/foo.cpp)
target_include_directories(foo-object PUBLIC include)
add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
add_library(foo_static STATIC $<TARGET_OBJECTS:${PROJECT_NAME}-object>)

Now, this all works fine, both libraries are generated. However I have a problem when I try to use it:

add_executable(bar src/main.cpp)
target_link_libraries(bar foo)

Target bar doesn't compile, because include directories from foo-object are not propagated. If I add target_include_directories directly on foo as well, everything will compile fine.

How can I make both foo and foo_static automatically use (and forward to stuff depending on them) include directories from foo-object?

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
graywolf
  • 7,092
  • 7
  • 53
  • 77

4 Answers4

9

Hm, at the moment I came up with following:

add_library(foo-object OBJECT src/foo.cpp)
target_include_directories(foo-object PUBLIC include)

get_property(object_include_dirs TARGET foo-object PROPERTY INCLUDE_DIRECTORIES)
get_property(object_link_libs TARGET foo-object PROPERTY LINK_LIBRARIES)

add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo PUBLIC ${object_include_dirs})
target_link_libraries(foo PUBLIC ${object_link_libs})

add_library(foo_static STATIC $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo_static PUBLIC ${object_include_dirs})
target_link_libraries(foo_static PUBLIC ${object_link_libs})

but come on, there must be better way :/

graywolf
  • 7,092
  • 7
  • 53
  • 77
  • Just `target_link_libraries(foo PUBLIC foo-object)` isn't enough? – rocambille Aug 09 '16 at 08:33
  • I will try that, I think it should work possibly? Thanks, I was pretty sure there must be smarted way :D – graywolf Aug 09 '16 at 10:01
  • @wasthishelpful: good idea but not working: "Target "foo-object" of type OBJECT_LIBRARY may not be linked into another target. One may link only to STATIC or SHARED libraries, or to executables with the ENABLE_EXPORTS property set." – graywolf Aug 09 '16 at 12:15
  • 3
    So just `target_include_directories(foo PUBLIC ${object_include_dirs})` isn't enough? Maybe with the [generator expression](https://cmake.org/cmake/help/v3.0/manual/cmake-generator-expressions.7.html) `$` to avoid the get line? – rocambille Aug 09 '16 at 12:25
  • 1
    the generator expressions allows me to avoid get line, cool, thanks :) – graywolf Aug 09 '16 at 12:37
  • Using `OBJECT` libraries ends up in a mess if using `install(EXPORT ...)`. From my latest experiences I do not recommend using `OBJECT` libraries at all, until CMake properly implements `target_link_libraries` support for them. Instead use a normal library (even if the physical file is not required in the project). Refer to the following issue on the offical CMake bug tracker for additional information: https://gitlab.kitware.com/cmake/cmake/issues/14778 – Florian Wolters Nov 02 '17 at 21:52
  • target_include_directories(foo PUBLIC include) was all I needed (not sure what generator expression pulls the include dir "include" from foo-object) – TFR Nov 10 '17 at 19:25
  • target_include_directories(foo PUBLIC $) is the version with generator expression – TFR Nov 10 '17 at 19:31
8

It seems that transitive properties only work when targets are linked through a chain of target_link_library calls. In your case, you do not have such a link between foo-object and foo.

If you add a source file to foo, that one should also not be able to see the include directory from foo-object.

This might be an oversight in the design of OBJECT libraries, as it essentially breaks the transitive properties for those.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
2

On CMake <3.12, use the following:

add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo
    PRIVATE
    $<TARGET_PROPERTY:${PROJECT_NAME}-object,INTERFACE_INCLUDE_DIRECTORIES>)

On CMake >=3.12, take a look at this answer (thanks @ian5v for the suggestion)


How it works:

target_include_directories(...)

...

PUBLIC and INTERFACE items will populate the INTERFACE_INCLUDE_DIRECTORIES property of <target>.

Therefore ${PROJECT_NAME}-object has INTERFACE_INCLUDE_DIRECTORIES set on it. We need to fetch this property and insert it into our own include path.

This looks like a job for "generator expressions"!. In particular, $<TARGET_PROPERTY:tgt,prop> looks like it will come in handy here.

Our tgt will be ${PROJECT_NAME}-object, and we're trying to extract all of the values of INTERFACE_INCLUDE_DIRECTORIES, so INTERFACE_INCLUDE_DIRECTORIES will be prop.

This comes out to $<TARGET_PROPERTY:${PROJECT_NAME}-object,INTERFACE_INCLUDE_DIRECTORIES>, which is exactly what we've used in the code above.

flaviut
  • 2,007
  • 3
  • 23
  • 32
0

For me something like the following seems to be working:

add_library(foo_objects OBJECT src/foo.cpp src/foo.hpp)
set_property(TARGET foo_objects PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(foo_objects PUBLIC
    "$<BUILD_INTERFACE:src>"
    "$<INSTALL_INTERFACE:include>")

add_library(foo_shared SHARED)
add_library(foo_static STATIC)
target_link_libraries(foo_shared PUBLIC foo_objects)
target_link_libraries(foo_static PUBLIC foo_objects)
levzettelin
  • 2,600
  • 19
  • 32