8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

如何使用 CMake 将 DLL 文件复制到与可执行文件相同的文件夹中?

anthony sottile 2月前

121 0

我们使用 CMake 在 SVN 中生成源代码的 Visual Studio 文件。现在我的工具需要一些 DLL 文件与可执行文件位于同一文件夹中。DLL 文件位于旁边的文件夹中...

我们使用 CMake 在 SVN 中生成源代码的 Visual Studio 文件。现在我的工具需要一些 DLL 文件与可执行文件位于同一文件夹中。DLL 文件位于源代码旁边的文件夹中。

我怎样才能更改 CMakeLists.txt 以使生成的 Visual Studio 项目已经在发布/调试文件夹中拥有特定的 DLL 文件或者在编译时复制它们?

帖子版权声明 1、本帖标题:如何使用 CMake 将 DLL 文件复制到与可执行文件相同的文件夹中?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由anthony sottile在本站《visual-studio》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 我会用来 add_custom_command 实现这一点 cmake -E copy_if_different... 。如需了解详细信息,请运行

    cmake --help-command add_custom_command
    cmake -E
    


    因此,就您而言,如果您具有以下目录结构:

    /CMakeLists.txt
    /src
    /libs/test.dll
    

    并且该命令适用的 CMake 目标是 MyTest ,那么您可以将以下内容添加到您的 CMakeLists.txt 中:

    add_custom_command(TARGET MyTest POST_BUILD        # Adds a post-build event to MyTest
        COMMAND ${CMAKE_COMMAND} -E copy_if_different  # which executes "cmake - E copy_if_different..."
            "${PROJECT_SOURCE_DIR}/libs/test.dll"      # <--this is in-file
            $<TARGET_FILE_DIR:MyTest>)                 # <--this is out-file path
    


    如果您只想 /libs/ 复制目录的全部内容,请使用 cmake -E copy_directory

    add_custom_command(TARGET MyTest POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
            "${PROJECT_SOURCE_DIR}/libs"
            $<TARGET_FILE_DIR:MyTest>)
    


    如果您需要根据配置(例如,Release、Debug)复制不同的 dll,那么您可以将这些 dll 放在以相应配置命名的子目录中: /libs/Release /libs/Debug 。然后,您需要在调用中将配置类型注入到 dll 的路径中 add_custom_command ,如下所示:

    add_custom_command(TARGET MyTest POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
            "${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
            $<TARGET_FILE_DIR:MyTest>)
    
  • 简要说明一下我的情况,以便将来对其他人有所帮助:我有一个静态库项目,主可执行文件可以选择链接到该项目,并且如果添加该库,则需要复制 DLL。因此,在该库的 CMakeLists.txt 文件中,我使用了 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$为目标目的地。否则它会将其复制到库构建路径,这是没用的。

  • cmake install() 指令的目标难道不是组装二进制文件吗?或者可能是 cmake LIBRARY 之类的东西?我真的不知道工具链。

  • 除非我在集成命令时遗漏了某些内容,否则此方法不适用于使用 IMPORTED_LIBRARIES 添加的库。它会抱怨在未构建任何内容时无法运行构建后命令。

  • 请注意,要运行多个后期构建命令,您需要将它们全部绑定到一条自定义命令中,例如 add_custom_command(TARGET MyTest POST_BUILD COMMAND #你的第一个命令# COMMAND #你的第二个命令#)经过测试和测试我的 cmake:如果您正在使用 IMPORTED 库,并且需要重新定位 DLL,则需要使用变体命令。如果您已将 IMPORTED 库添加为 MyImportedLib,则可以使用 COMMAND ${CMAKE_COMMAND} -E copy_if_different $$

  • cbr 2月前 0 只看Ta
    引用 7

    我把这些行放在我的顶层 CMakeLists.txt 文件中。CMake 编译的所有库和可执行文件都将放在构建目录的顶层,以便可执行文件可以找到库,并且可以轻松运行所有内容。

    set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
    set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
    

    请注意,这并不能解决 OP 从项目源目录复制预编译二进制文件的问题。

  • 对于 Windows 用户,有一个新的生成器表达式 $<TARGET_RUNTIME_DLLS:tgt> 在 CMake 3.21+ 中,您可以使用此官方代码片段复制目标所依赖的所有 DLL。

    find_package(foo REQUIRED)
    
    add_executable(exe main.c)
    target_link_libraries(exe PRIVATE foo::foo foo::bar)
    add_custom_command(TARGET exe POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
      COMMAND_EXPAND_LISTS
      )
    
  • 问题在于,如果 exe 不需要任何 DLL,它就会失败。您是否知道是否有办法解决这个问题,因为生成器表达式的求值较晚?

  • 不确定我是否理解了你的问题。你是否正在寻找一种方法来复制可执行文件不依赖的那些 dll?

  • @LukeZhou 这个答案非常接近,但不是你应该这样做的方式。请参阅下面的我的答案,它处理了你的无 DLL 情况。

  • 似乎在 cmake 3.26 中,新的 copy -t 可能会更好地处理这个问题

  • 引用 13

    。这还会将目标复制到其自己的目录中。这很浪费,也很愚蠢,但可以确保如果 *DLLS 是空字符串,它仍然是有效命令。$与此同时,这是我的解决方法:COMMAND ${CMAKE_COMMAND} -E copy $$

  • 引用 14

    今天,当我尝试为我的程序制作 Windows 版本时,我遇到了这个问题。由于所有这些答案都不能让我满意,所以我最终自己做了一些研究。主要有三个问题:

    • p1

    • p2

    • p3

    在浏览一些 CMake 手册和 github 上的一些多平台项目后,我找到了这个解决方案:

    使用“IMPORTED”属性将您的库声明为目标,引用其调试和发布.lib 和.dll 文件。

    add_library(sdl2 SHARED IMPORTED GLOBAL)
    set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB_RELEASE "${SDL_ROOT_PATH}/lib/SDL2.lib")
    set_property(TARGET sdl2 PROPERTY IMPORTED_LOCATION_RELEASE "${SDL_ROOT_PATH}/bin/SDL2.dll")
    set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB_DEBUG "${SDL_ROOT_PATH}/lib/SDL2d.lib")
    set_property(TARGET sdl2 PROPERTY IMPORTED_LOCATION_DEBUG "${SDL_ROOT_PATH}/bin/SDL2d.dll")
    

    照常将此目标与您的项目链接

    target_link_libraries(YourProg sdl2 ...)
    

    如果 dll 文件自上次构建后发生了某种改变,则进行自定义构建步骤以将其复制到目标位置

    add_custom_command ( TARGET YourProg POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
        $<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
    )
    
  • 我一直在努力理解生成器表达式的用处,并试图找出一种可靠的方法。这一下子解决了我两个问题,应该是公认的答案。

  • 如果我在所有构建中都有相同的 lib 和 dll 会怎么样?我可以执行相同的命令吗,例如 set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB \'${SDL_ROOT_PATH}/lib/SDL2.lib\')?

  • 1. 最正确的方式: install(TARGET_RUNTIME_DLLS) (CMake >= 3.21)

    install(FILES $<TARGET_RUNTIME_DLLS:your_exe_here> TYPE BIN)
    

    为了实现这一点,依赖项的 CMake 模块必须编写良好。换句话说,它们使用 CMake 3 目标,并且所有目标属性均已正确设置。如果它们正确设置了所有内容,则会自动收集所有 DLL 并将其与 exe 一起安装。CMake 将自动匹配哪种类型的 DLL(例如发布版还是调试版)以匹配目标 exe。

    这是 CMake 未来的发展方向,如果您可以选择的话,这是您应该选择的方式。

    您应该更喜欢 install() 直接将文件复制到其中, $CMAKE_RUNTIME_OUTPUT_DIRECTORY 因为 install() is the official CMake-sanctioned way to put things in $CMAKE_RUNTIME_OUTPUT_DIRECTORY . install() 之所以命名如此奇怪,是因为 CMake 不仅仅是一个构建工具。它也是一个安装程序生成工具。此安装程序生成功能称为 CPack。在 CMake 看来, $CMAKE_RUNTIME_OUTPUT_DIRECTORY 只是 CPack 的保存区域。 When you install() a file, you tell CMake that it should be considered a program file, to be copied around wherever the exe goes. If you don't go through install() , CMake will see it as a random unrecognized file.

    2. 第二正确的方法: install(RUNTIME_DEPENDENCIES) (CMake >= 3.21)

    install(TARGETS your_exe_here
        RUNTIME ARCHIVE LIBRARY RUNTIME FRAMEWORK BUNDLE PUBLIC_HEADER RESOURCE)
    install(TARGETS your_exe_here
        COMPONENT your_exe_here
        RUNTIME_DEPENDENCIES
        PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
        POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
        DIRECTORIES $<TARGET_FILE_DIR:your_exe_here>)
    

    关键在于 RUNTIME_DEPENDENCIES .

    在内部, RUNTIME_DEPENDENCIES 文件 file(GET_RUNTIME_DEPENDENCIES) ,尽力准确复制实际依赖项解析的样子,并记下在此过程中提到的所有 DLL。这些被传回到 install() .

    这意味着,这并不依赖于您的依赖项的 CMake 模块是否正确设置了其目标属性。您的实际可执行二进制文件将被扫描。所有内容都将被拾取。

    3. 第三个最正确的方法: file(GET_RUNTIME_DEPENDENCIES) (CMake >= 3.16)

    file(GET_RUNTIME_DEPENDENCIES) install(RUNTIME_DEPENDENCIES) 底层调用的,但 file(GET_RUNTIME_DEPENDENCIES) 在较早的 CMake 版本中可用 install(RUNTIME_DEPENDENCIES) 。我们仍然可以在较旧的 CMake 版本中执行相同的操作,只是需要更多样板。

    棘手的部分是 file(GET_RUNTIME_DEPENDENCIES) 只能在安装时调用。这意味着我们需要使用 install(CODE) 来运行一个脚本,该脚本反过来调用 file(GET_RUNTIME_DEPENDENCIES) 。有关实现, 请参阅此处 .

    4. 最后的手段: install(DIRECTORY)

    install(
      DIRECTORY "${DIR_CONTAINING_YOUR_DLLS}"
      TYPE BIN
      FILES_MATCHING REGEX "[^\\\\/.]\\.[dD][lL][lL]$"
    )
    

    要使用,请将适合您的构建的 DLL 放入 $DIR_CONTAINING_YOUR_DLLS .

    这里的技巧是,unlike install(FILES) , install(DIRECTORY) 在安装之前并不关心目录中有哪些特定文件。这意味着现在我们拥有所有的配置时间和编译时间,可以获取 DLL 列表并将它们填充进去 $DIR_CONTAINING_YOUR_DLLS 。只要 DLL 文件 $DIR_CONTAINING_YOUR_DLLS 在安装时存在, install(DIRECTORY) unlike 就会拾取它们。

    如果您选择此方法,则您有责任将 DLL 与构建配置进行匹配。 (考虑:静态与动态、调试与发布、导入库版本与 DLL 版本、具有可选多线程的库、忘记删除不再需要的 DLL。)

    如果选择此方法,则可能需要使用类似 vcpkg applocal.ps1。 (假设,应该可以 vcpkg 使用 重新实现纯 CMake 中 的功能 applocal.ps1 install(CODE) 但我还没有准备好发布的实现。)


    提示 vcpkg

    如果您使用 vpckg 启用 VCPKG_APPLOCAL_DEPS 的, vcpkg 它将为您找到并将您的 DLL 复制到您的 $CMAKE_RUNTIME_OUTPUT_DIRECTORY 目录中,但无需经过 install() 。您需要使用 install(DIRECTORY) 技巧让 CMake 拾取它们。

    (在内部, vcpkg 使用 dumpbin , llvm-objdump objdump 扫描您的可执行二进制文件以获取这些文件名。)

  • 感谢您提供以目标为中心的解决方案;这正是我所寻找的!我认为这个答案应该比公认的解决方案获得更多的投票,因为这是 CMake 现在的发展方向。我仍然不太高兴必须将文件从外部安装拉入我的安装,因为像 vcpkg 这样的包管理器会想要自己管理依赖项(这就是它们存在的原因,对吧?),但目前我真的没有看到其他替代方案。除此之外,这简直是完美的。

  • 尝试完成选项 1,但这是我第一次使用 cmake,似乎无法使其工作。我有一个预建库,其中包含一个 .dll 和一个头文件和 .lib。我使用了 add_library(extLib SHARED IMPORTED) 并设置了 IMPORTED_LOCATION 和 IMPORTED_IMPLIB。使用 target_link_libraries 和 target_include_directories 来包含标头。然后尝试安装命令,如您在选项 1 中写的那样。它构建了,但 dll 没有以 exe 结尾,并且 TARGET_RUNTIME_DLLS:myExe 变量为空。我在 VS2022 中完成了所有这些。在 Windows 上。有什么提示吗?

  • @midrare 又更新了一次,我想我已经搞明白了。似乎从 Visual Studio 运行/调试时,它使用 out/build/... 目录中的 exe。安装命令有效,但将文件放在 out/install/ 中,因此当您从 VS 运行时,它找不到 dll,而且一开始似乎什么都没发生。感谢您帮助解决此问题!

返回
作者最近主题: