قصة نجاح نقل ScreenPlay من QMake إلى CMake

ScreenPlay هو تطبيق مفتوح المصدر لنظام Windows (وقريبًا أيضًا لنظامي Linux و macOS) مصمم للعمل مع الخلفيات والأدوات. تم إنشاؤه باستخدام أدوات حديثة (C ++ / Qt / QML) ويعمل بنشاط عليه منذ النصف الأول من عام 2017. يتم تخزين كود المشروع على منصة GitLab . يقوم مؤلف المقال ، الذي ننشر ترجمته اليوم ، بتطوير ScreenPlay. واجه عددًا من المشكلات التي ساعدها الانتقال من QMake إلى CMake.











QMake وتطوير المشاريع الكبيرة



▍مشاركة الرمز في QMake أمر غير مريح للغاية



عند تطوير تطبيقات معقدة إلى حد ما ، فمن الأفضل عادةً تقسيمها إلى أجزاء صغيرة يمكن التحكم فيها. على سبيل المثال ، إذا كنت بحاجة إلى تقديم تطبيقك كملف تنفيذي رئيسي تتصل به المكتبات ، فعندئذٍ باستخدام QMake ، يمكن القيام بذلك فقط باستخدام مشروع يعتمد على قالب subdirs. هذا مشروع ، يتم تمثيله بملف ، على سبيل المثال MyApp.pro، يحتوي على إدخال للقالب المستخدم وقائمة مجلدات المشروع:



  TEMPLATE = subdirs
 
  SUBDIRS = \
            src/app \   #  
            src/lib \
            src/lib2


باستخدام هذا النهج ، لدينا العديد من المشاريع الفرعية تحت تصرفنا والتي نحتاج فيها إلى تنظيم مشاركة الكود. من أجل إخبار المترجم بالمكان الذي يحتاج إليه بالضبط في المشاريع الأخرى للبحث عن ملفات الرأس والملفات ذات التعليمات البرمجية المصدر ، تحتاج إلى إعطاء معلومات الرابط حول المكتبات التي يحتاج إلى تضمينها ومكان العثور على الملفات المترجمة. يقوم QMake بذلك عن طريق إنشاء ملفات .pri ضخمة تُستخدم فقط لوصف ما يجب تضمينه في المشروع. هذا مشابه لاستخدام بنيات عرض C ++ العادية #include <xyz.h>. نتيجة لذلك ، اتضح ، على سبيل المثال ، أن الملف MyProjectName.priمدرج في التكوين MyProjectName.pro. ولإصلاح مشكلة المسار النسبي ، أضف المسار المطلق الحالي لكل سطر.



▍ التبعيات الخارجية



يتم تقليل العمل مع التبعيات الخارجية المخصصة لأنظمة التشغيل المختلفة بشكل أساسي إلى نسخ المسارات إلى التبعيات المقابلة ولصقها في ملف .pro. هذه وظيفة مملة ومملة ، لأن كل نظام تشغيل له خصائصه الخاصة في هذا الصدد. على سبيل المثال، لينكس لايوجد المجلدات الفرعية منفصلة debugو release.



▍CONFIG + = Order هو قاتل لأداء الترجمة



عيب آخر في QMake هو أنه يحتوي على مشاكل ترجمة متقطعة. لذلك ، إذا كان المشروع يحتوي على العديد من المشاريع الفرعية ، وهي مكتبات مستخدمة في مشاريع فرعية أخرى ، فسيخفق التجميع بشكل دوري. قد يكون سبب الخطأ شيء من هذا القبيل: libAتعتمد المكتبة على المكتبات libBو libC. ولكن بحلول وقت التجميع ، لم تكن libAالمكتبة libCجاهزة بعد. عادة ما تنتهي المشكلة عند إعادة تجميع المشروع. لكن حقيقة حدوث ذلك على الإطلاق تشير إلى وجود مشكلة خطيرة في QMake. وهذه المشاكل لا يمكن حلها باستخدام شيء مثلlibA.depends = libB... ربما (وربما يكون الأمر كذلك) ، أنا أفعل شيئًا خاطئًا ، لكن لم أتمكن أنا ولا زملائي من التعامل مع المشكلة. الطريقة الوحيدة لحل مشكلة ترتيب إنشاء المكتبات هي استخدام التخصيص CONFIG += ordered، ولكن بسبب هذا ، من خلال التخلص من البناء المتوازي ، يعاني الأداء بشكل كبير.



QBS و CMake



▍لماذا تخسر QBS أمام CMake؟



كانت الرسالة المتعلقة بانتهاء دعم QBS (Qt Build System ، Qt build system) بمثابة صدمة حقيقية لي. كنت حتى من المبادرين لمحاولة تغيير هذا. يستخدم QBS صيغة لطيفة مألوفة لأي شخص سبق أن كتب كود QML. لا يمكنني قول الشيء نفسه عن CMake ، ولكن بعد العمل مع نظام بناء المشروع هذا لعدة أشهر ، يمكنني القول بثقة أن التحول إليه من QBS كان القرار الصحيح ، وأنني سأستمر في استخدام CMake ...



CMake ، على الرغم من أنه يحتوي على بعض العيوب النحوية ، يعمل بشكل موثوق. ومشاكل QBS سياسية أكثر منها فنية.



هذا هو أحد العوامل الرئيسية التي تجبر المبرمجين غير الراضين عن حجم Qt (سواء من حيث عدد أسطر الكود وحجم المكتبة) للبحث عن بديل. بالإضافة إلى ذلك ، يكره الكثير من الناس بشدة MOC. وهو عبارة عن مترجم كائن meta يقوم بتحويل كود C ++ المكتوب باستخدام Qt إلى C ++ عادي. بفضل هذا المجمع ، يمكنك ، على سبيل المثال ، استخدام التركيبات الملائمة ، مثل تلك التي تسمح لك بالعمل مع الإشارات.



▍ بدائل QBS



بالإضافة إلى QBS ، لدينا تحت تصرفنا أنظمة بناء المشاريع مثل build2 و CMake و Meson و SCons. يتم استخدامها ، خارج نظام Qt البيئي ، في العديد من المشاريع.



▍ دعم QBS ضعيف في IDE



وبقدر ما أعرف ، فإن IDE الوحيد الذي يدعم QBS هو QtCreator.



▍ اتحاد رائع بين vcpkg و CMake



تذكر كيف استاءت من مشاكل التبعيات الخارجية أعلاه؟ لذلك ليس من المستغرب مقدار المشاعر الإيجابية التي أعطاني إياها مدير حزم vcpkg. أمر واحد يكفي لتثبيت التبعية! أعتقد أن vcpkg يمكن أن يكون مفيدًا لأي مبرمج C ++.



بناء الجملة CMake يبدو غير جذاب



إذا حكمت على CMake من الروابط العشرة الأولى التي وجدتها Google ، فقد يبدو أن هذا النظام يستخدم صياغة غير جذابة للغاية. لكن المشكلة هنا هي أن Google هي أول من عرض عناصر CMake القديمة من Stack Overflow ، بتاريخ 2008. تظهر أيضًا روابط إلى الوثائق القديمة لإصدار CMake 2.8. يمكن أن تكون البنية المستخدمة عند العمل مع CMake جميلة جدًا. الحقيقة هي أن استخدام CMake يتضمن أساسًا استخدام الإنشاءات الموضحة أدناه (هذه نسخة مختصرة من ملف CMakeList.txt من مشروع ScreenPlay).



#   
cmake_minimum_required(VERSION 3.16.0)

#   .       
#       ${PROJECT_NAME}
project(ScreenPlay)

#   Qt,    MOC
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)

#  -   .    src,
#   .       add_executable
set(src main.cpp
        app.cpp
        #  - 
        src/util.cpp
        src/create.cpp)

set(headers app.h
        src/globalvariables.h
        #   - 
        src/util.h
        src/create.h)

#  Qt     
qt5_add_big_resources(resources  resources.qrc)

#  CMake  qml  C++   release
#   !
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    set(qml qml.qrc)
else()
    qtquick_compiler_add_resources(qml qml.qrc )
endif()

#  CMake   .  ,   ,  CMAKE_TOOLCHAIN_FILE
#         !
find_package(
  Qt5
  COMPONENTS Quick
             QuickCompiler
             Widgets
             Gui
             WebEngine
  REQUIRED)

#   vcpkg
find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(libzippp CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

#  CMake    : 
# add_executable    
# add_library   
add_executable(${PROJECT_NAME} ${src} ${headers} ${resources} ${qml})

#        Windows
# https://stackoverflow.com/questions/8249028/how-do-i-keep-my-qt-c-program-from-opening-a-console-in-windows
set_property(TARGET ${PROJECT_NAME} PROPERTY WIN32_EXECUTABLE true)

#     .    
#     vcpkg.      
# dll/lib/so/dynlib  vcpkg/installed
#      , 
#   project(MyLib)  target_link_libraries.
#        .
target_link_libraries(${PROJECT_NAME}
    PRIVATE
    Qt5::Quick
    Qt5::Gui
    Qt5::Widgets
    Qt5::Core
    Qt5::WebEngine
    nlohmann_json::nlohmann_json
    libzippp::libzippp
    ScreenPlaySDK
    QTBreakpadplugin)

#  CMake      build   ,   .
# ${CMAKE_BINARY_DIR} -   build!
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/assets/fonts)
configure_file(assets/fonts/NotoSansCJKkr-Regular.otf ${CMAKE_BINARY_DIR}/bin/assets/fonts COPYONLY)


النينجا يسرع CMake



دور CMake هو فقط إنشاء تعليمات لنظام بناء المشروع الذي اختاره المطور. يمكن أن تكون هذه إضافة كبيرة عند العمل مع الأشخاص الذين يستخدمون Visual Studio بدلاً من Qt Creator. عند استخدام CMake ، يمكنك (ويجب عليك) اختيار Ninja كنظام بناء افتراضي. يعد تجميع المشاريع باستخدام حزمة CMake + Ninja أمرًا رائعًا للغاية. يمكن العثور على كلاهما في مربع أدوات Qt Maintenance. من بين أشياء أخرى ، يمكن لهذه الأدوات التعامل مع التغييرات بسرعة كبيرة في نهج التطوير التكراري. في الواقع ، كل شيء يعمل بسرعة كبيرة لدرجة أنني عند استخدام Godot مع SCons ، أريد حقًا استخدام CMake هنا أيضًا.



يسمح Vcpkg لـ CMake بالتألق



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



لتثبيت الحزم ، استخدم الأمر التالي:



vcpkg install crashpad


بينما كان يعمل على سيناريو، ونحن ببساطة خلق install_dependencies_windows.bat و install_dependencies_linux_mac.sh البرامج النصية لاستنساخ مستودع vcpkg، وبناء عليه وتثبيت كافة التبعيات لدينا. عند العمل مع Qt Creator ، تحتاج إلى الكتابة إلى CMAKE_TOOLCHAIN_FILEالمسار النسبي لـ vcpkg. بالإضافة إلى ذلك ، يجب إخبار vcpkg بنوع نظام التشغيل والبنية التي نستخدمها.



    #  QtCreator. Extras -> Tools -> Kits ->  -> CMake Configuration.    :
    CMAKE_TOOLCHAIN_FILE:STRING=%{CurrentProject:Path}/Common/vcpkg/scripts/buildsystems/vcpkg.CMake
    VCPKG_TARGET_TRIPLET:STRING=x64-windows


هل تحتاج إلى تثبيت أي مكتبة أخرى؟ للقيام بذلك ، ما عليك سوى استخدام أمر النموذج vcpkg install myLibToInstall.



النتيجة



نهج استخدام الأحدث والأكثر شعبية له مزاياه. ولكن ماذا تفعل ، على سبيل المثال ، عند بناء أنظمة ذات إمكانات كبيرة مثل QBS تجد نفسها فجأة على الهامش؟ في النهاية ، يتخذ المطور نفسه قرارات بشأن ما يجب استخدامه في مشاريعه. لهذا السبب قررت نقل مشروعي إلى CMake. ويجب أن أقول إنه كان القرار الصحيح. اليوم ، في عام 2020 ، تبدو CMake جيدة جدًا.



هل تستخدم CMake و vcpkg؟










All Articles