Introducción: ¿por qué un monorepositorio para C++?
En el desarrollo moderno de C++, especialmente en proyectos de tamaño mediano y grande, la organización del código se convierte en una tarea crítica. El monorepositorio (monorepo) es un enfoque en el que el código de todos los componentes, bibliotecas y servicios se almacena en un solo repositorio. A diferencia de los multirepositorios, un monorepositorio ofrece un único punto de entrada, simplifica la refactorización y la gestión de dependencias. En la práctica, esto significa que puedes modificar la API de una biblioteca y ver inmediatamente cómo afecta a todos los módulos dependientes.
En este artículo, analizaremos los aspectos prácticos de organizar código C++ en un monorepositorio: desde la estructura de carpetas hasta la configuración de CI/CD. Aprenderás cómo evitar errores típicos y hacer que la compilación sea rápida y predecible.
1. Estructura del monorepositorio para C++
Una estructura de carpetas correcta es la base de un monorepositorio exitoso. Consideremos la jerarquía recomendada para proyectos C++.
Jerarquía básica de directorios
my_project/├── CMakeLists.txt # Archivo raíz de compilación├── cmake/ # Módulos CMake personalizados│ └── FindMyLib.cmake├── libs/ # Bibliotecas internas│ ├── 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/ # Aplicaciones ejecutables│ ├── server/│ │ ├── CMakeLists.txt│ │ └── src/│ │ └── main.cpp│ └── cli/│ ├── CMakeLists.txt│ └── src/│ └── main.cpp├── tests/ # Pruebas de integración│ ├── CMakeLists.txt│ └── test_integration.cpp├── third_party/ # Dependencias externas (vendoring)│ ├── fmt/│ └── spdlog/├── .github/ # Configuración de CI/CD│ └── workflows/│ └── build.yml└── README.mdPrincipios clave:
- libs/ — todas las bibliotecas internas. Cada biblioteca tiene su propia carpeta con
CMakeLists.txt. - apps/ — archivos ejecutables. Dependen de las bibliotecas en
libs/. - third_party/ — dependencias de proveedores (git submodules o código fuente copiado).
- tests/ — pruebas de integración que verifican la interacción de múltiples bibliotecas.
Ejemplo de CMakeLists.txt para una biblioteca
# 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)Importante: Usa PUBLIC y PRIVATE para la propagación correcta de dependencias. Este es un punto clave: si la biblioteca A usa la biblioteca B solo en sus propios archivos fuente, haz que B sea PRIVATE.
2. Gestión de dependencias en el monorepositorio
Uno de los principales problemas de los monorepositorios es el "infierno de las dependencias". En C++, esto se resuelve mediante el diseño orientado a objetivos de CMake (CMake target-oriented design).
Regla: un objetivo — una biblioteca
Cada biblioteca debe exportar exactamente un objetivo de CMake. Esto simplifica el enlace y previene dependencias cíclicas.
# CMakeLists.txt raízcmake_minimum_required(VERSION 3.20)project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Incluir todas las bibliotecasadd_subdirectory(libs/core)add_subdirectory(libs/network)