将可执行文件链接到的所有 dll 复制到可执行目录

Kan*_* Li 2 dll cmake

这个问题询问类似的问题,但只需要手动指定要复制哪些dll。我的问题是:有没有一种方法可以简单地让 CMake 复制链接到可执行库的所有 dll,而不需要手动告诉 CMake 要复制哪些文件?有时 CMake 更了解是否需要 dll。例如,在安装了同一库(例如Boost)的dll和静态库的系统中,并且我们选择静态链接到Boost,那么我们不需要复制dll。因此,与手动指定要复制哪些文件相比,CMake 可以做出更好的决定。另一个例子是,我的QT库链接到ICU库,并且在编写CMakeLists.txt时,我对此一无所知,所以我不可能告诉CMake复制ICU dll,所以这应该由CMake而不是完成人类。

假设操作系统是Windows。

Ale*_*ing 6

As of CMake 3.21 the generator expression $<TARGET_RUNTIME_DLLS:...> is helpful. It expands to a list of paths (in an unspecified order) to the locations of all the SHARED libraries in its transitive dependencies.

add_library(lib1 ...)
add_library(lib2 ...)

find_package(lib3 REQUIRED)

add_executable(exe ...)
target_link_libraries(exe PRIVATE lib1 lib2 imported::lib3)

# The following variable is defined only on DLL systems
if (CMAKE_IMPORT_LIBRARY_SUFFIX)
  add_custom_command(
    TARGET exe POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
    COMMAND_EXPAND_LISTS
  )
endif ()
Run Code Online (Sandbox Code Playgroud)

The above code assumes that $<TARGET_RUNTIME_DLLS:...> will not be empty, hence the check that we are on a DLL system (CMAKE_IMPORT_LIBRARY_SUFFIX is only defined when this is the case). If it is empty, the command will fail, as not enough arguments will be supplied to cmake -E copy. I have personally opened an issue about the bad ergonomics of this. https://gitlab.kitware.com/cmake/cmake/-/issues/23543

Note that $<TARGET_RUNTIME_DLLS:...> only captures CMake targets. This is a good example of why it is a good idea in general to link only to CMake targets, even if that means creating an imported target yourself.


If you find you're writing this a lot, you can wrap it up into a function:

function(copy_runtime_dlls TARGET)
  get_property(already_applied TARGET "${TARGET}" PROPERTY _copy_runtime_dlls_applied)

  if (CMAKE_IMPORT_LIBRARY_SUFFIX AND NOT already_applied)
    add_custom_command(
      TARGET "${TARGET}" POST_BUILD
      COMMAND "${CMAKE_COMMAND}" -E copy
        "$<TARGET_RUNTIME_DLLS:${TARGET}>" "$<TARGET_FILE_DIR:${TARGET}>"
      COMMAND_EXPAND_LISTS
    )
 
    set_property(TARGET "${TARGET}" PROPERTY _copy_runtime_dlls_applied 1)
  endif ()
endfunction()
Run Code Online (Sandbox Code Playgroud)

Then just call copy_runtime_dlls(exe) above.