Build shared libraries with CMake on Windows

There are two ways to build a library as shared. The first way is by passing a SHARED option to add_library():

add_library(${lib_name} SHARED ${sources})

The second way is by setting a global flag BUILD_SHARED_LIBS. It can be overridden individually.

set(BUILD_SHARED_LIBS=ON)
# or
option(BUILD_SHARED_LIBS "Build shared libraries." ON)

For the libraries to be built within the CMake project, their dll files will be generated inside the CMAKE_RUNTIME_OUTPUT_DIRECTORY. So the executables are able to find them on runtime.

On the Windows platform with the MinGW toolchain, linking against dll files are possible. However with Visual C++, extra steps need to be taken — the targets can only be linked against import libraries but not the dll files directly. Without handling this problem, it leads to a linking error LNK1104 — "cannot open file xxx.lib". The solution to this problem is to use a CMake macro GenerateExportHeader.

In the source folder:

include(GenerateExportHeader) #include the CMake macro
include_directories(${CMAKE_CURRENT_BINARY_DIR}/${lib_folder_name}) #include the generated header

In the library folder:

set(export_filename lib_exports.h)

set(sources
    ${sources}  
    ${export_filename} #add the generated header in the library source
)

add_library(${libname} ${sources})

GENERATE_EXPORT_HEADER(${libname}
    BASE_NAME ${libname}
    EXPORT_MACRO_NAME LIB_EXPORT
    EXPORT_FILE_NAME ${export_filename}
    STATIC_DEFINE SHARED_EXPORT_BUILD_AS_STATIC)

In the library header, include the generated header and process public function declarations with the export macro.

#include "lib_exports.h"
void LIB_EXPORT bar();

Note that if an external dll needs to be linked, we will need to copy the it into CMAKE_RUNTIME_OUTPUT_DIRECTORY:

set(dll_loc ${EXTERN_DLL_DIRECTORY})

function(copy_dll lib)
    get_filename_component(path ${dll_loc} PATH)
    get_filename_component(name ${lib} NAME)
        execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${lib} ${path}/${name})
endfunction()

file(GLOB dlls "${dll_loc}/*.dll")

foreach(file ${dlls})
    copy_dll(${file})
endforeach()