CMake程序包配置文件中的一般“传递行为”(find_dependency)

Flo*_*ers 5 c++ build cmake dependency-management package-management

目前,我正在尝试增强我的CMake配置约定框架。我的每个C ++组件(即CMake项目)都是通过该框架构建的,并且该框架已经能够使用以下命令创建CMake 软件包配置文件configure_package_config_file()

PackageConfig.cmake.in框架使用以下(最小)模板文件(v1)。

@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")
Run Code Online (Sandbox Code Playgroud)

如果一个组件一切工作正常美孚建造和安装这种做法被其它组件find_package(<package> CONFIG)命令(只要正确的目录路径指向的安装CMake的程序包配置文件美孚通过CLI设置)。

但是(当然)如果A本身具有一个或多个依赖项,则会出现问题。使用当前的方法,B必须拥有A本身的find_package()每个依赖关系 。这意味着当前不将传递依赖项报告给需要依赖项的组件。显然,这不是我想要实现的。

经过一番谷歌搜索后,我了解了该find_dependency()命令,该命令是为解决上述问题而创建的:

它旨在用于程序包配置文件(<package>Config.cmake)。find_dependency转发正确的参数, QUIET并将REQUIRED其传递给原始find_package()调用。指定的所有其他参数将转发给find_package()

到目前为止一切顺利,但请等待...我必须再次显式设置每个依赖项名称及其版本?我已经在声明CMakeLists.txt!中的依赖项时做到了这一点。如何使用该方法创建可重复使用的通用程序包配置文件?除了在中明确列出所有依赖项(及其版本)CMakeLists.txt并将该列表传递给之外,我目前无法确定该问题的任何解决方案PackageConfig.cmake.in

示例:未经测试PackageConfig.cmake.in(v2):

@PACKAGE_INIT@

include(CMakeFindDependencyMacro)

# TODO(wolters): How-to implement this with a generic approach? Would the
# following work? Does a better solution do the problem exist?
#
# 1. Add the following to `CMakeLists.txt`:
#    list(APPEND target_dependencies "Baz 1.2.3")
#    list(APPEND target_dependencies "Example 0.9.0")
# 2. "Pass" the variable `target_dependencies` to the
#    `configure_package_config_file` command.
# 3. Add the following code to the CMake package config file.

foreach(dependency "@target_dependencies@")
  find_dependency(${dependency})
endforeach()

include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")
Run Code Online (Sandbox Code Playgroud)

虽然这感觉很奇怪,但我还不知道它是否可以工作(但理论上应该可以)。

所以我的问题是:我该如何通用地实现CMake软件包配置文件的传递行为。

最后但并非最不重要的一点:我正在使用最新的稳定CMake版本3.9.4。

Ale*_*ing 3

这对于评论来说太长了,所以我发布作为答案......

作为第一步,您可以在调用configure_package_config_file名为 的变量之前计算已加载包的列表PACKAGE_DEPENDENCIES。首先像这样调整你的模板:

@PACKAGE_INIT@

include(CMakeFindDependencyMacro)
@PACKAGE_DEPENDENCIES@

include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")
Run Code Online (Sandbox Code Playgroud)

然后,要计算PACKAGE_DEPENDENCIES,您可以使用如下所示的代码片段:

set(PACKAGE_DEPENDENCIES "")
get_property(packages GLOBAL PROPERTY PACKAGES_FOUND)
foreach (pkg IN LISTS packages)
  get_property(is_transitive GLOBAL PROPERTY _CMAKE_${pkg}_TRANSITIVE_DEPENDENCY)
  if (is_transitive)
    continue()
  elseif (${pkg}_VERSION)
    string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${pkg} ${${pkg}_VERSION})\n")
  else ()
    string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${pkg})\n")
  endif()
endforeach ()
Run Code Online (Sandbox Code Playgroud)

然后您就可以准备打电话了configure_package_config_file

然而,这种方法在很多方面都很脆弱:

  1. 全局PACKAGES_FOUND属性包括所有包,甚至是通过 递归找到的包find_dependency,这很可能不是您想要的。在较新的 CMake 版本中,使用未记录的内部属性(如我在此处所做的那样)对其进行过滤_CMAKE_<PKG>_TRANSITIVE_DEPENDENCY可能会在没有警告或策略的情况下中断。它还依赖于包调用find_dependency而不是find_package.
  2. 通过查找模块(而不是配置文件)加载的包可能没有版本集。损坏的配置文件(即没有PackageConfigVersion.cmake文件的配置文件)也可能缺少版本信息。

(1) 的一种解决方案可能是仅将此行为应用于使用约定优于配置框架的包(例如,通过设置特殊的全局属性)。与(2)类似的故事。