4

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • "Why shouldn't foo-config.cmake also have information/commands regarding the installed version?" -- I assume this is a question about CMake's design (which I have answered) since it _is not an option_ to include this information meaningfully in `foo-config.cmake`. – Alex Reinking Jul 01 '21 at 06:20
  • @AlexReinking: Yes, sort of, see edit. – einpoklum Jul 01 '21 at 07:28
  • Cool, thanks. I don't think your edit changes my answer. – Alex Reinking Jul 01 '21 at 07:29

2 Answers2

0

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
)
vre
  • 6,041
  • 1
  • 25
  • 39
  • Since this is a half-comment-half-answer - would you mind if edited my question, invalidating it, reflecting the fact that I don't need a template for the version file? – einpoklum Jan 17 '20 at 17:45
  • Maybe my wording was bad. `foo-config-version.cmake` is a file in the `CMAKE_CURRENT_BINARY_DIR` generated by `write_basic_package_version_file` not a template. The default contents does not contain any information that can be found in the default `foo-config.cmake`. – vre Jan 17 '20 at 18:15
  • You're right, it's not based on a template, it's the `-config.cmake` file that's based on a template in the CMake documentation. But - not in Pfeiffer's talk. So I've snipped that part out of the question and left it just with the issue of the separation of files. – einpoklum Jan 17 '20 at 18:52
0

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.
Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • Isolation: That depends on what the package author put in each cmake file; it's not as though they're included in a different "mode", right? Well, in that case, it would have been possible to have the `foo-config.cmake` to be invokable with a boolean variable for whether you want just the meta-data about the package (including version etc.) or whether you want to go ahead and configure it. – einpoklum Jul 01 '21 at 07:30
  • @einpoklum - The `foo-config-version.cmake` files execute in their own scope. That's in the documentation [here](https://cmake.org/cmake/help/latest/command/find_package.html#version-selection). The `foo-config.cmake` files execute in the scope of the `find_package` caller. It's akin to `include()` versus `add_subdirectory()`. – Alex Reinking Jul 01 '21 at 07:32
  • 1
    Reduced boilerplate: That's upside-down. Both the contents of `foo-config.cmake` and `foo-config-version.cmake` should be, in general, generated by CMake when you issue an appropriate install command. In other words - whether it's 2 files or 1, the user shouldn't have to handle the boilerplate anyway. – einpoklum Jul 01 '21 at 07:34
  • Well, you could have the `foo-config.cmake` execute in its own scope, and be made aware of that, when it serves as today's `foo-config-version.cmake`, and execute in the caller scope otherwise. But, ok, I suppose it's not a frivolous distinction between the files. Also, I would name the two scripts differently to indicate that they're not "version" and "everything else", but rather "identifier" and "configurator" or something along those lines. – einpoklum Jul 01 '21 at 07:35
  • "Well, you could have the foo-config.cmake execute in its own scope, and be made aware of that" -- sort of, but in that case the package would have to set variables in the parent scope like `set(Foo_VERSION 1.0.0 PARENT_SCOPE)`, which would make migration from Find modules harder and generally be sad to look at. It's a trade-off. – Alex Reinking Jul 01 '21 at 07:37
  • "Reduced boilerplate: That's upside-down." -- it's reduced relative to CMake _not_ generating it. Generally the `foo-config.cmake` file is not auto-generated, but is configured (as in `configure_file`) and has calls to `find_dependency`, includes the `install(EXPORT)`-generated scripts, etc. – Alex Reinking Jul 01 '21 at 07:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/234396/discussion-between-einpoklum-and-alex-reinking). – einpoklum Jul 01 '21 at 07:40