[7oct2023] cmake header-only dependencies
Table of Contents
1. Update
My original investigation (below) mistaken.
It turns out I didn't understand that cmake error from target_link_libraries()
:
INTERFACE library can only be used with the INTERFACE keyword of target_link_libraries
applies to the depended-on library (3rd argument), not the depending library (1st argument).
Mistaken investigation below for posterity :)
2. Setup (before Update)
- NOTE: also asked on stack overflow here
- cmake version 3.25.3
Must introduce a header-only library like this:
add_library(foo INTERFACE)
(instead of
add_library(foo SHARED)
oradd_library(foo STATIC)
)Must specify include directories for a header-only library like this:
target_include_directories( foo INTERFACE $<INSTALL_INTERFACE:path/to/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/path/to/include>)
cmake enforces this explicitly; gives error
target_include_directories may only set INTERFACE properties on INTERFACE targets
Must specify dependency on a header-only library like this:
target_link_libraries(bar INTERFACE foo))
(instead of
target_link_libraries(bar PUBLIC foo)
)cmake enforces this explicitly; gives error
INTERFACE library can only be used with the INTERFACE keyword of target_link_libraries
Expected behavior: this is sufficient for compilation of
bar
to tell compiler about include paths forfoo
:gcc -Ipath/to/foo bar.cpp
This expectation is satisfied for regular non-INTERFACE libraries.
3. Problem (before Update)
- compilation fails to supply include paths for depended-on
foo
when compiling depending-onbar
, ifbar
is a regular library - predicted cause:
- for an INTERFACE library, cmake uses property
INTERFACE_INCLUDE_DIRECTORIES
; it does not populateINCLUDE_DIRECTORIES
. target_link_libraries
when applied to aSTATIC
orSHARED
target, picks up theINCLUDE_DIRECTORIES
property for the depended-on target, while ignoring theINTERFACE_INCLUDE_DIRECTORIES
property.
- for an INTERFACE library, cmake uses property
4. Workaround (before Update)
when depending on a header-only library, explictly incorporate depended-on
INTERFACE_INCLUDE_DIRECTORIES
toINCLUDE_DIRECTORIES
:macro(dependency_headeronly target dep) target_link_libraries(${target} INTERFACE ${dep}) get_target_property(dependency_headeronly__tmp ${dep} INTERFACE_INCLUDE_DIRECTORIES) set_property( TARGET ${target} APPEND PROPERTY INCLUDE_DIRECTORIES ${dependency_headeronly__tmp}) endmacro()