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.

2 Likes

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.

2 Likes

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.