Lesson 8 · Targets: the core model
Static and shared libraries
Turn code into a reusable library with add_library. Static versus shared, object libraries, and which to pick when.
An app with one main.cpp is fine. But real projects need libraries, reusable chunks of code, built once and linked wherever they are needed. In CMake, a library is just another target.
The two kinds of libraries
A static library (.a on Linux, .lib on Windows) is copied into your executable at link time. A shared library (.so, .dll, .dylib) is loaded at runtime and shared between programs.
Static (.a / .lib) | Shared (.so / .dll / .dylib) | |
|---|---|---|
| At link time | Code copied into the executable | Only a reference recorded |
| Executable size | Bigger | Smaller |
| Distribution | One file to ship | Ship the .so or .dll too |
| Updates | Rebuild the app to update | Replace the library file |
| Startup speed | Faster | Slightly slower |
Why
For your own project, default to static. It is simpler to distribute and has no runtime-path headaches. Reach for shared when the library is meant to be updated independently (plugins) or shared by many programs (system libraries, and most of what
find_packagegives you in lesson 17).
Creating a library
calc/
├── CMakeLists.txt
├── src/
│ └── calc.cpp
└── include/
└── calc/
└── calc.h
CMakeLists.txtcmake_minimum_required(VERSION 4.4) project(calc LANGUAGES CXX) # A STATIC library target named "calc" from this source file. add_library(calc STATIC src/calc.cpp) # PUBLIC include dir. Consumers of "calc" get this automatically (lesson 9). target_include_directories(calc PUBLIC include)
To make it shared instead, change one word:
add_library(calc SHARED src/calc.cpp)
Note
The target name
calcis what you use everywhere else in CMake, for exampletarget_link_libraries(app PRIVATE calc). The file on disk gets a platform prefix and suffix automatically,libcalc.a,libcalc.so,calc.lib,calc.dll. You never write those.
Building both at once
If you really need both, there is a third kind of library, the object library:
# Object library. Compiles the sources once, no archive is produced.
add_library(calc_obj OBJECT src/calc.cpp)
# It can be injected into other targets.
add_library(calc_static STATIC $<TARGET_OBJECTS:calc_obj>)
add_library(calc_shared SHARED $<TARGET_OBJECTS:calc_obj>)
Object libraries save time when the same source feeds several targets. The compile happens once. They are niche. Do not reach for them until you actually have two targets sharing sources.
Tip
BUILD_SHARED_LIBSis a cache variable that makes plainadd_library(foo ...)(no type) produce shared libraries. Being explicit withSTATICorSHAREDis clearer. Explicit beats implicit. Type it out.
Linking your library
In the app’s directory:
add_executable(app main.cpp)
target_link_libraries(app PRIVATE calc)
One line. Because calc declared its public headers with PUBLIC, app now compiles with calc’s include directory and links the library. That is the usage-requirement behavior from lesson 7, and the subject of the next two lessons.
Pitfall
On Windows, shared libraries need their symbols exported, or the consumer must use the same import library. If
calc_sharedshows link errors about missing symbols, checkCMAKE_WINDOWS_EXPORT_ALL_SYMBOLSor add explicit__declspec(dllexport). This is a common first encounter with DLLs, not a CMake bug.
Read the docs