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

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

anthony sottile 2月前

132 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)
  • 由于 CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES 变量通常包含完整的库目录列表,因此您可以使用以下代码:

    install(TARGETS <your target>
        RUNTIME_DEPENDENCIES
        DIRECTORIES ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}
        PRE_EXCLUDE_REGEXES "^[^lib]"
    )
    

    此代码还排除了所有不以 \'lib\' 前缀开头的库。例如,在 Windows 和 MinGW 的 g++ 上,只剩下 libgcc_s_seh-1.dll、libstdc++-6.dll 和 libwinpthread-1.dll,这正是所需的。

  • 我是 CMake 初学者,但我仍然想分享我的经验。就我而言,我需要一个安装后副本,以便所有二进制文件都包含在内。对于可以在 CMake 中导入的第三方二进制文件,以下方法对我有用:

    find_package( dependency REQUIRED )
    if( MSVC ) 
        # If done properly and if the dependency has a correct config file, IMPORTED_LOCATION_RELEASE should be defined
        get_target_property( DEP_SHARED_LIB_PATH dependency IMPORTED_LOCATION_RELEASE )
        # Create a bin directory in the install folder
        add_custom_command(TARGET BGS POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory  ${CMAKE_INSTALL_PREFIX}/bin/)
        # Copy the shared lib file
        add_custom_command(TARGET BGS POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${DEP_SHARED_LIB_PATH} ${CMAKE_INSTALL_PREFIX}/bin/)
    endif()
    

    显然,IMPORTED_LOCATION_RELEASE 可能会有变体,具体取决于共享库的构建/安装方式。可能是 IMPORTED_LOCATION_DEBUG。

    也许有更好的方法来获取该属性名称,我不知道。

  • 当前评分最高的答案中的以下命令取决于放入 /libs/ 中的输出。

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

    我使用下面的命令进行复制,这在任何地方都适用。请注意,我在这里只复制输出 dll,而不是整个目录。还请注意,我正在硬编码目标 /bin/ 目录,这是特定于此项目的。但我想分享语法 $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}> ,我认为这很简洁:

    add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
            $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>
            ${CMAKE_CURRENT_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>)
    
  • 您可能需要 添加自定义目标 并使其依赖于您的某个可执行目标。

    要使用上述函数复制文件,请使用:

    COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
    
  • sxs 2月前 0 只看Ta
    引用 6

    这对其中之一很有用

    SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
        ${PROJECT_SOURCE_DIR}/lib CACHE
        PATH "Directory where all the .lib files are dumped." FORCE)
    SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY
        ${PROJECT_SOURCE_DIR}/bin CACHE
        PATH "Directory where .exe and .dll files are dumped." FORCE)
    
  • 您也可以使用命令 find_library:

    find_library(<some_var> NAMES <name_of_lib> PATHS "<path/to/lib>")
    

    使用定义的 EXECUTABLE_PATH,例如:

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    

    你可以移动你的可执行文件需要的 .dll 文件,

    file(COPY ${<some_var>}
        DESTINATION ${EXECUTABLE_OUTPUT_PATH})
    
  • 对已接受答案的附录,作为单独的答案添加,以便我获得代码格式:

    如果您在同一个项目中构建 dll,它们通常位于 Release、Debug 等目录中。您必须使用 Visual Studio 环境变量才能正确复制它们。例如:

    "${PROJECT_BINARY_DIR}/your_library/\$\(Configuration\)/your_library.dll"
    

    来源和

    "${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/your_library.dll"
    

    为目的地。注意转义!

    您不能使用 CMake CMAKE_BUILD_TYPE 变量进行配置,因为它是在 VS 项目生成时解析的,并且始终是默认值。

  • 在构建过程中使用移动文件 install

    我在尝试按照 CMake 官方教程 第 9 步 。这是我想要移动的文件的位置:

    src
     |_build
        |_Debug
           - `MathFunctions.dll`
    

    这是我希望文件所在的位置:

    src
     |_build
        |_install
             |_bin
                 - `MathFunctions.dll`
    

    由于此 DLL 是作为共享库生成的,我所做的就是将此行包含在 CMakeLists.txt 包含 库源代码的 子目录 src/Mathfunctions/CMakeLists.txt

    install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
    DESTINATION bin)
    

    感谢您的回答,我可以考虑这一点。只有一行,所以我认为没问题。可以 $<CONFIG> 有两个值 Debug Release, 具体取决于项目的构建方式,如原始问题所要求的那样。

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

  • 尝试完成选项 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 上。有什么提示吗?

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

  • 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 扫描您的可执行二进制文件以获取这些文件名。)

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

  • 我一直在努力理解生成器表达式的用处,并试图找出一种可靠的方法。这一下子解决了我两个问题,应该是公认的答案。

  • 引用 16

    今天,当我尝试为我的程序制作 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>
    )
    
  • 引用 17

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

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

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

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

返回
作者最近主题: