Neymar87
Neymar87

Reputation: 165

What's the use of configure_package_config_file option INSTALL_DESTINATION

For the following CMakeLists.txt file

cmake_minimum_required(VERSION 3.15)
project(Testing)

include(CMakePackageConfigHelpers)
configure_package_config_file(FooConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
  INSTALL_DESTINATION lib/Goo/dmake
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
        DESTINATION lib/Foo/cmake )

and the FooConfig.cmake.in file

@PACKAGE_INIT@

check_required_components(Foo)

It would finally install FooConfig.cmake to lib/Foo/cmake

$ cmake ..
$ cmake --build .
$ cmake --install . --prefix ../install/
$ ls -R ../install/
../install/:
lib

../install/lib:
Foo

../install/lib/Foo:
cmake

../install/lib/Foo/cmake:
FooConfig.cmake

It seems that whatever value I set to the INSTALL_DESTINATION option of configure_package_config_file, it won't change FooConfig.cmake's installation directory. But if I comment INSTALL_DESTINATION lib/Goo/dmake, CMake Error prompts

No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()

Then, what is the use of the INSTALL_DESINATION option? What its value (specifically, the above setting lib/Goo/dmake) actually affects?


The documentation:

The <path> given to INSTALL_DESTINATION must be the destination where the FooConfig.cmake file will be installed to.

I know the right way is INSTALL_DESTINATION lib/Foo/cmake. But as I deliberately set it as lib/Goo/dmake, the FooConfig.cmake file still rests at the desired destination lib/Foo/cmake. So, why this option is designed as a must?

Upvotes: 4

Views: 6116

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 65938

The function configure_package_config_file only generates the file specified as the second argument. This function neither installs the generated file nor affects on its installation. So, its arguments can only affect on the content of the generated file.

If you look into the generated script FooConfig.cmake, then you could find the line like

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

This is how the script calculates installation prefix based on the path to the FooConfig.cmake file.

E.g. when you install the package with prefix /usr/local and your FooConfig.cmake script will be located in the directory /usr/local/lib/Foo/cmake, then the quoted path will be expanded into /usr/local/lib/Foo/cmake/../../../, which corresponds to /usr/local/.

Number of components ../ in the script equals to the number of components in INSTALL_DESTINATION parameter.

Why 'PACKAGE_PREFIX_DIR' is needed

Assume a package installs all public headers into directory include/ (relative to installation prefix), and config script needs to deliver that directory to the user via variable FOO_INCLUDE_DIR.

The most direct way would be to write in the script following:

# FooConfig.cmake.in
...
set(FOO_INCLUDE_DIR @CMAKE_INSTALL_PREFIX@/include)

so configure_package_config_file would substitute real install prefix instead of @CMAKE_INSTALL_PREFIX@.

But embedding absolute path into the config file prevents the package to be relocatable. So, once installed, the package could be used only from the installation directory, and cannot be copied elsewhere.

For relocatable package a config file computes all installation paths relative to the path of the config file itself, because it is the only path known to the script when it is called by find_package. It is like an executable can compute paths to the files located near to that executable.

If the script is installed into lib/Foo/cmake/FooConfig.cmake, then relative path to the include directory would be ../../../include, so one could use following assignment in that script:

# FooConfig.cmake.in
...
set(FOO_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../include)

So, when find_package(Foo) will execute this script, it expands the variable CMAKE_CURRENT_LIST_DIR to the actual directory with the possibly relocated script.

Operating with relative paths requires much attention from the coder, so CMake allows to automate this task:

# CMakeLists.txt
...
# Path to the include directory in the installation tree
set(INCLUDE_DIR 'include')

configure_package_config_file(FooConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
  INSTALL_DESTINATION lib/Foo/cmake
  # Tell CMake to handle variable `INCLUDE_DIR` as a path
  # under installation tree.
  PATH_VARS INCLUDE_DIR
)
# FooConfig.cmake.in
@PACKAGE_INIT@

# Here we could use `@PACKAGE_INCLUDE_DIR@` as reference
# to variable 'INCLUDE_DIR' set in the CMakeLists.txt.
set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_DIR@")

If you look into the generated file, you will find that @PACKAGE_INCLUDE_DIR@ is expanded into ${PACKAGE_PREFIX_DIR}/include, which uses the variable PACKAGE_PREFIX_DIR.

Upvotes: 10

Related Questions