How to Create a Shared Library with Cmake

How to create a shared library with cmake?

Always specify the minimum required version of cmake

cmake_minimum_required(VERSION 3.9)

You should declare a project. cmake says it is mandatory and it will define convenient variables PROJECT_NAME, PROJECT_VERSION and PROJECT_DESCRIPTION (this latter variable necessitate cmake 3.9):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

Declare a new library target. Please avoid the use of file(GLOB ...). This feature does not provide attended mastery of the compilation process. If you are lazy, copy-paste output of ls -1 sources/*.cpp :

add_library(mylib SHARED
sources/animation.cpp
sources/buffers.cpp
[...]
)

Set VERSION property (optional but it is a good practice):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

You can also set SOVERSION to a major number of VERSION. So libmylib.so.1 will be a symlink to libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION 1)

Declare public API of your library. This API will be installed for the third-party application. It is a good practice to isolate it in your project tree (like placing it include/ directory). Notice that, private headers should not be installed and I strongly suggest to place them with the source files.

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

If you work with subdirectories, it is not very convenient to include relative paths like "../include/mylib.h". So, pass a top directory in included directories:

target_include_directories(mylib PRIVATE .)

or

target_include_directories(mylib PRIVATE include)
target_include_directories(mylib PRIVATE src)

Create an install rule for your library. I suggest to use variables CMAKE_INSTALL_*DIR defined in GNUInstallDirs:

include(GNUInstallDirs)

And declare files to install:

install(TARGETS mylib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

You may also export a pkg-config file. This file allows a third-party application to easily import your library:

  • with Makefile, see pkg-config
  • with Autotools, see PKG_CHECK_MODULES
  • with cmake, see pkg_check_modules

Create a template file named mylib.pc.in (see pc(5) manpage for more information):

prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@

Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@

Requires:
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}

In your CMakeLists.txt, add a rule to expand @ macros (@ONLY ask to cmake to not expand variables of the form ${VAR}):

configure_file(mylib.pc.in mylib.pc @ONLY)

And finally, install generated file:

install(FILES ${CMAKE_BINARY_DIR}/mylib.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)

You may also use cmake EXPORT feature. However, this feature is only compatible with cmake and I find it difficult to use.

Finally the entire CMakeLists.txt should looks like:

cmake_minimum_required(VERSION 3.9)
project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
include(GNUInstallDirs)
add_library(mylib SHARED src/mylib.c)
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
PUBLIC_HEADER api/mylib.h)
configure_file(mylib.pc.in mylib.pc @ONLY)
target_include_directories(mylib PRIVATE .)
install(TARGETS mylib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_BINARY_DIR}/mylib.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)

EDIT

As mentioned in comments, to comply with standards you should be able to generate a static library as well as a shared library. The process is bit more complex and does not match with the initial question. But it worths to mention that it is greatly explained here.

How to create a shared library using object library in CMake

I was able to get it working by setting PARENT_SCOPE for the object library in the suddirectory. The modifications to the original code look like this.

main_dir/CMakeLists.txt

set(SUBARCHIVE_OBJECTS)

add_subdirectory(subdir)
target_link_libraries(mainlib private ${SUBARCHIVE_OBJECTS}

subdir/CMakeLists.txt

add_library(subarchive OBJECT src1.cpp src2.cpp src3.cpp)
list(APPEND SUBARCHIVE_OBJECTS ${TARGET_OBJECTS:subarchive>)
set(SUBARCHIVE_OBJECTS ${SUBARCHIVE_OBJECTS} PARENT_SCOPE)

Thanks for the responses.

Linking a shared library to an executable using CMAKE

To make it work on Windows, dll and executable has to be in the same folder. You can either copy them manually or set output directory in your top CMake file like this: set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "yours_output_directory"). This way, both executable and dll will end up in yours_output_directory.

how to link to a lib and create a shared .so file at the same time using g++ or CMake

The most convenient way of using a library located on the file system imho associating the info with a imported library target which allows you to just link it using target_link_libraries:

add_library(fst SHARED IMPORTED)
target_include_directories(fst INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/fst/include)
set_target_properties(fst PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/fst/lib/libfst.so)

...

target_link_libraries(MY_PROJ PRIVATE fst)

this way the info is easy to reuse and it listed in a single place instead of spreading it into different target properties of the linking target.

A bit more advanced, but even better would be to write fst-config.cmake and fst-config-version.cmake files and putting them in the "installation directory" of the library which would allow you to use find_package(fst) to execute the logic creating the imported library which makes the library simple to reuse across multiple cmake projects, but this is beyond the scope of this answer.
The find_package documentation provides more info about how to do this.


Btw: Note the use of CMAKE_CURRENT_SOURCE_DIR instead of CMAKE_SOURCE_DIR. If you're specifying absolute paths inside the directory containing the CMakeLists.txt file containing the variable reference, since CMAKE_SOURCE_DIR refers to the toplevel source dir which may be different, if you're including the CMakeLists.txt using add_subdirectory.



Related Topics



Leave a reply



Submit