Монорепозиторий с C++: организация кода и лучшие практики

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем нужен монорепозиторий в C++?

Разработка крупных C++ проектов часто сталкивается с проблемой организации кода. Команды выбирают между монорепозиторием (один репозиторий для всего кода) и мультирепозиторием (отдельные репозитории для каждой библиотеки). В контексте C++ монорепозиторий предлагает ряд преимуществ: упрощение рефакторинга, единая версия зависимостей и централизованная система сборки.

Эта статья проведет вас по ключевым аспектам организации монорепозитория для C++: от структуры папок до настройки CMake и управления зависимостями. Вы узнаете, как сделать проект масштабируемым и удобным для команды.

1. Структура монорепозитория для C++

Правильная структура — основа успешного монорепозитория. Рекомендуется использовать иерархию, разделяющую библиотеки, приложения и внешние зависимости.

Пример базовой структуры

my_monorepo/
├── CMakeLists.txt          # Корневой CMake-файл
├── cmake/                  # Вспомогательные CMake-модули
├── libs/                   # Библиотеки проекта
│   ├── core/               # Ядро (логика, утилиты)
│   │   ├── include/
│   │   │   └── core/
│   │   ├── src/
│   │   └── CMakeLists.txt
│   ├── network/            # Сетевые модули
│   │   ├── include/
│   │   ├── src/
│   │   └── CMakeLists.txt
│   └── utils/              # Вспомогательные утилиты
│       ├── include/
│       ├── src/
│       └── CMakeLists.txt
├── apps/                   # Исполняемые приложения
│   ├── server/
│   │   ├── src/
│   │   └── CMakeLists.txt
│   └── client/
│       ├── src/
│       └── CMakeLists.txt
├── tests/                  # Интеграционные тесты
├── third_party/            # Внешние зависимости (git submodules)
│   ├── fmt/
│   └── spdlog/
└── scripts/                # Скрипты для CI/CD и разработки

Ключевые принципы

  • libs/: Каждая библиотека — самодостаточный модуль со своим CMakeLists.txt.
  • apps/: Приложения используют библиотеки через target_link_libraries.
  • third_party/: Внешние зависимости добавляются через git submodules или FetchContent.
  • tests/: Отдельная директория для тестов, которые проверяют интеграцию компонентов.

2. Система сборки: CMake для монорепозитория

CMake — стандартный инструмент для C++ проектов. В монорепозитории он позволяет управлять зависимостями между библиотеками и приложениями.

Корневой CMakeLists.txt

Создайте корневой файл, который подключает все подпроекты:

cmake_minimum_required(VERSION 3.20)
project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Подключаем внешние зависимости
add_subdirectory(third_party/fmt)
add_subdirectory(third_party/spdlog)

# Подключаем библиотеки
add_subdirectory(libs/core)
add_subdirectory(libs/network)
add_subdirectory(libs/utils)

# Подключаем приложения
add_subdirectory(apps/server)
add_subdirectory(apps/client)

# Подключаем тесты
enable_testing()
add_subdirectory(tests)

Пример CMakeLists.txt для библиотеки

# libs/core/CMakeLists.txt
add_library(core
    src/core_module.cpp
    src/utilities.cpp
)

target_include_directories(core
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_link_libraries(core
    PUBLIC
        fmt::fmt
        spdlog::spdlog
)

Сборка и тестирование

Для сборки всего проекта используйте:

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --parallel
ctest --output-on-failure

3. Управление зависимостями в монорепозитории

Одна из главных проблем монорепозитория — контроль версий зависимостей. В C++ есть несколько подходов.

Вариант 1: Git submodules

Добавьте внешние библиотеки как submodules:

git submodule add https://github.com/fmtlib/fmt.git third_party/fmt
git submodule add https://github.com/gabime/spdlog.git third_party/spdlog

Плюсы: Простота, фиксация версий.
Минусы: Необходимость обновлять submodules вручную.

Вариант 2: FetchContent (рекомендуется)

CMake может автоматически загружать зависимости:

include(FetchContent)

FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        10.1.0
)

FetchContent_MakeAvailable(fmt)

Преимущества: Автоматизация, централизованное управление версиями.

Вариант 3: Conan или vcpkg

Менеджеры пакетов (Conan, vcpkg) также хорошо интегрируются с CMake:

# conanfile.txt
[requires]
fmt/10.1.0
spdlog/1.12.0

[generators]
CMakeDeps

4. Организация тестов и CI/CD

Монорепозиторий требует продуманной стратегии тестирования и непрерывной интеграции.

Структура тестов

Разделите тесты на модульные (внутри каждой библиотеки) и интеграционные (в tests/):

tests/
├── CMakeLists.txt
├── test_integration.cpp
└── test_network.cpp

Пример CMakeLists.txt для тестов:

add_executable(test_integration
    test_integration.cpp
)

target_link_libraries(test_integration
    PRIVATE
        core
        network
        GTest::GTest
)

add_test(NAME IntegrationTest COMMAND test_integration)

CI/CD

Блоги

Рекомендаций по книгам