CMake adalah generator build system lintas platform—alih-alih menulis Makefile manual untuk Linux dan .vcxproj untuk Windows, kita menulis satu CMakeLists.txt yang dapat menghasilkan Makefile, Ninja, atau Visual Studio project. CMake juga mengelola dependency, library search, dan platform check secara otomatis. Bab ini menyusun perintah CMake yang paling sering kita pakai untuk membangun program C.

Perintah Dasar #

PerintahKegunaan
cmake_minimum_required(VERSION 3.16)Wajib di awal—minimum versi CMake
project(name C)Deklarasi nama proyek dan bahasa
set(VAR value)Set variabel
message(STATUS "text")Cetak pesan status
option(NAME "desc" ON)Opsi konfigurasi ON/OFF
if(COND) ... elseif() ... else() ... endif()Kondisional
foreach(var IN LISTS list1) ... endforeach()Loop
while(COND) ... endwhile()Loop while
include(file)Sertakan file CMake lain
add_subdirectory(dir)Tambah direktori subproyek
find_package(Name)Cari library eksternal

Target dan Build #

PerintahKegunaan
add_executable(name src1.c src2.c)Definisi executable
add_library(name STATIC src...)Library statik .a
add_library(name SHARED src...)Library shared .so/.dll
add_library(name MODULE src...)Plugin yang di-load saat runtime
add_library(name INTERFACE)Header-only library
target_sources(name PRIVATE src...)Tambah source ke target
target_link_libraries(name PUBLIC lib)Link library ke target
target_include_directories(name PUBLIC dir)Tambah include path
target_compile_options(name PRIVATE -Wall)Tambah flag compiler
target_compile_definitions(name PRIVATE NAME=val)Definisikan macro -D
target_link_directories(name PUBLIC dir)Tambah path library
target_link_options(name PRIVATE -flto)Tambah flag linker

Visibility Keyword #

Perintah target_* menerima visibility keyword PRIVATE, PUBLIC, atau INTERFACE. Perbedaannya:

KeywordTarget sendiriDependents
PRIVATEYaTidak
PUBLICYaYa
INTERFACETidakYa

Sebagai contoh, jika library libfoo memakai pthread secara internal tetapi tidak mengeksposnya di header, gunakan PRIVATE. Jika libfoo header memakai tipe dari bar.h, gunakan PUBLIC agar dependents juga mendapatkan include bar.h.

Variabel Bawaan #

VariabelIsi
CMAKE_C_STANDARDStandar C yang dipakai (11, 17, 23)
CMAKE_C_STANDARD_REQUIREDON = standar wajib tersedia
CMAKE_C_EXTENSIONSON = pakai ekstensi (gnu11), OFF = iso (c11)
CMAKE_BUILD_TYPEDebug, Release, RelWithDebInfo, MinSizeRel
CMAKE_C_COMPILERPath compiler C
CMAKE_SOURCE_DIRDirektori source root
CMAKE_BINARY_DIRDirektori build
CMAKE_CURRENT_SOURCE_DIRDirektori source saat ini
CMAKE_INSTALL_PREFIXPrefix install (/usr/local default)
PROJECT_NAMENama proyek dari project()

Variabel Build Type #

Setiap CMAKE_BUILD_TYPE mengaktifkan flag otomatis:

Build TypeFlag tipikal
Debug-O0 -g
Release-O3 -DNDEBUG
RelWithDebInfo-O2 -g -DNDEBUG
MinSizeRel-Os -DNDEBUG

Program Demo #

Program berikut sama dengan demo Makefile—main.c memakai util.c dan util.h. Kode sumbernya valid dan teruji.

c
/* File: main.c - Demo CMake */
#include <stdio.h>
#include "util.h"

int main(void) {
    cetak_salam("Budi");
    printf("2 + 3 = %d\n", tambah(2, 3));
    return 0;
}
c
/* File: util.h */
#ifndef UTIL_H
#define UTIL_H
void cetak_salam(const char *nama);
int tambah(int a, int b);
#endif
c
/* File: util.c */
#include <stdio.h>
#include "util.h"
void cetak_salam(const char *nama) {
    printf("Halo, %s!\n", nama);
}
int tambah(int a, int b) { return a + b; }

CMakeLists.txt Minimal #

Berikut CMakeLists.txt minimal yang meng-compile program demo menjadi executable cmdemo:

cmake
cmake_minimum_required(VERSION 3.16)
project(cmdemo C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

add_executable(cmdemo main.c util.c)
target_compile_options(cmdemo PRIVATE -Wall -Wextra -g)

install(TARGETS cmdemo RUNTIME DESTINATION bin)

Langkah Build Standar #

CMake mengikuti pola “out-of-source build”—kita menjalankan CMake di direktori build terpisah sehingga source tree tetap bersih.

bash
mkdir build && cd build
cmake ..
make
./cmdemo

Output yang Dihasilkan (langkah cmake .. menghasilkan ringkasan konfigurasi, make menjalankan compiler, lalu program dijalankan):

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/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cc/test/cmdemo/build
[ 33%] Building C object CMakeFiles/cmdemo.dir/main.c.o
[ 66%] Building C object CMakeFiles/cmdemo.dir/util.c.o
[100%] Linking C executable cmdemo
[100%] Built target cmdemo
Halo, Budi!
2 + 3 = 5

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

  1. Baris 1 (cmake_minimum_required(VERSION 3.16)): Wajib di awal file. CMake versi 3.16 (rilis 2019) cukup untuk fitur modern seperti generator expression dan target_* commands. Versi yang lebih tua mungkin tidak mendukung sintaks terbaru.
  2. Baris 2 (project(cmdemo C)): Mendeklarasikan nama proyek dan bahasa. Parameter C memberi tahu CMake untuk hanya mengaktifkan compiler C, bukan C++. Tanpa parameter bahasa, CMake default mendeteksi keduanya.
  3. Baris 4-6 (set(CMAKE_C_STANDARD 11) dst.): Mengatur standar C secara global. CMAKE_C_STANDARD_REQUIRED ON memastikan compiler benar-benar mendukung standar tersebut—jika tidak, build gagal. CMAKE_C_EXTENSIONS OFF memakai mode ISO (mis. c11 alih-alih gnu11).
  4. Baris 8 (add_executable(cmdemo main.c util.c)): Membuat target executable bernama cmdemo dari dua file source. Nama target sekaligus menjadi nama file executable yang dihasilkan.
  5. Baris 9 (target_compile_options(cmdemo PRIVATE -Wall -Wextra -g)): Hanya target cmdemo yang mendapat flag ini. Jika ada target lain (mis. unit test), flag ini tidak ikut—menggunakan PUBLIC akan menyebar ke dependents.
  6. Baris 11 (install(TARGETS cmdemo RUNTIME DESTINATION bin)): Mendeklarasikan target instalasi. Saat kita menjalankan make install, executable akan dipasang ke ${CMAKE_INSTALL_PREFIX}/bin.

Find Package dan Library Eksternal #

Untuk library seperti libm, zlib, atau pthread, gunakan find_package atau find_library:

cmake
find_library(MATH_LIBRARY m)
target_link_libraries(cmdemo PRIVATE ${MATH_LIBRARY})

# Atau, untuk pthread:
find_package(Threads REQUIRED)
target_link_libraries(cmdemo PRIVATE Threads::Threads)

# Untuk zlib:
find_package(ZLIB REQUIRED)
target_link_libraries(cmdemo PRIVATE ZLIB::ZLIB)

Multi-Configuration Generator #

Beberapa generator (Visual Studio, Xcode, Ninja Multi-Config) mendukung multi-configuration—kita bisa memilih build type saat build, bukan saat configure.

bash
cmake -G "Ninja Multi-Config" ..
cmake --build . --config Release
cmake --build . --config Debug

Untuk generator single-config (Unix Makefiles), set CMAKE_BUILD_TYPE saat configure:

bash
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_BUILD_TYPE=Debug ..