Introduction: Why Do You Need a Monorepo in C++?
Developing large C++ projects often faces the problem of code organization. Teams choose between a monorepo (one repository for all code) and a multirepo (separate repositories for each library). In the context of C++, a monorepo offers several advantages: simplified refactoring, a single version of dependencies, and a centralized build system.
This article will guide you through the key aspects of organizing a monorepo for C++: from folder structure to setting up CMake and managing dependencies. You will learn how to make a project scalable and convenient for the team.
1. Monorepo Structure for C++
A proper structure is the foundation of a successful monorepo. It is recommended to use a hierarchy that separates libraries, applications, and external dependencies.
Example of a Basic Structure
my_monorepo/├── CMakeLists.txt # Root CMake file├── cmake/ # Auxiliary CMake modules├── libs/ # Project libraries│ ├── core/ # Core (logic, utilities)│ │ ├── include/│ │ │ └── core/│ │ ├── src/│ │ └── CMakeLists.txt│ ├── network/ # Network modules│ │ ├── include/│ │ ├── src/│ │ └── CMakeLists.txt│ └── utils/ # Auxiliary utilities│ ├── include/│ ├── src/│ └── CMakeLists.txt├── apps/ # Executable applications│ ├── server/│ │ ├── src/│ │ └── CMakeLists.txt│ └── client/│ ├── src/│ └── CMakeLists.txt├── tests/ # Integration tests├── third_party/ # External dependencies (git submodules)│ ├── fmt/│ └── spdlog/└── scripts/ # Scripts for CI/CD and developmentKey Principles
- libs/: Each library is a self-contained module with its own CMakeLists.txt.
- apps/: Applications use libraries via
target_link_libraries. - third_party/: External dependencies are added via git submodules or FetchContent.
- tests/: A separate directory for tests that check component integration.
2. Build System: CMake for a Monorepo
CMake is the standard tool for C++ projects. In a monorepo, it allows managing dependencies between libraries and applications.
Root CMakeLists.txt
Create a root file that includes all subprojects:
cmake_minimum_required(VERSION 3.20)project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Include external dependenciesadd_subdirectory(third_party/fmt)add_subdirectory(third_party/spdlog)
# Include librariesadd_subdirectory(libs/core)add_subdirectory(libs/network)add_subdirectory(libs/utils)
# Include applicationsadd_subdirectory(apps/server)add_subdirectory(apps/client)
# Include testsenable_testing()add_subdirectory(tests)Example CMakeLists.txt for a Library
# libs/core/CMakeLists.txtadd_library(core src/core_module.cpp src/utilities.cpp)
target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(core PUBLIC fmt::fmt spdlog::spdlog)Building and Testing
To build the entire project, use:
mkdir build && cd buildcmake .. -DCMAKE_BUILD_TYPE=Releasecmake --build . --parallelctest --output-on-failure3. Dependency Management in a Monorepo
One of the main problems of a monorepo is controlling dependency versions. In C++, there are several