Bài 4 · Nâng cao · 26 phút· Cập nhật 11/06/2026
CMake & Meson
Biên soạn bởi Nguyễn Anh Tuấn
Học CMake (và Meson) cho dự án C: CMakeLists.txt, build out-of-source, PUBLIC/PRIVATE/INTERFACE, find_package - chuẩn build system khi đi làm.
Makefile viết tay gắn chặt với một hệ (đường dẫn, cờ, công cụ). Dự án thật cần build trên Linux, macOS, Windows, với Make hay Ninja hay Visual Studio. CMake để bạn mô tả build một lần, rồi sinh ra build files cho từng nền/generator.
- ▸CMake là build system GENERATOR, không phải build system trực tiếp.
- ▸Một CMakeLists.txt → Makefiles / Ninja / Visual Studio / Xcode.
- ▸Là chuẩn de-facto của C/C++ - kỹ năng gặp nhiều nhất khi đi làm.
Mỗi dự án có một CMakeLists.txt ở gốc. Tối thiểu cần phiên bản, tên dự án, và một target:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(myapp C)
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
# tao thu vien tu util.c, roi chuong trinh app
add_library(util util.c)
add_executable(app main.c)
target_link_libraries(app PRIVATE util) - ▸add_executable(ten nguon...) - tạo một chương trình.
- ▸add_library(ten nguon...) - tạo một thư viện (tĩnh mặc định).
- ▸target_link_libraries(A PRIVATE B) - A dùng B.
CMake làm việc theo hai pha: cấu hình/sinh (đọc CMakeLists, sinh build files vào build/) rồi build (gọi generator bên dưới). Mọi sản phẩm nằm trong build/ - cây nguồn luôn sạch:
cau-hinh-va-build
cmake -S . -B build # PHA 1: cau hinh + sinh build files vao build/
cmake --build build # PHA 2: bien dich (goi Make/Ninja ben duoi)
./build/app
# Chon generator khac (vd Ninja, nhanh hon):
cmake -S . -B build -G Ninja
cmake --build build
# Cau hinh kieu Release (toi uu):
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release Nhớ
Điểm khó nhất - và là dấu hiệu của “CMake hiện đại”: usage requirement của một target (thư mục include, define) mang một scope quyết định nó có lan tới ai link target đó hay không. Chỉnh scope và xem nó lan truyền:
- 📁 include/
- 📁 src/
- 🔧 UTIL_INTERNAL
- 📁 include/
PRIVATE = chỉ util dùng · INTERFACE = chỉ ai LINK util dùng · PUBLIC = cả hai. Thử đổi include/ sang PRIVATE → app mất quyền thấy header của util (lỗi biên dịch ở app). Đây là lý do thư viện đặt header công khai là PUBLIC.
target-based.cmake
add_library(util util.c)
target_include_directories(util
PUBLIC include # ai link util CUNG thay include/
PRIVATE src) # chi util thay src/
target_compile_definitions(util PRIVATE UTIL_INTERNAL)
add_executable(app main.c)
target_link_libraries(app PRIVATE util) # app thua huong include/ PUBLIC cua util Dùng thư viện ngoài qua find_package; nó tìm thư viện đã cài và cho target để link khả chuyển:
find_package.cmake
find_package(Threads REQUIRED)
target_link_libraries(app PRIVATE Threads::Threads) # dung pthreads, kha chuyen Meson là lựa chọn thay thế hiện đại: cú pháp gọn hơn, chạy trên Ninja:
meson.build + lệnh
# meson.build
project('myapp', 'c', default_options: ['c_std=c17'])
util = static_library('util', 'util.c')
executable('app', 'main.c', link_with: util)
# lenh:
meson setup build
meson compile -C build # (hoac: ninja -C build) Chuyển dự án nhiều file sang CMake: viết CMakeLists.txt, build out-of-source, thử generator Ninja, và quan sát PUBLIC vs PRIVATE include.
$ unzip 04-cmake-meson.zip && cd 04-cmake-meson # tai zip bang nut o tren $ cmake -S . -B build $ cmake --build build $ ./build/app $ cmake -S . -B build-ninja -G Ninja && cmake --build build-ninja
Xem từng file (6) ↡
Câu hỏi thường gặp
Không hẳn - nó là build system GENERATOR. CMake đọc CMakeLists.txt rồi SINH RA build files cho một generator cụ thể: Unix Makefiles, Ninja, Visual Studio, Xcode… Việc biên dịch thật do generator đó chạy. Bạn mô tả “muốn gì” một lần, CMake lo phần khả chuyển.
Mọi sản phẩm (.o, binary, file cache) nằm trong thư mục build/ riêng, tách khỏi mã nguồn. Cây nguồn luôn sạch (dễ git), build/ vứt đi build lại được, và bạn có thể giữ nhiều cấu hình (build-debug/, build-release/) song song.
Đó là phạm vi của một usage requirement (include dir, compile define, link). PRIVATE: chỉ chính target này dùng. INTERFACE: chỉ ai LINK target này dùng (bản thân target thì không). PUBLIC: cả hai. Nhờ chúng, header/define tự lan truyền đúng tới nơi cần - không phải khai báo lại thủ công.
Cùng kết quả, khác cú pháp. Kiểu mới (CMake ≥ 3.13): cmake -S . -B build nói rõ source dir (-S) và build dir (-B). Kiểu cũ: mkdir build && cd build && cmake .. Khuyến nghị kiểu -S/-B vì rõ ràng và viết script dễ.
Tìm một thư viện đã cài trên máy (header + file lib) và cung cấp các target/biến để dùng nó một cách khả chuyển. Vd find_package(Threads REQUIRED) rồi target_link_libraries(app PRIVATE Threads::Threads) để dùng pthreads mà không cần biết cờ -pthread của từng hệ.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Mô tả nào đúng nhất về vai trò của CMake?
- 1
Scope của header
Trong công cụ ở Bước 4, đặt
include/làPUBLICrồiPRIVATE.appcó thấyinclude/không trong mỗi trường hợp? Vì sao thư viện nên để header công khai làPUBLIC?Hoàn thành khi:
PUBLIC→appthấyinclude/(link được).PRIVATE→appKHÔNG thấy → lỗi biên dịch ởapp. Header dùng chung phảiPUBLICđể consumer include được. - 2
CMakeLists tối thiểu
Viết
CMakeLists.txtbuild chương trìnhapptừmain.cvàutil.c.Hoàn thành khi:
cmake_minimum_required(VERSION 3.16); project(myapp C); add_executable(app main.c util.c) - 3
Hai pha
Lệnh nào CẤU HÌNH (sinh build files), lệnh nào BUILD?
Hoàn thành khi:
cmake -S . -B build= cấu hình/sinh.cmake --build build= build (gọi Make/Ninja bên dưới). - 4
build/ ở đâu
Vì sao
build/nên nằm ngoài cây nguồn (hoặc trong.gitignore)?Hoàn thành khi: Để cây nguồn sạch (không commit sản phẩm build);
build/vứt đi tái tạo được; tránh lẫn lộn nhiều cấu hình. - 5
PRIVATE cho exe
target_link_libraries(app PRIVATE util)- vì saoPRIVATEở đây là đủ?Hoàn thành khi:
applà exe cuối, không ai LINKapp, nên không cần lan truyền (PUBLIC/INTERFACE).PRIVATElà đúng & gọn nhất. - 6
find_package Threads
Đoạn
find_package(Threads REQUIRED)+ linkThreads::Threadsdùng để làm gì?Hoàn thành khi: Dùng pthreads (đa luồng) một cách khả chuyển - CMake tự thêm cờ đúng (
-pthread…) cho từng hệ.