Copying dependent DLLs to executable directory?

I got a project where I use a third party library (Windows).

  • A.lib
  • A.dll
  • B.dll

B.dll is a dependency of A.dll not exposed to the A.dll interface.

I’m setting up an imported library:

add_library(Example::LibA SHARED IMPORTED)
set_target_properties(Example::LibA PROPERTIES
  IMPORTED_LOCATION "${LibA_LIBRARY}"
  IMPORTED_IMPLIB "${LibA_LIBRARY}"
  INTERFACE_INCLUDE_DIRECTORIES "${LibA_INCLUDE_DIR}"
)

I’m looking for a way to have both the DLLs copied to the executable’s output directory (unit tests executable). I was hoping for a way to define this as part of the interface to the imported library so consumers only need to link to the imported target.

Is there any such way?

If not, what would be the CMake way for this? Set up a POST build event that copies the DLLs per executable target that links to it? (How would you then obtain the paths for the DLL from the importer target?)

1 Like

CMake 3.16 has file(GET_RUNTIME_DEPENDENCIES) that you can use for this.

1 Like

Looking at that it looks to be used along with install which is then used with a separate install comment to install it to the system.

I was looking for a build-time command/script that would copy dependencies to the build output directory so when I run a target the necessary dependency is there along with the executable.

You can add a custom command to run cmake -P get_runtime_deps.cmake at build time rather than install(CODE) or install(SCRIPT) for install time.

1 Like

And what if you’re stuck on 3.14? What’s the right way to place DLL dependencies for tests (with CTest)?

You could try GetPrerequisites, but it’s finicky (though probably better on Windows due to the simpler linking model). If this is a necessary thing, I recommend just bumping your minimum.

Is get_runtime_deps.cmake something that exists? Or a CMake script I have to write myself? I’m still not clear on how this allows me to get the dependencies of a binary in a build command.

You have to write that script. It should call the file(GET_RUNTIME_DEPENDENCIES) command I linked above.

I have a lot of trouble with get_prerequisites(). One of the problems is that it returns prerequisite dlls without their path on windows. Your cmake code then needs to take care of finding the libraries. You could now remember all the dlls found during configure and take those. This does not work with if a dll is used by the prerequisite but not needed at link time. You can then try to remember the paths of the dlls found in the configure phase and look for a prerequisites there. This may help but is still not garanteed to work if the “lower level dll” is located elsewhere.
Yesterday I had another problem with get_prerequisites() which I posted today.

I recommend not using get_prerequisites anymore. file(GET_RUNTIME_DEPENDENCIES) is a better implementation of that logic. There are many corner cases get_prerequisites does not consider and fixing it is not really feasible (it would basically involve writing the dynamic loader logic for each platform in CMake…which is incidentally why it behaves so poorly today).

1 Like

Thanks for the hint. I already saw that GET_RUNTIME_DEPENDENCIES has been added in a recent cmake version.
Do you know if it is also faster than get_prerequisites(). The latter is terribly slooooooow.

It is indeed faster, since it’s written in native C++ :slight_smile:

It is also more accurate. We painstakingly drew out the flowchart of dependency searching for Windows, Linux, and macOS and mimicked it as best as we could. GetPrerequisites was not so rigorous.

Hello Kyle,
sounds superb. Thanks so much for providing this new functionality. Does the windows version now provide absolute paths to the dependencies as on linux? This would make my day even more and might make me code over night!
Best regards, Stephan.

Yes, all resolved dependencies are returned as absolute paths.

GREAT! Just started downloading 3.17.2 …
Thanks so much again. I hope I can contribute to cmake some time in other ways than asking stupid questions:)

Revisting this again. This .cmake file to run at build time, how will that know what file to be checking? Do I need to pass the path to the binary to check dependency for as a command line parameter? If so, how do I then obtain the path where the binary for a target will be?

Your CMakeLists.txt specifies what library or executable is being built and where it is going to be located. This is the place where you can add this information to “some variable”, e.g. a list of all libraries/executables that you are building. Towards the end of the CMakeLists.txt (i.e. after all libraries/executables are defined) you can advise cmake to call installation code (using install(CODE …)) at install time. This code would then evaluate the content of the “variable” and gather all depencies using file(GET_RUNTIME_DEPENCIES …). Works for me although I do not like my “global variable”. I also have problems with file (INSTALL …) and symbolic links on windows, but this is another story.

You might be able to avoid the global variable by using file(GENERATE) to create scripts to call with install(SCRIPT). Using file(GENERATE), you can use things like generator expressions that provide the locations of build executables and libraries.

1 Like

CMake 3.21 introduced TARGET_RUNTIME_DLLS generator expression that can also help.

3 Likes