Introduction : pourquoi un monorepo pour le C++ ?
Dans le développement C++ moderne, en particulier pour les projets de taille moyenne et grande, l'organisation du code devient une tâche cruciale. Le monorepo est une approche où le code de tous les composants, bibliothèques et services est stocké dans un seul dépôt. Contrairement aux multi-repos, le monorepo offre un point d'entrée unique, simplifie le refactoring et la gestion des dépendances. En pratique, cela signifie que vous pouvez modifier l'API d'une bibliothèque et voir immédiatement l'impact sur tous les modules dépendants.
Dans cet article, nous aborderons les aspects pratiques de l'organisation du code C++ dans un monorepo : de la structure des dossiers à la configuration du CI/CD. Vous apprendrez à éviter les erreurs courantes et à rendre la compilation rapide et prévisible.
1. Structure du monorepo pour le C++
Une structure de dossiers correcte est la base d'un monorepo réussi. Examinons la hiérarchie recommandée pour les projets C++.
Hiérarchie de base des répertoires
my_project/├── CMakeLists.txt # Fichier de build racine├── cmake/ # Modules CMake personnalisés│ └── FindMyLib.cmake├── libs/ # Bibliothèques internes│ ├── core/│ │ ├── CMakeLists.txt│ │ ├── include/│ │ │ └── core/│ │ │ ├── utils.h│ │ │ └── types.h│ │ └── src/│ │ ├── utils.cpp│ │ └── types.cpp│ └── network/│ ├── CMakeLists.txt│ ├── include/│ │ └── network/│ │ └── http_client.h│ └── src/│ └── http_client.cpp├── apps/ # Applications exécutables│ ├── server/│ │ ├── CMakeLists.txt│ │ └── src/│ │ └── main.cpp│ └── cli/│ ├── CMakeLists.txt│ └── src/│ └── main.cpp├── tests/ # Tests d'intégration│ ├── CMakeLists.txt│ └── test_integration.cpp├── third_party/ # Dépendances externes (vendoring)│ ├── fmt/│ └── spdlog/├── .github/ # Configuration CI/CD│ └── workflows/│ └── build.yml└── README.mdPrincipes clés :
- libs/ — toutes les bibliothèques internes. Chaque bibliothèque a son propre dossier avec
CMakeLists.txt. - apps/ — les fichiers exécutables. Ils dépendent des bibliothèques de
libs/. - third_party/ — dépendances vendorisées (sous-modules git ou sources copiées).
- tests/ — tests d'intégration qui vérifient l'interaction de plusieurs bibliothèques.
Exemple de CMakeLists.txt pour une bibliothèque
# libs/core/CMakeLists.txtadd_library(core src/utils.cpp src/types.cpp)
target_include_directories(core PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
target_link_libraries(core PUBLIC fmt::fmt spdlog::spdlog)Important : Utilisez PUBLIC et PRIVATE pour une propagation correcte des dépendances. C'est un point clé — si la bibliothèque A utilise la bibliothèque B uniquement dans ses sources, faites de B une dépendance PRIVATE.
2. Gestion des dépendances dans le monorepo
L'un des principaux problèmes des monorepos est « l'enfer des dépendances ». En C++, cela se résout grâce à la conception orientée cibles CMake (target-oriented design).
Règle : une cible — une bibliothèque
Chaque bibliothèque doit exporter exactement une cible CMake. Cela simplifie la liaison et empêche les dépendances cycliques.
# CMakeLists.txt racinecmake_minimum_required(VERSION 3.20)project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Inclure toutes les bibliothèquesadd_subdirectory(libs/core)add_subdirectory(libs/network)