O que é um monorepositório e por que ele é necessário em C++?
Um monorepositório (monorepo) é uma abordagem de gerenciamento de código onde todos os componentes do projeto, bibliotecas, utilitários e até mesmo a documentação são armazenados em um único repositório. Para projetos em C++, isso é especialmente relevante, pois a linguagem tradicionalmente exige uma estrutura clara de arquivos e dependências.
Vantagens do monorepositório para C++:
- Versão única de dependências — todos os módulos usam a mesma versão das bibliotecas, eliminando conflitos de ABI.
- Compilação simplificada — um único CMakeLists.txt ou sistema de build gerencia todo o projeto.
- Commits atômicos — alterações em vários módulos podem ser combinadas em um único commit, facilitando o code review.
- Refatoração fácil — a alteração de uma API em um local é imediatamente visível em todos os módulos dependentes.
No entanto, o monorepositório não é adequado para todos: se as equipes são totalmente independentes e usam versões diferentes de compiladores, é melhor considerar um multirepositório. Para iniciantes, o monorepositório é uma excelente maneira de evitar dores de cabeça com a configuração de caminhos e dependências.
Parte principal: criando um monorepositório para um projeto C++
1. Estrutura de diretórios
Uma estrutura correta é a base de qualquer monorepositório. Modelo recomendado para C++:
my_monorepo/├── CMakeLists.txt # Arquivo CMake raiz├── cmake/ # Módulos CMake auxiliares│ └── helpers.cmake├── libs/ # Bibliotecas│ ├── core/ # Núcleo do projeto│ │ ├── CMakeLists.txt│ │ ├── include/core/│ │ └── src/│ └── utils/ # Utilitários│ ├── CMakeLists.txt│ ├── include/utils/│ └── src/├── apps/ # Aplicativos executáveis│ ├── main_app/│ │ ├── CMakeLists.txt│ │ └── main.cpp│ └── test_app/│ ├── CMakeLists.txt│ └── main.cpp├── tests/ # Testes (se não incorporados aos módulos)│ └── test_core.cpp├── scripts/ # Scripts de compilação e CI│ └── build.sh├── .gitignore└── README.mdRegra fundamental: cada módulo (biblioteca ou aplicativo) deve ser autossuficiente. Ele contém seu próprio CMakeLists.txt, arquivos de cabeçalho em include/ e código-fonte em src/.
2. Configuração do CMakeLists.txt raiz
O CMakeLists.txt raiz gerencia toda a compilação. Ele deve definir as configurações comuns do compilador, o padrão C++ e incluir os módulos filhos:
cmake_minimum_required(VERSION 3.20)project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)
# Define o padrão C++17 único para todo o projetoset(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_CXX_EXTENSIONS OFF)
# Opção para compilar testesoption(BUILD_TESTS "Build tests" OFF)
# Inclui módulos auxiliaresinclude(cmake/helpers.cmake)
# Adiciona subdiretórios com bibliotecasadd_subdirectory(libs/core)add_subdirectory(libs/utils)
# Adiciona aplicativosadd_subdirectory(apps/main_app)add_subdirectory(apps/test_app)
# Se os testes estiverem ativados, inclui-osif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()3. Exemplo de biblioteca: libs/core
Vamos considerar a biblioteca core, que contém uma classe para trabalhar com números. Estrutura de arquivos:
libs/core/├── CMakeLists.txt├── include/core/│ └── calculator.h└── src/ └── calculator.cppConteúdo 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