Frank
Frank

Reputation: 2696

Create include dir for CMake install target

I'm am trying to create an install target for a shared lib that uses links to a interface lib. I would like to have create a lib folder that contains the lib and an include folder that contains the necessary headers (public ones of both the shared and interface lib).

First the interface lib:

file(GLOB_RECURSE PUBLIC_HDRS include/*.h)

add_library(MyLib INTERFACE ${PUBLIC_HDRS})
target_include_directories(MyLib INTERFACE include)

Then the shared lib:

set(PUBLIC_INCLUDE include)
set(PRIVATE_INCLUDE src)
set(SRC src)

file(GLOB_RECURSE SRCS ${SRC}/*.cpp)
file(GLOB_RECURSE PUB_HDRS ${PUBLIC_INCLUDE}/*.h)
file(GLOB_RECURSE PRIV_HDRS ${PRIVATE_INCLUDE}/*.h)

add_library(MySharedLib SHARED ${SRCS} ${PUB_HDRS} ${PRIV_HDRS})                                         
target_include_directories(MySharedLib PRIVATE ${PRIVATE_INCLUDE} 
                                       PUBLIC ${PUBLIC_INCLUDE})

target_link_libraries(MySharedLib PUBLIC MyLib)

target_sources(MySharedLib PUBLIC "${PUB_HDRS}")
get_target_property(MYLIB_PUBLIC_HEADERS MySharedLib INTERFACE_SOURCES)
set_target_properties(MySharedLib PROPERTIES PUBLIC_HEADER "${MYLIB_PUBLIC_HEADERS}")
install(TARGETS MySharedLib 
        LIBRARY DESTINATION lib
        PUBLIC_HEADER DESTINATION include)

However the INTERFACE_SOURCES seem to only contain the headers of the shared lib and not the ones of the interface lib. Even after adding to the interface lib:

target_sources(MyLib PUBLIC "${PUBLIC_HDRS}")

How can I fix this? Is there an easier way to do this? I feels a bit strange I have to set the target_sources since these are also specified in the target_include_directories.

Upvotes: 0

Views: 414

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 65928

CMake's INTERFACE library target behaves like a "normal" library target in many ways. Among other things, it is allowed to specify PUBLIC_HEADER property for an INTERFACE target and even install that target:

file(GLOB_RECURSE PUBLIC_HDRS include/*.h)

add_library(MyLib INTERFACE)
target_include_directories(MyLib INTERFACE include)
set_property(TARGET MyLib PROPERTY PUBLIC_HEADER ${PUBLIC_HDRS})
install(TARGETS MyLib PUBLIC_HEADER DESTINATION include)

Installation of a target doesn't need to be specified in the same CMakeLists.txt which creates that target. So you may install both targets MySharedLib and MyLib with the single command in the MySharedLib/CMakeLists.txt:

install(TARGETS MySharedLib MyLib
        LIBRARY DESTINATION lib
        PUBLIC_HEADER DESTINATION include)

That way you will need to specify the layout for installed artifacts only once.


Your approach with using get_target_property for extract properties, derived by target MySharedLib from the target MyLib doesn't work.

While call

target_link_libraries(MySharedLib PUBLIC MyLib)

propagates interface properties of MyLib target to MySharedLib, this propagation is performed only at the end of the configuration process. But get_target_property reads only current value of the property.

Upvotes: 1

Related Questions