تنظيم أرقام إصدارات البرامج المضمّنة
“لقد قمت بتجربته وبرمجته على الدارة مسبقاً وصدقاً كان يعمل كالمتوقع … بعد عدّة ساعات: آه … من أعطاك هذه النسخة لتبرمج المتحكم بها؟ إنها ليست النسخة التي كنت أريدك أن تستخدمها”
كم منا تعرض لمواقف وحوارات مشابهة؟ ماذا لو قام أعضاء فرق تطوير النظم المضمّنة بتنظم وإعطاء أرقام نسخ واضحة سهلة الملاحظة والمراجعة.
إن مطوري النظم المضمّنة والإلكترونيات -برأيي- أقل تعرضاً لتجربة تطوير برمجيات كاملة المراحل بالمقارنة مع تخصصات برمجية أخرى، حيث تستخدم هناك عادة أدوات متخصصة في إدارة النسخ والإصدارات.
قد لا يكون ما ورد في المقدمة هو الحالة لدى الكل ولكن بناءً على ما خبرته سابقاً، أظن أنه من المفيد مشاركة طريقة بسيطة يمكن أن تشكل حلّاً لهذه المعضلة، وهذا الحل لا يتطلب سوى مستودع GIT للمشروع وسلسلة أدوات تطويرية Tool-chain تسمح للمستخدم بإضافة بعض الأوامر قبل وبعد بناء building المشروع وإخراج ملفات البرمجة مثل HEX.
سيوضح هذا المقال كيفية القيام بذلك في كل من بيئة تطوير Arduino، و بيئة تطوير MPLAB وملف makefile.
كيف يمكن للـGIT أن يساعد؟
كما هو معروف فإن الـGIT هو نظام إدارة نسخ version control system يدير التعديلات التي نقوم بها في الملفات وتخزن على شكل إيداعات commits يميز كل إيداع بقيمة مميزة Hash. قد يحوي كل مستودع Repo على أكثر من مساق/فرع branch ولكل فرع استخدام معين، ربما فرع لإصلاح ثغرة أو تطوير ميزة … وهكذا. , يمتلك الـ GIT ميزة الوسم tag وهي علامة مميزة يمكن وضعها لكل إيداع ويمكن تمييز رقم النسخة من خلالها.
فرع يسمى ‘Master’ مع رقم إيداع de46f03 موسوم بـ V1.0. حقوق الصورة لـ devopsbyravi
هذه الميزات أكثر من كافية لتنظيم الإصدارات بطريقة منظمة للأكواد المضمّنة لتفادي أي سوء تنظيم للنسخ في المستقبل.
كيف لـ GIT أن يتكامل مع الكود المضمّنة؟
أحد أبسط الميزات للنظام GIT في تنظيم إصدارات الكود المضمّن هي عندما تستمح للمستخدم النهائي (مديرك أو زميلك أو زبونك) أن يلاحظ النسخة بسهولة.
إن عرض رقم الإيداع ومعلومات أخرى في اسم الملف مثل ‘fw-de46f03-master-11_2_2020.hex’ هو أمر ضروري للغاية لمعرفة صحة النسخة من نظرة واحدة. كما يمكن تضمين رقم النسخة كالشكل التالي: ‘fw-v.1.13.0-8c0ab15-master-10_1_2020.hex’.
وفي حال امتلاك الجهاز لأي طريقة إظهار للعالم الخارجي فإن الوثوقية الأكبر تكون بتضمين معلومات الإصدار داخلياً في الكود وإظهارها للمستخدم لاحقاً وبهذا يمكن مطابقة اسم الملف مع المعلومات المخزّنة داخلياً التي تظهر عبر الشاشة مثلاً أو الكونسول التسلسلي Serial Console أو عبر معلومات مرسلة عبر الاتصال اللاسلكي.
في الجدول التالي التعليمات التي سنعتمد عليها للحصول على معلومات من المستودع Repo:
الأمر | المعنى |
---|---|
git describe --match=NeVeRmAtCh --always --abbrev=7 --dirty | يعيد قيمة الـHash للإيداع Commit |
git describe --abbrev=0 | يعيد اسم آخر وسم في الفرع branch |
git show -s --format=%cd --date=format:'-%y-%m-%d' | يعيد تاريخ الإيداع |
git rev-parse --abbrev-ref HEAD | يعيد اسم الفرع الحالي |
إجراء تجربة
كل ما تحتاج له لتجربة هذه الطريقة هي نسخة مثّبة من برمجية GIT وأي برنامج GUI GIT client ليكون التعامل رسومياً مع GIT. أقترح برنامج Sourcetree على سبيل المثال. الآن، قم بإنشاء مستودع وقم باستنساخه clone إلى جهازك.
يجب القيام ببعض الخطوات للاستفادة من المعلومات التي ستحصل عليها من GIT وذلك تبعاً لنوع بيئة التطوير التي لديك لتقوم بتمرير معلومات أثناء وبعد البناء مثل FW_VER, COMMIT_NUMBER, COMMIT_DATE وCOMMIT_BRANCH
Arduino IDE
تستخدم بيئة الأردوينو الأصلية عملية بناء وترجمة للكود مرمزة داخل البرنامج بمعنى أنه لن تجد makefile في ملفات برنامج الأردوينو توضح مراحل البناء والترجمة Compiling/building، مع العلم أنه يوجد مشروع غير رسمي يستخدم الـmakefile يسمى Arduino-Makefile
قام الفريق المطور وبناء على هذه المحدودة في إمكانية تعديل الخطوات كما ذكرنا بإضافة طريقة لإضافة وتعديل بعض الأمور أثناء بناء المشروع وتسمى برمجياً hooks وهي مشروحة في مقال رحلة داخل نواة الأردوينو.
بناءً على عدم إمكانية التحكم الكامل في عملية البناء في الأردوينو، فإن الحل الأنسب هو استدعاء سكربت يقوم باستدعاء تعليمات الـGIT المذكورة في الجدول أعلاه ويخزن النتيجة في ملف ترويسة header file لتستخدم المعلومات داخلة في الكود وإن هذه الطريقة هي مستوحاة من تعليقات في أحد النقاشات في موقع Stackoverflow. قمت بكتابة السكربت كـbatch بحيث يعمل على الويندور إلى جانب السكربت المكتوب للينكس في الموقع المذكور.
fw_ver.bat
:: Autohr: Yahya Tawil :: How to use: :: Add this file to you Arduino project directory and add the following line to platform.txt from Arduino core files :: recipe.hooks.sketch.prebuild.1.pattern = {build.source.path}\fw_ver.bat GIT {build.path} {build.source.path} :: recipe.hooks.objcopy.postobjcopy.2.pattern = {build.source.path}\fw_ver.bat SAVE {build.path} {build.source.path} @echo off :: arg1 -> "GIT" print version, commit number, brnach name and commit date , "SAVE" copy the .hex file from temp build directory to project directory set arg1=%1 :: arg2 -> path to temp build directory set arg2=%2 :: arg3 -> path to project source files set arg3=%3 cd %arg3% FOR /F "tokens=* USEBACKQ" %%F IN (`git describe --match^=NeVeRmAtCh --always --abbrev^=7`) DO ( SET commit=%%F ) FOR /F "tokens=* USEBACKQ" %%F IN (`git show -s --format^=%%cd --date^=format:%%y_%%m_%%d %commit%`) DO ( SET date=%%F ) FOR /F "tokens=* USEBACKQ" %%F IN (`git rev-parse --abbrev-ref HEAD`) DO ( SET branch=%%F ) FOR /F "tokens=* USEBACKQ" %%F IN (`git describe --abbrev^=0`) DO ( SET ver=%%F ) ::Export to version.h ECHO #define FW_VER "%ver%" > version.h ECHO #define COMMIT_NUMBER "%commit%" >> version.h ECHO #define COMMIT_DATE "%date%" >> version.h ECHO #define COMMIT_BRANCH "%branch%" >> version.h IF "%arg1%"=="GIT" ( @echo on @echo version:%ver% Commit:%commit% Branch:%branch% Date:%date% @echo off ) IF "%arg1%"=="SAVE" ( @echo on @echo Save fw_%ver%_%branch%_%date%_%commit%.hex to %arg3% COPY "%arg2%\fw.ino.hex" "fw_%ver%_%branch%_%date%_%commit%.hex" )
سيقوم هذا السكربت أيضاً بنسخ الملف الناتج .hex من المسار المؤقت الذي تستخدمه عادة أردوينو إلى مسار المشروع باسم له النمط التالي:
fw_{version/tag name}_{branch name}_{commit date}_{commit hash}
لجعل الأردوينو يقوم باستدعاء السكربت يجب إضافة السطريين التاليين إلى ملف platform.txt والموجود في الملفات الخاصة بنوع النواة التي تعمل عليها مثلاً نواة avr أو نواة esp32 وهكذا … تذكر تفعيل خيار verbose of compilation من إعدادات البرنامج:
recipe.hooks.sketch.prebuild.1.pattern = {build.source.path}\fw_ver.bat GIT {build.path} {build.source.path} recipe.hooks.objcopy.postobjcopy.2.pattern = {build.source.path}\fw_ver.bat SAVE {build.path} {build.source.path}
إن السطر الأول هو لاستدعاء السكربت لكي يجد الكود الملف الترويسة header file ويستخدمه قبل استدعاء المترجم Compiler
والسطر الثاني هو لاستدعاء تفس السكربت مع تحديد خيار لكي ينفّذ نسخ ملف hex إلى مسار المشروع بالاسم التالي: fw_{version/tag name}_{branch name}_{commit date}_{commit hash}.hex
Makefile
إن تعديل الخطوات في ملف الـmakefile مباشر أكثر وأسهل بحيث يمكن تنفيذ تعليمات الـGIT وتخزين خرجها في متحولات وإعادة استخدامها متى ما أردنا كاستخدامها داخل الكود أو في تسمية ملف الخرج.
GIT_HASH := $(shell git describe --match=NeVeRmAtCh --always --abbrev=7 --dirty) GIT_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD) GIT_TAG:= $(shell git describe --abbrev=0) COMMIT_DATE := $(shell git show -s --format=%cd --date=format:'-%y-%m-%d' $(GIT_HASH))
لاحقاً يتم تمرير هذه المتغيرات إلى المترجم كمعرفات defines أو ما تسمى بعلامات flags (هذا المثال هو لملف makefile لسلسلة أدوات arm-gcc):
CFLAGS += -DFW_VERSION=\"$(GIT_TAG)\" CFLAGS += -DFW_DATE=\"$(COMMIT_DATE)\" CFLAGS += -DFW_HASH=\"$(GIT_HASH)\" CFLAGS += -DFW_BRANCH=\"$(GIT_BRANCH)\"
يمكن الآن استخدام FW_VERSION, FW_DATE, FW_HASH و FW_BRANCH في الكود.
MPLAB X IDE
بعد إنشاء مشروع وبناؤه في MPLAB IDE يتولد تلقائياً ملف makefile في مسار <اسم المشروع>.X وباستخدام هذا الملف سنقوم بإضافة المتحولات التالية:
BRANCH = $(shell git rev-parse --abbrev-ref HEAD) HASH = $(shell git rev-parse --short HEAD) DATE = $(shell git log -1 --format=%cd --date=short) VERSION = $(shell git describe --abbrev=0)
من إعدادات المشروع ومن قائمة Conf[default] > Building يجب تفعيل خيار Execute this line after build وإضافة التالي:
${MKDIR} image && ${CP} ${ImagePath} image && ${MV} image/${ImageName} image/FW_${RELEASE}_${BRANCH}_${HASH}_${DATE}.${OUTPUT_SUFFIX}
سيقوم هذا الأمر بنسخ ملف الـHEX لمسار يُسمى image في مسار المشروع مع اسم يحوي معلومات النسخة المستخرجة من GIT
إن السبب وراء عدم تعديل الملف Makefile المسؤول عن استدعاء المترجم كما فعلنا في الفقرة السابقة هو أن هذا الملف هو مولد بشكل تلقائي وبالتالي بتحديد التعليمة فإنه سيقوم بإضافتها إلى هذا الملف.
لقد رأينا في هذا المقال كيف نستفيد من مزايا بسيطة من GIT لنستخدمها في تنظيم النسخ، هل تستخدم طرق أخرى لتنظيم ذلك؟ شاركها معنا في تعليق!