This is a basic end-to-end guide for modern CMake packaging.
We will create a tiny library called SimpleMath that:
- links a lightweight dependency (
spdlog), - exposes public headers with correct include directories,
- installs binaries + headers,
- exports
SimpleMathConfig.cmakeandSimpleMathConfigVersion.cmake, - is found by other projects with
find_package(SimpleMath CONFIG REQUIRED), - uses
find_dependency(spdlog)in its package config.
Useful references:
- CMake documentation
find_packagetarget_link_librariestarget_include_directoriesCMakePackageConfigHelpersfind_dependency- Importing and exporting guide
Table of contents
- Minimal project structure
- Library source
- Full
CMakeLists.txt SimpleMathConfig.cmake.inandfind_dependency- Build + install
- Consume it with
find_package - Quick checklist
Minimal project structure
SimpleMath/
├─ CMakeLists.txt
├─ cmake/
│ └─ SimpleMathConfig.cmake.in
├─ include/
│ └─ simplemath/simplemath.hpp
└─ src/
└─ simplemath.cpp
Library source
include/simplemath/simplemath.hpp
#pragma once
namespace simplemath {
double add(double a, double b);
double divide(double numerator, double denominator);
} // namespace simplemath
src/simplemath.cpp
#include <simplemath/simplemath.hpp>
#include <spdlog/spdlog.h>
#include <stdexcept>
namespace simplemath {
double add(double a, double b) {
const auto result = a + b;
spdlog::debug("add({}, {}) = {}", a, b, result);
return result;
}
double divide(double numerator, double denominator) {
if (denominator == 0.0) {
spdlog::error("divide({}, {}): division by zero", numerator, denominator);
throw std::invalid_argument("division by zero");
}
const auto result = numerator / denominator;
spdlog::debug("divide({}, {}) = {}", numerator, denominator, result);
return result;
}
} // namespace simplemath
Full CMakeLists.txt
This file handles building, linking, include directories, install rules, exported targets, and package config generation.
cmake_minimum_required(VERSION 3.20)
project(SimpleMath VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
add_library(SimpleMath src/simplemath.cpp)
add_library(SimpleMath::SimpleMath ALIAS SimpleMath)
target_compile_features(SimpleMath PUBLIC cxx_std_17)
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(SimpleMath
PUBLIC
spdlog::spdlog
)
target_include_directories(SimpleMath
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(TARGETS SimpleMath
EXPORT SimpleMathTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
set(SIMPLEMATH_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/SimpleMath")
install(EXPORT SimpleMathTargets
FILE SimpleMathTargets.cmake
NAMESPACE SimpleMath::
DESTINATION ${SIMPLEMATH_INSTALL_CMAKEDIR}
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/SimpleMathConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimpleMathConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/SimpleMathConfig.cmake"
INSTALL_DESTINATION ${SIMPLEMATH_INSTALL_CMAKEDIR}
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/SimpleMathConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/SimpleMathConfigVersion.cmake"
DESTINATION ${SIMPLEMATH_INSTALL_CMAKEDIR}
)
SimpleMathConfig.cmake.in and find_dependency
Create cmake/SimpleMathConfig.cmake.in:
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(spdlog CONFIG REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/SimpleMathTargets.cmake")
Why this matters:
- Downstream projects call
find_package(SimpleMath CONFIG REQUIRED). - CMake loads
SimpleMathConfig.cmake. find_dependency(spdlog)ensuresspdlog::spdlogis also available to the consumer.
Build + install
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PWD/install"
cmake --build build
cmake --install build
Expected install output (simplified):
install/
├─ include/simplemath/simplemath.hpp
└─ lib/cmake/SimpleMath/
├─ SimpleMathConfig.cmake
├─ SimpleMathConfigVersion.cmake
└─ SimpleMathTargets.cmake
Consume it with find_package
Consumer CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(Consumer LANGUAGES CXX)
find_package(SimpleMath CONFIG REQUIRED)
add_executable(consumer_app main.cpp)
target_link_libraries(consumer_app PRIVATE SimpleMath::SimpleMath)
Consumer main.cpp:
#include <simplemath/simplemath.hpp>
#include <iostream>
int main() {
std::cout << simplemath::add(2.0, 3.0) << "\n";
std::cout << simplemath::divide(10.0, 2.0) << "\n";
return 0;
}
If CMake cannot locate SimpleMath, point it at your install prefix:
cmake -S . -B build -DCMAKE_PREFIX_PATH="/path/to/SimpleMath/install"
cmake --build build
Quick checklist
-
add_libraryfor your project target. -
find_package(spdlog CONFIG REQUIRED)+target_link_libraries(... PUBLIC spdlog::spdlog). -
BUILD_INTERFACEandINSTALL_INTERFACEinclude directories. -
install(TARGETS ...)+install(DIRECTORY include/ ...). -
install(EXPORT ...)for generated targets. -
configure_package_config_file+write_basic_package_version_file. -
find_dependency(spdlog)inSimpleMathConfig.cmake.in.
This pattern is the standard baseline for a small, reusable CMake library.