Giao diện
🚀 Modern CMake Fundamentals Core
CMake là industry standard để build C++ projects. Nhưng có 2 cách viết CMake: Legacy (sai) và Modern (đúng). Tài liệu này dạy cách Modern.
Old CMake vs Modern CMake
❌ LEGACY CMAKE (PROHIBITED)
cmake
# SAI - Đừng bao giờ viết như này
cmake_minimum_required(VERSION 2.8) # Quá cũ
project(MyApp)
include_directories(${CMAKE_SOURCE_DIR}/include)
add_definitions(-DDEBUG_MODE)
link_directories(/usr/local/lib)
add_executable(app main.cpp utils.cpp)
link_libraries(pthread boost_system)✅ MODERN CMAKE (ENFORCED)
cmake
# ĐÚNG - Target-based approach
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)
add_executable(app main.cpp utils.cpp)
target_include_directories(app PRIVATE ${CMAKE_SOURCE_DIR}/include)
target_compile_definitions(app PRIVATE DEBUG_MODE)
target_link_libraries(app PRIVATE pthread Boost::system)
target_compile_features(app PRIVATE cxx_std_20)Sự khác biệt cốt lõi
┌─────────────────────────────────────────────────────────────────────────┐
│ LEGACY vs MODERN CMAKE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ LEGACY (Directory-scoped) MODERN (Target-scoped) │
│ ───────────────────────── ───────────────────── │
│ include_directories(...) → target_include_directories() │
│ add_definitions(...) → target_compile_definitions() │
│ link_libraries(...) → target_link_libraries() │
│ set(CMAKE_CXX_STANDARD 20) → target_compile_features() │
│ │
│ ❌ Ảnh hưởng TẤT CẢ targets ✅ Chỉ ảnh hưởng target cụ thể │
│ ❌ Không encapsulation ✅ Clean dependency graph │
│ ❌ Order-dependent ✅ Declarative, không phụ thuộc│
│ (phải viết trước add_executable) thứ tự │
│ │
└─────────────────────────────────────────────────────────────────────────┘CMakeLists.txt Anatomy
Một CMakeLists.txt chuẩn có cấu trúc:
cmake
# ============================================
# 1. MINIMUM VERSION & PROJECT
# ============================================
cmake_minimum_required(VERSION 3.20)
project(HPN_Engine
VERSION 1.0.0
DESCRIPTION "High Performance Network Engine"
LANGUAGES CXX
)
# ============================================
# 2. GLOBAL SETTINGS (trước khi define targets)
# ============================================
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Cho clangd/LSP
set(CMAKE_CXX_EXTENSIONS OFF) # -std=c++20 thay vì -std=gnu++20
# ============================================
# 3. DEPENDENCIES (imported targets)
# ============================================
find_package(Threads REQUIRED)
find_package(fmt REQUIRED)
# ============================================
# 4. DEFINE TARGETS
# ============================================
add_library(hpn_core STATIC
src/core/engine.cpp
src/core/network.cpp
src/core/utils.cpp
)
add_executable(hpn_app
src/main.cpp
)
# ============================================
# 5. TARGET PROPERTIES
# ============================================
target_include_directories(hpn_core PUBLIC include)
target_compile_features(hpn_core PUBLIC cxx_std_20)
target_link_libraries(hpn_core
PUBLIC Threads::Threads
PRIVATE fmt::fmt
)
target_link_libraries(hpn_app PRIVATE hpn_core)add_executable vs add_library
add_executable — Tạo binary chạy được
cmake
add_executable(my_app
src/main.cpp
src/app.cpp
)add_library — Tạo library
cmake
# Static library (.a / .lib)
add_library(my_lib STATIC
src/lib.cpp
)
# Shared/Dynamic library (.so / .dll)
add_library(my_lib SHARED
src/lib.cpp
)
# Header-only library (no compilation)
add_library(my_header_lib INTERFACE)
target_include_directories(my_header_lib INTERFACE include)target_include_directories — The Right Way
cmake
add_library(network STATIC src/network.cpp)
# PUBLIC: Cả library VÀ consumers cần include path này
target_include_directories(network PUBLIC include/network)
# PRIVATE: Chỉ library cần (implementation detail)
target_include_directories(network PRIVATE src/internal)
# INTERFACE: Chỉ consumers cần (header-only components)
target_include_directories(network INTERFACE include/network/api)target_link_libraries — Dependency Chain
cmake
# Library dependencies
add_library(utils STATIC src/utils.cpp)
add_library(network STATIC src/network.cpp)
add_library(engine STATIC src/engine.cpp)
# network phụ thuộc utils
target_link_libraries(network PRIVATE utils)
# engine phụ thuộc network (và transitively, utils)
target_link_libraries(engine PUBLIC network)
# app chỉ cần link engine, nhận network + utils tự động
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE engine)┌─────────────────────────────────────────────┐
│ DEPENDENCY GRAPH │
├─────────────────────────────────────────────┤
│ │
│ app ─────────► engine │
│ │ │
│ ▼ │
│ network │
│ │ │
│ ▼ │
│ utils │
│ │
│ CMake tự động resolve transitive deps! │
│ │
└─────────────────────────────────────────────┘target_compile_features — C++ Standard
cmake
# Cách ĐÚNG: Sử dụng compile features
target_compile_features(my_lib PUBLIC cxx_std_20)
# Cách SAI: Set global variable
# set(CMAKE_CXX_STANDARD 20) # Ảnh hưởng MỌI targets💡 PRO TIP
cxx_std_20 đảm bảo compiler hỗ trợ C++20. Nếu không, CMake báo lỗi rõ ràng thay vì để compiler fail với message khó hiểu.
target_compile_definitions — Preprocessor Macros
cmake
# Định nghĩa macros
target_compile_definitions(my_lib
PUBLIC
HPN_VERSION="1.0.0"
PRIVATE
DEBUG_INTERNAL
$<$<CONFIG:Debug>:DEBUG_MODE> # Chỉ Debug build
$<$<CONFIG:Release>:NDEBUG> # Chỉ Release build
)Project Structure chuẩn
project/
├── CMakeLists.txt # Root CMake
├── cmake/ # Custom CMake modules
│ ├── CompilerWarnings.cmake
│ └── Sanitizers.cmake
├── include/ # PUBLIC headers
│ └── project/
│ ├── engine.hpp
│ └── network.hpp
├── src/ # Source files
│ ├── CMakeLists.txt # Subdirectory CMake
│ ├── engine.cpp
│ ├── network.cpp
│ └── internal/ # PRIVATE headers
│ └── impl.hpp
├── tests/ # Unit tests
│ ├── CMakeLists.txt
│ └── test_engine.cpp
├── apps/ # Executables
│ ├── CMakeLists.txt
│ └── main.cpp
└── build/ # OUT-OF-SOURCE (gitignored)Root CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.20)
project(HPN_Project VERSION 1.0.0 LANGUAGES CXX)
# Options
option(BUILD_TESTS "Build unit tests" ON)
option(ENABLE_SANITIZERS "Enable ASan/UBSan" OFF)
# Include custom modules
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
include(CompilerWarnings)
include(Sanitizers)
# Add subdirectories
add_subdirectory(src)
add_subdirectory(apps)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()src/CMakeLists.txt
cmake
add_library(hpn_core STATIC
engine.cpp
network.cpp
)
target_include_directories(hpn_core
PUBLIC
${CMAKE_SOURCE_DIR}/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/internal
)
target_compile_features(hpn_core PUBLIC cxx_std_20)Out-of-Source Build — The Only Way
powershell
# Từ project root
cmake -B build # Configure vào thư mục build/
cmake --build build # Compile
cmake --build build --target test # Run tests (nếu có)❌ NEVER DO THIS
powershell
cd project
cmake . # In-source build = pollution
make
# Giờ project/ chứa đầy CMakeCache, CMakeFiles, Makefile...✅ ALWAYS DO THIS
powershell
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel 8Generator Expression — Conditional Logic
cmake
target_compile_definitions(my_lib PRIVATE
# Debug-only
$<$<CONFIG:Debug>:DEBUG_ENABLED>
# Platform-specific
$<$<PLATFORM_ID:Windows>:WIN32_LEAN_AND_MEAN>
$<$<PLATFORM_ID:Linux>:_GNU_SOURCE>
# Compiler-specific
$<$<CXX_COMPILER_ID:GNU>:GCC_SPECIFIC>
$<$<CXX_COMPILER_ID:Clang>:CLANG_SPECIFIC>
)
target_compile_options(my_lib PRIVATE
# Warnings per compiler
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)CMake 3.x: find_package
cmake
# Tìm package đã install trong system
find_package(fmt REQUIRED)
find_package(Boost 1.74 REQUIRED COMPONENTS system filesystem)
find_package(OpenSSL REQUIRED)
# Sử dụng imported targets
target_link_libraries(my_app PRIVATE
fmt::fmt
Boost::system
Boost::filesystem
OpenSSL::SSL
OpenSSL::Crypto
)📦 DEPENDENCY NOTE
find_package yêu cầu library đã được install. Để quản lý installation, sử dụng Conan hoặc Vcpkg (xem bài Package Management).
Quick Reference
| Task | Command |
|---|---|
| Configure | cmake -B build |
| Configure Release | cmake -B build -DCMAKE_BUILD_TYPE=Release |
| Build | cmake --build build |
| Build parallel | cmake --build build -j8 |
| Run tests | ctest --test-dir build |
| Install | cmake --install build --prefix /usr/local |
| Clean | cmake --build build --target clean |
| Full rebuild | rm -rf build && cmake -B build && cmake --build build |