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

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

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

Начать курс

Что такое монорепозиторий и зачем он нужен в C++?

Монорепозиторий (monorepo) — это подход к управлению кодом, при котором все компоненты проекта, библиотеки, утилиты и даже документация хранятся в одном репозитории. Для проектов на C++ это особенно актуально, так как язык традиционно требует чёткой структуры файлов и зависимостей.

Преимущества монорепозитория для C++:

  • Единая версия зависимостей — все модули используют одинаковую версию библиотек, что исключает конфликты ABI.
  • Упрощённая сборка — один CMakeLists.txt или система сборки управляет всем проектом.
  • Атомарные коммиты — изменения в нескольких модулях можно объединить в один коммит, что упрощает code review.
  • Лёгкий рефакторинг — изменение API в одном месте сразу видно во всех зависимых модулях.

Однако монорепозиторий подходит не всем: если команды полностью независимы и используют разные версии компиляторов, лучше рассмотреть мультирепозиторий. Для начинающих же монорепозиторий — отличный способ избежать головной боли с настройкой путей и зависимостей.

Основная часть: создаём монорепозиторий для C++ проекта

1. Структура директорий

Правильная структура — основа любого монорепозитория. Рекомендуемый шаблон для C++:

my_monorepo/
├── CMakeLists.txt          # Корневой CMake-файл
├── cmake/                  # Вспомогательные CMake-модули
│   └── helpers.cmake
├── libs/                   # Библиотеки
│   ├── core/               # Ядро проекта
│   │   ├── CMakeLists.txt
│   │   ├── include/core/
│   │   └── src/
│   └── utils/              # Утилиты
│       ├── CMakeLists.txt
│       ├── include/utils/
│       └── src/
├── apps/                   # Исполняемые приложения
│   ├── main_app/
│   │   ├── CMakeLists.txt
│   │   └── main.cpp
│   └── test_app/
│       ├── CMakeLists.txt
│       └── main.cpp
├── tests/                  # Тесты (если не встроены в модули)
│   └── test_core.cpp
├── scripts/                # Скрипты сборки и CI
│   └── build.sh
├── .gitignore
└── README.md

Ключевое правило: каждый модуль (библиотека или приложение) должен быть самодостаточным. Он содержит свой CMakeLists.txt, заголовочные файлы в include/ и исходники в src/.

2. Настройка корневого CMakeLists.txt

Корневой CMakeLists.txt управляет всей сборкой. Он должен определять общие настройки компилятора, C++ стандарт и подключать дочерние модули:

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

# Устанавливаем единый стандарт C++17 для всего проекта
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Опция для сборки тестов
option(BUILD_TESTS "Build tests" OFF)

# Подключаем вспомогательные модули
include(cmake/helpers.cmake)

# Добавляем поддиректории с библиотеками
add_subdirectory(libs/core)
add_subdirectory(libs/utils)

# Добавляем приложения
add_subdirectory(apps/main_app)
add_subdirectory(apps/test_app)

# Если включены тесты, подключаем их
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

3. Пример библиотеки: libs/core

Рассмотрим библиотеку core, которая содержит класс для работы с числами. Структура файлов:

libs/core/
├── CMakeLists.txt
├── include/core/
│   └── calculator.h
└── src/
    └── calculator.cpp

Содержимое include/core/calculator.h:

#ifndef CORE_CALCULATOR_H
#define CORE_CALCULATOR_H

namespace core {

class Calculator {
public:
    Calculator() = default;
    int add(int a, int b);
    int multiply(int a, int b);
};

} // namespace core

#endif // CORE_CALCULATOR_H

Содержимое src/calculator.cpp:

#include "core/calculator.h"

namespace core {

int Calculator::add(int a, int b) {
    return a + b;
}

int Calculator::multiply(int a, int b) {
    return a * b;
}

} // namespace core

CMakeLists.txt для этой библиотеки:

add_library(core STATIC
    src/calculator.cpp
)

# Указываем, где искать заголовочные файлы
target_include_directories(core
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

# Указываем зависимости (если есть)
# target_link_libraries(core PUBLIC utils)

4. Пример приложения: apps/main_app

Приложение использует библиотеку core для выполнения операций. Структура:

apps/main_app/
├── CMakeLists.txt
└── main.cpp

CMakeLists.txt для приложения:

add_executable(main_app main.cpp)

# Подключаем библиотеку core
target_link_libraries(main_app
    PRIVATE
        core
)

# Указываем include директории для библиотек (если нужно)
target_include_directories(main_app
    PRIVATE
        ${CMAKE_SOURCE_DIR}/libs/core/include
)

Содержимое main.cpp:

#include <iostream>
#include "core/calculator.h"

int main() {
    core::Calculator calc;
    int sum = calc.add(10, 20);
    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

5. Сборка проекта

Для сборки монорепозитория используйте стандартный подход CMake из корневой директории:

# Создаём директорию сборки
mkdir build && cd build

# Конфигурация
cmake ..

# Сборка всех модулей
cmake --build .

# Запуск приложения
./apps/main_app/main_app

Если нужно собрать только один модуль, укажите его имя:

Блоги

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