Chuyển đến nội dung

Hệ thống xây dựng: Quản lý gói & CMake

Nắm vững các nguyên tắc cơ bản về CMake, CMake dựa trên mục tiêu hiện đại, FetchContent cho các phần phụ thuộc, trình quản lý gói vcpkg và Conan cũng như cờ trình biên dịch cho các bản dựng an toàn, tối ưu hóa.

Tại sao xây dựng hệ thống lại quan trọng

Khi các dự án C++ phát triển vượt xa phạm vi một tệp duy nhất, bạn cần một hệ thống xây dựng để quản lý việc biên dịch, liên kết, các phần phụ thuộc và sự khác biệt về nền tảng. Chạyg++ *.cpptheo cách thủ công không mở rộng quy mô: bạn cần các bản dựng tăng dần (chỉ biên dịch lại các tệp đã thay đổi), phát hiện thư viện, hỗ trợ đa nền tảng và các bản dựng có thể tái tạo. CMake đã trở thành trình tạo hệ thống xây dựng tiêu chuẩn trên thực tế cho C++. Nó không tự biên dịch mã — nó tạo ra các tệp xây dựng gốc (Makefiles, tệp Ninja, dự án Visual Studio) cho nền tảng của bạn.

Khái niệm cơ bản về CMake

TệpCMakeLists.txtmô tả cấu trúc dự án của bạn. CMake hiện đại (3.12+) sử dụng cách tiếp cận dựa trên mục tiêu trong đó mọi thứ được gắn vào mục tiêu (tệp thực thi hoặc thư viện):

CMakeLists.txt
# CMakeLists.txt — minimum viable project
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)

# Set C++ standard for the entire project
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)  # use -std=c++20, not -std=gnu++20

# Create an executable target
add_executable(myapp
    src/main.cpp
    src/engine.cpp
    src/utils.cpp
)

# Add include directories for this target
target_include_directories(myapp PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

# Create a library target
add_library(mathlib STATIC
    src/math/vector.cpp
    src/math/matrix.cpp
)

target_include_directories(mathlib PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/include/math
)

# Link the library to the executable
target_link_libraries(myapp PRIVATE mathlib)

# Compiler warnings — essential for quality code
target_compile_options(myapp PRIVATE
    $<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic -Werror>
    $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)

Xây dựng với CMake

CMake sử dụng quy trình gồm hai bước: configure (tạo tệp bản dựng) và build (biên dịch). Luôn sử dụng các bản dựng ngoài nguồn để giữ cho cây nguồn luôn sạch sẽ:

terminal
# Configure — generate build files in a 'build' directory
cmake -B build -DCMAKE_BUILD_TYPE=Release

# Build — compile the project
cmake --build build --parallel $(nproc)

# Install (optional)
cmake --install build --prefix /usr/local

# Common build types:
# Debug          — -O0 -g (full debug symbols, no optimization)
# Release        — -O3 -DNDEBUG (max optimization, no asserts)
# RelWithDebInfo — -O2 -g -DNDEBUG (optimized with debug info)
# MinSizeRel     — -Os -DNDEBUG (optimize for size)

# Run with sanitizers (add to CMakeLists.txt or command line)
cmake -B build-asan -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"

FetchContent: Quản lý phụ thuộc

Mô-đunFetchContentcủa CMake tải xuống và xây dựng các phần phụ thuộc vào thời điểm định cấu hình, giúp bạn dễ dàng lấy thư viện mà không cần thiết lập thủ công:

CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)

include(FetchContent)

# Fetch GoogleTest
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        v1.14.0
)

# Fetch fmt (formatting library)
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        10.2.1
)

# Download and make available
FetchContent_MakeAvailable(googletest fmt)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)

# Test executable
enable_testing()
add_executable(tests tests/test_main.cpp)
target_link_libraries(tests PRIVATE GTest::gtest_main)
add_test(NAME unit_tests COMMAND tests)

vcpkg và Conan

Đối với các dự án lớn hơn có nhiều phần phụ thuộc, trình quản lý gói C++ chuyên dụng cung cấp độ phân giải và bộ đệm phụ thuộc tốt hơn:

vcpkg (Microsoft) — tích hợp trực tiếp với CMake thông qua tệp chuỗi công cụ. Cài đặt các gói trên toàn cầu hoặc ở "chế độ kê khai" bằng tệpvcpkg.json:

{
 "dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}

Use with: cmake -B bản dựng -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

Conan — uses a conanfile.txt or conanfile.py to declare dependencies. Supports multiple build configurations and binary caching:

[requires]
fmt/10.2.1
spdlog/1.13.0

[generators]
CMakeDeps
CMakeToolchain

Cả hai trình quản lý gói đều duy trì kho lưu trữ của hàng nghìn thư viện C++ với các tệp nhị phân được tạo sẵn, đã được thử nghiệm.

Thực hành tốt

Hãy tuân theo các quy tắc sau để có thể bảo trì CMake:

1. Luôn sử dụng các lệnh dựa trên mục tiêu:target_include_directories,target_compile_options,target_link_libraries— không bao giờ sử dụnginclude_directorieshoặcadd_definitions(làm ô nhiễm phạm vi toàn cầu).
2. Hiểu RIÊNG TƯ/PUBLIC/Giao diện:PRIVATE= chỉ dành cho mục tiêu này,PUBLIC= dành cho mục tiêu này và người tiêu dùng của nó,INTERFACE= chỉ dành cho người tiêu dùng.
3. Đặt tiêu chuẩn C++ cho mỗi mục tiêu hoặc trên toàn dự án, không bao giờ thông qua các cờ thô như-std=c++20.
4. Bật cảnh báo tối đa:-Wall -Wextra -Wpedantic(GCC/Clang) hoặc/W4(MSVC). Hãy xem xét-Werror//WXtrong CI.
5. Sử dụngcmake --preset cho các bản dựng có thể tái tạo (CMakePresets.json).
6. Không bao giờ mã hóa đường dẫn — sử dụngfind_package,FetchContenthoặc trình quản lý gói.

Lỗi thường gặp

Sử dụngfile(GLOB ...)để thu thập nguồn:file(GLOB SOURCES src/*.cpp)KHÔNG chạy lại khi tệp được thêm hoặc xóa. Các tệp mới sẽ không được biên dịch cho đến khi bạn định cấu hình lại theo cách thủ công. Luôn liệt kê các tệp nguồn một cách rõ ràng.

QuênCMAKE_CXX_EXTENSIONS OFF: Nếu không có tính năng này, GCC sẽ sử dụng-std=gnu++20thay vì-std=c++20, cho phép các tiện ích mở rộng không chuẩn làm hỏng tính di động.

Kết hợpadd_subdirectoryvàFetchContent: Cả hai đều mang đến các dự án bên ngoài nhưng chúng tương tác khác nhau với bộ nhớ đệm bản dựng. Chọn một cách tiếp cận cho mỗi phụ thuộc.

Không đặtCMAKE_EXPORT_COMPILE_COMMANDS ON: Điều này tạo racompile_commands.json, cần thiết cho clang-tidy, clangd (LSP) và nhiều tích hợp IDE. Luôn kích hoạt nó.

Điểm chính
  • CMake là trình tạo hệ thống xây dựng C++ tiêu chuẩn - tìm hiểu API dựa trên mục tiêu của nó
  • Sử dụng các lệnhtarget_*(không phải phạm vi toàn cầu) với khả năng hiển thị RIÊNG TƯ/PUBLIC/Giao diện
  • FetchContent tải xuống và xây dựng các phần phụ thuộc vào thời điểm định cấu hình — lý tưởng cho các nhóm phụ thuộc nhỏ
  • vcpkg (chế độ hiển thị) và Conan xử lý các biểu đồ phụ thuộc lớn hơn bằng bộ nhớ đệm nhị phân
  • Luôn bật cảnh báo (-Wall -Wextra -Wpedantic) và sử dụng các bản dựng ngoài nguồn
  • Tạocompile_commands.jsoncho IDE và công cụ phân tích tĩnh

Câu đố — Kiểm tra kiến thức

(15 XP)

1. `target_link_libraries(myapp PRIVATE mathlib)`làm gì?

2. Tại sao`file(GLOB SOURCES src/*.cpp)`bị coi là phương pháp không tốt trong CMake?

3. Mục đích của`CMAKE_CXX_EXTENSIONS OFF`là gì?