Reputation: 8797
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.
Upvotes: 2
Views: 5755
Reputation: 19916
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 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()
Then just call copy_runtime_dlls(exe)
above.
Upvotes: 6
Reputation: 4626
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
Upvotes: 1