org-howto/articles/2023/10/cmake-headeronly-dependencies.org
Roland Conybeare e3cb417ee8
All checks were successful
Deploy / publish (push) Successful in 20s
tidy up one more til.org article
2026-06-21 17:15:20 -04:00

3.7 KiB

[2023] cmake handling of header-only dependencies

#

#

#

#

#

#

Problem

Update

My original investigation 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).

Setup

  • 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.

Problem

  • 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.

Workaround

  • 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()