Системы сборки: CMake & Package Management
Основы Master CMake, современные целевые CMake, FetchContent для зависимостей, менеджеры пакетов vcpkg и Conan и флаги компиляторов для безопасных, оптимизированных сборок.
Почему важно строить системы
Поскольку проекты C++ выходят за рамки одного файла, вам нужна система для управления компиляцией, ссылками, зависимостями и различиями платформы. Ручно работающий g++ *.cpp не масштабируется: вам нужны дополнительные сборки (рекомпилировать только измененные файлы), обнаружение библиотеки, кроссплатформенная поддержка и воспроизводимые сборки. CMake** стал де-факто стандартным генератором систем сборки для C++. Он не компилирует сам код — он генерирует собственные файлы сборки (Makefiles, файлы Ninja, проекты Visual Studio) для вашей платформы.
CMake Основы
Файл CMakeLists.txt описывает структуру вашего проекта. Современный CMake (3.12+) использует подход target-based, где все прикрепляется к целям (исполняемым файлам или библиотекам):
# 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>
)Строительство с помощью CMake
CMake использует двухэтапный процесс: configure (генерировать файлы сборки) и build (компиляция). Всегда используйте внешние сборки, чтобы сохранить дерево источника чистым:
# 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: Управление зависимостью
Модуль FetchContent от CMake загружает и создает зависимости во время настройки, что позволяет легко извлекать библиотеки без ручной настройки:
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 и Конан
Для более крупных проектов со многими зависимостями выделенные менеджеры пакетов C++ обеспечивают лучшее разрешение зависимостей и кэширование:
vcpkg (Microsoft) — интегрируется непосредственно с CMake через файл инструментальной цепи. Установите пакеты глобально или в «проявленном режиме» с файлом vcpkg.json:
{
"dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}Использование: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Conan — использует conanfile.txt или conanfile.py для объявления зависимостей. Поддерживает несколько конфигураций сборки и бинарное кэширование:
[requires]
fmt/10.2.1
spdlog/1.13.0
[generators]
CMakeDeps
CMakeToolchainОба менеджера пакетов поддерживают хранилища тысяч библиотек C++ с проверенными, предварительно построенными двоичными файлами.
Следуйте этим правилам для поддержания CMake:
1. Всегда используйте целевые команды : target_include_directories, target_compile_options, target_link_libraries — никогда include_directories или add_definitions (загрязнение окружающей среды).
2. Понять PRIVATE/PUBLIC/INTERFACE: PRIVATE = только для этой цели, PUBLIC = для этой цели и ее потребителей, INTERFACE = только для потребителей.
3. Установите стандарт C++ для каждой цели или всего проекта, никогда не используя сырые флаги, такие как -std=c++20.
4. Включить максимальные предупреждения: -Wall -Wextra -Wpedantic (GCC/Clang) или /W4 (MSVC). Рассмотрим -Werror / /WX в CI.
5. Используйте cmake --preset для воспроизводимых сборок (CMakePresets.json).
6. Никогда не используйте траектории жесткого кода — используйте find_package, FetchContent или менеджеры пакетов.
Использование file(GLOB ...) для сбора источников: file(GLOB SOURCES src/*.cpp) НЕ повторяется при добавлении или удалении файлов. Новые файлы не будут скомпилированы, пока вы не перенастроите их вручную. Всегда перечислите исходные файлы явно.
Забыв CMAKE_CXX_EXTENSIONS OFF: Без этого GCC использует -std=gnu++20 вместо -std=c++20, обеспечивая нестандартные расширения, которые нарушают переносимость.
Смешивание add_subdirectory и FetchContent: Оба вводят внешние проекты, но они по-разному взаимодействуют с кэшем сборки. Выберите один подход к зависимости.
Не устанавливайте CMAKE_EXPORT_COMPILE_COMMANDS ON Это создает compile_commands.json, который необходим для clang-tidy, clangd (LSP) и многих интеграций IDE. Всегда позволяйте.
- CMake - это стандартный генератор систем C++ - изучите его целевой API
- Использование команд
target_*(не глобального масштаба) с видимостью PRIVATE/PUBLIC/INTERFACE - FetchContent загружает и создает зависимости во время настройки - идеально подходит для небольших наборов зависимостей
- vcpkg (режим манифеста) и Conan обрабатывают большие графы зависимостей с бинарным кэшированием
- Всегда включайте предупреждения (
-Wall -Wextra -Wpedantic) и используйте сборки извне - Создание
compile_commands.jsonдля инструментов IDE и статического анализа
Тест — проверьте свои знания
(15 XP)1. Чем занимается `target_link_libraries(myapp PRIVATE mathlib)`?
2. Почему `file(GLOB SOURCES src/*.cpp)` считается плохой практикой в CMake?
3. Что такое `CMAKE_CXX_EXTENSIONS OFF`?