3

I'm trying to organize auto-copying runtime dependencies of executable targets to its built directories.

As case I use GTest for unit-testing that is linking as shared libraries. After building if I try to launch unit-tests there will be an error like "gtest.dll isn't found". With other external dependencies the same problem (as with local where target is shared library).

I can just add every external library to install process after linking and run tests in that directory but it isn't a practical way as launching CTest.

I wanted to use file(GET_RUNTIME_DEPENDENCIES) but it must be called after build and I didn't find INSTALL(CODE) analog for POST_BUILD stage.

Just coping DLLs with add_custom_command don't work too because of possible dependency inheritance. For example, A depends on B that depends on C. If I try to make dependency copying by this approach then only B will be copied in POST_BUILD process of A.

I don't like to store all built artifacts in one directory because it creates a mess and could create collisions in the future.

  • Use CMake 3.21 and the new `install(RUNTIME_DEPENDENCY_SET)` command instead of plain `file(GET_RUNTIME_DEPENDENCIES)`: https://cmake.org/cmake/help/latest/command/install.html?highlight=install#runtime-dependency-set – Alex Reinking Jul 29 '21 at 18:16
  • @Alex Reinking I read about it and agree that it's better but I want to copy files at POST_BUILD step, not INSTALL step. It will be great if CMake has an alternative of the functionality for required task. – Alexander Khristosenko Jul 30 '21 at 08:52

1 Answers1

3

Found out a solution but it's not portable and only works in Windows.

In CMake version 3.21 there is a new TARGET_RUNTIME_DLLS generator expression that returns all runtime dependencies of runtime target as a list of file paths. The expression can be used in ADD_CUSTOM_COMMAND command at POST_BUILD stage.

There are targets of a shared library SomeLib and a test executable SomeLib_tests in project. The targets of GTest shared libraries are found by FIND_PACKAGE function.

cmake_minimum_required(VERSION 3.21)
project(ExampleProject)

add_library(SomeLib SHARED
            "include/SomeLib/api.h"
            "src/SomeLib/api.cpp")
target_include_directories(SomeLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")


find_module(GTest COMPONENTS gtest gtest_main REQUIRED)

add_executable(SomeLib_tests
               "ut/api_tests.cpp")
target_link_libraries(SomeLib_tests
                      PRIVATE SomeLib
                      PRIVATE GTest::gtest GTest::gtest_main)

gtest_add_tests(TARGET SomeLib_tests)

At this step, if I call CTest under Windows there will be an error like "gtest.dll isn't found".

It's needed to copy all dependencies of the executable to its directory. It can be achieved by calling ADD_CUSTOM_COMMAND in conjunction with the TARGET_RUNTIME_DLLS expression. The target directory is gotten from TARGET_FILE_DIR generator expression.

add_custom_command( TARGET SomeLib_tests POST_BUILD
                    COMMAND ${CMAKE_COMMAND} -E copy
                        $<TARGET_RUNTIME_DLLS:SomeLib_tests>
                        $<TARGET_FILE_DIR:SomeLib_tests>
                    COMMAND_EXPAND_LISTS)

With that, if I build the project and call CTest again the tests will run as expected.

  • 1
    FYI CMake 3.27 added [$](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLL_DIRS), intended for use in batch files to set the `PATH`. – alexchandel Jul 25 '23 at 00:46
  • @alexchandel It looks promising. Right now I don't work with CMake project but will keep in mind. Thanks – Alexander Khristosenko Jul 25 '23 at 17:46