دليل CMake الكامل. الجزء الثاني: بناء النظام


مقدمة


تتناول هذه المقالة استخدام نظام بناء 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: cmake_minimum_required(VERSION 3.0) 

كما هو موضح في التعليقات ، يقوم الأمر cmake_minimum_required بتعيين جميع إشارات التوافق (انظر cmake_policy ). عمد بعض المطورين إلى تعيين نسخة منخفضة من CMake عن قصد ، ثم ضبط الوظيفة يدويًا. يتيح لك هذا إمكانية دعم الإصدارات القديمة من CMake في وقت واحد وفي بعض الأماكن الاستفادة من الميزات الجديدة.


تصميم المشروع


في بداية أي CMakeLists.txt يجب CMakeLists.txt تحديد خصائص المشروع مع فريق المشروع لتصميم أفضل مع بيئات متكاملة وأدوات التطوير الأخرى.


 #    "MyProject": project(MyProject VERSION 1.2.3.4 LANGUAGES C CXX) 

تجدر الإشارة إلى أنه إذا تم حذف الكلمة الأساسية LANGUAGES ، فإن اللغات الافتراضية هي C CXX . يمكنك أيضًا تعطيل الإشارة إلى أي لغة عن طريق كتابة الكلمة الأساسية NONE كقائمة لغات أو مجرد ترك قائمة فارغة.


تشغيل ملفات البرامج النصية


يستبدل أمر include سطر مكالمته برمز الملف المحدد ، ويتصرف بشكل مشابه لأمر الأمر C / C ++ pre include . يعمل هذا المثال على تشغيل ملف البرنامج النصي MyCMakeScript.cmake الأمر الموضح:


 message("'TEST_VARIABLE' is equal to [${TEST_VARIABLE}]") #   `MyCMakeScript.cmake`  : 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> ). مثال نموذجي لاستدعاء هذا الأمر:


 #    "MyExecutable"  #  "ObjectHandler.c", "TimeManager.c"  "MessageGenerator.c": add_executable(MyExecutable ObjectHandler.c TimeManager.c MessageGenerator.c) 

تجميع المكتبة


يقوم الأمر add_library بتجميع المكتبة add_library المحددة والاسم من المصدر. من المهم ملاحظة أن اسم المكتبة النهائي يعتمد على النظام الأساسي الهدف (على سبيل المثال ، lib<LibraryName>.a أو <LibraryName>.lib ). مثال نموذجي لاستدعاء هذا الأمر:


 #    "MyLibrary"  #  "ObjectHandler.c", "TimeManager.c"  "MessageConsumer.c": add_library(MyLibrary STATIC ObjectHandler.c TimeManager.c MessageConsumer.c) 

  • يتم تعريف المكتبات الثابتة بواسطة الكلمة الأساسية STATIC باعتبارها الوسيطة الثانية وهي محفوظات لملفات الكائنات المرتبطة بالملفات القابلة للتنفيذ والمكتبات الأخرى في وقت الترجمة ؛
  • يتم تحديد المكتبات الديناميكية بواسطة الكلمة الأساسية SHARED باعتبارها الوسيطة الثانية ويتم تحميل مكتبات ثنائية بواسطة نظام التشغيل أثناء تنفيذ البرنامج ؛
  • يتم تعريف المكتبات النمطية بواسطة الكلمة الأساسية MODULE باعتبارها الوسيطة الثانية ويتم تحميل مكتبات ثنائية باستخدام تقنية التنفيذ بواسطة الملف التنفيذي نفسه ؛
  • يتم تعريف مكتبات الكائنات بواسطة الكلمة الأساسية OBJECT أنها الوسيطة الثانية وهي عبارة عن مجموعة من ملفات الكائنات المرتبطة بالملفات القابلة للتنفيذ والمكتبات الأخرى في وقت الترجمة.

إضافة مصدر إلى الهدف


هناك حالات تتطلب إضافات متعددة لملفات المصدر إلى الهدف. للقيام بذلك ، يتم target_sources الأمر target_sources ، والذي يمكن أن يضيف المصادر إلى الهدف عدة مرات.


الوسيطة الأولى للأمر target_sources هي اسم الهدف المحدد مسبقًا باستخدام add_executable أو add_executable ، add_executable اللاحقة هي قائمة بالملفات المصدر المطلوب إضافتها.


تضيف المكالمات المتكررة إلى target_sources الملفات المصدر إلى الهدف بالترتيب الذي تم استدعاؤه به ، وبالتالي فإن مجموعتي الكود السفليتين من الكود متساويان وظيفيًا:


 #    "MyExecutable"   # "ObjectPrinter.c"  "SystemEvaluator.c": add_executable(MyExecutable ObjectPrinter.c SystemEvaluator.c) #    "MyExecutable"  "MessageConsumer.c": target_sources(MyExecutable MessageConsumer.c) #    "MyExecutable"  "ResultHandler.c": target_sources(MyExecutable ResultHandler.c) 

 #    "MyExecutable"   # "ObjectPrinter.c", "SystemEvaluator.c", "MessageConsumer.c"  "ResultHandler.c": add_executable(MyExecutable ObjectPrinter.c SystemEvaluator.c MessageConsumer.c ResultHandler.c) 

الملفات التي تم إنشاؤها


يتم تحديد موقع ملفات الإخراج التي تم إنشاؤها بواسطة add_library و add_library فقط في مرحلة التوليد ، ومع ذلك ، يمكن تغيير هذه القاعدة بعدة متغيرات تحدد الموقع النهائي للملفات الثنائية:



تعتبر الملفات القابلة للتنفيذ دائمًا أهدافًا للتنفيذ ، وتعتبر المكتبات الثابتة أهدافًا للأرشيف ، وتعتبر المكتبات النمطية أهدافًا للمكتبات. بالنسبة للأنظمة الأساسية "غير DLL" ، تعتبر المكتبات الديناميكية أهدافًا للمكتبة ، ومن أجل "منصات DLL" ، أهداف التنفيذ. لا يتم توفير هذه المتغيرات لمكتبات الكائنات ، حيث يتم إنشاء هذا النوع من المكتبات في أحشاء دليل CMakeFiles .


من المهم ملاحظة أن جميع الأنظمة الأساسية المستندة إلى Windows ، بما في ذلك Cygwin ، تعتبر "أنظمة DLL".


تخطيط المكتبة


يقوم الأمر target_link_libraries مكتبة أو تنفيذ مع المكتبات الأخرى المتوفرة. الوسيطة الأولى لهذا الأمر هي اسم الهدف الذي تم إنشاؤه بواسطة add_library أو add_library ، add_library اللاحقة هي أسماء أهداف المكتبة أو المسارات الكاملة للمكتبات. مثال:


 #    "MyExecutable"  #  "JsonParser", "SocketFactory"  "BrowserInvoker": target_link_libraries(MyExecutable JsonParser SocketFactory BrowserInvoker) 

تجدر الإشارة إلى أن المكتبات المعيارية لا يمكن ربطها بالملفات القابلة للتنفيذ أو المكتبات الأخرى ، لأنها مخصصة فقط للتحميل بواسطة تقنيات التنفيذ.


العمل مع الأهداف


كما هو مذكور في التعليقات ، تخضع الأهداف في CMake أيضًا للتلاعب اليدوي ، مهما كانت محدودة للغاية.


من الممكن التحكم في خصائص الأهداف المصممة لضبط عملية تجميع المشروع. get_target_property الأمر get_target_property قيمة الخاصية الهدف إلى المتغير get_target_property . يعرض هذا المثال قيمة خاصية C_STANDARD لهدف C_STANDARD على الشاشة:


 #   "VALUE"   "C_STANDARD": get_target_property(VALUE MyTarget C_STANDARD) #      : message("'C_STANDARD' property is equal to [${VALUE}]") 

set_target_properties الأمر set_target_properties خصائص الهدف المحددة إلى القيم المحددة. يقبل هذا الأمر قائمة بالأهداف التي سيتم تعيين قيم الخصائص لها ، ثم الكلمة الرئيسية PROPERTIES ، متبوعة بقائمة النموذج < > < > :


 #   'C_STANDARD'  "11", #   'C_STANDARD_REQUIRED'  "ON": set_target_properties(MyTarget PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED ON) 

المثال أعلاه MyTarget خصائص أهداف MyTarget التي تؤثر على عملية التحويل البرمجي ، وهي: عند ترجمة هدف MyTarget CMake MyTarget المحول البرمجي استخدام معيار C11. كل أسماء العقارات المستهدفة المعروفة مدرجة في هذه الصفحة .


من الممكن أيضًا التحقق من الأهداف المحددة مسبقًا باستخدام بنية if(TARGET <TargetName>) :


 #  "The target was defined!"   "MyTarget"  , #    "The target was not defined!": if(TARGET MyTarget) message("The target was defined!") else() message("The target was not defined!") endif() 

مضيفا المشاريع الفرعية


يطالب الأمر add_subdirectory CMake بمعالجة ملف المشروع الفرعي المحدد على الفور. يوضح المثال التالي تطبيق الآلية الموضحة:


 #   "subLibrary"    , #       "subLibrary/build": add_subdirectory(subLibrary subLibrary/build) 

في هذا المثال ، تكون الوسيطة الأولى للأمر 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 ثم ربط:


 #     "GSL": find_package(GSL 2.5 REQUIRED) #      "GSL": target_link_libraries(MyExecutable GSL::gsl) #      "GSL": target_include_directories(MyExecutable ${GSL_INCLUDE_DIRS}) 

في المثال أعلاه ، يقبل الأمر 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) #     "RequestGenerator": target_include_directories(RequestGenerator headers/specific) #    "RequestGenerator"  "ResponseGenerator": include_directories(headers) 

يشار في التعليقات إلى أن استخدام link_libraries include_directories و link_libraries غير مرغوب فيه في المشروعات الحديثة. البديل هو target_link_libraries target_include_directories و target_link_libraries التي تعمل فقط على أهداف محددة ، وليس على النطاق الحالي بأكمله.


تركيب المشروع


ينشئ الأمر install قواعد تثبيت لمشروعك. هذا الأمر قادر على العمل مع الأهداف والملفات والمجلدات والمزيد. أولا ، النظر في تحديد الأهداف.


لتعيين الأهداف ، يجب أن تمر كلمة TARGETS باعتبارها الوسيطة الأولى للوظيفة الموصوفة ، متبوعة بقائمة الأهداف المراد تعيينها ، ثم الكلمة الأساسية DESTINATION مع موقع الدليل حيث سيتم تعيين الأهداف المحددة. يوضح هذا المثال إعداد هدف نموذجي:


 #   "TimePrinter"  "DataScanner"   "bin": install(TARGETS TimePrinter DataScanner DESTINATION bin) 

تتشابه عملية وصف تثبيت الملفات ، باستثناء أنه يجب TARGETS تحديد FILES بدلاً من الكلمة الأساسية TARGETS . مثال يوضح تثبيت الملفات:


 #   "DataCache.txt"  "MessageLog.txt"   "~/": install(FILES DataCache.txt MessageLog.txt DESTINATION ~/) 

تتشابه عملية وصف تثبيت المجلدات ، باستثناء أنه يجب عليك تحديد DIRECTORY بدلاً من الكلمة الأساسية FILES . من المهم ملاحظة أنه أثناء التثبيت ، سيتم نسخ محتويات المجلد بالكامل ، وليس فقط اسمها. مثال على تثبيت المجلدات كما يلي:


 #   "MessageCollection"  "CoreFiles"   "~/": install(DIRECTORY MessageCollection CoreFiles DESTINATION ~/) 

بعد الانتهاء من معالجة 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 ، والذي يحفز معالجة المشروع الفرعي ، وأخيراً ، يتم ربط الملف القابل للتنفيذ بالمكتبة المترجمة:


 #    CMake: cmake_minimum_required(VERSION 3.0) #   : project(MyProgram VERSION 1.0.0 LANGUAGES C) #      "MyProgram": add_executable(MyProgram StartProgram.c) #    "core/CMakeFiles.txt": add_subdirectory(core) #    "MyProgram"  #    "MyProgramCore": target_link_libraries(MyProgram MyProgramCore) #    "MyProgram"   "bin": install(TARGETS MyProgram DESTINATION bin) 

يتم core/CMakeLists.txt الملف core/CMakeLists.txt بواسطة ملف التجميع الرئيسي ويقوم بتجميع المكتبة الثابتة MyProgramCore المعدة للربط مع الملف القابل للتنفيذ:


 #    CMake: cmake_minimum_required(VERSION 3.0) #      "MyProgramCore": add_library(MyProgramCore STATIC ProcessInvoker.c SystemManager.c) 

بعد سلسلة من أوامر cmake . && make && sudo checkinstall cmake . && make && sudo checkinstall يكمل نظام بناء CMake بنجاح. يبدأ الأمر الأول في معالجة ملف CMakeLists.txt في الدليل الجذر للمشروع ، CMakeLists.txt الأمر الثاني في النهاية بتجميع الملفات الثنائية اللازمة ، ويقوم الأمر الثالث بتثبيت MyProgram المترجمة MyProgram في النظام.


الخاتمة


أنت الآن قادر على كتابة ملفاتك الخاصة وفهم ملفات CMake الخاصة بالأشخاص الآخرين ، ويمكنك قراءة التفاصيل حول الآليات الأخرى على الموقع الرسمي .


ستركز المقالة التالية في هذا الدليل على اختبار وإنشاء حزم باستخدام CMake وسيتم إصدارها في غضون أسبوع. اراك قريبا!

Source: https://habr.com/ru/post/ar432096/


All Articles