A project is declared with project(name), and targets are the build artifacts you create — executables, libraries, or custom outputs. Each target has properties (source files, include paths, linked libraries, compiler flags). In modern CMake, you apply settings to targets, not globally.
A project groups related targets:
cmake_minimum_required(VERSION 3.20) project(MyApp VERSION 1.0) add_executable(myapp main.cpp) add_library(mylib utils.cpp)
This creates a project MyApp with two targets: the executable myapp and a library mylib.
Target types:
add_executable(name source.cpp) — builds an executableadd_library(name source.cpp) — builds a library (static by default)add_library(name SHARED source.cpp) — shared library (.so, .dll)add_library(name STATIC source.cpp) — static library (.a, .lib)add_library(name INTERFACE) — header-only library (no build, only interface)add_custom_target(name COMMAND ...) — runs a command (not a traditional build output)
Each target owns its properties: source files, include directories, linked libraries, compiler flags. Properties are set with target_* commands:
target_sources(mylib PRIVATE utils.cpp utils.h) target_include_directories(mylib PUBLIC include/) target_link_libraries(mylib PRIVATE somelib) target_compile_options(mylib PRIVATE -Wall)
The PRIVATE, PUBLIC, INTERFACE keywords control visibility:
PRIVATE — only the target itself uses itPUBLIC — target and anything that links to it use itINTERFACE — only consumers of the target use it (for header-only libraries)
Modern CMake means thinking in targets. Don't set global flags with set(CMAKE_CXX_FLAGS ...). Instead, configure each target individually. This makes builds predictable and modular.