What should the DESTINATION be for a header only library's CMake config file?

I have the following CMake code for a header only library that uses FILE_SET.

My question is what is the optimal DESTINATION for the CMake config file generated.

It’s unclear to me what it should be for a header only library from looking at the documentation.

cmake_minimum_required(VERSION 3.23...3.26)

project(HEADER_ONLY LANGUAGES CXX)

add_library(bar INTERFACE)
add_library(foo::bar ALIAS bar)

target_sources(bar PUBLIC
    FILE_SET bar_file_set
    BASE_DIRS include
    TYPE "HEADERS"
    FILES include/bar.h
)

if (PROJECT_IS_TOP_LEVEL)
    include(GNUInstallDirs)

    install(TARGETS bar EXPORT barConfig FILE_SET bar_file_set)
    install(EXPORT barConfig NAMESPACE "foo::" DESTINATION ?)
endif()

You can pick any location that would be searched by the default find_package() search path. My typical recommendations are:

  • If the package contains binaries, then config files should be associated with the architecture-specific library subdirectory. On multi-architecture platforms, this matters because packages may be co-located under a common prefix, but libraries get installed to architecture-specific subdirectories below lib. To handle this in a convenient way, I put the package config files in ${CMAKE_INSTALL_LIBDIR}/cmake/<packageName>, where ${CMAKE_INSTALL_LIBDIR} is provided by the GNUInstallDirs module.
  • For packages that do not produce binaries (e.g. packages that provide CMake targets that are header-only libraries), it may be better to put the package config files in an architecture-independent location. For this, share/cmake/<packageName> may be more suitable.

Note that whatever you choose, package managers may force the use of something else anyway in their own recipes. For example, from what I recall, vcpkg will force all scenarios to use share/cmake/<packageName>. You don’t need to follow that in your own project, but do be aware that your choice may not be respected by others who repackage your project.

Gotcha, looking at GNUInstallDirs that translates to: ${CMAKE_INSTALL_DATADIR}/cmake/<packageName>

DATAROOTDIR

read-only architecture-independent data root (share)

DATADIR

read-only architecture-independent data (DATAROOTDIR)

I was originally tempted to use ${CMAKE_INSTALL_DATADIR} in my previous answer too, but that’s not what you want. The path searched by find_package() is share, it can’t be something else. The situation for ${CMAKE_INSTALL_LIBDIR} is different because that is meant to handle cases like lib/some-arch-specific-subdir. There is no equivalent for share, so it has to literally be share, not whatever ${CMAKE_INSTALL_DATADIR} evaluates to.

Thanks for the clarification. I’m updating our header only libraries to use share/cmake/<packageName>.

I guess it was working fine since DATADIR was defaulting to share. However, from experience I know package managers can change these variables to suit their own means.

Thank you both! It’s unfortunate that these essential considerations are not explained in CMake documentation and tutorials.