如何在安装语句中使用 cmake 文件(GET_RUNTIME_DEPENDENCIES?

Kly*_*pto 10 cmake

您如何在 cmake install 脚本语句中使用file(GET_RUNTIME_DEPENDENCIES...)?我在网上找不到这种用法的例子,而且我不清楚使用 [[ ]] 嵌入式自定义脚本的文档和错误消息中的声明。

我得到的印象是,在安装时,这可用于定位您的 cmake 目标的文件依赖项,并可能将它们与您的安装操作一起带过来,使其以独立形式使用。

例如,我的应用程序依赖于 QT,并且期望如果配置正确,则该应用程序所需的 QT dll 将被复制到 bin。(我只是想确保在这种情况下我也没有误解它的功能)。它可能不会直接复制文件,但我假设提供了要复制的文件列表,然后安装将处理(全部在安装时完成)。

我天真地尝试只是抛出一些东西开始是:

set(TARGET_NAME "myapp")

#  installation settings
install(TARGETS ${TARGET_NAME}
    [[
    file(GET_RUNTIME_DEPENDENCIES
        RESOLVED_DEPENDENCIES_VAR RES
        UNRESOLVED_DEPENDENCIES_VAR UNRES
        CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
        EXECUTABLES ${TARGET_NAME}
    )]]

    RUNTIME DESTINATION "${INSTALL_X_BIN}" COMPONENT libraries
    LIBRARY DESTINATION "${INSTALL_X_LIB}" COMPONENT libraries
)
Run Code Online (Sandbox Code Playgroud)

然而,这当然给了我:

CMake Error at applications/CMakeLists.txt:117 (install):
install TARGETS given target " file(GET_RUNTIME_DEPENDENCIES

      RESOLVED_DEPENDENCIES_VAR RES
      UNRESOLVED_DEPENDENCIES_VAR UNRES
      CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
      EXECUTABLES ${TARGET_NAME}
  )" which does not exist.


-- Configuring incomplete, errors occurred!
Run Code Online (Sandbox Code Playgroud)

我觉得这很愚蠢,就像我错过了一些非常基本的东西。

FeR*_*eRD 17

第零,更新

从 CMake 的下一个版本 (3.21) 开始,您可能不想file(GET_RUNTIME_DEPENDENCIES)在某些情况下使用。(这将是一件好事,因为它工作......很差。一方面,它无法区分 32 位和 64 位共享库,因此在 Linux 上返回错误的架构库是很常见的. 再说一次,这种发展不会改变这一事实。)

如果您使用的是需要GET_RUNTIME_DEPENDENCIES逻辑的最常见平台 Windows,那么下一个版本的 CMake 正在寻求使用新的生成器表达式再次尝试(希望第四次(?)时间的魅力):$<TARGET_RUNTIME_DLLS:target>.

它被记录为“目标在运行时依赖的 DLL 列表。这是由目标的传递依赖项中所有SHAREDMODULE目标的位置决定的。[...] 此生成器表达式可用于复制所有 DLL目标依赖于 POST_BUILD 自定义命令中的输出目录。”

考虑到我目前在 a 中有自定义逻辑CMakeLists.txt来精确地做到这一点,因为它是使库的单元测试从构建目录可执行的唯一方法,我希望这个新表达式能让这更容易一些。

进一步更新...

$<TARGET_RUNTIME_DLLS>不会解决 . 的问题file(GET_RUNTIME_DEPENDENCIES),但一些提交刚刚合并到 CMake 即将推出的 3.21 分支中,通过教它如何区分不同架构的库。万岁!)

首先,一个警告

你提到了Qt。无论您在这里做什么,这种方法都不太可能单独对 Qt 起作用,因为无法仅使用程序/库的运行时依赖项来发现您的安装可能还需要的任何 Qt插件或其他组件。Qt 的依赖项比库更复杂。

(我在这里的回答演示了如何获取用于捆绑的 Qt 插件信息,以QCocoaIntegrationPluginmacOS 上的QPA 为例。IMPORTED在最近的版本中,所有 Qt 插件都由它们自己的CMake 目标表示,因此通常可以编写install(CODE ...)脚本来获取这些目标以类似于以下代码的方式使用生成器表达式。)

file(GET_RUNTIME_DEPENDENCIES)

正如 Tsyvarev 在评论中指出的那样,GET_RUNTIME_DEPENDENCIES旨在用于安装阶段,而不是配置阶段。因此,它需要放在install(CODE ...)orinstall(SCRIPT ...)语句中,这将导致代码评估延迟到构建完成之后。(实际上,install(CODE ...)将给定的代码直接插入到当前目录的cmake_install.cmake脚本中。您只需查看该文件即可检查结果,甚至无需运行安装。)

延迟评估也带来了一些皱纹。主要是:代码不理解目标。安装阶段不再存在目标。因此,要包含任何目标信息,您必须使用生成器表达式来插入正确的值。

虽然 CMake 文档表明变量引用和转义不在括号参数内计算,但生成器表达式. 因此,您可以组合CODE包装中的内容[[ ]]以避免转义所有内容。

您仍然必须小心变量扩展/转义。大多数变量(包括您创建的任何变量)在安装上下文中都不可用 — 只有少数变量可用,例如CMAKE_INSTALL_PREFIX. 您必须扩展或设置任何其他内容。

AFAICT,没有生成器表达式来访问任意变量。有一些用于特定的变量/值,但你不能说类似$<LIST:MY_LIST_VAR>$<VALUE:MY_STRING_VAR>组合变量和括号参数。

因此,如果您想使用 .config 中的配置上下文中的变量(CODE它们将在安装时进行评估),最简单的方法是通过set()-ing 中的变量将它们“传输”到安装脚本中CODE

file(INSTALL TYPE SHARED_LIBRARY)

要安装共享库依赖项,您可以使用file(INSTALL)CMake 本身在cmake_install.cmake构建共享库目标时使用的相同命令。它使用TYPE SHARED_LIBRARY选项来添加一些额外的处理。该FOLLOW_SYMLINK_CHAIN选项也特别方便。它们将一起file(INSTALL)解析源文件中的符号链接,在目标路径中自动重新创建它们。

示例代码

总而言之,你想要做这样的事情:

set(MY_DEPENDENCY_PATHS /path/one /path/two)

# Transfer the value of ${MY_DEPENDENCY_PATHS} into the install script
install(CODE "set(MY_DEPENDENCY_PATHS \"${MY_DEPENDENCY_PATHS}\")")

install(CODE [[
  file(GET_RUNTIME_DEPENDENCIES
    LIBRARIES $<TARGET_FILE:mylibtarget>
    EXECUTABLES $<TARGET_FILE:myprogtarget>
    RESOLVED_DEPENDENCIES_VAR _r_deps
    UNRESOLVED_DEPENDENCIES_VAR _u_deps
    DIRECTORIES ${MY_DEPENDENCY_PATHS}
  )
  foreach(_file ${_r_deps})
    file(INSTALL
      DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
      TYPE SHARED_LIBRARY
      FOLLLOW_SYMLINK_CHAIN
      FILES "${_file}"
    )
  endforeach()
  list(LENGTH _u_deps _u_length)
  if("${_u_length}" GREATER 0)
    message(WARNING "Unresolved dependencies detected!")
  endif()
]])
Run Code Online (Sandbox Code Playgroud)

* –(请注意,DIRECTORIES在非 Windows 系统上使用该参数将导致 CMake 发出警告,因为文件的依赖项应该仅使用当前环境才能解析。)

如果代码变得太复杂,总有创建一个单独的脚本文件的选项copy_deps.cmake${CMAKE_CURRENT_SOURCE_DIR}和使用install(SCRIPT copy_deps.cmake)。(此答案的先前版本建议使用file(GENERATE...)来构建脚本 - 这将不起作用,因为在处理CMakeLists.txt.之后才会写入文件。)

  • 没问题!实际上[BenS](/sf/users/174005821/)在下面的答案中提出了它(现已删除) (2认同)