第十一讲 添加导出配置

        在前面几讲中,我们介绍过如何为CMake添加安装库文件和头文件的能力;也介绍过如何添加额外信息,以便发布软件包的内容。

        接下来这一讲,我们将在工程中添加一些必要信息,以便其它CMake工程可以利用我们的工程,这可以是从build目录中引引用,也可以本地安装,甚至是软件打包。

        首先,修改 install(TARGETS) 命令,让它不仅指定DESTINATION ,还指定EXPORT。关键字EXPORT 生成一个CMake文件,其中包含从安装树导出安装命令目标的相关代码。让我们继续,修改MathFunctions/CMakeLists.txt里的install命令,显式地EXPORT MathFunctions 库。修改后的代码:

set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs}
        EXPORT MathFunctionsTargets
        DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)

        现在,我们已经导出MathFunctions ,我们还需要明确安装所生成的MathFunctionsTargets.cmake文件。它是这样实现的:在顶层CMakeLists.txt的末尾添加以下代码:

install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

这时,你可以尝试执行一下CMake。如果一切正常,你将看到CMake的报错:

CMake想要表达的是:在生成导出信息时,它将导出一个与当前机器绑定的路径,把它用在其它机器上可能是无效的。解决方案是修改MathFunctions 的target_include_directories() ,让它理解:从build工作目录出发,和从安装包出发,它需要不同的INTERFACE位置。这意味着为MathFunctions 转换  target_include_directories() 调用:

MathFunctions/CMakeLists.txt

target_include_directories(MathFunctions
                           INTERFACE
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                            $<INSTALL_INTERFACE:include>
                           )

         修改完毕之后,可以重试CMake,这时警告信息已经消除。

        这时,我们的CMake已经能正确打包所要求的目标信息,但是还不够,我们还要生成一个MathFunctionsConfig.cmake 文件,这样  CMake的 find_package() 命令才能发现我们的工程。让我们继续,在工程顶层目录下新增一个名为Config.cmake.in的文件,其内容如下:

Config.cmake.in

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

接下来,配置及安装这个文件,在顶层目录的 CMakeLists.txt末尾添加以下内容:

install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)

接下来,执行 configure_package_config_file()。这条命令将配置一个预置的文件,但它与标准的 configure_file() 用法有点差异。要正确应用这一特性,除了所需要的内容之外,输入文件里应当有这样的一个独立行,其内容为: @PACKAGE_INIT@ 。这个变量将被替换为一个代码块,其功能是转换一系列的路径值。这些值的引用名字相同,但是带有 PACKAGE_前缀。

CMakeLists.txt

install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)
# generate the config file that includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/example"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

下一个命令是write_basic_package_version_file() 。这条令命令会写一个文件,用于find_package()命令,记录软件包的版本及兼容性信息。这里,我们用Tutorial_VERSION_* 变量,说明它与AnyNewerVersion兼容,这表示高于此版本的所有版本都与请求的版本兼容。

CMakeLists.txt

write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
  COMPATIBILITY AnyNewerVersion
)

最后,生成的文件也是要安装的:

CMakeLists.txt

install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake
  DESTINATION lib/cmake/MathFunctions
  )

至此,我们已经为工程生成一个可重定位的CMake配置,在工程安装或打包发布之后,都是可用的。如果我们想让工程在build目录下同样可用,仅需要在顶层CMakeLists.txt文件的末尾添加以下内容:

CMakeLists.txt

export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)

通过export调用,将会生成一个MathFunctionsTargets.cmake,它允许其它工程直接使用build目录下的MathFunctionsConfig.cmake,而无需事先安装库。