Real projects are a graph of libraries and executables; CMake models exactly that.
Most projects are a few libraries plus one or more executables and tests that use them. Create a library target with add_library:
add_library(core STATIC a.cpp b.cpp)an archive linked into each user.add_library(core SHARED ...)a.so/.dll/.dylibloaded at run time.add_library(core a.cpp b.cpp)without a type: static or shared depending on theBUILD_SHARED_LIBSoption, letting whoever builds decide.add_library(core INTERFACE)a header-only library with no sources to compile.add_library(core OBJECT ...)compiled objects without an archive, reused by other targets.
Then connect targets: target_link_libraries(app PRIVATE core). This does far more than pass -lcore. CMake builds core first, links it into app, and propagates core's usage requirements (include directories, compile definitions, further dependencies) to app. That propagation is the heart of modern CMake and is covered next.
Always link by target name, not by file path. A target name lets CMake track the dependency, choose the correct file name per platform and pass on requirements.
It's common to give libraries a namespaced alias, add_library(myproj::core ALIAS core), so code linking myproj::core works the same whether the library is built in the project or found as an installed package. A name containing :: is known to be a target, so typos become errors instead of a silent -lmyproj::core.
Example
cmake_minimum_required(VERSION 3.25)
project(shop LANGUAGES CXX)
add_library(core STATIC src/cart.cpp src/price.cpp)
add_library(shop::core ALIAS core)
target_include_directories(core PUBLIC include)
target_compile_features(core PUBLIC cxx_std_20)
add_executable(shop src/main.cpp)
target_link_libraries(shop PRIVATE shop::core) # include/ and C++20 come along
add_executable(cart_test tests/cart_test.cpp)
target_link_libraries(cart_test PRIVATE shop::core)
# $ cmake -S . -B build -G Ninja && cmake --build build
# [1/6] Building CXX object CMakeFiles/core.dir/src/cart.cpp.o
# ...
# [7/7] Linking CXX executable cart_testRemember
- add_library creates STATIC, SHARED, INTERFACE or OBJECT libraries
- target_link_libraries links and propagates usage requirements
- Link by target name; provide a namespaced ALIAS like proj::core
Watch out for
- Linking by a path like ${CMAKE_BINARY_DIR}/libcore.a, which loses build ordering and usage requirements.