C++ PROGRAMMING ยท 25 MIN STUDY

Build multi-file C++ projects with CMake

Organize a small C++ project with source files, headers, CMake targets, and repeatable builds.

Learning objectives

  • Explain why a project needs a build system as it grows beyond one file.
  • Understand CMakeLists.txt, targets, and source organization.
  • Configure and build a small C++ project.
  • Keep build output separate from source files.

Prerequisites: compiler command-line use, functions, and header/source file organization. Review Compiler toolchain and Header files first.

Example project layout

student-app/
  CMakeLists.txt
  include/
    student.hpp
  src/
    main.cpp
    student.cpp

Headers declare the interface; source files provide definitions. A build system records how these pieces form an executable or library.

Header: include/student.hpp

#pragma once
#include <string>
struct Student {
    std::string name;
    int score;
};
bool passed(const Student& student);

Implementation: src/student.cpp

#include "student.hpp"
bool passed(const Student& student) {
    return student.score >= 40;
}

Program: src/main.cpp

#include "student.hpp"
#include <iostream>
int main() {
    Student s{"Asha", 82};
    std::cout << passed(s) << '\n';
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(StudentApp LANGUAGES CXX)
add_executable(student_app
    src/main.cpp
    src/student.cpp
)
target_include_directories(student_app PRIVATE include)
target_compile_features(student_app PRIVATE cxx_std_17)
if(MSVC)
    target_compile_options(student_app PRIVATE /W4 /permissive-)
else()
    target_compile_options(student_app PRIVATE -Wall -Wextra -Wpedantic)
endif()

add_executable creates a target. CMake tracks the source files and generates build instructions for a selected build tool. target_include_directories tells the compiler where to find project headers. target_compile_features requests C++17 support for this target.

Configure and build

cmake -S . -B build
cmake --build build
./build/student_app

On Windows, the executable may be under a configuration-specific subdirectory when using a multi-configuration generator such as Visual Studio. Run the generated executable from its actual build location.

Compiler options and portability

The warning flags shown are common to GCC and Clang. For a cross-platform project, conditionally select compiler-specific warning options rather than assuming every compiler accepts the same flags. CMake itself is cross-platform; the generated build tool depends on the chosen generator.

Common mistakes

  • Including a .cpp file instead of compiling it as part of the target.
  • Forgetting to add a source file to add_executable.
  • Putting generated build files into the source directory.
  • Using a relative include path that works only from one working directory.
  • Assuming the executable path is identical for every generator.

Practice

  1. Add a second function in student.cpp and declare it in the header.
  2. Change the target to require C++20.
  3. Explain the difference between configure and build.
Show solutions
  1. Add a declaration to the header and definition to student.cpp; call it from main.
  2. Use cxx_std_20.
  3. Configure generates build-system files; build compiles and links the target.

Summary

CMake describes targets and their dependencies, then generates build instructions. Keep headers, source files, and build output organized so projects are repeatable and portable.

References