Why is manual copying of .dll needed when linking library?

I am trying to figure out how CMake is supposed to work for shared libraries. I create a shared library like so:

(in /mdp_opt/CMakeLists.txt:)

add_library (mdp_opt SHARED librarycomponent.cpp)

Now, a test executable uses this:
(/test/CMakeLists.txt:)
add_executable (test test.cpp)

target_link_libraries(test PRIVATE mdp_opt)

This works fine if the library is marked STATIC (instead of SHARED as above). When it is marked SHARED (as in above), I need to add:

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

I halfway understand why, but let’s forget that. What is more important is that with this change, it only works if I copy the file mdp_opt.dll from build/x64-debug/mdp_opt to build/x64-debug/test. I can do that - it is not a problem - but I don’t understand why?

In the book “professional CMake”, I read (emphasis mine):

LINK_LIBRARIES
This target property holds a list of all libraries the target should link to directly. It is initially
empty when the target is created and it supports generator expressions. An associated interface
property INTERFACE_LINK_LIBRARIES is supported. Each library listed can be one of the following:
• A path to a library, usually specified as an absolute path.
• Just the library name without a path, usually also without any platform-specific file name
prefix (e.g. lib) or suffix (e.g. .a, .so, .dll).
• The name of a CMake library target. CMake will convert this to a path to the built library
when generating the linker command, including supplying any prefix or suffix to the file
name as appropriate for the platform. Because CMake handles all the various platform
differences and paths on the project’s behalf, using a CMake target name is generally the
preferred method.

So didn’t I just state with:

target_link_libraries(test PRIVATE mdp_opt)

that I intend to link the output associated with the target mdp_opt with the test executable? Also, in my reading of the above resource, my understanding is that the executable will convert to a path? Anyhow?

So if the answer is that this somehow does not work and that some manual copying is needed (e.g. post-build), what is the preferred way to deal with this in a cross-platform way?

---------- Setup output Directories -------------------------

if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${${PROJECT_NAME} _BINARY_DIR}/Bin
CACHE PATH
“Single Directory for all Libraries”
)
endif()

--------- Setup the Executable output Directory -------------

if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
${${PROJECT_NAME} _BINARY_DIR}/Bin
CACHE PATH
“Single Directory for all Executables.”
)
endif()

--------- Setup the Executable output Directory -------------

if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
${${PROJECT_NAME} _BINARY_DIR}/Bin
CACHE PATH
“Single Directory for all static libraries.”
)
endif()

This will make sure all libraries and executables are placed into the same directory. This is one way to solve the issue.

This is essentially because Windows lacks an “rpath” mechanism where a library can contain a set of directories for the runtime loader to look at. On Windows, there is just PATH and various special flags one can pass to LoadLibrary (which is outside of your control).

1 Like

Thanks, that is very helpful; I think I understand now why all this fuss is needed. It would help if documentation (like the quoted book) would mention these limitations on windows; I found the statements a bit misleading.