Was ist ein Monorepository und warum wird es in C++ benötigt?
Ein Monorepository (Monorepo) ist ein Ansatz zur Code-Verwaltung, bei dem alle Komponenten eines Projekts, Bibliotheken, Hilfsprogramme und sogar die Dokumentation in einem einzigen Repository gespeichert werden. Für Projekte in C++ ist dies besonders relevant, da die Sprache traditionell eine klare Datei- und Abhängigkeitsstruktur erfordert.
Vorteile eines Monorepositorys für C++:
- Einheitliche Abhängigkeitsversion – alle Module verwenden dieselbe Version der Bibliotheken, was ABI-Konflikte ausschließt.
- Vereinfachter Build – eine einzige CMakeLists.txt oder ein Build-System verwaltet das gesamte Projekt.
- Atomare Commits – Änderungen in mehreren Modulen können in einem einzigen Commit zusammengefasst werden, was das Code-Review vereinfacht.
- Einfaches Refactoring – eine API-Änderung an einer Stelle ist sofort in allen abhängigen Modulen sichtbar.
Allerdings ist ein Monorepository nicht für jeden geeignet: Wenn Teams völlig unabhängig sind und unterschiedliche Compiler-Versionen verwenden, ist ein Multirepository besser geeignet. Für Anfänger ist ein Monorepository jedoch eine hervorragende Möglichkeit, Kopfschmerzen bei der Einrichtung von Pfaden und Abhängigkeiten zu vermeiden.
Hauptteil: Erstellen eines Monorepositorys für ein C++-Projekt
1. Verzeichnisstruktur
Eine korrekte Struktur ist die Grundlage jedes Monorepositorys. Empfohlenes Schema für C++:
my_monorepo/├── CMakeLists.txt # Root-CMake-Datei├── cmake/ # Hilfs-CMake-Module│ └── helpers.cmake├── libs/ # Bibliotheken│ ├── core/ # Projektkern│ │ ├── CMakeLists.txt│ │ ├── include/core/│ │ └── src/│ └── utils/ # Hilfsprogramme│ ├── CMakeLists.txt│ ├── include/utils/│ └── src/├── apps/ # Ausführbare Anwendungen│ ├── main_app/│ │ ├── CMakeLists.txt│ │ └── main.cpp│ └── test_app/│ ├── CMakeLists.txt│ └── main.cpp├── tests/ # Tests (falls nicht in Module integriert)│ └── test_core.cpp├── scripts/ # Build- und CI-Skripte│ └── build.sh├── .gitignore└── README.mdGrundregel: Jedes Modul (Bibliothek oder Anwendung) sollte in sich abgeschlossen sein. Es enthält seine eigene CMakeLists.txt, Header-Dateien in include/ und Quelldateien in src/.
2. Einrichtung der root-CMakeLists.txt
Die root-CMakeLists.txt verwaltet den gesamten Build. Sie sollte allgemeine Compiler-Einstellungen, den C++-Standard definieren und die untergeordneten Module einbinden:
cmake_minimum_required(VERSION 3.20)project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)
# Einheitlichen C++17-Standard für das gesamte Projekt festlegenset(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_CXX_EXTENSIONS OFF)
# Option zum Erstellen von Testsoption(BUILD_TESTS "Build tests" OFF)
# Hilfsmodule einbindeninclude(cmake/helpers.cmake)
# Unterverzeichnisse mit Bibliotheken hinzufügenadd_subdirectory(libs/core)add_subdirectory(libs/utils)
# Anwendungen hinzufügenadd_subdirectory(apps/main_app)add_subdirectory(apps/test_app)
# Wenn Tests aktiviert sind, diese einbindenif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()3. Beispiel einer Bibliothek: libs/core
Betrachten wir die Bibliothek core, die eine Klasse für die Arbeit mit Zahlen enthält. Dateistruktur:
libs/core/├── CMakeLists.txt├── include/core/│ └── calculator.h└── src/ └── calculator.cppInhalt von 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