Expert · Exceptions, concurrency and building projects with CMake · topic 37 of 42 · 35 min
Building real projects: CMake, warnings, debuggers and sanitizers
Explanation
Why a build system?
With two files you can type g++ main.cpp gst.cpp -o app. With 200 files, external libraries, debug and release builds, Windows and Linux, typing commands is impossible, and recompiling everything after a one-line change wastes minutes. A build system knows which files depend on which, rebuilds only what changed, and works the same on every machine. CMake is the most widely used build system generator for C++.
A typical project layout
billing/
├── CMakeLists.txt
├── include/billing/gst.h
├── src/gst.cpp
├── src/main.cpp
└── tests/gst_test.cppA minimal CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(Billing LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(billing src/gst.cpp)
target_include_directories(billing PUBLIC include)
add_executable(billing_app src/main.cpp)
target_link_libraries(billing_app PRIVATE billing)
if (MSVC)
target_compile_options(billing PRIVATE /W4)
else()
target_compile_options(billing PRIVATE -Wall -Wextra -Wpedantic)
endif()Modern CMake is "target-based": you describe targets (libraries and executables) and attach settings to them with target_* commands, instead of setting global flags. PUBLIC settings pass on to whoever links the target; PRIVATE ones do not.
Configure, build, run
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
./build/billing_app
# Release build in a separate folder
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release
cmake --build build-releaseUsing libraries
find_package(fmt CONFIG REQUIRED) # a library installed on the system or via vcpkg/Conan
target_link_libraries(billing_app PRIVATE fmt::fmt)
include(FetchContent) # or download one at configure time
FetchContent_Declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2)
FetchContent_MakeAvailable(googletest)Debug versus Release
| Build type | Flags (GCC/Clang) | Use for |
|---|---|---|
| Debug | -g, no optimisation | stepping through code in a debugger |
| Release | -O3 -DNDEBUG | shipping; asserts are removed |
| RelWithDebInfo | -O2 -g -DNDEBUG | profiling and debugging optimised code |
Debuggers and sanitizers
- A debugger (gdb on Linux, lldb on macOS, the Visual Studio or VS Code debugger) lets you pause at a breakpoint, step line by line and inspect variables. Build with -g (Debug) first.
- AddressSanitizer (-fsanitize=address) finds out-of-bounds access, use-after-free and leaks.
- UndefinedBehaviorSanitizer (-fsanitize=undefined) finds signed overflow, bad shifts and similar undefined behaviour.
- ThreadSanitizer (-fsanitize=thread) finds data races (do not combine it with AddressSanitizer in the same build).
- Valgrind (Linux) finds memory errors without recompiling, but runs programs much slower.
target_compile_options(billing_app PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(billing_app PRIVATE -fsanitize=address,undefined)Formatting and static analysis
clang-format formats code automatically from a .clang-format file, so reviews focus on logic, not spaces. clang-tidy and compiler warnings catch bug patterns and suggest modern replacements. Many teams run all of these in CI (continuous integration: automatic checks on every pull request).
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Building real projects: CMake, warnings, debuggers and sanitizers · 1/6
Real projects have many files and libraries. A build system like CMake builds them reliably on every machine.
Prefer a video by a teacher? Find free videos on “cmake tutorial for beginners modern cmake targets” on YouTube ↗
In real life: A small team project for a college hackathon
Three students build a library-management tool in C++. With one CMakeLists.txt, one student on Windows with Visual Studio and two on Ubuntu with g++ all build the same project with the same two commands. A GitHub Actions workflow builds it with -Wall -Wextra and runs the tests with AddressSanitizer on every push, catching a use-after-free the night before the demo.
cmake_minimum_required(VERSION 3.20)
project(LibraryManager LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(library_core src/book.cpp src/member.cpp src/loans.cpp)
target_include_directories(library_core PUBLIC include)
add_executable(library_app src/main.cpp)
target_link_libraries(library_app PRIVATE library_core)
enable_testing()
add_executable(library_tests tests/loans_test.cpp)
target_link_libraries(library_tests PRIVATE library_core)
add_test(NAME loans COMMAND library_tests)Practice (0/4 done)
Which command makes CMake generate build files into a folder named build?
What does target_link_libraries(app PRIVATE billing) do?
Which build type is best for stepping through code in a debugger?
Turn your earlier GST functions into a CMake project: a library target with gst.cpp and include/gst.h, an executable target using it, C++20 required, warnings on, and a Debug build with AddressSanitizer. Build it with cmake -S . -B build and cmake --build build, and run it.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
- What C++ is and how compiling worksFirst steps: compiling, output, variables and input
- Reading input and formatting outputFirst steps: compiling, output, variables and input
- Concurrency 2: atomics, async, futures and condition variablesExceptions, concurrency and building projects with CMake · the topic just before this one
Related topics
- Concurrency 1: threads, mutexes and data racesExceptions, concurrency and building projects with CMake · same module
- Exceptions: throw, try, catch and noexceptExceptions, concurrency and building projects with CMake · same module
- Sanitizers and warningsC Programming (from zero to systems) · Debugging and tools
- Building with makeC Programming (from zero to systems) · The preprocessor, multi-file programs and make
- Debugging and performanceKotlin and Android App Development · Testing Android apps
- Reading errors and debuggingJava Programming (Core to Spring Boot) · Control flow and methods
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does CMake produce?
Answer: Build files for a native tool such as Make, Ninja or Visual Studio
CMake is a build system generator; the native tool then compiles.
What does PUBLIC mean in target_include_directories(lib PUBLIC include)?
Answer: lib uses the folder AND so does anything that links lib
PUBLIC requirements propagate to consumers; PRIVATE ones do not.
Which sanitizer finds use-after-free bugs?
Answer: AddressSanitizer
AddressSanitizer checks memory accesses.
Why keep the build folder out of Git?
Answer: It is generated, machine-specific and large; anyone can regenerate it
Commit sources and CMakeLists.txt, not build output.
Should sanitizer builds be shipped to customers?Related
Answer: No; use them in development and testing
Ship normal hardened builds.
Which tool generates Makefiles for cross-platform projects?Related
What does "Step Into" do in a debugger?Related
Answer: Goes inside the method called on the current line
Step Into enters the called method.
Which flag adds debug information for gdb?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Exceptions, concurrency and building projects with CMake”. The module exam comes next.
- Module 9 exam: Exceptions, concurrency and CMake · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Competitive programming, interviews and careers. 42 topics left in the course before the final exam.
← Previous: Concurrency 2: atomics, async, futures and condition variables