Olson CloudWorks 🚀

How to create a shared library with cmake

September 19, 2026

How to create a shared library with cmake

Creating shared libraries with CMake is a crucial skill for any C++ developer aiming to build modular and maintainable software. Shared libraries, also known as dynamically linked libraries (DLLs) on Windows, offer numerous advantages, including reduced executable size, code reusability across multiple applications, and simplified updates. This guide will walk you through the process of building a shared library using CMake, a powerful cross-platform build system generator. We’ll cover everything from setting up your project structure to defining the CMakeLists.txt file, ensuring you have a solid foundation for creating your own shared libraries. Understanding how to leverage CMake for shared library creation streamlines your development workflow and enhances the overall quality of your software projects, promoting collaboration and efficient resource utilization. This comprehensive tutorial will empower you to build robust and scalable applications with ease.

Setting Up Your Project Structure for a Shared Library

Before diving into the CMake configuration, it’s essential to organize your project files logically. A well-structured project makes development, maintenance, and collaboration easier. Start by creating a root directory for your project. Inside this directory, create subdirectories for your source code (typically named “src”), header files (“include”), and build files (“build”). You might also consider adding a “test” directory for unit tests.

For example, let’s say you’re creating a “mylibrary” shared library. Your directory structure might look like this:
- mylibrary/
- src/
- mylibrary.cpp
- include/
- mylibrary.h
- CMakeLists.txt
This structure allows for clear separation of concerns, making it easier to navigate and understand your project. Keeping your source code and header files separate from the build directory prevents clutter and ensures a clean build process.

Consider adding a README file to your project root explaining the purpose of the library and how to build it. This is especially important if you plan to share your library with others. Version control, such as Git, is also highly recommended for managing changes and collaborating effectively. A well-organized project structure is the cornerstone of a successful shared library project, enabling you to focus on the core functionality rather than struggling with file management.

Crafting Your CMakeLists.txt File

The CMakeLists.txt file is the heart of your CMake project. It contains the instructions that CMake uses to generate the build system for your target platform. This file defines the project name, specifies the source files, and sets the build options. For creating a shared library, you’ll use the add_library command with the SHARED keyword.

Here’s a basic example of a CMakeLists.txt file for creating a shared library: cmake cmake_minimum_required(VERSION 3.10) project(mylibrary) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) include_directories(include) file(GLOB SOURCES “src/.cpp”) add_library(mylibrary SHARED ${SOURCES}) set_target_properties(mylibrary PROPERTIES PUBLIC_HEADER “include/mylibrary.h”) This CMakeLists.txt file first specifies the minimum CMake version required. Then, it sets the project name to “mylibrary” and defines the C++ standard to use (C++11 in this case). The include_directories command tells CMake where to find the header files. The file(GLOB SOURCES …) command collects all the .cpp files in the src directory into a variable named SOURCES. Finally, the add_library command creates the shared library named “mylibrary” using the source files specified in the SOURCES variable. The set_target_properties command is important for making the header file publicly available for users of the library.

To improve portability, consider using the CMAKE_INSTALL_PREFIX variable to control the installation directory. You can also add install rules to copy the shared library and header files to the installation directory. This ensures that your library can be easily used by other projects. Remember to adapt this template to fit your specific project requirements. For more complex projects, you might need to add additional dependencies or compiler flags. CMake offers a high degree of flexibility, allowing you to customize the build process to meet your specific needs. Explore more advanced CMake options here.

Building and Installing Your Shared Library

Once you have created your CMakeLists.txt file, you can generate the build system and build your shared library. This process typically involves creating a build directory, running CMake to generate the build files, and then using the build system (e.g., Make, Ninja, Visual Studio) to compile the code. This step is crucial for transforming your source code and CMake configuration into a functional shared library that can be linked against other applications.

Here’s a step-by-step guide to building your shared library:

  1. Create a build directory: mkdir build
  2. Change to the build directory: cd build
  3. Run CMake to generate the build files: cmake ..
  4. Build the library: make (or ninja if you’re using Ninja, or open the generated solution in Visual Studio)
  5. Install the library (optional): sudo make install

The cmake .. command tells CMake to look for the CMakeLists.txt file in the parent directory (i.e., the root of your project). The make command then compiles the code and creates the shared library. The sudo make install command copies the shared library and header files to the installation directory (usually /usr/local/lib and /usr/local/include on Linux). You may need to adjust the installation directory based on your system and preferences. After building the library, you can link it against other applications. To do this, you’ll need to tell the linker where to find the shared library and the header files. This usually involves adding the installation directory to the linker’s search path and including the header file in your source code. For example, if you installed the library to /usr/local/lib, you might need to add -L/usr/local/lib to the linker flags. Proper installation ensures that your library is accessible and usable by other projects on your system. For more information on CMake and build systems, refer to the official CMake documentation [^1^].

Testing and Using Your Shared Library

After building and installing your shared library, it’s important to test it to ensure that it works correctly. Create a simple test application that uses the functions provided by the library. Compile and link this application against your shared library. Run the application and verify that the library functions are called correctly.

Here’s an example of a simple test application: cpp include include “mylibrary.h” int main() { std::cout << “Result: " << mylibrary_function() << std::endl; return 0; } This test application includes the mylibrary.h header file and calls the mylibrary_function function. To compile and link this application, you’ll need to tell the compiler and linker where to find the header file and the shared library. For example, you might use the following command: bash g++ -o test test.cpp -I/usr/local/include -L/usr/local/lib -lmylibrary This command compiles the test.cpp file, includes the /usr/local/include directory in the header file search path, includes the /usr/local/lib directory in the library search path, and links against the mylibrary shared library. This process verifies that your shared library is functioning as expected and can be successfully integrated into other applications.

To ensure the quality of your shared library, consider writing unit tests using a testing framework such as Google Test [^2^]. Unit tests allow you to test individual functions in isolation, making it easier to identify and fix bugs. Furthermore, using a Continuous Integration (CI) system like Jenkins or GitHub Actions can automate the build and test process, ensuring that your library is always in a working state. Proper testing and CI integration are essential for maintaining the reliability and stability of your shared library. The key benefits of using shared libraries include:

  • Reduced executable size
  • Code reusability
  • Simplified updates
Infographic here showcasing the shared library creation process
Troubleshooting Common Issues -----------------------------

Creating shared libraries can sometimes be challenging, and you might encounter various issues during the process. One common problem is linker errors, which occur when the linker cannot find the shared library or the required symbols. This can happen if the library is not installed correctly or if the linker’s search path is not set up properly. Ensure that the library is installed in a standard location (e.g., /usr/local/lib) or that the linker’s search path includes the directory where the library is installed.

Another common issue is header file not found errors. This occurs when the compiler cannot find the header file for the shared library. Make sure that the header file is installed in a standard location (e.g., /usr/local/include) or that the compiler’s include path includes the directory where the header file is installed. Double-check the paths specified in your CMakeLists.txt file and your build commands. Incorrect paths are a frequent cause of these errors. Always verify that the paths are correct and that the necessary files are in the expected locations.

Symbol versioning issues can also arise, especially when dealing with complex dependencies. This happens when different versions of a library define the same symbols, leading to conflicts at runtime. Consider using versioned symbols to avoid these conflicts. Addressing these common issues proactively will help you streamline the shared library creation process and ensure its successful integration into your projects. Remember, careful attention to detail and thorough testing are crucial for creating reliable and robust shared libraries. Using tools like ldd (on Linux) to check library dependencies can also be helpful in diagnosing issues [^3^].

Did you know a well-crafted shared library can significantly reduce your application’s memory footprint? Shared libraries allow multiple applications to share the same code in memory, leading to more efficient use of system resources. This is particularly beneficial for large applications or systems with limited memory.

To summarize, the featured snippet optimized paragraph is:The CMakeLists.txt file is the heart of your CMake project. It contains the instructions that CMake uses to generate the build system for your target platform. This file defines the project name, specifies the source files, and sets the build options. For creating a shared library, you’ll use the add_library command with the SHARED keyword.

FAQ: Shared Libraries with CMake

What are the benefits of using shared libraries?
Shared libraries reduce executable size, promote code reusability, and simplify updates.
How do I specify the C++ standard in CMake?
Use the set(CMAKE\_CXX\_STANDARD 11) command to specify the C++11 standard.
How do I link against a shared library?
Add the library's installation directory to the linker's search path and include the header file in your source code.
What is the purpose of the `set_target_properties` command?
This command makes the header file publicly available for users of the library.
Creating a shared library with CMake might seem daunting at first, but with a clear understanding of the process and a well-structured project, you can easily build reusable and maintainable code. Remember to organize your project files, craft a comprehensive CMakeLists.txt file, build and install your library correctly, and thoroughly test it. By following these steps, you'll be well on your way to creating robust shared libraries that can enhance your software development projects. Take what you've learned here and start building your own shared libraries today to improve your code's modularity and efficiency.
  • Organize your project files.
  • Craft a comprehensive CMakeLists.txt file.
  • Build and install your library correctly.
  • Test your library thoroughly.

Ready to take your C++ skills to the next level? Explore other advanced CMake features, such as custom commands and generators, to further customize your build process. Consider investigating dependency management tools like Conan or vcpkg to streamline the integration of external libraries into your projects. The possibilities are endless when you master the art of shared library creation with CMake.

[^1^]: CMake Documentation: https://cmake.org/documentation/ [^2^]: Google Test: [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 the major number of VERSION. So libmylib.so.1 will be a symlink to libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) 

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:

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 ${PROJECT_VERSION_MAJOR} 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.](https://github.com/google/googletest)