
مقدمة
تتناول هذه المقالة استخدام نظام بناء CMake المستخدم في عدد كبير من مشاريع C / C ++. يوصى بشدة بقراءة الجزء الأول من الدليل لتجنب سوء فهم بناء جملة لغة CMake ، والذي يظهر بوضوح في جميع أنحاء المقالة.
إطلاق CMake
فيما يلي أمثلة لاستخدام لغة CMake التي يجب عليك ممارستها. تجربة مع شفرة المصدر عن طريق تعديل الأوامر الحالية وإضافة جديدة. لتشغيل هذه الأمثلة ، قم بتثبيت CMake من الموقع الرسمي .
مبدأ العمل
نظام بناء CMake عبارة عن مجمّع على الأدوات المساعدة الأخرى التي تعتمد على النظام الأساسي (على سبيل المثال ، Ninja أو Make ). وبالتالي ، في عملية التجميع نفسها ، بغض النظر عن مدى التناقض الذي قد يبدو هذا ، فإنه لا يشارك مباشرة.
يقبل نظام إنشاء CMake ملف CMakeLists.txt
مع وصف لقواعد الإنشاء بلغة CMake الرسمية ، ثم يقوم بإنشاء ملفات إنشاء وسيطة CMakeLists.txt
في نفس الدليل المقبول على النظام الأساسي الخاص بك.
ستحتوي الملفات التي تم إنشاؤها على أسماء محددة من أدوات النظام المساعدة والدلائل والمترجمين ، بينما أوامر CMake تستخدم فقط المفهوم التجريدي للمجمع البرمجي ولا ترتبط بأدوات تعتمد على النظام الأساسي والتي تختلف اختلافًا كبيرًا عن أنظمة التشغيل المختلفة.
التحقق من CMake الإصدار
يتحقق الأمر cmake_minimum_required
من الإصدار قيد التشغيل من CMake: إذا كان أقل من الحد الأدنى المحدد ، فسيتم إنهاء CMake مع حدوث خطأ فادح. مثال يوضح الاستخدام النموذجي لهذا الأمر في بداية أي ملف CMake:
كما هو موضح في التعليقات ، يقوم الأمر cmake_minimum_required
بتعيين جميع إشارات التوافق (انظر cmake_policy
). عمد بعض المطورين إلى تعيين نسخة منخفضة من CMake عن قصد ، ثم ضبط الوظيفة يدويًا. يتيح لك هذا إمكانية دعم الإصدارات القديمة من CMake في وقت واحد وفي بعض الأماكن الاستفادة من الميزات الجديدة.
في بداية أي CMakeLists.txt
يجب CMakeLists.txt
تحديد خصائص المشروع مع فريق المشروع لتصميم أفضل مع بيئات متكاملة وأدوات التطوير الأخرى.
تجدر الإشارة إلى أنه إذا تم حذف الكلمة الأساسية LANGUAGES
، فإن اللغات الافتراضية هي C CXX
. يمكنك أيضًا تعطيل الإشارة إلى أي لغة عن طريق كتابة الكلمة الأساسية NONE
كقائمة لغات أو مجرد ترك قائمة فارغة.
تشغيل ملفات البرامج النصية
يستبدل أمر include
سطر مكالمته برمز الملف المحدد ، ويتصرف بشكل مشابه لأمر الأمر C / C ++ pre include
. يعمل هذا المثال على تشغيل ملف البرنامج النصي MyCMakeScript.cmake
الأمر الموضح:
message("'TEST_VARIABLE' is equal to [${TEST_VARIABLE}]")
في هذا المثال ، TEST_VARIABLE
الرسالة الأولى أن المتغير TEST_VARIABLE
لم يتم تعريفه بعد ، ومع ذلك ، إذا كان البرنامج النصي MyCMakeScript.cmake
هذا المتغير ، MyCMakeScript.cmake
الرسالة الثانية بالفعل عن القيمة الجديدة لمتغير الاختبار. وبالتالي ، لا يُنشئ ملف البرنامج النصي المضمن في أمر include
نطاقه الخاص ، والذي تم ذكره في التعليقات على المقالة السابقة .
تجميع الملفات القابلة للتنفيذ
يقوم الأمر add_executable
بتصنيف الملف القابل للتنفيذ بالاسم المحدد من القائمة المصدر. من المهم ملاحظة أن اسم الملف النهائي يعتمد على النظام الأساسي الهدف (على سبيل المثال ، <ExecutableName>.exe
أو فقط <ExecutableName>
). مثال نموذجي لاستدعاء هذا الأمر:
تجميع المكتبة
يقوم الأمر add_library
بتجميع المكتبة add_library
المحددة والاسم من المصدر. من المهم ملاحظة أن اسم المكتبة النهائي يعتمد على النظام الأساسي الهدف (على سبيل المثال ، lib<LibraryName>.a
أو <LibraryName>.lib
). مثال نموذجي لاستدعاء هذا الأمر:
- يتم تعريف المكتبات الثابتة بواسطة الكلمة الأساسية
STATIC
باعتبارها الوسيطة الثانية وهي محفوظات لملفات الكائنات المرتبطة بالملفات القابلة للتنفيذ والمكتبات الأخرى في وقت الترجمة ؛ - يتم تحديد المكتبات الديناميكية بواسطة الكلمة الأساسية
SHARED
باعتبارها الوسيطة الثانية ويتم تحميل مكتبات ثنائية بواسطة نظام التشغيل أثناء تنفيذ البرنامج ؛ - يتم تعريف المكتبات النمطية بواسطة الكلمة الأساسية
MODULE
باعتبارها الوسيطة الثانية ويتم تحميل مكتبات ثنائية باستخدام تقنية التنفيذ بواسطة الملف التنفيذي نفسه ؛ - يتم تعريف مكتبات الكائنات بواسطة الكلمة الأساسية
OBJECT
أنها الوسيطة الثانية وهي عبارة عن مجموعة من ملفات الكائنات المرتبطة بالملفات القابلة للتنفيذ والمكتبات الأخرى في وقت الترجمة.
إضافة مصدر إلى الهدف
هناك حالات تتطلب إضافات متعددة لملفات المصدر إلى الهدف. للقيام بذلك ، يتم target_sources
الأمر target_sources
، والذي يمكن أن يضيف المصادر إلى الهدف عدة مرات.
الوسيطة الأولى للأمر target_sources
هي اسم الهدف المحدد مسبقًا باستخدام add_executable
أو add_executable
، add_executable
اللاحقة هي قائمة بالملفات المصدر المطلوب إضافتها.
تضيف المكالمات المتكررة إلى target_sources
الملفات المصدر إلى الهدف بالترتيب الذي تم استدعاؤه به ، وبالتالي فإن مجموعتي الكود السفليتين من الكود متساويان وظيفيًا:
الملفات التي تم إنشاؤها
يتم تحديد موقع ملفات الإخراج التي تم إنشاؤها بواسطة add_library
و add_library
فقط في مرحلة التوليد ، ومع ذلك ، يمكن تغيير هذه القاعدة بعدة متغيرات تحدد الموقع النهائي للملفات الثنائية:
تعتبر الملفات القابلة للتنفيذ دائمًا أهدافًا للتنفيذ ، وتعتبر المكتبات الثابتة أهدافًا للأرشيف ، وتعتبر المكتبات النمطية أهدافًا للمكتبات. بالنسبة للأنظمة الأساسية "غير DLL" ، تعتبر المكتبات الديناميكية أهدافًا للمكتبة ، ومن أجل "منصات DLL" ، أهداف التنفيذ. لا يتم توفير هذه المتغيرات لمكتبات الكائنات ، حيث يتم إنشاء هذا النوع من المكتبات في أحشاء دليل CMakeFiles
.
من المهم ملاحظة أن جميع الأنظمة الأساسية المستندة إلى Windows ، بما في ذلك Cygwin ، تعتبر "أنظمة DLL".
تخطيط المكتبة
يقوم الأمر target_link_libraries
مكتبة أو تنفيذ مع المكتبات الأخرى المتوفرة. الوسيطة الأولى لهذا الأمر هي اسم الهدف الذي تم إنشاؤه بواسطة add_library
أو add_library
، add_library
اللاحقة هي أسماء أهداف المكتبة أو المسارات الكاملة للمكتبات. مثال:
تجدر الإشارة إلى أن المكتبات المعيارية لا يمكن ربطها بالملفات القابلة للتنفيذ أو المكتبات الأخرى ، لأنها مخصصة فقط للتحميل بواسطة تقنيات التنفيذ.
العمل مع الأهداف
كما هو مذكور في التعليقات ، تخضع الأهداف في CMake أيضًا للتلاعب اليدوي ، مهما كانت محدودة للغاية.
من الممكن التحكم في خصائص الأهداف المصممة لضبط عملية تجميع المشروع. get_target_property
الأمر get_target_property
قيمة الخاصية الهدف إلى المتغير get_target_property
. يعرض هذا المثال قيمة خاصية C_STANDARD
لهدف C_STANDARD
على الشاشة:
set_target_properties
الأمر set_target_properties
خصائص الهدف المحددة إلى القيم المحددة. يقبل هذا الأمر قائمة بالأهداف التي سيتم تعيين قيم الخصائص لها ، ثم الكلمة الرئيسية PROPERTIES
، متبوعة بقائمة النموذج < > < >
:
المثال أعلاه MyTarget
خصائص أهداف MyTarget
التي تؤثر على عملية التحويل البرمجي ، وهي: عند ترجمة هدف MyTarget
CMake MyTarget
المحول البرمجي استخدام معيار C11. كل أسماء العقارات المستهدفة المعروفة مدرجة في هذه الصفحة .
من الممكن أيضًا التحقق من الأهداف المحددة مسبقًا باستخدام بنية if(TARGET <TargetName>)
:
مضيفا المشاريع الفرعية
يطالب الأمر add_subdirectory
CMake بمعالجة ملف المشروع الفرعي المحدد على الفور. يوضح المثال التالي تطبيق الآلية الموضحة:
في هذا المثال ، تكون الوسيطة الأولى للأمر add_subdirectory
هي المشروع الفرعي add_subdirectory
الفرعية ، والوسيطة الثانية اختيارية وتُعلم CMake بالمجلد المخصص للملفات التي تم إنشاؤها للمشروع الفرعي المضمّن (على سبيل المثال ، CMakeCache.txt
و cmake_install.cmake
).
تجدر الإشارة إلى أن جميع المتغيرات من النطاق الأصل يتم توارثها بواسطة الدليل المُضاف ، وأن جميع المتغيرات المعرّفة PARENT_SCOPE
تعريفها في هذا الدليل ستكون مرئية فقط له (إذا PARENT_SCOPE
يتم تحديد الكلمة الأساسية PARENT_SCOPE
بواسطة وسيطة أمر set
). تم ذكر هذه الميزة في التعليقات على المقال السابق .
حزمة البحث
find_package
الأمر find_package
عن إعدادات مشروع خارجي ويقوم find_package
. في معظم الحالات ، يتم استخدامه للربط اللاحق للمكتبات الخارجية مثل Boost و GSL . يستدعي هذا المثال الأمر الموضح للبحث عن مكتبة GSL ثم ربط:
في المثال أعلاه ، يقبل الأمر find_package
اسم الحزمة find_package
الأولى ، ثم الإصدار المطلوب. يتطلب الخيار REQUIRED
طباعة خطأ فادح وإنهاء CMake إذا لم يتم العثور على الحزمة المطلوبة. العكس هو خيار QUIET
، الذي يتطلب CMake لمواصلة عملها ، حتى لو لم يتم العثور على الحزمة.
بعد ذلك ، MyExecutable
ربط MyExecutable
بمكتبة GSL باستخدام الأمر target_link_libraries
باستخدام متغير GSL::gsl
، الذي يتضمن موقع GSL المترجمة بالفعل.
في النهاية ، يتم target_include_directories
الأمر target_include_directories
، لإعلام المترجم بموقع ملفات رأس مكتبة GSL. يرجى ملاحظة أن متغير GSL_INCLUDE_DIRS
يُستخدم GSL_INCLUDE_DIRS
موقع الرؤوس التي وصفتها (هذا مثال على إعدادات الحزمة المستوردة).
قد ترغب في التحقق من نتائج بحث الحزمة إذا حددت خيار QUIET
. يمكن القيام بذلك عن طريق التحقق من <PackageName>_FOUND
، والذي يتم تحديده تلقائيًا بعد find_package
أمر find_package
. على سبيل المثال ، إذا قمت باستيراد إعدادات GSL بنجاح إلى مشروعك ، GSL_FOUND
متغير GSL_FOUND
صحيحًا.
بشكل عام ، find_package
الأمر find_package
على اثنين من نكهات التشغيل: وحدات وتكوين. المثال أعلاه تطبيق نموذج وحدات. هذا يعني أنه عند استدعاء الأمر ، يبحث CMake عن ملف نصي للنموذج. Find<PackageName>.cmake
في دليل CMAKE_MODULE_PATH
، ثم يبدأ تشغيله ويستورد كل الإعدادات اللازمة (في هذه الحالة ، أطلقت CMake ملف FindGSL.cmake
القياسي).
طرق لتشمل الرؤوس
يمكنك إبلاغ المحول البرمجي بموقع الرؤوس المضمنة باستخدام أمرين: include_directories
و target_include_directories
. عليك أن تقرر أي واحد لاستخدام ، ومع ذلك ، فإنه يستحق النظر في بعض الاختلافات بينهما (الفكرة المقترحة في التعليقات ).
يؤثر الأمر include_directories
على نطاق الدليل. هذا يعني أنه سيتم استخدام جميع دلائل الرأس المحددة بواسطة هذا الأمر لجميع أغراض CMakeLists.txt
الحالية ، وكذلك للمشاريع الفرعية التي تمت معالجتها (انظر add_subdirectory
).
يؤثر الأمر target_include_directories
على الهدف المحدد بواسطة الوسيطة الأولى فقط ولا يؤثر على الأهداف الأخرى. يوضح المثال التالي الفرق بين الأمرين:
add_executable(RequestGenerator RequestGenerator.c) add_executable(ResponseGenerator ResponseGenerator.c)
يشار في التعليقات إلى أن استخدام link_libraries
include_directories
و link_libraries
غير مرغوب فيه في المشروعات الحديثة. البديل هو target_link_libraries
target_include_directories
و target_link_libraries
التي تعمل فقط على أهداف محددة ، وليس على النطاق الحالي بأكمله.
تركيب المشروع
ينشئ الأمر install
قواعد تثبيت لمشروعك. هذا الأمر قادر على العمل مع الأهداف والملفات والمجلدات والمزيد. أولا ، النظر في تحديد الأهداف.
لتعيين الأهداف ، يجب أن تمر كلمة TARGETS
باعتبارها الوسيطة الأولى للوظيفة الموصوفة ، متبوعة بقائمة الأهداف المراد تعيينها ، ثم الكلمة الأساسية DESTINATION
مع موقع الدليل حيث سيتم تعيين الأهداف المحددة. يوضح هذا المثال إعداد هدف نموذجي:
تتشابه عملية وصف تثبيت الملفات ، باستثناء أنه يجب TARGETS
تحديد FILES
بدلاً من الكلمة الأساسية TARGETS
. مثال يوضح تثبيت الملفات:
تتشابه عملية وصف تثبيت المجلدات ، باستثناء أنه يجب عليك تحديد DIRECTORY
بدلاً من الكلمة الأساسية FILES
. من المهم ملاحظة أنه أثناء التثبيت ، سيتم نسخ محتويات المجلد بالكامل ، وليس فقط اسمها. مثال على تثبيت المجلدات كما يلي:
بعد الانتهاء من معالجة CMake لجميع ملفاتك ، يمكنك تثبيت جميع الكائنات الموصوفة باستخدام sudo checkinstall
(إذا كان CMake ينشئ Makefile
) ، أو يمكنك تنفيذ هذا الإجراء مع بيئة التطوير المتكاملة التي تدعم CMake.
مثال مرئي للمشروع
لن يكون هذا الدليل كاملاً دون إظهار مثال في العالم الحقيقي لاستخدام نظام بناء CMake. النظر في مخطط مشروع بسيط باستخدام CMake كنظام البناء الوحيد:
+ MyProject - CMakeLists.txt - Defines.h - StartProgram.c + core - CMakeLists.txt - Core.h - ProcessInvoker.c - SystemManager.c
يصف ملف التجميع الرئيسي CMakeLists.txt
مجموعة البرنامج بأكمله: أولاً ، يتم add_executable
الأمر add_executable
الذي يقوم بتجميع الملف القابل للتنفيذ ، ثم يتم add_subdirectory
الأمر add_subdirectory
، والذي يحفز معالجة المشروع الفرعي ، وأخيراً ، يتم ربط الملف القابل للتنفيذ بالمكتبة المترجمة:
يتم core/CMakeLists.txt
الملف core/CMakeLists.txt
بواسطة ملف التجميع الرئيسي ويقوم بتجميع المكتبة الثابتة MyProgramCore
المعدة للربط مع الملف القابل للتنفيذ:
بعد سلسلة من أوامر cmake . && make && sudo checkinstall
cmake . && make && sudo checkinstall
يكمل نظام بناء CMake بنجاح. يبدأ الأمر الأول في معالجة ملف CMakeLists.txt
في الدليل الجذر للمشروع ، CMakeLists.txt
الأمر الثاني في النهاية بتجميع الملفات الثنائية اللازمة ، ويقوم الأمر الثالث بتثبيت MyProgram
المترجمة MyProgram
في النظام.
الخاتمة
أنت الآن قادر على كتابة ملفاتك الخاصة وفهم ملفات CMake الخاصة بالأشخاص الآخرين ، ويمكنك قراءة التفاصيل حول الآليات الأخرى على الموقع الرسمي .
ستركز المقالة التالية في هذا الدليل على اختبار وإنشاء حزم باستخدام CMake وسيتم إصدارها في غضون أسبوع. اراك قريبا!