Pada Chapter sebelumnya kita mengompilasi proyek dua file .cpp dengan tiga perintah g++ berturut-turut. Bayangkan jika proyek berisi 50 file: kita harus mengetik puluhan perintah dan mengulang semuanya setiap kali ada satu file berubah. Build system mengatasi masalah ini dengan mengotomatiskan kompilasi dan mengelola dependensi antar file. CMake adalah build system paling populer di ekosistem C++; hampir semua library C++ modern (Boost, OpenCV, LLVM) memakai CMake. Pada Chapter ini kita akan mempelajari struktur dasar CMakeLists.txt, membangun proyek multi-file, dan menerapkan beberapa best practices yang akan dipakai sepanjang Course.

Mengapa Perlu CMake #

Beberapa alasan memilih CMake sebagai build system:

  • Lintas platform: konfigurasi yang sama menghasilkan Makefile di Linux, Ninja file di macOS, dan Visual Studio solution di Windows.
  • Deteksi compiler dan library: CMake dapat menemukan compiler yang terpasang, mendeteksi versi standar C++, dan mencari library eksternal melalui mekanisme find_package.
  • Incremental build: hanya file yang berubah yang dikompilasi ulang, bukan seluruh proyek.
  • Standar industri: mayoritas proyek C++ open source memakai CMake, sehingga kita dapat mengintegrasikan library pihak ketiga dengan mudah.
TugasTanpa CMakeDengan CMake
Kompilasi 1 fileg++ -c file.cpp -o file.ocmake --build .
Kompilasi 50 file50 baris perintahSatu perintah
Ganti compilerEdit tiap perintahUbah satu variabel
Tambah compiler flagEdit tiap perintahSatu tempat di CMakeLists.txt
Distribusi libraryManualinstall() otomatis

Memasang CMake #

CMake tersedia di hampir semua sistem operasi:

bash
# Linux (Debian/Ubuntu)
sudo apt install cmake

# macOS (Homebrew)
brew install cmake

# Windows (Scoop)
scoop install cmake

Verifikasi pemasangan:

bash
cmake --version

Output yang Dihasilkan (contoh):

text
cmake version 3.30.2

CMake suite maintained and supported by Kitware (kitware.com/cmake).

Versi minimal yang direkomendasikan untuk Course ini adalah CMake 3.16 (memiliki dukungan C++17 yang matang). Pada contoh di atas, versi 3.30 jauh melampaui kebutuhan.

Struktur CMakeLists.txt Minimum #

Setiap proyek CMake dimulai dengan satu berkas CMakeLists.txt di root proyek. Berikut contoh minimum:

cmake
cmake_minimum_required(VERSION 3.16)

project(dsa_demo
    VERSION 1.0.0
    LANGUAGES CXX)

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

add_executable(dsa_demo main.cpp)

Penjelasan tiap baris:

  1. cmake_minimum_required(VERSION 3.16): menentukan versi minimum CMake. Versi lebih lama mungkin tidak mendukung beberapa perintah.
  2. project(dsa_demo VERSION 1.0.0 LANGUAGES CXX): mendeklarasikan nama proyek, versi, dan bahasa yang dipakai. CXX adalah kode CMake untuk C++.
  3. set(CMAKE_CXX_STANDARD 17): menetapkan standar C++17 untuk seluruh target di file ini. Setara dengan flag -std=c++17.
  4. set(CMAKE_CXX_STANDARD_REQUIRED ON): memaksa compiler mendukung standar yang ditetapkan. Jika tidak, konfigurasi gagal.
  5. set(CMAKE_CXX_EXTENSIONS OFF): menonaktifkan ekstensi compiler (mis. GNU extensions). Kode menjadi portable antar GCC, Clang, dan MSVC.
  6. add_executable(dsa_demo main.cpp): mendefinisikan target executable bernama dsa_demo dari source file main.cpp.

Proyek Multi-File #

Pada Chapter Proses Kompilasi kita membangun proyek dengan main_pipeline.cpp dan math_utils.cpp. Sekarang kita akan mengelola proyek serupa dengan CMake. Struktur folder yang dipakai:

text
statistik/
├── CMakeLists.txt
├── src/
│   ├── main.cpp
│   ├── stat_utils.hpp
│   └── stat_utils.cpp
└── build/         (dibuat CMake, jangan commit ke VCS)

Source File Proyek #

Berikut isi tiga file source yang dipakai. Header guard #ifndef ... #endif pada stat_utils.hpp mencegah double inclusion:

cpp
// File: src/stat_utils.hpp
#ifndef STAT_UTILS_HPP
#define STAT_UTILS_HPP

#include <vector>

long long hitungTotal(const std::vector<int>& data);
double hitungRataRata(const std::vector<int>& data);
int cariMaksimum(const std::vector<int>& data);

#endif
cpp
// File: src/stat_utils.cpp
#include "stat_utils.hpp"

long long hitungTotal(const std::vector<int>& data) {
    long long total = 0;
    for (int v : data) {
        total += v;
    }
    return total;
}

double hitungRataRata(const std::vector<int>& data) {
    if (data.empty()) return 0.0;
    return static_cast<double>(hitungTotal(data)) / static_cast<double>(data.size());
}

int cariMaksimum(const std::vector<int>& data) {
    if (data.empty()) return 0;
    int maks = data[0];
    for (size_t i = 1; i < data.size(); ++i) {
        if (data[i] > maks) maks = data[i];
    }
    return maks;
}
cpp
// File: src/main.cpp
#include <iostream>
#include <vector>
#include "stat_utils.hpp"

int main() {
    std::vector<int> data = {85, 92, 78, 90, 65};

    std::cout << "--- Statistik ---\n";
    std::cout << "Total      : " << hitungTotal(data) << "\n";
    std::cout << "Rata-rata  : " << hitungRataRata(data) << "\n";
    std::cout << "Maksimum   : " << cariMaksimum(data) << "\n";

    return 0;
}

CMakeLists.txt untuk Proyek Multi-File #

cmake
cmake_minimum_required(VERSION 3.16)

project(statistik
    VERSION 1.0.0
    LANGUAGES CXX)

# Standar C++ yang dipakai seluruh target
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Default ke build type Release jika belum diset
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

# Aktifkan peringatan compiler untuk seluruh target berikut
add_compile_options(-Wall -Wextra)

# Definisikan executable beserta source file-nya
add_executable(statistik
    src/main.cpp
    src/stat_utils.cpp
)

# Tambahkan src/ sebagai include directory untuk target ini
target_include_directories(statistik PRIVATE src)

Bedah Kode CMakeLists.txt #

  1. add_executable(statistik src/main.cpp src/stat_utils.cpp): mendefinisikan target executable statistik dari dua file source. Kita bisa menambah file lain kemudian dengan mendaftarkannya di sini.
  2. target_include_directories(statistik PRIVATE src): menambah direktori src/ ke path include untuk target statistik. Dengan ini, #include "stat_utils.hpp" dapat menemukan file tanpa perlu path relatif penuh. PRIVATE berarti direktori ini hanya dipakai untuk target statistik, tidak diteruskan ke target lain yang mungkin menautkannya nanti.
  3. add_compile_options(-Wall -Wextra): menambah flag compiler untuk seluruh target yang didefinisikan setelah baris ini. Setara dengan flag yang kita pakai pada Chapter sebelumnya.
  4. if(NOT CMAKE_BUILD_TYPE) ... endif(): memastikan default build type adalah Release (optimasi diaktifkan) ketika pengguna tidak menentukan build type lain saat konfigurasi.

Melakukan Build #

Konfigurasi CMake dilakukan dalam dua tahap: configure (membuat file build) dan build (menjalankan compiler). Direkomendasikan menggunakan folder build/ terpisah agar file build tidak mencemari folder source.

bash
# Dari root proyek (folder yang berisi CMakeLists.txt)
cmake -S . -B build
cmake --build build

Penjelasan:

  • -S .: direktori source (current directory).
  • -B build: direktori output build (akan dibuat jika belum ada).
  • cmake --build build: menjalankan compiler sesuai file build yang sudah dibuat pada tahap sebelumnya.

Setelah build berhasil, jalankan binary hasilnya:

bash
./build/statistik

Output yang Dihasilkan:

text
--- Statistik ---
Total      : 410
Rata-rata  : 82
Maksimum   : 92

Output tersebut identik dengan output yang dihasilkan kompilasi langsung dengan g++ pada Chapter Proses Kompilasi. CMake mengotomatiskan tiga perintah g++ sebelumnya menjadi satu perintah cmake --build.

Variabel dan Target #

CMake menyediakan beberapa perintah untuk mengelola variabel dan target:

PerintahKegunaanContoh
set(NAMA nilai)Menetapkan variabelset(VERSION 1.0.0)
target_compile_definitions()Menambah -D definetarget_compile_definitions(statistik PRIVATE DEBUG=1)
target_compile_options()Menambah flag compilertarget_compile_options(statistik PRIVATE -O3)
target_link_libraries()Menautkan librarytarget_link_libraries(statistik PRIVATE mylib)
option(NAMA "deskripsi" ON/OFF)Pilihan konfigurasi booleanoption(BUILD_TESTS "Aktifkan testing" OFF)

Contoh Penggunaan Variabel #

cmake
option(ENABLE_DEBUG_LOG "Aktifkan log debug" OFF)

if(ENABLE_DEBUG_LOG)
    target_compile_definitions(statistik PRIVATE DEBUG_LOG=1)
    message(STATUS "Debug logging diaktifkan")
endif()

Pengguna dapat mengatur opsi saat configure:

bash
cmake -S . -B build -DENABLE_DEBUG_LOG=ON

Best Practices CMake #

Beberapa praktik yang akan dipakai sepanjang Course:

  • Pisahkan folder build/: jangan pernah configure di root source; file build akan mencemari struktur.
  • Pakai target_* alih-alih add_* global: target_include_directories, target_compile_options, dan target_link_libraries lebih terkontrol daripada include_directories dan add_compile_options.
  • Set CMAKE_CXX_STANDARD di root: satu sumber kebenaran untuk standar C++.
  • Jangan pakai file(GLOB ...) untuk source list: CMake tidak akan mendeteksi penambahan file baru secara otomatis. Daftar file secara eksplisit pada add_executable().
  • Pakai CMAKE_BUILD_TYPE: pilih Debug, Release, RelWithDebInfo, atau MinSizeRel sesuai kebutuhan.
  • Tambah install() rule jika berbagi library: untuk Course, kita jarang memerlukan ini, tetapi penting untuk proyek nyata.

Integrasi dengan Editor #

VS Code dengan ekstensi CMake Tools mengintegrasikan CMake langsung ke editor. Setelah memasang ekstensi, kita dapat:

  • Memilih preset build (Debug/Release) dari status bar.
  • Menjalankan configure dan build lewat Command Palette.
  • Men-debug target lewat tombol Run and Debug yang otomatis memilih executable.

File .vscode/settings.json dapat memetakan preset CMake ke konfigurasi editor:

json
{
    "cmake.buildDirectory": "${workspaceFolder}/build",
    "cmake.configureOnOpen": true,
    "cmake.defaultVariants": {
        "buildType": {
            "default": "Release",
            "choices": {
                "Debug":   { "short": "Debug",   "buildType": "Debug" },
                "Release": { "short": "Release", "buildType": "Release" }
            }
        }
    }
}

Ringkasan #

Pada Chapter ini kita telah mempelajari dasar CMake:

  • Peran CMake: mengotomatiskan kompilasi multi-file, lintas platform.
  • Struktur CMakeLists.txt: dari cmake_minimum_required hingga add_executable.
  • Variabel global: CMAKE_CXX_STANDARD, CMAKE_BUILD_TYPE.
  • Perintah target_*: cara modern mengelola flag dan dependency per target.
  • Best practices: hindari file(GLOB), pisahkan folder build/, pakai target_*.

CMake akan menjadi alat yang dipakai pada Chapter proyek di Section akhir Course. Untuk sebagian besar Chapter DSA, kita akan tetap memakai kompilasi langsung dengan g++ karena lebih cepat untuk file tunggal. Pada Section berikutnya, kita akan masuk ke Fondasi C++ untuk DSA: variabel, tipe data, operator, hingga template dan memory management.