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
Waitforinputidle Doesn't Work for Starting Mspaint Programmatically
Why Do Some People Use Swap for Move Assignments
Does Std::Mt19937 Require Warmup
How to Use Source_Location in a Variadic Template Function
Compute Median of Values Stored in Vector - C++
Why Lifetime of Temporary Doesn't Extend Till Lifetime of Enclosing Object
What's the Most Reliable Way to Prohibit a Copy Constructor in C++
How to Read Output from Cmd.Exe Using Createprocess() and Createpipe()
C++ What Is the Purpose of Casting to Void
C++: Can Vector<Base> Contain Objects of Type Derived
Map Set/Get Requests into C++ Class/Structure Changes
How to Overload the Operator++ in Two Different Ways for Postfix A++ and Prefix ++A
How to Use Formatmessage() Properly in C++
How to Create a Sparse Array in C++
Uninitialized Pointers in Code
Cuda How to Get Grid, Block, Thread Size and Parallalize Non Square Matrix Calculation