• Portfolio and blog of sydneyzh.

# 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()