Введение: почему монорепозиторий для C++?
В современной C++ разработке, особенно в проектах среднего и крупного размера, организация кода становится критически важной задачей. Монорепозиторий (monorepo) — это подход, при котором код всех компонентов, библиотек и сервисов хранится в одном репозитории. В отличие от мультирепозиториев, монорепозиторий предлагает единую точку входа, упрощает рефакторинг и управление зависимостями. На практике это означает, что вы можете изменять API библиотеки и сразу видеть, как это повлияет на все зависимые модули.
В этой статье мы разберем практические аспекты организации C++ кода в монорепозитории: от структуры папок до настройки CI/CD. Вы узнаете, как избежать типичных ошибок и сделать сборку быстрой и предсказуемой.
1. Структура монорепозитория для C++
Правильная структура папок — основа успешного монорепозитория. Рассмотрим рекомендуемую иерархию для C++ проектов.
Базовая иерархия каталогов
my_project/
├── CMakeLists.txt # Корневой файл сборки
├── cmake/ # Пользовательские CMake-модули
│ └── FindMyLib.cmake
├── libs/ # Внутренние библиотеки
│ ├── core/
│ │ ├── CMakeLists.txt
│ │ ├── include/
│ │ │ └── core/
│ │ │ ├── utils.h
│ │ │ └── types.h
│ │ └── src/
│ │ ├── utils.cpp
│ │ └── types.cpp
│ └── network/
│ ├── CMakeLists.txt
│ ├── include/
│ │ └── network/
│ │ └── http_client.h
│ └── src/
│ └── http_client.cpp
├── apps/ # Исполняемые приложения
│ ├── server/
│ │ ├── CMakeLists.txt
│ │ └── src/
│ │ └── main.cpp
│ └── cli/
│ ├── CMakeLists.txt
│ └── src/
│ └── main.cpp
├── tests/ # Интеграционные тесты
│ ├── CMakeLists.txt
│ └── test_integration.cpp
├── third_party/ # Внешние зависимости (vendoring)
│ ├── fmt/
│ └── spdlog/
├── .github/ # CI/CD конфигурация
│ └── workflows/
│ └── build.yml
└── README.md
Ключевые принципы:
- libs/ — все внутренние библиотеки. Каждая библиотека имеет свою папку с
CMakeLists.txt. - apps/ — исполняемые файлы. Они зависят от библиотек из
libs/. - third_party/ — вендорные зависимости (git submodules или скопированные исходники).
- tests/ — интеграционные тесты, которые проверяют взаимодействие нескольких библиотек.
Пример CMakeLists.txt для библиотеки
# libs/core/CMakeLists.txt
add_library(core
src/utils.cpp
src/types.cpp
)
target_include_directories(core
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(core
PUBLIC
fmt::fmt
spdlog::spdlog
)
Важно: Используйте PUBLIC и PRIVATE для правильного проброса зависимостей. Это ключевой момент — если библиотека A использует библиотеку B только в своих исходниках, сделайте B PRIVATE.
2. Управление зависимостями в монорепозитории
Одна из главных проблем монорепозиториев — «ад зависимостей». В C++ она решается через CMake target-oriented design (проектирование через цели).
Правило: одна цель — одна библиотека
Каждая библиотека должна экспортировать ровно один CMake target. Это упрощает линковку и предотвращает циклические зависимости.
# Корневой CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Подключаем все библиотеки
add_subdirectory(libs/core)
add_subdirectory(libs/network)
# Подключаем приложения
add_subdirectory(apps/server)
add_subdirectory(apps/cli)
# Тесты
enable_testing()
add_subdirectory(tests)
Работа с внешними зависимостями
Для внешних библиотек используйте FetchContent или vendoring. Пример с FetchContent:
# cmake/fetch_dependencies.cmake
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.1.0
)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.12.0
)
FetchContent_MakeAvailable(fmt spdlog)
Совет: Для стабильности лучше зафиксировать версии через GIT_TAG. Это гарантирует, что сборка не сломается из-за изменений в upstream.
3. CI/CD для C++ монорепозитория
Автоматизация сборки и тестирования — обязательный элемент. Рассмотрим настройку GitHub Actions.
Пример workflow: сборка и тестирование
# .github/workflows/build.yml
name: Build and Test
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake g++
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --parallel
- name: Run tests
run: |
cd build
ctest --output-on-failure
Инкрементальная сборка с кэшированием
Для ускорения сборки используйте ccache
Блоги
Рекомендаций по книгам
Читайте также