引言:为什么在 C++ 中需要单体仓库?
大型 C++ 项目的开发常常面临代码组织的问题。团队需要在单体仓库(所有代码在一个仓库中)和多仓库(每个库有独立的仓库)之间做出选择。在 C++ 的背景下,单体仓库提供了一系列优势:简化重构、统一的依赖版本以及集中化的构建系统。
本文将引导您了解 C++ 单体仓库组织的关键方面:从文件夹结构到 CMake 配置和依赖管理。您将学习如何使项目具有可扩展性并方便团队协作。
1. C++ 单体仓库的结构
正确的结构是成功单体仓库的基础。建议使用将库、应用程序和外部依赖项分开的层级结构。
基本结构示例
my_monorepo/├── CMakeLists.txt # 根 CMake 文件├── cmake/ # 辅助 CMake 模块├── libs/ # 项目库│ ├── core/ # 核心(逻辑、工具)│ │ ├── include/│ │ │ └── core/│ │ ├── src/│ │ └── CMakeLists.txt│ ├── network/ # 网络模块│ │ ├── include/│ │ ├── src/│ │ └── CMakeLists.txt│ └── utils/ # 辅助工具│ ├── include/│ ├── src/│ └── CMakeLists.txt├── apps/ # 可执行应用程序│ ├── server/│ │ ├── src/│ │ └── CMakeLists.txt│ └── client/│ ├── src/│ └── CMakeLists.txt├── tests/ # 集成测试├── third_party/ # 外部依赖项(git submodules)│ ├── fmt/│ └── spdlog/└── scripts/ # CI/CD 和开发脚本关键原则
- libs/:每个库都是一个自包含的模块,拥有自己的 CMakeLists.txt。
- apps/:应用程序通过
target_link_libraries使用库。 - third_party/:外部依赖项通过 git submodules 或 FetchContent 添加。
- tests/:用于测试组件集成的独立目录。
2. 构建系统:用于单体仓库的 CMake
CMake 是 C++ 项目的标准工具。在单体仓库中,它允许管理库和应用程序之间的依赖关系。
根 CMakeLists.txt
创建一个包含所有子项目的根文件:
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)
# 包含外部依赖项add_subdirectory(third_party/fmt)add_subdirectory(third_party/spdlog)
# 包含库add_subdirectory(libs/core)add_subdirectory(libs/network)add_subdirectory(libs/utils)
# 包含应用程序add_subdirectory(apps/server)add_subdirectory(apps/client)
# 包含测试enable_testing()add_subdirectory(tests)库的 CMakeLists.txt 示例
# 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)构建和测试
要构建整个项目,请使用:
mkdir build && cd buildcmake .. -DCMAKE_BUILD_TYPE=Releasecmake --build . --parallelctest --output-on-failure3. 单体仓库中的依赖管理
单体仓库的主要问题之一是依赖项的版本控制。在 C++ 中,有几种方法可以解决这个问题。