What Is CMake and Why Should You Use It?
CMake is a cross-platform build system generator. Rather than writing compiler commands by hand, you describe your project in a CMakeLists.txt file, and CMake generates the appropriate build files for your platform — Makefiles on Linux/macOS, Visual Studio projects on Windows, Ninja files, and more.
Almost every serious C++ project uses CMake today. It's supported by major IDEs (CLion, VS Code, Visual Studio), integrates with package managers like vcpkg and Conan, and handles complex multi-target projects cleanly.
Installing CMake
- Windows: Download the installer from cmake.org or use
winget install cmake. - macOS:
brew install cmake - Linux:
sudo apt install cmake
Verify your installation: cmake --version
Your First CMakeLists.txt
Create a project directory with this structure:
my_project/
├── CMakeLists.txt
└── src/
└── main.cpp
Here's a minimal CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(my_app src/main.cpp)
Breaking it down:
cmake_minimum_required— ensures a compatible CMake version.project()— names your project and sets metadata.set(CMAKE_CXX_STANDARD 17)— enforces C++17 across all targets.add_executable()— defines a binary target and its source files.
Building the Project
CMake uses an out-of-source build pattern — you build in a separate directory to keep source files clean:
cd my_project
mkdir build
cd build
cmake .. # Configure (generates build files)
cmake --build . # Compile
Your compiled binary will appear in the build/ directory.
Adding Multiple Source Files
# Option 1: list files explicitly (recommended)
add_executable(my_app
src/main.cpp
src/utils.cpp
src/parser.cpp
)
# Option 2: use a glob (convenient but not recommended for large projects)
file(GLOB SOURCES "src/*.cpp")
add_executable(my_app ${SOURCES})
Tip: Prefer listing files explicitly. Globbing doesn't always detect new files without re-running CMake.
Linking Libraries
CMake makes library linking clean and explicit:
# Add a static library
add_library(mylib STATIC src/mylib.cpp)
# Link it to your executable
target_link_libraries(my_app PRIVATE mylib)
# Link a system library (e.g., pthreads on Linux)
target_link_libraries(my_app PRIVATE pthread)
Using find_package for Third-Party Libraries
find_package(OpenSSL REQUIRED)
target_link_libraries(my_app PRIVATE OpenSSL::SSL OpenSSL::Crypto)
CMake ships with find modules for many common libraries. For others, use vcpkg or Conan as a package manager alongside CMake.
Common CMake Best Practices
- Always use out-of-source builds.
- Use
target_include_directoriesinstead ofinclude_directories. - Prefer
PRIVATE,PUBLIC, orINTERFACEscoping on all target commands. - Set
CMAKE_CXX_STANDARD_REQUIRED ONto avoid silent fallbacks. - Use
cmake --build . --config Releasefor optimized builds.
Summary
CMake has a learning curve, but once you understand its core model — targets, properties, and linking — it becomes an indispensable tool. Start simple, then build up to multi-target projects, testing with CTest, and packaging with CPack as your projects grow.