How to copy DLL files into the same folder as the executable using CMake?

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

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

99033 次浏览

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

使用以上函数复制文件:

COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`

我会使用 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>)


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

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

已接受答案的附录,作为一个单独的答案添加,所以我得到代码格式:

如果您在同一个项目中构建 dls,那么它们通常位于 Release、 Debug 等目录中。您必须使用 VisualStudio 环境变量来正确地复制它们。例如:

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

来源和

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

到达目的地,注意逃跑的情况!

不能对配置使用 CMake CMAKE _ BUILD _ TYPE 变量,因为它在 VS 项目生成时被解析,并且总是默认值。

这对其中一个很有用

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)

我将这些代码行放在我的顶级 CMakeLists.txt 文件中。所有由 CMake 编译的库和可执行文件都将放置在构建目录的顶层,这样可执行文件就可以找到库,并且很容易运行所有文件。

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

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

You can also use the command find_library:

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

With a defined EXECUTABLE_PATH, for instance:

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

您可以移动您的可执行文件所需的. dll 文件,使用

file(COPY ${<some_var>}
DESTINATION ${EXECUTABLE_OUTPUT_PATH})

I've had this problem today when tried to make a Windows build of my program. And I ended up doing some research myself since all these answers didn't satisfy me. There were three main issues:

  • 我希望将调试构建与库的调试版本链接起来 以及与库的发布版本相连接的发布版本,

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

  • 我希望在不编写复杂而脆弱的脚本的情况下实现所有这些目标。

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

将库声明为具有“ IMPORTED”属性的目标,引用其 debug 和 release. 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>
)

I'm a CMake beginner, but still I wanted to shared my experience. In my case I needed a post-install copy so that all my binaries are in. In the case of third-party binary that can be imported within CMake, the following works for me:

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()

Obviously IMPORTED_LOCATION_RELEASE can have variants depending on how the shared library was built / installed. Could be IMPORTED_LOCATION_DEBUG.

也许还有更好的方法,我不知道。

使用 install在生成期间移动文件

我在尝试遵循 Step 9上的 CMake 官方教程时遇到了这个问题。这是我想要移动的文件的位置:

src
|_build
|_Debug
- `MathFunctions.dll`

这就是我想要的文件所在的位置:

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

由于这个 DLL 是作为一个共享库生成的,所以我所做的就是在 subdirectory中的 CMakeLists.txt中包含这一行,它包含库 src/Mathfunctions/CMakeLists.txt的源代码

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

多亏了你的回答,我才能思考这个问题。只有一句台词,所以我认为没问题。$<CONFIG>可以有两个值 调试放手取决于项目是如何建立的,作为原始问题的要求。

当前最佳答案中的以下命令取决于放在/libs/中的输出。

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

I use the below command to do the copy, which works for me everywhere. Note that I'm only copying the output dll here, not the entire directory. Also note that I'm hard coding a destination /bin/ directory, which is specific to this project. But I wanted to share the $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}> syntax, which I think is neat:

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}>)

对于 Windows 用户,在 CMake 3.21 + 中有一个新的生成器表达式 $<TARGET_RUNTIME_DLLS:tgt>,您可以使用这个官方代码片段来复制目标所依赖的所有 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
)