Skip to main content
تخط المحتوى

فهم CMakeLists

التعرف على كيفية عمل ملفات CMakeLists.txt

CMake

في دليلنا التمهيدي، استخدمنا CMake كنظام بناء لتطبيقنا، لكننا ركزنا فقط على أحد ملفات CMakeLists.txt الخاصة بنا. هنا، سنستعرض كيفية عمله بمزيد من التفصيل.

CMake مفيد لأنه يسمح لنا بأتمتة الكثير من الأمور التي يجب القيام بها قبل التجميع.

ملف CMakeLists.txt الجذر

قد تتذكر ملف CMakeLists.txt هذا من الدليل الأول:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
cmake_minimum_required(VERSION 3.20)
project(kirigami-tutorial)

find_package(ECM 6.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMFindQmlModule)
include(ECMQmlModule)

find_package(Qt6 REQUIRED COMPONENTS
    Core
    Quick
    Test
    Gui
    QuickControls2
    Widgets
)

find_package(KF6 REQUIRED COMPONENTS
    Kirigami
    I18n
    CoreAddons
    QQC2DesktopStyle
    IconThemes
)

ecm_find_qmlmodule(org.kde.kirigami REQUIRED)

add_subdirectory(src)

install(PROGRAMS org.kde.tutorial.desktop DESTINATION ${KDE_INSTALL_APPDIR})

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

السطر الأول، cmake_minimum_required() يضبط إصدار CMake الذي سنستدعيه.

بعد ذلك، project(kirigami-tutorial) يُعرِّف اسم المشروع.

ثم نصل إلى قسم حيث نضمّن عددًا من إعدادات CMake وكيدي الضرورية باستخدام extra-cmake-modules. توفر مجموعة من الأدوات المفيدة:

  • يوفر KDEInstallDirs متغيرات تسهيلية مثل ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} و${KDE_INSTALL_QMLDIR} و${KDE_INSTALL_BINDIR} و${KDE_INSTALL_LIBDIR}.
  • يوفر KDECMakeSettings أشياء مثل CMAKE_AUTORCC ON، وهدف uninstall يمكن استخدامه مع cmake --build build/ --target uninstall، وENABLE_CLAZY.
  • يوفر KDECompilerSettings معيار C++ أدنى، وعلميات مترجم مثل -pedantic، ووحدات ماكرو لأفضل الممارسات مثل -DQT_NO_CAST_FROM_ASCII لطلب تحويلات صريحة مثل QStringLiteral().
  • يوفر ECMFindQmlModule طريقة لضمان العثور على تبعية QML وقت التشغيل في وقت التجميع.
  • يوفر ECMQmlModule أوامر CMake مثل ecm_add_qml_module() وecm_target_qml_sources().

القسم التالي مهم، لأنه يحدد التبعيات التي سنضيفها في وقت التجميع. لننظر إلى الأول:

13
14
15
16
17
18
19
20
21
22
23
24
25
26
find_package(Qt6 REQUIRED COMPONENTS
    Core
    Quick
    Test
    Gui
    QuickControls2
    Widgets
)

find_package(KF6 REQUIRED COMPONENTS
    Kirigami
    I18n
    CoreAddons
    QQC2DesktopStyle
  • يجد find_package() المكتبة الخارجية ومكوناتها ويحملها.
  • يخبر REQUIRED CMake بالخروج مع خطأ إذا تعذر العثور على الحزمة.
  • COMPONENTS هو معامل يسبق المكونات المحددة للإطار الذي سنضمّنه.
  • كل كلمة بعد COMPONENTS تشير إلى مكون محدد من المكتبة.

يوجه سطر التثبيت CMake لتثبيت ملف سطح المكتب في ${KDE_INSTALL_APPDIR}، والذي يُترجم على لينكس إلى $XDG_DATA_DIRS/applications، عادةً /usr/share/applications، وعلى ويندوز يُترجم إلى C:/Program Files/${PROJECT_NAME}/bin/data/applications:

34
install(PROGRAMS org.kde.tutorial.desktop DESTINATION ${KDE_INSTALL_APPDIR})

يسمح السطر الأخير لـ CMake بطباعة الحزم التي وجدها، ويجعل التجميع يفشل فورًا إذا واجه خطأً:

36
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

وفوق ذلك، يشير add_subdirectory(src) إلى CMake داخل الدليل src/، حيث يجد ملف CMakeLists.txt آخر.

src/CMakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
add_executable(kirigami-hello)

ecm_add_qml_module(kirigami-hello
    URI
    org.kde.tutorial
)

target_sources(kirigami-hello
    PRIVATE
    main.cpp
)

ecm_target_qml_sources(kirigami-hello
    SOURCES
    Main.qml
)

target_link_libraries(kirigami-hello
    PRIVATE
    Qt6::Quick
    Qt6::Qml
    Qt6::Gui
    Qt6::QuickControls2
    Qt6::Widgets
    KF6::I18n
    KF6::CoreAddons
    KF6::IconThemes
)

install(TARGETS kirigami-hello ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

بينما تعامل الملف الأول مع البيانات الوصفية وإيجاد المكتبات، سيتكون هذا الملف من معالجة التبعيات وتثبيت التطبيق. يحتوي على استدعاءات CMake التالية:

  • ينشئ add_executable() الهدف القابل للتنفيذ الذي سنستخدمه لتشغيل مشروعنا.
  • ينشئ ecm_add_qml_module() هدف وحدة QML سيكون متاحًا عبر الاستيراد "org.kde.tutorial".
  • يضيف target_sources() ملفات مصدر C++ إلى الهدف القابل للتنفيذ.
  • يضيف ecm_target_qml_sources() ملفات QML إلى الوحدة.
  • يربط target_link_libraries() مكتبات C++ المستخدمة في كودنا بملفنا القابل للتنفيذ. لم تُدرج Kirigami هنا لأننا نستخدم وحدة QML الخاصة بها فقط.
  • يثبت install() الملف القابل للتنفيذ في النظام.

يمكن العثور على توثيق الأمرين ECM في واجهة برمجة تطبيقات extra-cmake-modules لوحدة ECMQmlModule.

استُخدم استدعاء ecm_add_qml_module() هنا لتعديل الهدف القابل للتنفيذ التقليدي لرمز C++ المصدر وتحويله إلى شيء يمكنه قبول ملفات QML ورمز C++ المصدر الذي يمكن الوصول إليه من QML فيما يُسمى استخدام الملف القابل للتنفيذ كهدف داعم لوحدة QML. هذا يعني أن ملفات QML تُشغّل مباشرة كجزء من التطبيق، وهو الحال غالبًا للتطبيقات.

يمكنك أيضًا إنشاء وحدة QML منفصلة لا تستخدم الملف القابل للتنفيذ كهدف داعم باستخدام ecm_add_qml_module(). في هذه الحالة، ستنشئ هدف مكتبة باستخدام add_library()، وتربطه بهدف قابل للتنفيذ موجود باستخدام target_link_libraries()، وبالإضافة إلى تثبيت المكتبة بـ install() ستحتاج إلى إنهاء وحدة QML بـ ecm_finalize_qml_module() حتى تتمكن من إنشاء ملفين: qmldir و qmltypes. تُستخدم هذه الملفات بواسطة تطبيقات QtQuick لإيجاد وحدات QML المنفصلة.

طريقة إنشاء وحدة QML منفصلة موضحة بشكل أفضل في استخدام ملفات منفصلة.

هذه إضافات مقدمة من extra-cmake-modules لتسهيل استخدام تسجيل Qt التصريحي (بديل ملفات موارد Qt).

يمكن العثور على توثيق الأوامر الثلاثة في واجهة برمجة تطبيقات extra-cmake-modules لوحدة ECMQmlModule.

src/components/CMakeLists.txt

في البرنامج التعليمي حول كيفية تقسيم كودك إلى ملفات منفصلة، قُدّم ملف CMake جديد للسماح بوحدات QML منفصلة:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
add_library(kirigami-hello-components)

ecm_add_qml_module(kirigami-hello-components
    URI "org.kde.tutorial.components"
    GENERATE_PLUGIN_SOURCE
)

ecm_target_qml_sources(kirigami-hello-components
    SOURCES
    AddDialog.qml
    KountdownDelegate.qml
)

ecm_finalize_qml_module(kirigami-hello-components)

install(TARGETS kirigami-hello-components ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

شرط قراءة هذا الملف بواسطة CMake هو إضافة استدعاء لـ add_subdirectory() في src/CMakeLists.txt يشير إليه.

ننشئ هدفًا جديدًا يُدعى kirigami-hello-components ثم نحوله إلى وحدة QML باستعمال ecm_add_qml_module() تحت اسم الاستيراد org.kde.tutorial.components ونضيف ملفات QML ذات الصلة.

ينشئ استدعاء add_library() هدفًا جديدًا يُسمى kirigami-hello-components. سيكون لهذا الهدف مجموعته الخاصة من ملفات الكود المصدري وملفات QML ويربط مكتباته الخاصة وهكذا، لكنه يحتاج إلى الربط بالملف القابل للتنفيذ، ولكن بمجرد ترجمته يحتاج إلى الربط بالملف القابل للتنفيذ الذي أُنشئ في src/CMakeLists.txt. يتم ذلك بإضافة اسم الهدف إلى قائمة المكتبات التي سترتبط بالملف القابل للتنفيذ في target_link_libraries().

يغير استدعاء ecm_add_qml_module() المكتبة للسماح لها بقبول ملفات QML كما في السابق، لكن هذه المرة نحتاج إلى استخدام GENERATE_PLUGIN_SOURCE. عندما يُستخدم الملف القابل للتنفيذ كهدف داعم (مثل kirigami-hello) لا يحتاج إلى إنشاء كود إضافة لأنه مدمج في الملف القابل للتنفيذ؛ مع وحدات QML المنفصلة مثل kirigami-hello-components يكون كود الإضافة ضروريًا.

ينشئ qt_add_qml_module() الخاص بـ Qt المنبع افتراضيًا إضافة مع وحدة QML، لكن ecm_add_qml_module() الخاص بـ كيدي لا يفعل ذلك افتراضيًا للتوافق مع الإصدارات السابقة.

شيء آخر ضروري لوحدات QML المنفصلة هو إنهاء الهدف. هذا يعني بشكل أساسي أن CMake ينشئ ملفين، qmldir و qmltypes، يصفان وحدات QML التي لدينا ويصدران رموزها للاستخدام في المكتبة. إنها مهمة عند تثبيت تطبيقك حتى يتمكن الملف القابل للتنفيذ الذي يُشغّل من العثور على مكان ملفات QML لكل وحدة، لذا تُضاف تلقائيًا إلى الهدف.

يمكنك بعد ذلك تثبيت الهدف كما في السابق.

في المرة القادمة التي تحتاج فيها إلى إضافة المزيد من ملفات QML، تذكر تضمينها في هذا الملف. يمكن أيضًا إضافة ملفات C++ التي تستخدم الكلمة المفتاحية QML_ELEMENT والتي سنراها لاحقًا في البرنامج التعليمي هنا باستخدام target_sources(). يمكنك فصل كودك منطقيًا عن طريق إنشاء المزيد من وحدات QML باستيرادات مختلفة حسب الحاجة.

سيكون هذا الإعداد مفيدًا عند تطوير معظم تطبيقات Kirigami.