مستودع واحد (مونوريبو) مع C++: تنظيم الكود وأفضل الممارسات
غالبًا ما يواجه تطوير المشاريع الكبيرة بلغة C++ مشكلة تجزئة الكود: حيث تكون المكتبات موزعة عبر مستودعات مختلفة، والإصدارات غير متزامنة، ويصبح التكامل بمثابة صداع. الحل هو المستودع الواحد (monorepo). هذا نهج يتم فيه تخزين جميع الكود، بما في ذلك المكتبات والأدوات المساعدة والاختبارات، في مستودع واحد. في هذه المقالة، سنشرح كيفية تنظيم مستودع واحد لمشاريع C++ بشكل صحيح، باستخدام أدوات حديثة (CMake, Conan, Git) وتجنب الأخطاء النموذجية.
1. هيكل الدلائل: أساس النظام
الخطوة الأولى هي إنشاء هيكل مجلدات موحد وواضح. إليك المخطط الموصى به لمستودع واحد بلغة C++:
my-monorepo/├── cmake/ # وحدات CMake المشتركة├── libs/ # المكتبات│ ├── core/ # النواة (تسجيل، مخصصات)│ ├── math/ # مكتبة رياضيات│ └── network/ # مكتبة شبكات├── apps/ # التطبيقات القابلة للتنفيذ│ ├── server/ # تطبيق الخادم│ └── client/ # تطبيق العميل├── tests/ # اختبارات التكامل├── scripts/ # نصوص البناء والنشر├── third_party/ # التبعيات الخارجية (submodules)├── CMakeLists.txt # ملف CMake الجذري├── conanfile.txt # مدير التبعيات (اختياري)└── .github/ # CI/CD (GitHub Actions)لماذا يعمل هذا؟
- libs/ — جميع المكتبات في مكان واحد، سهلة إعادة الاستخدام.
- apps/ — التطبيقات التي تعتمد على libs.
- third_party/ — التبعيات الخارجية (عبر git submodules أو CMake FetchContent).
- cmake/ — دوال مخصصة (مثل
add_library_with_tests).
2. CMake: إعداد المشروع الجذري
CMake هو المعيار لبناء C++ في المستودعات الواحدة. قم بإعداد ملف CMakeLists.txt الجذري بحيث يضم جميع المشاريع الفرعية:
cmake_minimum_required(VERSION 3.20)project(MyMonorepo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)set(CMAKE_CXX_STANDARD_REQUIRED ON)
# خيارات عامةoption(BUILD_TESTS "Build tests" ON)
# تضمين المكتباتadd_subdirectory(libs/core)add_subdirectory(libs/math)add_subdirectory(libs/network)
# تضمين التطبيقاتadd_subdirectory(apps/server)add_subdirectory(apps/client)
# تضمين الاختباراتif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()نقاط مهمة:
- استخدم
target_link_librariesلتحديد التبعيات بين المكتبات بشكل صريح. - بالنسبة للتبعيات الخارجية (مثل Boost, fmt) استخدم
FetchContentأو Conan. - تجنب استخدام
include_directoriesالعامة — استخدمtarget_include_directoriesمعPUBLICأوPRIVATE.
3. إدارة التبعيات داخل المستودع الواحد
إحدى المشكلات الرئيسية للمستودع الواحد هي التبعيات الدائرية. الحل: تسلسل هرمي صارم. على سبيل المثال:
- core (لا يعتمد على أي شيء باستثناء المكتبة القياسية)
- math (يعتمد على core)
- network (يعتمد على core و math)
- server (يعتمد على core, math, network)
مثال لملف CMakeLists.txt للمكتبة math:
add_library(math src/vector.cpp src/matrix.cpp include/math/vector.h include/math/matrix.h)
target_include_directories(math PUBLIC include PRIVATE src)
target_link_libraries(math PUBLIC core # نحدد صراحة اعتماد