Monorepositorio con C++: organización del código y mejores prácticas
El desarrollo de proyectos grandes en C++ a menudo se enfrenta al problema de la fragmentación del código: las bibliotecas están dispersas en diferentes repositorios, las versiones no están sincronizadas y la integración se convierte en un dolor de cabeza. La solución es un monorepositorio (monorepo). Este es un enfoque en el que todo el código, incluidas bibliotecas, utilidades y pruebas, se almacena en un solo repositorio. En este artículo, analizaremos cómo organizar correctamente un monorepositorio para proyectos de C++ utilizando herramientas modernas (CMake, Conan, Git) y evitando errores típicos.
1. Estructura de directorios: la base del orden
El primer paso es crear una estructura de carpetas única y clara. Aquí hay un esquema recomendado para un monorepositorio de C++:
my-monorepo/├── cmake/ # Módulos CMake comunes├── libs/ # Bibliotecas│ ├── core/ # Núcleo (logging, allocators)│ ├── math/ # Biblioteca matemática│ └── network/ # Biblioteca de red├── apps/ # Aplicaciones ejecutables│ ├── server/ # Aplicación de servidor│ └── client/ # Aplicación de cliente├── tests/ # Pruebas de integración├── scripts/ # Scripts de compilación y despliegue├── third_party/ # Dependencias externas (submodules)├── CMakeLists.txt # Archivo CMake raíz├── conanfile.txt # Gestor de dependencias (opcional)└── .github/ # CI/CD (GitHub Actions)¿Por qué funciona esto?
- libs/ — todas las bibliotecas en un solo lugar, fáciles de reutilizar.
- apps/ — aplicaciones que dependen de libs.
- third_party/ — dependencias externas (a través de git submodules o CMake FetchContent).
- cmake/ — funciones personalizadas (por ejemplo,
add_library_with_tests).
2. CMake: configuración del proyecto raíz
CMake es el estándar para la compilación de C++ en monorepositorios. Configure el CMakeLists.txt raíz para que incluya todos los subproyectos:
cmake_minimum_required(VERSION 3.20)project(MyMonorepo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Opciones comunesoption(BUILD_TESTS "Build tests" ON)
# Incluir bibliotecasadd_subdirectory(libs/core)add_subdirectory(libs/math)add_subdirectory(libs/network)
# Incluir aplicacionesadd_subdirectory(apps/server)add_subdirectory(apps/client)
# Incluir pruebasif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()Puntos importantes:
- Use
target_link_librariespara indicar explícitamente las dependencias entre bibliotecas. - Para dependencias externas (por ejemplo, Boost, fmt), use
FetchContento Conan. - Evite los
include_directoriesglobales — usetarget_include_directoriesconPUBLICoPRIVATE.
3. Gestión de dependencias dentro del monorepositorio
Uno de los principales problemas del monorepositorio son las dependencias cíclicas. Solución: una jerarquía estricta. Por ejemplo:
- core (no depende de nada excepto de la biblioteca estándar)
- math (depende de core)
- network (depende de core y math)
- server (depende de core, math, network)
Ejemplo de CMakeLists.txt para la biblioteca 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 # Indicamos explícitamente la de