我们使用CMake为SVN中的源代码生成Visual Studio文件。 现在我的工具需要一些DLL文件与可执行文件在同一个文件夹中。 DLL文件在源代码旁边的一个文件夹中。
我应该如何更改我的CMakeLists.txt
,以便生成的Visual Studio项目将在发布/调试文件夹中拥有特定的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 COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/res)
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
。然后你需要将配置类型注入到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>)
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>
作为目标路径。否则,它会将其复制到库构建路径中,这是无用的。 - AberrantWolfinstall()
指令的目标不是将二进制文件组装起来吗?或者是cmake的LIBRARY
相关内容?我并不真正了解这个工具链。 - Sandburg$<TARGET_FILE_DIR:MyTest>
- 这是什么?如何打印信息,它确切的意思是什么。 - ilwIMPORTED_LIBRARIES
添加的库不起作用。当没有构建任何内容时,它会抱怨无法运行后构建命令。 - TzalumenIMPORTED
库,并且需要重新定位DLL,则需要使用变体命令。
如果您已将IMPORTED
库添加为MyImportedLib
,则应使用
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:MyImportedLib> $<TARGET_FILE_DIR:MyTest>
请注意,要运行多个后构建命令,您需要将它们全部绑定到一个自定义命令中,例如
add_custom_command(TARGET MyTest POST_BUILD COMMAND #your first command# COMMAND #Your second command#)
- Tzalumen我把这些行代码放在我的顶层CMakeLists.txt文件中。由CMake编译的所有库和可执行文件都将放置在构建目录的顶层,以便可执行文件可以找到库并且易于运行。
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
请注意,这并不能解决原帖作者从项目源目录复制预编译二进制文件的问题。对于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
)
copy -t
似乎可以更好地处理这个问题。 - Alec JacobsonCOMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:exe> $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
。这也将目标复制到它自己的目录中。这很浪费和愚蠢,但确保如果 *DLLS 是一个空字符串,它仍然是一个有效的命令。 - Alec Jacobson今天我尝试制作程序的Windows版本时遇到了问题。所有这些答案都不能令我满意,所以我不得不自己进行一些研究。主要有三个问题:
我希望将调试版本的库与调试版本的程序进行链接, 并将发布版本的库与发布版本的程序进行链接。
此外,我希望正确版本的DLL文件(Debug/Release)被复制到输出目录中。
同时,我不想编写复杂而容易出错的脚本来实现这一切。
在浏览了一些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>
)
set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB "${SDL_ROOT_PATH}/lib/SDL2.lib")
? - KulaGGininstall(FILES $<TARGET_RUNTIME_DLLS:your_exe_here> TYPE BIN)
install()
而不是直接将文件复制到$CMAKE_RUNTIME_OUTPUT_DIRECTORY
,因为install()
是官方认可的CMake方式,用于将文件放置在$CMAKE_RUNTIME_OUTPUT_DIRECTORY
中。install()
的命名方式很奇怪,因为CMake不仅仅是一个构建工具,它还是一个生成安装程序的工具。这个生成安装程序的功能被称为CPack。在CMake的眼中,$CMAKE_RUNTIME_OUTPUT_DIRECTORY
只是CPack的临时存储区。当你使用install()
安装一个文件时,你告诉CMake它应该被视为一个程序文件,将随着可执行文件一起复制到其他位置。如果你不通过install()
进行安装,CMake将把它视为一个未识别的随机文件。
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。这些 DLL 将传递回到 install()
。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)
。有關實現,請參見這裡。
install(DIRECTORY)
install(
DIRECTORY "${DIR_CONTAINING_YOUR_DLLS}"
TYPE BIN
FILES_MATCHING REGEX "[^\\\\/.]\\.[dD][lL][lL]$"
)
$DIR_CONTAINING_YOUR_DLLS
中。install(FILES)
不同,install(DIRECTORY)
在安装时不关心目录中具体的文件。这意味着我们现在有了配置时间和编译时间来获取您的DLL列表,并将它们放入$DIR_CONTAINING_YOUR_DLLS
中。只要在安装时DLL文件在$DIR_CONTAINING_YOUR_DLLS
中,install(DIRECTORY)
就会将它们拾取起来。
如果您选择这种方法,您有责任将DLL与构建配置匹配。(考虑:静态 vs 动态,调试 vs 发布,导入库版本 vs DLL版本,带有可选多线程的库,忘记删除不再需要的DLL等。)vcpkg
的提示如果你启用了 VCPKG_APPLOCAL_DEPS
并使用 vcpkg
,它会将你的 DLL 文件定位并复制到你的 $CMAKE_RUNTIME_OUTPUT_DIRECTORY
中,但不会通过 install()
进行操作。你需要使用 install(DIRECTORY)
的技巧来让 CMake 加载它们。
(在内部,vcpkg
使用 dumpbin
、llvm-objdump
和 objdump
来扫描你的可执行二进制文件以获取这些文件名。)
out/build/...
目录下的exe文件。安装命令是有效的,但是将文件放在out/install/
目录下,所以当你从VS运行时,它找不到dll文件,一开始似乎什么都没发生。感谢你帮助解决这个问题! - undefinedinstall
在构建过程中移动文件我在尝试按照CMake官方教程第9步操作时遇到了此问题。这是我想要移动的文件位置:
src
|_build
|_Debug
- `MathFunctions.dll`
这是我想要文件所在的位置:
src
|_build
|_install
|_bin
- `MathFunctions.dll`
src/Mathfunctions/CMakeLists.txt
的CMakeLists.txt
中添加了这一行。install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
DESTINATION bin)
$<CONFIG>
可以有两个值 Debug 或 Release ,具体取决于项目如何构建,就像原始问题所要求的那样。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项目生成时会被解析,并且始终是默认值。
$<CONFIGURATION>
。 - Chuck Claunch这对它们中的一个很有用
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)
您可能需要添加自定义目标,并使其依赖于其中一个可执行目标。
使用上述函数复制文件:
COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`