0

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 library:

(/test/CMakeLists.txt:)

add_executable (test test.cpp)

target_link_libraries(test PRIVATE mdp_opt)

If the library is marked STATIC (instead of SHARED as above), I can cmake -> built (under Visual Studio) and successfully run the executable. When it is marked SHARED (as in above), I need to do two things. First thing, is adding:

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

That is fine. However, now it still only works if I copy the file mdp_opt.dll from build/x64-debug/mdp_opt to build/x64-debug/test. But I don’t understand why this is needed?

In the book “professional CMake”, I read:

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 (emphasis mine):

• 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.

I was under the impression that

target_link_libraries(test PRIVATE mdp_opt)

expresses that I intend to link the output associated with the target mdp_opt with the test executable? Also, in my reading of the above book excerpt, my understanding is that the location of the .dll will convert to a path? If the purpose of this conversion is not to somehow tell the executable where to find the shared library, then what is that conversion for?

Basically, can anybody tell me how CMake is supposed to work for shared libraries, and why is works like that? Is a manual post-copy (possibly via CMake instructions) really needed, and the best for this scenario (of intermediate builds while developing)?

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
willem
  • 2,617
  • 5
  • 26
  • 38
  • 1
    "Basically, can anybody tell me how CMake is supposed to work for shared libraries, and why is works like that?" - It is a **platform** (Windows) **constraint** that for being able to find a `.dll` at **runtime** the dll needs either to be near the executable or to be in the directory listed in PATH variable. CMake cannot change environment variables used for run the executable. (Normally an IDE [has such functionality](https://stackoverflow.com/a/2119707/3440745), but **only** when an executable is **run under IDE**). Also, CMake doesn't *silently* copy build artifacts. – Tsyvarev Nov 29 '22 at 08:53
  • "Is a manual post-copy (possibly via CMake instructions) really needed, and the best for this scenario (of intermediate builds while developing)?" - Instead of copy, you could adjust your project in a way when CMake will **create** executables and libraries in the **same directory**: https://stackoverflow.com/a/34445220/3440745. – Tsyvarev Nov 29 '22 at 08:56
  • @Tsyvarev Thanks your first comment is basically the clarification I needed. – willem Nov 29 '22 at 11:48

1 Answers1

0

On windows you need to export symbols of a shared library or add a linker option that results in symbols being exported; otherwise the .lib file for linking the dll simply isn't generated. This is why you need

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

, use __declspec( dllexport ) or similar.

The other issue is the way windows locates dlls: It basically first searches the directory containing the executable and then continues the search via the PATH environment variable. This can result in issues locating the dll at runtime.

You can avoid this issue by setting the directory to place the runtime artefacts before generating the first one of the targets involved:

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
...

add_library(mdp_opt ...)

...

add_executable(test ...)

Alternatively you could simply change the environment variable visible to the vs debugger:

set_target_properties(test PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$<TARGET_FILE_DIR:mdp_opt>;$ENV{PATH}")

If you're working with CTest, there's also a similar property for tests: ENVIRONMENT

If the purpose of [target_link_libraryes] is not to somehow tell the executable where to find the shared library, then what is that conversion for?

You still need to link a dll, or more precisely the corresponding import library; this is what target_link_libraries does, in addition to adding build system dependencies, transferring some properties (e.g. include directories with PUBLIC visibility), ect.

fabian
  • 80,457
  • 12
  • 86
  • 114