Build Systems & Modern CMake

বিল্ড সিস্টেম ও আধুনিক CMake

Read: ~30 min 8 practice problems

1. Why CMake?

Real C++ projects span hundreds of files, third-party libraries, multiple platforms, and multiple compilers. Hand-written Makefiles don't scale. CMake generates platform-specific build files (Makefiles, Ninja, MSBuild, Xcode) from one declarative spec.

2. A Minimal CMakeLists.txt

CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(myapp
    src/main.cpp
    src/utils.cpp)

target_include_directories(myapp PRIVATE include)
target_compile_options(myapp PRIVATE -Wall -Wextra -Wpedantic)

3. Build Workflow

terminal
# Configure (once per build dir)
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release

# Build
cmake --build build -j

# Run
./build/myapp

# Clean rebuild
cmake --build build --clean-first

4. Libraries & Linking

CMakeLists.txt
add_library(mathlib STATIC src/math.cpp)
target_include_directories(mathlib PUBLIC include)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mathlib)
  • PUBLIC: this and consumers see it
  • PRIVATE: only this target sees it
  • INTERFACE: only consumers see it (header-only libs)

5. find_package for Third-Party Libs

CMakeLists.txt
find_package(Threads REQUIRED)
target_link_libraries(myapp PRIVATE Threads::Threads)

find_package(fmt CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE fmt::fmt)

For dependency management, modern projects use vcpkg or Conan alongside CMake.

6. Modern CMake Rules

  • Target-based, not variable-based. Set properties on targets, not globally.
  • Use target_* commands (target_link_libraries, target_include_directories).
  • Avoid include_directories, link_libraries (old style).
  • Pin a minimum version >= 3.20.
  • Use generator expressions $<CONFIG:Debug> for build-type specific options.

7. Practice Problems

  1. Write a CMakeLists.txt for a 2-file project.
    ✨ Show Answer

    See section 2 above.

  2. Difference between add_library STATIC and SHARED?
    ✨ Show Answer

    STATIC: .a/.lib linked into executable. SHARED: .so/.dll loaded at runtime — smaller exe, more dependencies.

  3. Why prefer target_* commands?
    ✨ Show Answer

    Properties propagate correctly via PUBLIC/PRIVATE/INTERFACE. Old-style globals leak everywhere and cause subtle bugs.

  4. Configure a Release build.
    ✨ Show Answer
    cmake -B build -DCMAKE_BUILD_TYPE=Release
  5. Add the C++20 standard requirement.
    ✨ Show Answer
    set(CMAKE_CXX_STANDARD 20)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
  6. Find and link the Threads library.
    ✨ Show Answer
    find_package(Threads REQUIRED)
    target_link_libraries(myapp PRIVATE Threads::Threads)
  7. Why use a separate build/ directory?
    ✨ Show Answer

    Out-of-source builds keep generated files separate from sources. Easy to rm -rf build for a fresh start.

  8. What is cmake --install?
    ✨ Show Answer

    Copies built artifacts (executables, libraries, headers) to install prefix. Set in CMakeLists with install(TARGETS ...).

Summary

CMake is the de-facto C++ build system. Use modern, target-based CMake (3.x+). Set properties with target_* commands. Use PUBLIC/PRIVATE/INTERFACE correctly. For dependencies, use find_package with vcpkg/Conan.

Next Module → Testing, Debugging & Sanitizers.