einpoklum
einpoklum

Reputation: 131547

Why should foo-config.cmake and foo-config-version.cmake be separate?

In talks by Daniel Pfeiffer (Effective CMake) and by Deniz Bahadir (More Modern CMake), and even in the CMake documentation, it is suggested that (at least) to .cmake files be generated for using a repository with CMake in other projects: foo-config.cmake and foo-config-version.cmake (for package foo; another possible naming scheme is FooConfig.cmake and FooConfigVersion.cmake).

This already seems strange to me. Why shouldn't foo-config.cmake also have information/commands regarding the installed version? Is there some objective reason for the two separate files to exist, or is it just a CMake design 'gaffe'?

Edit: snipped the rest of this question, for focus and since I got something wrong.

Upvotes: 5

Views: 1707

Answers (2)

Alex Reinking
Alex Reinking

Reputation: 19916

Why shouldn't foo-config.cmake also have information/commands regarding the installed version?

The package script foo-config.cmake is free to create targets (which cannot be deleted), set variables in the parent scope, and so on. Splitting foo-config-version.cmake into a script that runs in an isolated scope makes it easy for the package search to proceed without fear of clobbering variables. It would have been possible to incorporate all this functionality in one file (the way find modules do) but history has shown this practice to be error-prone and redundant.

Basically, there are two advantages:

  1. Isolation. Running a version script won't clobber variables in the find_package caller. Once foo-config.cmake runs, it may set package variables, create targets, and so on with full confidence that the user intended this action.
  2. Reduced boilerplate. The CMakePackageConfigHelpers can generate good version scripts at the cost of two lines in your CMakeLists.txt file. Given the new version range features in CMake 3.19, this is a welcome task to automate.

Upvotes: 1

vre
vre

Reputation: 6734

To much for a comment but not a full answer:

A typical sequence in the projects I generate is using the CMakePackageConfigHelper and GNUInstallDirs modules from CMake.

There is no need to generate a foo-config-version.cmake template. Just use the write_basic_package_version_file command from this module to get things done.

The export command comes handy if you want to use the config packages from the build directory without the need to install them.

See example:

# Support find_package(Foo NO_MODULE).
set(FOO_DOC_DIR ${CMAKE_INSTALL_DOCDIR})
set(FOO_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(FOO_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
set(FOODIR ${CMAKE_INSTALL_PREFIX})

include(CMakePackageConfigHelpers)
include(GNUInstallDirs)

configure_package_config_file(foo-config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/foo-config.cmake"
  INSTALL_DESTINATION ${FOO_CONFIG_PACKAGE_LOCATION}
  PATH_VARS FOODIR FOO_INCLUDE_DIR FOO_LIB_DIR FOO_DOC_DIR
)

write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/foo-config-version.cmake"
  VERSION ${PROJECT_VERSION}
  COMPATIBILITY SameMajorVersion
)

# To make the component usable not only from the install directory but also from the build directory
export(
  TARGETS Foo
  FILE foo-export.cmake
)

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/foo-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/foo-config-version.cmake"
  DESTINATION ${FOO_CONFIG_PACKAGE_LOCATION}
  COMPONENT development
)

install(EXPORT Foo
  DESTINATION ${FOO_CONFIG_PACKAGE_LOCATION}
  NAMESPACE ${PROJECT_NAME}::
  FILE foo-export.cmake
  COMPONENT development
)

Upvotes: 0

Related Questions