configure_package_config_file 选项 INSTALL_DESTINATION 有什么用

What's the use of configure_package_config_file option INSTALL_DESTINATION

对于以下 CMakeLists.txt 个文件

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 )

FooConfig.cmake.in 文件

@PACKAGE_INIT@

check_required_components(Foo)

最终会安装FooConfig.cmakelib/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

似乎无论我为configure_package_config_fileINSTALL_DESTINATION选项设置什么值,都不会改变FooConfig.cmake的安装目录。但是如果我评论INSTALL_DESTINATION lib/Goo/dmake,CMake Error提示

No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()

那么,INSTALL_DESINATION选项有什么用呢?它的值(具体来说,上面的设置 lib/Goo/dmake)实际上会影响什么?


documentation:

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

我知道正确的方法是INSTALL_DESTINATION lib/Foo/cmake。但是当我故意将其设置为 lib/Goo/dmake 时,FooConfig.cmake 文件仍然位于所需的目的地 lib/Foo/cmake。那么,为什么要把这个选项设计成必须的呢?

函数configure_package_config_file 只生成指定为第二个参数的文件。该函数既不安装生成的文件,也不影响其安装。所以,它的参数只能影响生成文件的内容

如果您查看生成的脚本 FooConfig.cmake,那么您可以找到类似

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

这是脚本根据 FooConfig.cmake 文件的路径计算安装前缀的方式。

例如当您安装带有前缀 /usr/local 的包并且您的 FooConfig.cmake 脚本将位于目录 /usr/local/lib/Foo/cmake 中时,引用的路径将扩展为 /usr/local/lib/Foo/cmake/../../../,对应于 /usr/local/.

脚本中的组件数 ../ 等于 INSTALL_DESTINATION 参数中的组件数。

为什么需要'PACKAGE_PREFIX_DIR'

假设一个包将所有 public 头文件安装到目录 include/ 中(相对于安装前缀),并且配置脚本需要通过变量 FOO_INCLUDE_DIR 将该目录传递给用户。

最直接的方法是在脚本中编写如下:

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

所以 configure_package_config_file 将替换真正的安装前缀而不是 @CMAKE_INSTALL_PREFIX@

但是将绝对路径嵌入到配置文件中会阻止包可重定位。因此,一旦安装,该包只能从安装目录使用,不能复制到其他地方。

对于可重定位包,配置文件计算所有安装路径相对于配置文件本身的路径,因为它是脚本调用时唯一已知的路径find_package。这就像一个可执行文件可以计算出位于该可执行文件附近的文件的路径。

如果脚本安装到 lib/Foo/cmake/FooConfig.cmake,那么包含目录的相对路径将为 ../../../include,因此可以在该脚本中使用以下分配:

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

因此,当 find_package(Foo) 将执行此脚本时,它会将变量 CMAKE_CURRENT_LIST_DIR 扩展到包含可能重定位脚本的实际目录。

使用相对路径操作需要编码人员的大量关注,因此 CMake 允许自动执行此任务:

# 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@")

如果查看生成的文件,您会发现 @PACKAGE_INCLUDE_DIR@ 扩展为 ${PACKAGE_PREFIX_DIR}/include,它使用变量 PACKAGE_PREFIX_DIR.