Что такое монорепозиторий и зачем он нужен в 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
Если нужно собрать только один модуль, укажите его имя: