Monorepositório com C++: organização de código na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que um monorepositório para C++?



No desenvolvimento moderno de C++, especialmente em projetos de médio e grande porte, a organização do código se torna uma tarefa criticamente importante. Monorepositório (monorepo) é uma abordagem onde o código de todos os componentes, bibliotecas e serviços é armazenado em um único repositório. Ao contrário dos multirrepositórios, o monorepositório oferece um único ponto de entrada, simplifica a refatoração e o gerenciamento de dependências. Na prática, isso significa que você pode alterar a API de uma biblioteca e ver imediatamente como isso afetará todos os módulos dependentes.



Neste artigo, abordaremos os aspectos práticos da organização de código C++ em um monorepositório: desde a estrutura de pastas até a configuração de CI/CD. Você aprenderá como evitar erros típicos e tornar a compilação rápida e previsível.



1. Estrutura do monorepositório para C++



Uma estrutura de pastas correta é a base de um monorepositório bem-sucedido. Vamos considerar a hierarquia recomendada para projetos C++.



Hierarquia básica de diretórios



my_project/├── CMakeLists.txt          # Arquivo raiz de compilação├── 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/                   # Aplicações executáveis│   ├── server/│   │   ├── CMakeLists.txt│   │   └── src/│   │       └── main.cpp│   └── cli/│       ├── CMakeLists.txt│       └── src/│           └── main.cpp├── tests/                  # Testes de integração│   ├── CMakeLists.txt│   └── test_integration.cpp├── third_party/            # Dependências externas (vendoring)│   ├── fmt/│   └── spdlog/├── .github/                # Configuração de CI/CD│   └── workflows/│       └── build.yml└── README.md


Princípios-chave:

  • libs/ — todas as bibliotecas internas. Cada biblioteca tem sua própria pasta com CMakeLists.txt.
  • apps/ — arquivos executáveis. Eles dependem das bibliotecas em libs/.
  • third_party/ — dependências de fornecedores (git submodules ou códigos-fonte copiados).
  • tests/ — testes de integração que verificam a interação de várias bibliotecas.


Exemplo de CMakeLists.txt para uma 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: Use PUBLIC e PRIVATE para o encaminhamento correto de dependências. Este é um ponto-chave — se a biblioteca A usa a biblioteca B apenas em seus próprios fontes, torne B PRIVATE.



2. Gerenciamento de dependências no monorepositório



Um dos principais problemas dos monorepositórios é o "inferno das dependências". Em C++, isso é resolvido através do CMake target-oriented design (design orientado a alvos).



Regra: um alvo — uma biblioteca



Cada biblioteca deve exportar exatamente um alvo CMake. Isso simplifica a vinculação e previne dependências cíclicas.



# CMakeLists.txt raizcmake_minimum_required(VERSION 3.20)project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# Inclui todas as bibliotecasadd_subdirectory(libs/core)add_subdirectory(libs/network)

Blogs

Book Recommendations