2

This question asks about similar question, but only requires to manually specify which dlls to copy. My question is: is there a way to simply let CMake to copy all dlls that are linked to the executable library, without manually telling the CMake which files to copy? Sometimes CMake knows more about whether the dlls is needed. For example, in a system where both dlls and static libs for the same library (e.g. Boost) are installed and we choose to link to Boost statically, then we don't need to copy the dll. So CMake can make a better decision than manually specifying which files to copy. Another example is, my QT library links to ICU library, and when writing CMakeLists.txt, I have no knowledge about that, so I can't possibly tell CMake to copy over the ICU dlls, so this should be done by CMake instead of human.

Suppose the OS is Windows.

Community
  • 1
  • 1
Kan Li
  • 8,557
  • 8
  • 53
  • 93
  • I see one big problem: You can not know at compile-time all the necessary dlls. Delay-loaded dlls or plugins (with their own dependent dlls) are "found" at runtime... – André Dec 31 '12 at 12:43

2 Answers2

5

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

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

Then just call copy_runtime_dlls(exe) above.

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
1

Have a look at CMake's BundleUtilities.cmake which provides fixup_bundle(...)

Details can be found here:

https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack/BundleUtilities

http://www.cmake.org/cmake/help/v2.8.8/cmake.html#module:BundleUtilities

drescherjm
  • 10,365
  • 5
  • 44
  • 64
Johannes S.
  • 4,566
  • 26
  • 41
  • It is only for MacOSX. Doesn't look like work for Windows, or maybe I missed something. I looked at the source code of the BundleUtilties and it checks the existance of .app directory. – Kan Li Dec 30 '12 at 18:14
  • I have to admit I didn't use it yet because we had a custom implementation running before it was available in CMake for non-Mac platforms. I guess the code shows some "historical" variable/function names. The code contains case-handling for Windows and Linux. Eg: `fixup_bundle` calls `get_dotapp_dir` which looks like Mac OS, but in there, there is an `IF` which *separates* between Mac and non-Mac handling by checking for the `.app` directory. – Johannes S. Dec 31 '12 at 00:00
  • BundleUtilities is supposed to work for windows as well although I use windows I have not tested this. I prefer to have my own script in cmake to generate a list of dlls it needs to copy and from that list generate a batch file that copies the needed dlls. – drescherjm Dec 31 '12 at 03:25