[CMake] Copying Shared Libraries (DLLs) Next to the Executable

Marek Vojtko (Firaxis) Marek.Vojtko at firaxis.com
Wed Feb 21 22:20:40 EST 2018


Hi,

I need to copy external shared libraries (DLLs on Windows) next to the generated executable. Is calling "cmake -E copy_if_different" through a custom command added to the executable target still the best way to achieve this? CMake tracks include directories, compile definitions or options, link flags, etc. through its dependency system, but it doesn't provide an easy way to list / copy all shared libraries a target depends on?

I am trying to follow the "new" CMake paradigms, using add_subdirectory(), set_target*() with PUBLIC/PRIVATE/INTERFACE scope, etc. to create a modular, re-usable setup, but that actively prevents me from using a custom command on the executable target. To wit:

App depends on Lib. Lib depends on several third-party, pre-built DLLs and encapsulates the logic of when to depend on them. The third-party, pre-built shared libraries (DLLs) are located through custom Find*.cmake modules and used as IMPORTED targets.

/CMakelists.txt
/App
    /CMakelists.txt
/Lib
    /CMakelists.txt

/CMakelists.txt
*************
set(CMAKE_MODULE_PATH "<my_modules>")
project("DLLTest")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Lib")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/App")

/App/CMakelists.txt
*****************
add_executable(App WIN32 main.cpp)
target_link_libraries(App PRIVATE Lib)

/Lib/CMakelists.txt
****************
add_library(Lib STATIC lib.h lib.cpp)
target_include_directories(Lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

if(Lib_NEEDS_DEPENDENCY)
    find_package(Dependency REQUIRED)
    target_link_libraries(Lib PUBLIC Dependency::Dependency)
endif()

FindDependency.cmake
********************
[snip]
add_library(Dependency::Dependency SHARED IMPORTED)
set_target_properties(Dependency::Dependency PROPERTIES
            INTERFACE_INCLUDE_DIRECTORIES "inc"
            IMPORTED_IMPLIB "dependency.lib"
            IMPORTED_LOCATION "dependency.dll"
)

In this setup, it is impossible to propagate a shared library (DLL) from Lib to App. This is because Lib and App have different BINARY_DIRs and Lib must create its target before App calls target_link_libraries(). That means that Lib does not know where App will generate its executable. If Lib is a SHARED target, I cannot set its RUNTIME_OUTPUT_DIRECTORY to be the same as App's. If Lib depends on IMPORTED targets (as is my case), I cannot create either a file(COPY) step, or an install(FILES) step, or even an add_custom_command() step in Lib to copy the shared library, because I don't know the destination.

The only solution, it would seem, is to add a custom command to App's target, because then I know where to copy the shared libraries to. But that means that App now has to know it needs to copy a shared library from a "hidden" dependency of Lib. App also needs to know about every SHARED or IMPORTED target Lib depends on, not to mention duplicate the logic in Lib's CMakelists.txt that decided whether Lib depends on Dependency in the first place.

I was looking into GetPrerequisites and FixupBundle, but both of those operate on an already existing executable and try to guess what shared libraries (DLLs) it might need. It feels silly to guess at something that CMake already knows (as the IMPORTED target sets the IMPORTED_IMPLIB and IMPORTED_LOCATION properties).

Setting a common CMAKE_RUNTIME_OUTPUT_DIRECTORY for both App and Lib is problematic if I have multiple executables in my root CMakelists.txt and they depend on different versions of the shared libraries or I have other name clashes.

Is there no automated way to get the list of shared libraries a target depends on?

Thanks,
Marek


More information about the CMake mailing list