My build system of choice is CMake. But using shared libraries on Windows was causing me some headaches. So here is how I have solved it for a small project.

Setup

Consider having a your project structured like this:

  +-> src
  |   +-> CMakeLists.txt
  |   +-> utilities.h
  |   \-> utilities.cpp
  +-> test
  |    +-> CMakeLists.txt
  |    \-> test.cpp
  +-> cmake-build
  |    +-> ...
  \-> CMakeLists.txt 

Where the source directory contains the code for a shared library util which is tested in the test directory. And the CMake cache and build files reside in cmake-build. And the ./CMakeLists.txt includes both CMakeLists.txt in src and test.

CMake will mirror this structre in the build cmake-build directory. And so the cmake-build directory will contain both a test and a src library containing the test.exe and the util.dll.

The problem

If you now run test.exe you will end up with an error message stating that util.dll faild to load.

This is due to the way how windows looks up .dlls. First it will look in the folder of the .exe and then it will look in each folder in the PATH variable.

The solution

There are two solutions:

  1. Put the cmake-buid\src directory in the path
  2. Place the util.dll besides test.exe.

The letter is the option that I chose. It is quite easy once you know how.

You can control where cmake will place the output of certain targets, by setting the appropriate variables. The most interesting are:

  • CMAKE_RUNTIME_OUTPUT_DIRECTORY
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY
  • CMAKE_ARCHIVE_OUTPUT_DIRECTORY

There are others, have a look at the variable list and search for output_directory.

So all I did to solve this is to define two variables in the root CMakeLists.txt

  set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
  set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

This will create two directories in the cmake-build directory: bin and lib and on windows all shared libraries and executable targets will place their output into this folder. And since the .dll now is in the same folder as the .exe running the test works just fine.