cmakeheader-onlyfind-package

cmake: regular (SHARED) library depending on a header-only (INTERFACE) library


Include paths for header-only dependencies not propagating to non-header-only library targets.

cmake 3.25.3, gcc 12.2.0.

  1. I have a header-only library, let's call it foo:
# foo CMakeLists.txt
add_library(foo INTERFACE)

foo exists to provide some c++ header files

# foo CMakeLists.txt 

target_include_directories(foo INTERFACE
  $<INSTALL_INTERFACE:include>
  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)

install(
  TARGETS foo
  EXPORT fooTargets
  LIBRARY DESTINATION lib COMPONENT Runtime
  ARCHIVE DESTINATION lib COMPONENT Development
  RUNTIME DESTINATION lib COMPONENT Runtime
  PUBLIC_HEADER DESTINATION include COMPONENT Development
  BUNDLE DESTINATION bin COMPONENT Runtime)
  1. foo generates+exports .cmake config files to drive find_package
# foo cmake/fooConfig.cmake.in
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/fooTargets.cmake")
check_required_components("@PROJECT_NAME@")

and

# foo CMakeLists.txt
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  "${PROJECT_BINARY_DIR}/fooConfigVersion.cmake"
  VERSION 1.0
  COMPATIBILITY AnyNewerVersion)
configure_package_config_file(
  "${PROJECT_SOURCE_DIR}/cmake/fooConfig.cmake.in"
  "${PROJECT_BINARY_DIR}/fooConfigVersion.cmake
  INSTALL_DESTINATION lib/cmake/foo)
install(
  EXPORT fooTargets
  DESTINATION lib/cmake/foo)
install(
  FILES
  "${PROJECT_BINARY_DIR}/fooConfigVersion.cmake"
  "${PROJECT_BINARY_DIR}/fooConfig.cmake"
  DESTINATION lib/cmake/foo)
  1. I have a shared library, let's call it bar.
    bar is in a separate codebase
# bar CMakeLists.txt
find_package(foo CONFIG REQUIRED)
add_library(bar SHARED bar.cpp)
target_include_directories(bar PUBLIC 
  $<INSTALL_INTERFACE:include>
  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
  1. bar needs foo headers to compile:
# bar CMakeLists.txt
target_link_libraries(bar INTERFACE foo)
  1. PROBLEM compilation of bar.cpp doesn't receive foo headers:
$ make VERBOSE=1 
g++ -Ipath/to/bar/include path/to/bar.cpp ...   # path/to/foo/include missing

This only happens for an INTERFACE dependency. If I make foo a SHARED target + use it in bar as a PUBLIC dependency, then foo headers get forwarded to the compiler

  1. (UGLY) WORKAROUND: when depending on a header-only library, add a hint:
# bar CMakeLists.txt

get_target_property(tmp foo INTERFACE_INCLUDE_DIRECTORIES)
set_property(
  TARGET bar
  APPEND PROPERTY INCLUDE_DIRECTORIES ${tmp})

QUESTIONS

Is this necessary? Is there something wrong with my header-only packaging?
Some missing ingredient in fooConfig.cmake.in ?


Solution

  • From comments:

    1. regular library bar depending on header-only library foo:
    target_link_libraries(bar PUBLIC foo)
    
    1. header-only library quux depending on header-only library foo:
    target_link_libraries(quux INTERFACE foo)