UP | HOME

[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) or add_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 for foo:

    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-on bar, if bar is a regular library
  • predicted cause:
    1. for an INTERFACE library, cmake uses property INTERFACE_INCLUDE_DIRECTORIES; it does not populate INCLUDE_DIRECTORIES.
    2. target_link_libraries when applied to a STATIC or SHARED target, picks up the INCLUDE_DIRECTORIES property for the depended-on target, while ignoring the INTERFACE_INCLUDE_DIRECTORIES property.

4. Workaround (before Update)

  • when depending on a header-only library, explictly incorporate depended-on INTERFACE_INCLUDE_DIRECTORIES to INCLUDE_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()
    

Author: Roland Conybeare

Created: 2024-09-08 Sun 18:33

Validate