CMake adalah build system generator — bukan build system itu sendiri. CMake membaca file CMakeLists.txt dan menghasilkan file build (Makefile di Linux, project file Visual Studio di Windows, file Xcode di macOS). Pendekatan ini membuat satu CMakeLists.txt bisa dipakai di banyak platform tanpa modifikasi. Mayoritas proyek C/C++ modern (LLVM, OpenCV, Boost, KDE) memakai CMake. Pada chapter ini kita akan menulis CMakeLists.txt minimal, mengonfigurasi build, dan mengamati bagaimana CMake menghasilkan Makefile di balik layar.

CMakeLists.txt Minimal #

Struktur proyek sama dengan chapter sebelumnya: dua file C dan satu header. Kita tambah CMakeLists.txt:

text
greet.h, greet.c   -- library greet
main.c              -- program utama
CMakeLists.txt      -- script CMake

Berikut CMakeLists.txt minimal untuk proyek ini:

cmake
cmake_minimum_required(VERSION 3.10)
project(greet-demo C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

add_executable(app main.c greet.c)

target_compile_options(app PRIVATE -Wall -Wextra)

Mari kita telaah baris-baris CMakeLists.txt di atas:

  1. Baris 1 (cmake_minimum_required(VERSION 3.10)): mensyaratkan CMake versi minimum 3.10. Bila versi CMake lebih lama, CMake akan menolak dengan error. Flag ini melindungi proyek dari perubahan perilaku antar versi CMake.
  2. Baris 2 (project(greet-demo C)): mendefinisikan nama proyek greet-demo dan bahasa C. CMake akan mencari compiler C yang sesuai (gcc atau clang). Bila kita juga punya kode C++, sebutkan C CXX.
  3. Baris 4 (set(CMAKE_C_STANDARD 11)): menetapkan standar C yang dipakai ke C11. Variabel CMAKE_C_STANDARD menyebabkan CMake menambahkan flag -std=c11 (atau -std=gnu11 bila CMAKE_C_EXTENSIONS aktif) ke command line compiler.
  4. Baris 5 (set(CMAKE_C_STANDARD_REQUIRED ON)): memaksa compiler mendukung C11. Bila compiler tidak mendukung C11, konfigurasi gagal. Tanpa flag ini, compiler yang tidak mendukung C11 akan diam-diam fallback ke standar lawas.
  5. Baris 7 (add_executable(app main.c greet.c)): mendeklarasikan target executable bernama app yang dibangun dari main.c dan greet.c. CMake otomatis mengompilasi tiap .c menjadi .o, lalu men-link semuanya menjadi app.
  6. Baris 9 (target_compile_options(app PRIVATE -Wall -Wextra)): menambahkan flag -Wall -Wextra ke target app. Keyword PRIVATE berarti flag hanya berlaku untuk target ini (tidak diturunkan ke target lain yang men-link app). Alternatif: PUBLIC (flag diturunkan) atau INTERFACE (flag hanya diturunkan, tidak dipakai internal).

Konfigurasi dan Build #

CMake memisahkan dua tahap: configure (membaca CMakeLists.txt, mendeteksi compiler, menghasilkan Makefile) dan build (menjalankan Makefile). Pola standar adalah out-of-source build — file build diletakkan di subdirektori, tidak mencampur source:

bash
cmake -B build

Output yang Dihasilkan:

text
-- The C compiler identification is GNU 14.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/test_cmake/build

Mari kita telaah baris-baris output konfigurasi di atas:

  1. -- The C compiler identification is GNU 14.2.0: CMake mengidentifikasi compiler C yang tersedia di sistem (di sini GCC 14.2.0). CMake menguji compiler dengan mengompilasi file C kecil dan memeriksa output __GNUC__ macro.
  2. -- Detecting C compiler ABI info: CMake mendeteksi ABI target (32-bit/64-bit, calling convention). Informasi ini dipakai untuk menentukan flag seperti -m64 atau path library yang benar.
  3. -- Check for working C compiler: /usr/bin/cc - skipped: CMake memverifikasi compiler bisa menghasilkan executable. -skipped menandakan verifikasi sudah dilakukan di tahap sebelumnya (caching hasil).
  4. -- Detecting C compile features: CMake menguji fitur compiler — misalnya apakah compiler mendukung C11 _Static_assert, _Generic, atau <stdatomic.h>. Hasil tes disimpan di CMakeCache.txt agar tidak perlu diulang.
  5. -- Configuring done (0.1s): konfigurasi selesai tanpa error. Semua target, variabel, dan dependensi sudah diproses.
  6. -- Generating done (0.0s): file build (Makefile untuk Unix Makefiles generator) sudah ditulis ke direktori build/.
  7. -- Build files have been written to: /tmp/test_cmake/build: konfirmasi path output build.

Tahap kedua adalah build:

bash
cmake --build build

Output yang Dihasilkan:

text
[ 33%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/greet.c.o
[100%] Linking C executable app
[100%] Built target app

Output ini adalah output Makefile yang dihasilkan CMake. Persentase [ 33%], [ 66%], [100%] adalah fitur Makefile CMake yang menampilkan progress build. Mari kita jalankan executable:

bash
./build/app

Output yang Dihasilkan:

text
Halo Budi!
Halo Siti!

Mengamati Makefile yang Dihasilkan #

CMake dengan Unix Makefiles generator (default di Linux) menghasilkan build/Makefile yang bisa kita periksa:

bash
head -30 build/Makefile

Output yang Dihasilkan:

text
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 4.4

# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target

# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:

#=============================================================================
# Special targets provided by cmake.

# Disable implicit rules so canonical targets will work.
.SUFFIXES:

# Disable VCS-based implicit rules.
% : %,v

Header # CMAKE generated file: DO NOT EDIT! menandakan file ini dihasilkan otomatis — bila kita mengubahnya manual, perubahan akan ditimpa saat cmake dijalankan ulang. Konvensi: jangan edit file build CMake secara langsung, edit CMakeLists.txt lalu jalankan cmake -B build ulang.

compile_commands.json untuk IDE #

CMake bisa menghasilkan compile_commands.json — database yang berisi command line kompilasi tiap file. File ini dipakai oleh clangd, VSCode C/C++ extension, dan IDE lain untuk fitur autocomplete dan go-to-definition:

bash
cmake -B build_cc -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
head -20 build_cc/compile_commands.json

Output yang Dihasilkan:

text
[
{
  "directory": "/tmp/test_cmake/build_cc",
  "command": "/usr/bin/cc   -std=gnu11 -Wall -Wextra -o CMakeFiles/app.dir/main.c.o -c /tmp/test_cmake/main.c",
  "file": "/tmp/test_cmake/main.c",
  "output": "CMakeFiles/app.dir/main.c.o"
},
{
  "directory": "/tmp/test_cmake/build_cc",
  "command": "/usr/bin/cc   -std=gnu11 -Wall -Wextra -o CMakeFiles/app.dir/greet.c.o -c /tmp/test_cmake/greet.c",
  "file": "/tmp/test_cmake/greet.c",
  "output": "CMakeFiles/app.dir/greet.c.o"
}
]

Setiap entry memiliki directory (cwd saat kompilasi), command (command line compiler lengkap), file (path absolut source), dan output (path object file). Editor seperti VSCode memakai file untuk mencocokkan source yang sedang kita edit, lalu menjalankan command di background untuk memperoleh preprocessor info yang akurat untuk fitur autocomplete.

Build Configuration #

CMake mendukung beberapa build type: Debug, Release, RelWithDebInfo, MinSizeRel. Kita atur lewat variabel CMAKE_BUILD_TYPE saat konfigurasi:

bash
cmake -B build_release -DCMAKE_BUILD_TYPE=Release
cmake --build build_release

Output yang Dihasilkan:

text
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/test_cmake/build_release
[ 33%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/greet.c.o
[100%] Linking C executable app
[100%] Built target app

Build type menentukan flag compiler default — Release menambah -O3 -DNDEBUG, Debug menambah -O0 -g, RelWithDebInfo menambah -O2 -g -DNDEBUG. Kita akan membahas perbedaan flag ini lebih detail pada chapter Build Configurations.