Qu'est-ce qu'un monorepo et pourquoi en a-t-on besoin en C++ ?
Un monorepo (monorepository) est une approche de gestion du code où tous les composants du projet, bibliothèques, utilitaires et même la documentation sont stockés dans un seul dépôt. Pour les projets en C++, cela est particulièrement pertinent car le langage nécessite traditionnellement une structure claire des fichiers et des dépendances.
Avantages du monorepo pour le C++ :
- Version unique des dépendances — tous les modules utilisent la même version des bibliothèques, ce qui élimine les conflits ABI.
- Compilation simplifiée — un seul CMakeLists.txt ou système de compilation gère l'ensemble du projet.
- Commits atomiques — les modifications dans plusieurs modules peuvent être regroupées en un seul commit, ce qui simplifie la revue de code.
- Refactorisation facile — un changement d'API à un endroit est immédiatement visible dans tous les modules dépendants.
Cependant, le monorepo ne convient pas à tout le monde : si les équipes sont totalement indépendantes et utilisent différentes versions de compilateurs, il est préférable d'envisager un multirepo. Pour les débutants, le monorepo est un excellent moyen d'éviter les maux de tête liés à la configuration des chemins et des dépendances.
Partie principale : créer un monorepo pour un projet C++
1. Structure des répertoires
Une structure correcte est la base de tout monorepo. Modèle recommandé pour le C++ :
my_monorepo/├── CMakeLists.txt # Fichier CMake racine├── cmake/ # Modules CMake auxiliaires│ └── helpers.cmake├── libs/ # Bibliothèques│ ├── core/ # Cœur du projet│ │ ├── CMakeLists.txt│ │ ├── include/core/│ │ └── src/│ └── utils/ # Utilitaires│ ├── CMakeLists.txt│ ├── include/utils/│ └── src/├── apps/ # Applications exécutables│ ├── main_app/│ │ ├── CMakeLists.txt│ │ └── main.cpp│ └── test_app/│ ├── CMakeLists.txt│ └── main.cpp├── tests/ # Tests (si non intégrés dans les modules)│ └── test_core.cpp├── scripts/ # Scripts de compilation et CI│ └── build.sh├── .gitignore└── README.mdRègle clé : chaque module (bibliothèque ou application) doit être autonome. Il contient son propre CMakeLists.txt, les fichiers d'en-tête dans include/ et les sources dans src/.
2. Configuration du CMakeLists.txt racine
Le CMakeLists.txt racine gère toute la compilation. Il doit définir les paramètres communs du compilateur, le standard C++ et inclure les modules enfants :
cmake_minimum_required(VERSION 3.20)project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)
# Définit un standard C++17 unique pour tout le projetset(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_CXX_EXTENSIONS OFF)
# Option pour la compilation des testsoption(BUILD_TESTS "Build tests" OFF)
# Inclut les modules auxiliairesinclude(cmake/helpers.cmake)
# Ajoute les sous-répertoires avec les bibliothèquesadd_subdirectory(libs/core)add_subdirectory(libs/utils)
# Ajoute les applicationsadd_subdirectory(apps/main_app)add_subdirectory(apps/test_app)
# Si les tests sont activés, les inclureif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()3. Exemple de bibliothèque : libs/core
Considérons la bibliothèque core, qui contient une classe pour travailler avec des nombres. Structure des fichiers :
libs/core/├── CMakeLists.txt├── include/core/│ └── calculator.h└── src/ └── calculator.cppContenu de 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