أنظمة البناء: CMake وإدارة الحزم
أساسيات CMake الرئيسية، وCMake الحديث القائم على الهدف، وFetchContent للتبعيات، ومديري الحزم vcpkg وConan، وعلامات المترجم من أجل إنشاءات آمنة ومحسّنة.
لماذا يهم بناء الأنظمة
مع نمو مشاريع C++ إلى ما هو أبعد من ملف واحد، فأنت بحاجة إلى نظام بناء لإدارة التجميع والربط والتبعيات واختلافات النظام الأساسي. لا يؤدي تشغيلg++ *.cppيدويًا إلى التوسع: فأنت تحتاج إلى إصدارات تزايدية (إعادة ترجمة الملفات التي تم تغييرها فقط)، واكتشاف المكتبة، والدعم عبر الأنظمة الأساسية، والإنشاءات القابلة للتكرار. أصبح CMake هو منشئ نظام البناء القياسي الفعلي لـ C++. فهو لا يقوم بتجميع التعليمات البرمجية بنفسه، بل يقوم بإنشاء ملفات إنشاء أصلية (ملفات Makefiles وملفات Ninja ومشاريع Visual Studio) لنظامك الأساسي.
أساسيات CMake
يصف ملفCMakeLists.txtبنية مشروعك. يستخدم CMake الحديث (3.12+) أسلوبًا معتمدًا على الهدف حيث يتم ربط كل شيء بالأهداف (الملفات التنفيذية أو المكتبات):
# CMakeLists.txt — minimum viable project
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard for the entire project
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++20, not -std=gnu++20
# Create an executable target
add_executable(myapp
src/main.cpp
src/engine.cpp
src/utils.cpp
)
# Add include directories for this target
target_include_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Create a library target
add_library(mathlib STATIC
src/math/vector.cpp
src/math/matrix.cpp
)
target_include_directories(mathlib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include/math
)
# Link the library to the executable
target_link_libraries(myapp PRIVATE mathlib)
# Compiler warnings — essential for quality code
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)البناء باستخدام CMake
يستخدم CMake عملية من خطوتين: التكوين (إنشاء ملفات البناء) والإنشاء (التحويل البرمجي). استخدم دائمًا تصميمات خارج المصدر للحفاظ على نظافة شجرة المصدر:
# Configure — generate build files in a 'build' directory
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build — compile the project
cmake --build build --parallel $(nproc)
# Install (optional)
cmake --install build --prefix /usr/local
# Common build types:
# Debug — -O0 -g (full debug symbols, no optimization)
# Release — -O3 -DNDEBUG (max optimization, no asserts)
# RelWithDebInfo — -O2 -g -DNDEBUG (optimized with debug info)
# MinSizeRel — -Os -DNDEBUG (optimize for size)
# Run with sanitizers (add to CMakeLists.txt or command line)
cmake -B build-asan -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"FetchContent: إدارة التبعية
تقوم وحدةFetchContentالخاصة بـ CMake بتنزيل وبناء التبعيات في وقت التكوين، مما يجعل من السهل سحب المكتبات دون الإعداد اليدوي:
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)
include(FetchContent)
# Fetch GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# Fetch fmt (formatting library)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
# Download and make available
FetchContent_MakeAvailable(googletest fmt)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)
# Test executable
enable_testing()
add_executable(tests tests/test_main.cpp)
target_link_libraries(tests PRIVATE GTest::gtest_main)
add_test(NAME unit_tests COMMAND tests)vcpkg وكونان
بالنسبة للمشاريع الأكبر حجمًا التي تحتوي على العديد من التبعيات، يوفر مديرو حزم C++ المخصصون دقة أفضل للتبعيات والتخزين المؤقت:
vcpkg (Microsoft) — يتكامل مباشرةً مع CMake عبر ملف سلسلة أدوات. قم بتثبيت الحزم عالميًا أو في "وضع البيان" باستخدام ملفvcpkg.json:
{
"dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}Use with: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Conan — uses a conanfile.txt or conanfile.py to declare dependencies. Supports multiple build configurations and binary caching:
[requires]
fmt/10.2.1
spdlog/1.13.0
[generators]
CMakeDeps
CMakeToolchainيحتفظ كل من مديري الحزم بمستودعات تضم آلاف مكتبات C++ التي تحتوي على ثنائيات تم اختبارها مسبقًا.
اتبع هذه القواعد لـ CMake القابل للصيانة:
1. استخدم دائمًا الأوامر المستندة إلى الهدف :target_include_directories، وtarget_compile_options، وtarget_link_libraries— لا تستخدم أبدًاinclude_directoriesأوadd_definitions(التي تلوث النطاق العالمي).
2. فهم الخاص/العامة/الواجهة:PRIVATE= لهذا الهدف فقط،PUBLIC= لهذا الهدف ومستهلكيه،INTERFACE= للمستهلكين فقط.
3. قم بتعيين معيار C++ لكل هدف أو على مستوى المشروع، ولا يتم ذلك مطلقًا عبر العلامات الأولية مثل-std=c++20.
4. تمكين الحد الأقصى من التحذيرات :-Wall -Wextra -Wpedantic(GCC/Clang) أو/W4(MSVC). خذ بعين الاعتبار-Werror//WXفي CI.
5. استخدمcmake --preset للبنيات القابلة للتكرار (CMakePresets.json).
6. لا تستخدم المسارات الثابتة مطلقًا — استخدمfind_package، أوFetchContent، أو مديري الحزم.
استخدامfile(GLOB ...)لجمع المصادر: لا يتم إعادة تشغيلfile(GLOB SOURCES src/*.cpp)عند إضافة الملفات أو إزالتها. لن يتم تجميع الملفات الجديدة حتى تقوم بإعادة التكوين يدويًا. قم دائمًا بإدراج الملفات المصدر بشكل صريح .
نسيانCMAKE_CXX_EXTENSIONS OFF: بدون ذلك، يستخدم مجلس التعاون الخليجي-std=gnu++20بدلاً من-std=c++20، مما يتيح الامتدادات غير القياسية التي تعطل إمكانية النقل.
خلطadd_subdirectoryوFetchContent: كلاهما يجلب مشاريع خارجية، لكنهما يتفاعلان بشكل مختلف مع ذاكرة التخزين المؤقت للإنشاء. اختر نهجا واحدا لكل تبعية.
عدم تعيينCMAKE_EXPORT_COMPILE_COMMANDS ON: يؤدي هذا إلى إنشاءcompile_commands.json، وهو أمر مطلوب من خلال clang-tidy و clangd (LSP) والعديد من عمليات تكامل IDE. تمكينه دائما.
- CMake هو منشئ نظام البناء القياسي C++ - تعرف على واجهة برمجة التطبيقات المستندة إلى الهدف
- استخدم أوامر
target_*(وليس النطاق العام) مع إمكانية الرؤية الخاصة/العامة/الواجهة - يقوم FetchContent بتنزيل التبعيات وإنشاءها في وقت التهيئة - وهو مثالي لمجموعات التبعيات الصغيرة
- يتعامل vcpkg (الوضع الواضح) وConan مع الرسوم البيانية التبعية الأكبر باستخدام التخزين المؤقت الثنائي
- قم دائمًا بتمكين التحذيرات (
-Wall -Wextra -Wpedantic) واستخدم الإصدارات الخارجية - قم بإنشاء
compile_commands.jsonلـ IDE وأدوات التحليل الثابتة
اختبار — اختبر معرفتك
(15 نقطة خبرة)1. ماذا يفعل`target_link_libraries(myapp PRIVATE mathlib)`؟
2. لماذا يعتبر`file(GLOB SOURCES src/*.cpp)`ممارسة سيئة في CMake؟
3. ما هو الغرض من`CMAKE_CXX_EXTENSIONS OFF`؟