116

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

如何更改CMakeLists.txt生成的 Visual Studio 项目在发布/调试文件夹中已有特定的 DLL 文件或在编译时复制它们?

4

11 回答 11

142

我会add_custom_commandcmake -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>)


如果您需要根据配置(例如发布、调试等)复制不同的 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>)
于 2012-05-20T10:44:43.497 回答
28

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

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

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

于 2015-12-23T23:21:22.543 回答
15

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

  • 我希望调试版本与库的调试版本链接,而发布版本分别与库的发布版本链接。

  • 除此之外,我希望将正确版本的 DLL 文件(调试/发布)复制到输出目录。

  • 我想在不编写复杂而脆弱的脚本的情况下实现这一切。

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

将您的库声明为具有“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>
)
于 2020-01-04T19:27:38.807 回答
5

已接受答案的附录,作为单独的答案添加,因此我得到了代码格式:

如果您在同一个项目中构建 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 项目生成时已解决,并且始终是默认值。

于 2014-11-17T22:45:54.257 回答
5

您还可以使用命令 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})
于 2018-02-02T18:23:43.810 回答
4

在构建期间移动文件使用 install

我在尝试遵循 CMake 官方教程Step 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>可以有两个值DebugRelease取决于项目的构建方式,如原始问题所要求的。

于 2020-07-29T17:12:09.927 回答
2

这对其中一个很有用

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)
于 2015-06-19T10:40:22.070 回答
2

对于 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
  )
于 2021-10-27T09:36:59.953 回答
1

您可能需要添加自定义目标并使其依赖于您的可执行目标之一。

要使用上述功能复制文件,请使用:

COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
于 2012-05-20T08:31:58.233 回答
1

当前评分最高的答案中的以下命令取决于放在 /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}>)
于 2021-07-02T12:23:07.513 回答
0

我是 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。

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

于 2020-06-09T13:23:38.160 回答