Appearance
Modern CMake
CMake reads a project model and generates a build system for Ninja, Make, Visual Studio, Xcode, and other backends. It does not compile source itself. The durable unit of modern CMake design is a target with usage requirements.
Configure, generate, build, and test are distinct
sh
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build --output-on-failure
cmake --install build --prefix stagingConfigure evaluates CMakeLists.txt, finds dependencies, checks the toolchain, and fills a cache. Generate writes the backend build graph. cmake --build invokes that backend. Install copies declared artifacts into a layout.
Changing compiler in an existing build tree is unreliable because detection results are cached. Configure a fresh build directory for a new toolchain.
Targets carry requirements
cmake
cmake_minimum_required(VERSION 3.25)
project(Report VERSION 1.0.0 LANGUAGES C CXX)
add_library(report src/report.cpp)
target_include_directories(report
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(report PUBLIC cxx_std_20)
add_executable(report-cli src/main.cpp)
target_link_libraries(report-cli PRIVATE report)Linking to report tells CMake both to link the artifact and propagate its public usage requirements. Do not globally add include directories and flags when they belong to one target.
PRIVATE affects only the target, PUBLIC affects the target and consumers, and INTERFACE affects only consumers. If a public header contains a dependency's types, that dependency is usually public; if only the .cpp uses it, it is private.
Generators shape configurations
Ninja and Make are commonly single-config: configure one build tree with CMAKE_BUILD_TYPE. Visual Studio, Xcode, and Ninja Multi-Config choose Debug or Release at build time:
sh
cmake --build build --config ReleaseDo not assume CMAKE_BUILD_TYPE exists in a Visual Studio build. Use generator expressions such as $<CONFIG:Debug> for configuration-dependent target properties when needed.
Presets record shared workflows
CMakePresets.json can version configure, build, test, and workflow presets. CMakeUserPresets.json can contain local inheritance and machine paths. Presets reduce undocumented command variations and integrate with IDEs.
Dependencies should produce targets
cmake
find_package(ZLIB REQUIRED)
target_link_libraries(report PRIVATE ZLIB::ZLIB)An imported target carries artifact paths, include directories, definitions, and transitive dependencies. Old variable-based packages may expose ${ZLIB_LIBRARIES} and ${ZLIB_INCLUDE_DIRS}; recognize them but prefer targets when provided.
FetchContent can configure a source dependency as part of the project. Package managers can install binaries and provide CMake packages/toolchain integrations. CMake coordinates the build graph; it is not itself a universal package manager.
Install and export complete a library
cmake
include(GNUInstallDirs)
install(TARGETS report
EXPORT ReportTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT ReportTargets
NAMESPACE Report::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Report
)A full package also supplies a configured ReportConfig.cmake and version file so consumers can call find_package(Report). Test the installed package from a separate consumer project; the build tree can hide missing install requirements.
Policies and minimum versions stabilize behavior
cmake_minimum_required selects policy behavior as well as requiring a tool version. Avoid copying obsolete commands from old tutorials. Target properties keep requirements attached to the library or executable that needs them, while directory-wide state can silently affect unrelated targets as a project grows.
Optional prompts
Explain: Why is target_link_libraries(app PRIVATE report) more than a linker command?
Answer: CMake links the artifact and propagates
report's public/interface usage requirements such as include paths, definitions, features, and transitive targets.
Debug: CMAKE_BUILD_TYPE=Release seems ignored in Visual Studio. Why?
Answer: Visual Studio is multi-config; select the configuration with
--config Releaseor the IDE configuration, rather than configuring one build type.