Monorepo avec C++ : organisation du code et bonnes pratiques
Le développement de grands projets en C++ est souvent confronté au problème de la fragmentation du code : les bibliothèques sont dispersées dans différents dépôts, les versions ne sont pas synchronisées et l'intégration devient un casse-tête. La solution est le monorepo. Il s'agit d'une approche où tout le code, y compris les bibliothèques, les utilitaires et les tests, est stocké dans un seul dépôt. Dans cet article, nous verrons comment organiser correctement un monorepo pour les projets C++, en utilisant des outils modernes (CMake, Conan, Git) et en évitant les erreurs typiques.
1. Structure des répertoires : la base de l'ordre
La première étape consiste à créer une structure de dossiers unique et claire. Voici un schéma recommandé pour un monorepo C++ :
my-monorepo/├── cmake/ # Modules CMake communs├── libs/ # Bibliothèques│ ├── core/ # Noyau (logging, allocateurs)│ ├── math/ # Bibliothèque mathématique│ └── network/ # Bibliothèque réseau├── apps/ # Applications exécutables│ ├── server/ # Application serveur│ └── client/ # Application client├── tests/ # Tests d'intégration├── scripts/ # Scripts de build et de déploiement├── third_party/ # Dépendances externes (submodules)├── CMakeLists.txt # Fichier CMake racine├── conanfile.txt # Gestionnaire de dépendances (optionnel)└── .github/ # CI/CD (GitHub Actions)Pourquoi cela fonctionne-t-il ?
- libs/ — toutes les bibliothèques au même endroit, faciles à réutiliser.
- apps/ — les applications qui dépendent des libs.
- third_party/ — les dépendances externes (via git submodules ou CMake FetchContent).
- cmake/ — les fonctions personnalisées (par exemple,
add_library_with_tests).
2. CMake : configuration du projet racine
CMake est la norme pour la compilation C++ dans les monorepos. Configurez le CMakeLists.txt racine de manière à ce qu'il inclue tous les sous-projets :
cmake_minimum_required(VERSION 3.20)project(MyMonorepo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Options communesoption(BUILD_TESTS "Build tests" ON)
# Inclure les bibliothèquesadd_subdirectory(libs/core)add_subdirectory(libs/math)add_subdirectory(libs/network)
# Inclure les applicationsadd_subdirectory(apps/server)add_subdirectory(apps/client)
# Inclure les testsif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()Points importants :
- Utilisez
target_link_librariespour spécifier explicitement les dépendances entre les bibliothèques. - Pour les dépendances externes (par exemple, Boost, fmt), utilisez
FetchContentou Conan. - Évitez les
include_directoriesglobaux — utiliseztarget_include_directoriesavecPUBLICouPRIVATE.
3. Gestion des dépendances au sein du monorepo
L'un des principaux problèmes du monorepo est les dépendances cycliques. Solution : une hiérarchie stricte. Par exemple :
- core (ne dépend de rien d'autre que de la bibliothèque standard)
- math (dépend de core)
- network (dépend de core et math)
- server (dépend de core, math, network)
Exemple de CMakeLists.txt pour la bibliothèque math :
add_library(math src/vector.cpp src/matrix.cpp include/math/vector.h include/math/matrix.h)
target_include_directories(math PUBLIC include PRIVATE src)
target_link_libraries(math PUBLIC core # Spécifions explicitement la dépen