# cmake ## What is CMake? **CMake** is a build system generator. It does not compile anything itself; instead it reads a project description written in its own scripting language (`CMakeLists.txt`) and generates the native build files for whatever toolchain you actually want to use, Makefiles, Ninja files, Visual Studio projects, Xcode projects, and drives that generated build through a single uniform `cmake --build` interface. The problem CMake solves is portability of the build itself. A hand-written Makefile encodes assumptions about compiler flags, library paths, and OS conventions that break the moment someone tries to build the same project on a different machine or platform. CMake pushes that platform-specific knowledge into the generation step: you describe targets, sources, and dependencies abstractly, and CMake works out how to turn that into a build recipe for the local environment. In HPC and embedded work, CMake is the de facto standard because most numerical and systems libraries (Eigen, OpenCV, PCL, most CUDA and MPI-enabled projects) ship a `CMakeLists.txt` and expect to be consumed via `find_package`. Knowing CMake is less about writing elaborate build scripts and more about being able to read and extend someone else's. ## Install On Debian and Ubuntu: ```bash sudo apt install cmake ``` On Fedora and RHEL: ```bash sudo dnf install cmake ``` Distro-packaged CMake often lags upstream by a year or more, which matters if a project requires a recent feature (like `FetchContent` or C++20 module support). To get the latest release, use the Kitware APT repository or download a binary release directly: ```bash wget https://github.com/Kitware/CMake/releases/download/vX.Y.Z/cmake-X.Y.Z-linux-x86_64.sh chmod +x cmake-X.Y.Z-linux-x86_64.sh sudo ./cmake-X.Y.Z-linux-x86_64.sh --prefix=/usr/local --skip-license ``` ## Practice The canonical CMake workflow is out-of-source: never write generated files into the same directory as your sources. ```bash mkdir build && cd build cmake .. # generate the build system cmake --build . # invoke the underlying generator (make, ninja, ...) ``` A minimal `CMakeLists.txt` for a single executable looks like: ```cmake cmake_minimum_required(VERSION 3.20) project(hello LANGUAGES C) add_executable(hello main.c) ``` Running `cmake ..` from `build/` produces Makefiles (or Ninja files, if `-G Ninja` is passed) in that directory, and `cmake --build .` runs the underlying build tool without you needing to know whether it's `make` or `ninja` under the hood. ## Concepts ### Targets CMake's central abstraction is the **target**, not the file. `add_executable` and `add_library` create targets; everything else (`target_link_libraries`, `target_include_directories`, `target_compile_options`) attaches properties to a target rather than to a raw list of flags. ```cmake add_library(mathutils STATIC mathutils.c) target_include_directories(mathutils PUBLIC include/) add_executable(app main.c) target_link_libraries(app PRIVATE mathutils) ``` The `PUBLIC`/`PRIVATE`/`INTERFACE` keywords control propagation: a `PUBLIC` include directory on `mathutils` is automatically inherited by anything that links against it, so `app` sees `include/` without repeating the flag. This transitive propagation is the main reason modern CMake code reads so differently from an old-style Makefile. ### find_package `find_package` locates an already-installed dependency and, if it ships CMake config files (most modern libraries do), imports it as a target you can link against directly: ```cmake find_package(MPI REQUIRED) target_link_libraries(app PRIVATE MPI::MPI_C) ``` `MPI::MPI_C` is an imported target that carries the correct include paths, compiler flags, and link libraries for the MPI implementation CMake found on the system, so the project doesn't need to know whether that's OpenMPI or MPICH. ### FetchContent `FetchContent` pulls a dependency's source at configure time and builds it as part of the current project, useful when a library either has no system package or you want a pinned version: ```cmake include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0 ) FetchContent_MakeAvailable(googletest) target_link_libraries(app_tests PRIVATE gtest_main) ``` ### Build types CMake's `CMAKE_BUILD_TYPE` selects a predefined set of compiler flags: `Debug` (no optimisation, debug symbols), `Release` (optimised, no symbols), `RelWithDebInfo` (optimised, with symbols, useful when profiling with [[perf]]). ```bash cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. ``` For HPC work, `RelWithDebInfo` is usually the right default: it keeps the optimisation that makes the timing meaningful while retaining the symbols that make [[perf]] and [[gdb]] output readable.