Hướng dẫn toàn diện về CMake và CMakeLists.txt

CMake là một hệ thống xây dựng mã nguồn mở, đa nền tảng, cho phép mô tả quy trình biên dịch, kiểm thử và đóng gói phần mềm bằng ngôn ngữ đơn giản. Từ đó, CMake tạo ra các tập tin Makefile hoặc project file phù hợp với hệ điều hành và trình biên dịch mục tiêu.

Các khái niệm cốt lõi

Trình biên dịch (gcc): Chuyển đổi mã nguồn thành tệp thực thi hoặc thư viện.
Make: Quản lý quá trình xây dựng (build) nhiều tệp nguồn, dựa trên định nghĩa trong Makefile.
CMake: Tự động sinh Makefile từ tập tin cấu hình CMakeLists.txt, giúp đơn giản hóa việc viết Makefile thủ công cho dự án lớn.

Quy trình sử dụng CMake cơ bản

  1. Tạo hệ thống build: Dùng lệnh cmake để sinh Makefile.
    • -S <source_dir>: Thư mục chứa CMakeLists.txt.
    • -B <build_dir>: Thư mục chứa kết quả build trung gian.
    • -D <var>=<value>: Gán giá trị cho biến CMake.
    Ví dụ:
    cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
  2. Thực thi build: Dùng cmake --build hoặc trực tiếp gọi make trong thư mục build.
    cmake --build build
    # hoặc
    cd build && make

Ví dụ minh họa: Hello World

Cấu trúc dự án:

project/
├── CMakeLists.txt
└── src/
    └── main.c

Nội dung src/main.c:

#include <stdio.h>
int main() {
    printf("Hello from CMake!\n");
    return 0;
}

Nội dung CMakeLists.txt:

cmake_minimum_required(VERSION 3.12)
project(hello_cmake VERSION 1.0 LANGUAGES C)
add_executable(app src/main.c)

Build và chạy:

cmake -B build
cmake --build build
./build/app

Cú pháp CMake cơ bản

Biến và chú thích

  • Chú thích dòng: # comment
  • Chú thích khối: #[[ ... ]]
  • Đặt biến: set(VAR_NAME value)
  • Danh sách: set(LIST item1 item2) hoặc set(LIST "item1;item2")
  • Truy xuất biến: ${VAR_NAME}

Câu lệnh điều kiện

if(NOT VAR OR FLAG STREQUAL "ON")
    message(STATUS "Condition met")
elseif(DEFINED OTHER_VAR)
    # ...
endif()

Các lệnh phổ biến

  • message(STATUS "Info"): In thông báo.
  • list(APPEND MY_LIST new_item): Thao tác danh sách.
  • file(GLOB_RECURSE SOURCES "src/**/*.c"): Liệt kê tệp tin.
  • configure_file(template.in output.h): Sinh tệp cấu hình.
  • execute_process(COMMAND git rev-parse HEAD OUTPUT_VARIABLE COMMIT): Gọi lệnh hệ thống.

Xây dựng dự án đầy đủ với CMakeLists.txt

Cấu hình cơ bản

cmake_minimum_required(VERSION 3.12)
project(MyProject VERSION 2.1.0 LANGUAGES CXX)

# Chuẩn ngôn ngữ
set(CMAKE_CXX_STANDARD 17)

# Tùy chọn biên dịch
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

# Thư mục include
include_directories(include)

Tạo mục tiêu (targets)

# Thư viện tĩnh
add_library(core STATIC src/core.cpp)

# Thư viện động
add_library(utils SHARED src/utils.cpp)

# Tệp thực thi
add_executable(main_app src/main.cpp)
target_link_libraries(main_app core utils)

Cài đặt và đóng gói

# Cài đặt
install(TARGETS main_app core
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)

# Đóng gói
include(CPack)
set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})

Kiểm thử với Google Test

enable_testing()
add_subdirectory(third_party/googletest)

add_executable(test_core test/test_core.cpp)
target_link_libraries(test_core core gtest_main)
add_test(NAME core_tests COMMAND test_core)

Quản lý mô-đun và phụ thuộc

Sử dụng add_subdirectory() để chia nhỏ dự án:

# Thư mục gốc/CMakeLists.txt
add_subdirectory(src/math)
add_executable(app src/main.cpp)
target_link_libraries(app math_lib)
# src/math/CMakeLists.txt
add_library(math_lib STATIC add.cpp subtract.cpp)

Biểu thức sinh (Generator Expressions)

Cho phép cấu hình linh hoạt theo điều kiện:

# Tùy chọn biên dịch theo chế độ
add_compile_options("$<$<CONFIG:Debug>:-Wall;-Wextra>")

# Đường dẫn thư viện theo nền tảng
target_link_libraries(app "$<IF:$<PLATFORM_ID:Darwin>,lib_mac.a,lib_linux.a>")

Thẻ: cmake C++ build-system GoogleTest static-library

Đăng vào ngày 14 tháng 6 lúc 20:56