عند تطوير البرمجيات من المهم الأخذ بالحسبان نوع نظام التشغيل الذي تبني التطبيق عليه والمعمارية التي ستُصرّف تطبيقك من أجلها إلى ملف ثنائي تنفيذي Binary، وتكون عملية تشغيل التطبيق على نظام تشغيل مختلف أو معمارية مختلفة غالبًا عمليةً بطيئة أو مستحيلة أحيانًا، لذا من الممارسات العمليّة الشائعة بناء ملف تنفيذي يعمل على العديد من المنصات المختلفة، وذلك لزيادة شعبية وعدد مستخدمي تطبيقك، إلا أن ذلك غالبًا ما يكون صعبًا عندما تختلف المنصة التي تطوّر تطبيقك عليها عن المنصة التي تريد نشره عليها؛ إذ كان يتطلب مثلًا تطوير برنامج على ويندوز ونشره على لينوكس Linux أو ماك أو إس MacOS سابقًا إعدادات بناء مُحددة من أجل كل بيئة تُريد تصريف البرنامج من أجلها، ويتطلب الأمر أيضًا الحفاظ على مزامنة الأدوات، إضافةً إلى الاعتبارات الأخرى التي قد تضيف تكلفةً وتجعل الاختبار التعاوني Collaborative Testing والنشر أكثر صعوبة.
تحل لغة جو هذه المشكلة عن طريق بناء دعم لمنصّات متعددة مباشرةً من خلال الأداة go build
وبقية أدوات اللغة. يمكنك باستخدام متغيرات البيئة ووسوم البناء التحكم في نظام التشغيل والمعمارية التي بُنى الثنائي النهائي من أجلها، إضافةً إلى وضع مُخطط لسير العمل يمكن من خلاله التبديل إلى التعليمات البرمجية المُضمّنة والمتوافقة مع المنصة المطلوب تشغيل التطبيق عليها دون تغيير الشيفرة البرمجية الأساسية.
ستُنشئ في هذا المقال تطبيقًا يربط السلاسل النصية مع بعضها في مسار ملف، إضافةً إلى كتابة شيفرات برمجية يمكن تضمينها اختياريًّا، يعتمد كلٌ منها على منصة مُحددة. ستُنشئ ملفات تنفيذية لأنظمة تشغيل ومعماريات مختلفة على نظامك الخاص، وسيُبيّن لك ذلك كم أن لغة جو قوية في هذا الجانب.
ملاحظة: في تكنولوجيا المعلومات، المنصة هي أي عتاد Hardware أو برمجية Software تُستخدم لاستضافة تطبيق أو خدمة. على سبيل المثال قد تتكون من عتاديات ونظام تشغيل وبرامج أخرى تستخدم مجموعة التعليمات الخاصة بالمعالج.
المتطلبات
- تفترض هذه المقالة أنك على دراية بوسوم البناء في لغة جو، وإذا لم يكن لديك معرفةً بها، راجع مقالة استخدام وسوم البناء لتخصيص الملفات التنفيذية Binaries.
- أن يكون لديك مساحة عمل خاصة في لغة جو، وكنا قد تحدّثنا عن ذلك في بداية السلسلة في المقال تثبيت لغة جو Go وإعداد بيئة برمجة محلية على أبونتو Ubuntu، وتثبيت لغة جو وإعداد بيئة برمجة محلية على نظام ماك macOS، وتثبيت لغة جو وإعداد بيئة برمجة محلية على ويندوز.
المنصات التي يمكن أن تبني لها تطبيقك باستخدام لغة جو
قبل أن نعرض كيف يمكننا التحكم بعملية البناء من أجل بناء ملفات تنفيذية تتوافق مع منصات مختلفة، سنستعرض أولًا أنواع المنصات التي يمكن لجو البناء من أجلها، وكيف تشير جو إلى هذه المنصات باستخدام متغيرات البيئة GOOS
و GOARCH
.
يمكن عرض قائمة بأسماء المنصات التي يمكن لجو أن تبني تطبيقًا من أجلها، وتختلف هذه القائمة من إصدار لآخر، لذا قد تكون القائمة التي سنعرضها عليك مختلفةً عن القائمة التي تظهر عندك وذلك تبعًا لإصدار جو الذي تعمل عليه (الإصدار الحالي 1.13).
نفّذ الأمر التالي لعرض القائمة:
$ go tool dist list
ستحصل على الخرج التالي:
aix/ppc64 freebsd/amd64 linux/mipsle openbsd/386 android/386 freebsd/arm linux/ppc64 openbsd/amd64 android/amd64 illumos/amd64 linux/ppc64le openbsd/arm android/arm js/wasm linux/s390x openbsd/arm64 android/arm64 linux/386 nacl/386 plan9/386 darwin/386 linux/amd64 nacl/amd64p32 plan9/amd64 darwin/amd64 linux/arm nacl/arm plan9/arm darwin/arm linux/arm64 netbsd/386 solaris/amd64 darwin/arm64 linux/mips netbsd/amd64 windows/386 dragonfly/amd64 linux/mips64 netbsd/arm windows/amd64 freebsd/386 linux/mips64le netbsd/arm64 windows/arm
نلاحظ أن الخرج هو مجموعة من أزواج المفتاح والقيمة key-value مفصولة بالرمز "/"؛ إذ يمثّل المفتاح key (على اليسار) نظام التشغيل. تُعد هذه الأنظمة قيمًا محتملة لمتغير البيئة GOOS
(يُنطق "goose")، اختصارًا إلى Go Operating System؛ بينما تشير القيمة value (على اليمين) إلى المعمارية، وتمثّل القيم المُحتملة لمتغير البيئة GOARCH
(تُنطق "gore-ch") وهي اختصار Go Architecture.
دعنا نأخذ مثال linux/386 لفهم ما تعنيه وكيف يجري الأمر: تُمثّل linux المفتاح وهي قيمة المتغير GOOS
، بينما تمثّل 386 المعالج Intel 80386 والتي ستكون هي القيمة، وتُمثّل قيمة المتغير GOARCH
.
هناك العديد من المنصات التي يمكن أن تتعامل معها من خلال الأداة go build
، لكن غالبًا سيكون تعاملك مع منصة لينكس أو ويندوز أو داروين darwin في قيم المتغير GOOS
. إذًا، هذا يُغطي المنصات الثلاثة الأكبر: ويندوز ولينكس وماك، إذ يعتمد الأخير على نظام التشغيل داروين. يمكن لجو عمومًا تغطية المنصات الأقل شهرة مثل nacl.
عند تشغيل أمر مثل go build
، يستخدم جو مُتغيرات البيئة GOOS
و GOARCH
المرتبطين بالمنصة الحالية، لتحديد كيفية بناء الثنائي. لمعرفة تركيبة المفتاح-قيمة للمنصة التي تعمل عليها، يمكنك استخدام الأمر go env
وتمرير GOOS
و GOARCH
مثل وسيطين:
$ go env GOOS GOARCH
يعمل الجهاز الذي نستخدمه بنظام ماك ومعمارية AMD64 لذا سيكون الخرج:
darwin amd64
أي أن منصتنا لديها القيم التالية لمتغيرات البيئة GOOS=darwin
و GOARCH=amd64
.
أنت الآن تعرف ما هي GOOS
و GOARCH
، إضافةً إلى قيمهما المحتملة. سنكتب الآن برنامجًا لاستخدامه مثالًا على كيفية استخدام متغيرات البيئة هذه ووسوم البناء، بهدف بناء ملفات تنفيذية لمنصات أخرى.
بناء تطبيق يعتمد على المنصة
سنبدأ أولًا ببناء برنامج بسيط، ومن الأمثلة الجيدة لهذا الغرض الدالة Join
من الحزمة path/filepath
من مكتبة جو القياسية، إذ تأخذ هذه الدالة عدة سلاسل وتُرجع سلسلةً مكونةً من تلك السلاسل بعد ربطها اعتمادًا على فاصل مسار الملف filepath.
يُعد هذا المثال التوضيحي مناسبًا لأن تشغيل البرنامج يعتمد على نظام التشغيل الذي يعمل عليه، ففي نظام التشغيل ويندوز، يكون فاصل المسار \
، بينما تستخدم الأنظمة المستندة إلى يونيكس Unix الفاصل /
.
سنبدأ ببناء تطبيق يستخدم ()filepath.Join
، وسنكتب لاحقًا تنفيذًا خاصًا لهذه الدالة يُخصص الشيفرة للثنائيات التي تتبع لمنصة محددة.
أنشئ مجلدًا داخل المجلد src باسم تطبيقك:
$ mkdir app
انتقل إلى المجلد:
$ cd app
أنشئ ملفًا باسم main.go من خلال محرر النصوص نانو nano أو أي محرر آخر:
$ nano main.go
ضع في الملف الشيفرة التالية:
package main import ( "fmt" "path/filepath" ) func main() { s := filepath.Join("a", "b", "c") fmt.Println(s) }
تستخدم الدالة الرئيسية ()main
هنا الدالة ()filepath.Join
لربط ثلاث سلاسل مع فاصل المسار الصحيح المعتمد على المنصة.
احفظ الملف واخرج منه، ثم نفّذه من خلال الأمر التالي:
$ go run main.go
عند تشغيل هذا البرنامج، ستتلقى مخرجات مختلفة بناءً على المنصة التي تستخدمها، ففي نظام التشغيل ويندوز، سترى السلاسل مفصولة بالفاصل \
:
a\b\c
أما في أنظمة يونِكس مثل ماك ولِنُكس:
a/b/c
يوضح هذا أن اختلاف بروتوكولات نظام الملفات المستخدمة في أنظمة التشغيل هذه، يقتضي على البرنامج بناء شيفرات مختلفة للمنصات المختلفة. نحن نعلم أن الاختلاف هنا سيكون بفاصل الملفات كما تحدثنا، وبما أننا نستخدم الدالة ()filepath.Join
فلا خوف من ذلك، لأنها ستأخذ بالحسبان اختلاف نظام التشغيل الذي تُستخدم ضمنه. تفحص سلسلة أدوات جو تلقائيًا GOOS
و GOARCH
في جهازك وتستخدم هذه المعلومات لاستخدام الشيفرة المناسبة مع وسوم البناء الصحيحة وفاصل الملفات المناسب.
سنرى الآن من أين تحصل الدالة ()filepath.Join
على الفاصل المناسب. شغّل الأمر التالي لفحص المقتطف ذي الصلة من مكتبة جو القياسية:
$ less /usr/local/go/src/os/path_unix.go
سيُظهر هذا الأمر محتويات الملف "path_unix.go". ألقِ نظرةً على السطر الأول منه، والذي يُمثّل وسوم البناء.
. . . // +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os const ( PathSeparator = '/' // OS-specific path separator PathListSeparator = ':' // OS-specific path list separator ) . . .
تعرّف الشيفرة الفاصل PathSeparator
المستخدم مع الأنواع المختلفة من الأنظمة التي تستند على يُنِكس والتي يدعمها جو. لاحظ في السطر الأول وسوم البناء التي تمثل كل واحدة منها قيمةً محتملةً للمتغير GOOS
وهي جميعها تمثل أنظمة تستند إلى ينكس. يأخذ GOOS
أحد هذه القيم ويُنتج الفاصل المناسب تبعًا لنوع النظام.
اضغط q
للعودة لسطر الأوامر. افتح الآن ملف path_windows الذي يُعبر عن سلوك الدالة ()filepath.Join
على نظام ويندوز:
. . . package os const ( PathSeparator = '\\' // OS-specific path separator PathListSeparator = ';' // OS-specific path list separator ) . . .
على الرغم من أن قيمة PathSeparator
هنا هي \\
، إلا أن الشيفرة ستعرض الشرطة المائلة الخلفية المفردة \
اللازمة لمسارات ملفات ويندوز، إذ تُستخدم الشرطة المائلة الأولى بمثابة مفتاح هروب Escape character.
لاحظ أنه في الملف الخاص بينكس كان لدينا وسوم بناء، أما في ملف ويندوز فلا يوجد وسوم بناء، وذلك لأن GOOS
و GOARCH
يمكن تمريرهما أيضًا إلى go build
عن طريق إضافة شرطة سفلية "_" وقيمة متغير البيئة مثل لاحقة suffix لاسم الملف (سنتحدث عن ذلك أكثر بعد قليل).
يجعل الجزء windows_
من path_windows.go الملف يعمل كما لو كان يحتوي على وسم البناء build windows+ //
في أعلى الملف، لذلك عند تشغيل البرنامج على ويندوز، سيستخدم الثوابت PathSeparator
و PathListSeparator
من الشيفرة الموجودة في الملف "path_windows.go". اضغط q
للعودة لسطر الأوامر.
أنشأت في هذه الخطوة برنامجًا يوضّح كيف يمكن لجو أن يجري التبديل تلقائيًا بين الشيفرات من خلال متغيرات البيئة GOOS
و GOARCH
ووسوم البناء. يمكنك الآن تحديث برنامجك وكتابة تنفيذك الخاص للدالة ()filepath.Join
، والاعتماد على وسوم البناء لتحديد الفاصل PathSeparator
المناسب لمنصات ويندوز ويونيكس يدويًا.
تنفيذ دالة خاصة بالمنصة
بعد أن تعرّفت على كيفية تحقيق مكتبة جو القياسية للتعليمات البرمجية الخاصة بالمنصة، يمكنك استخدام وسوم البناء لأجل ذلك في تطبيقك. ستكتب الآن تعريفًا خاصًا للدالة ()filepath.Join
. افتح ملف main.go الخاص بتطبيقك:
$ nano main.go
استبدل محتويات main.go وضع فيه الشيفرة التالية التي تتضمن دالة خاصة اسميناها Join
:
package main import ( "fmt" "strings" ) func Join(parts ...string) string { return strings.Join(parts, PathSeparator) } func main() { s := Join("a", "b", "c") fmt.Println(s) }
تأخذ الدالة Join
عدة سلاسل نصية من خلال المعامل parts
وتربطهما معًا باستخدام الفاصل PathSeparator
، وذلك من خلال الدالة ()strings.Join
من حزمة strings
. لم نُعرّف PathSeparator
بعد، لذا سنُعرّفه الآن في ملف آخر. احفظ main.go واخرج منه، وافتح المحرر المفضل لديك، وأنشئ ملفًا جديدًا باسم path.go:
nano path.go
صرّح عن الثابت PathSeparator
وأسند له فاصل المسارات الخاص بملفات يونيكس "/":
package main const PathSeparator = "/"
صرّف التطبيق وشغّله:
$ go build $ ./app
ستحصل على الخرج التالي:
a/b/c
هذا جيد بالنسبة لأنظمة ينكس، لكنه ليس ما نريده تمامًا، فهو يُعطي دومًا a/b/c
بغض النظر عن المنصة، وهذا لا يتناسب مع ويندوز مثلًا. إذًا، نحن بحاجة إلى نسخة خاصة من PathSeparator
لنظام ويندوز، وإخبار go build
أي من هذه النسخ يجب استخدامها وفقًا للمنصة المطلوبة. هنا يأتي دور وسوم البناء.
استخدام وسوم البناء مع متغيرات البيئة
لكي نعالج حالة كون النظام هو ويندوز، سنُنشئ الآن ملفًا بديلًا للملف path.go وسنضيف وسوم بناء Build Tags مهمتها المطابقة مع المتغيرات GOOS
و GOARCH
، وذلك لضمان أن الشيفرة المستخدمة هي الشيفرة التي تعمل على المنصة المحددة.
أضف بدايةً وسم بناء إلى الملف "path.go" لإخباره أن يبني لكل شيء باستثناء ويندوز، افتح الملف:
$ nano path.go
أضِف وسم البناء التالي إلى الملف:
// +build !windows package main const PathSeparator = "/"
تُقدّم وسوم البناء في لغة جو إمكانية "العكس inverting" مما يعني أنه يمكنك توجيه جو لبناء هذا الملف من أجل أي منصة باستثناء ويندوز. لعكس وسم بناء، ضع "!" قبل الوسم كما فعلنا أعلاه.
احفظ واخرج من الملف.
والآن إذا حاولت تشغيل هذا البرنامج على ويندوز، ستتلقى الخطأ التالي:
./main.go:9:29: undefined: PathSeparator
في هذه الحالة لن تكون جو قادرةً على تضمين path.go لتعريف فاصل المسار PathSeparator
.
الآن بعد أن تأكدت من أن path.go لن يعمل عندما يكون GOOS
هو ويندوز. أنشئ ملفًا جديدًا windows.go:
$ nano windows.go
أضِف ضمن هذا الملف PathSeparator
ووسم بناء أيضًا لإخبار الأمر go build
أن هذا الملف هو التحقيق المقابل للويندوز:
// +build windows package main const PathSeparator = "\\"
احفظ الملف واخرج من محرر النصوص. يمكن للتطبيق الآن تصريف نسخة لنظام ويندوز ونسخة أخرى لباقي الأنظمة. ستُبنى الآن ملفات تنفيذية بطريقة صحيحة وفقًا للمنصة المطلوبة، إلا أنه هناك المزيد من التغييرات التي يجب عليك إجراؤها من أجل التصريف على المنصة التي لا يمكنك الوصول إليها.
سنُعدّل في الخطوة التالية متغيرات البيئة المحلية GOOS
و GOARCH
.
استخدام متغيرات البيئة المحلية GOOS و GOARCH
استخدمنا سابقًا الأمر go env GOOS GOARCH
لمعرفة النظام والمعمارية التي تعمل عليها. عند استخدام الأمر go env
، سيبحث عن المتغيرين GOOS
و GOARCH
، فإذا وجدهما سيستخدم قيمهما وإلا سيفترض أن قيمهما هي معلومات المنصة الحالية (نظام ومعمارية المنصة). نستنتج مما سبق أنه بإمكاننا تحديد نظام ومعمارية لمتغيرات البيئة غير نظام ومعمارية المنصة الحالية. يعمل الأمر go build
بطريقة مشابهة للأمر السابق، إذ يمكنك تحديد قيم لمتغيرات البيئة GOOS
و GOARCH
مختلفة عن المنصة الحالية.
إذا كنت لا تستخدم نظام ويندوز، ابنِ نسخةً ثنائيةً من تطبيقك لنظام ويندوز عن طريق تعيين قيمة متغير البيئة GOOS
على windows
عند تشغيل الأمر go build
:
$ GOOS=windows go build
اسرد الآن محتويات مجلدك الحالي:
$ ls
ستجد في الخرج ملفًا باسم app.exe، إذ يكون امتداده exe والذي يشير إلى ملف ثنائي تنفيذي في نظام ويندوز.
app app.exe main.go path.go windows.go
يمكنك باستخدام الأمر file
الحصول على مزيد من المعلومات حول هذا الملف، للتأكد من بنائه:
$ file app.exe
ستحصل على:
app.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
يمكنك أيضًا إعداد واحد أو اثنين من متغيرات البيئة أثناء وقت البناء. نفّذ الأمر التالي:
$ GOOS=linux GOARCH=ppc64 go build
بذلك يكون قد استُبدل ملفك التنفيذي app بملف لمعمارية مختلفة. شغّل الأمر file
على تطبيقك الثنائي:
$ file app
ستحصل على الخرج التالي:
app: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, not stripped
من خلال متغيرات البيئة GOOS
و GOARCH
يمكنك الآن إنشاء تطبيق قابل للتنفيذ على منصات مختلفة، دون الحاجة إلى إعدادات ضبط مُعقدة. سنستخدم في الخطوة التالية بعض "الاصطلاحات" لأسماء الملفات لكي نجعل تنسيقها دقيقًا، إضافةً إلى البناء تلقائيًّا من دون الحاجة إلى وسوم البناء.
استخدام لواحق اسم الملف مثل دليل إلى المنصة المطلوبة
كما لاحظت سابقًا، تعتمد جو على استخدام وسوم البناء كثيرًا لفصل الإصدارات أو النسخ المختلفة من التطبيق إلى ملفات مختلفة بغية تبسيط التعليمات البرمجية، فعندما فتحت ملف "os/path_unix.go" كان هناك وسم بناء يعرض جميع التركيبات المحتملة التي تُعبر عن منصات شبيهة أو تستند إلى ينكس، إلا أن ملف "os/path_windows.go" لا يحتوي على وسوم بناء، لأن لاحقة اسم الملف كانت كافية لإخبار جو بالمنصة المطلوبة.
دعونا نلقي نظرةً على تركيب أو قاعدة هذه الميزة، فعند تسمية ملف امتداده "go."، يمكنك إضافة GOOS
و GOARCH
مثل لواحق إلى اسم الملف بالترتيب، مع فصل القيم عن طريق الشرطات السفلية "_". إذا كان لديك ملف جو يُسمى filename.go، يمكنك تحديد نظام التشغيل والمعمارية عن طريق تغيير اسم الملف إلى filename_GOOSGO_ARCH.go. على سبيل المثال، إذا كنت ترغب في تصريفه لنظام التشغيل ويندوز باستخدام معمارية 64-bit ARM، فيمكنك كتابة اسم الملف filename_windows_arm64.go، إذ يساعد اصطلاح التسمية هذا في الحفاظ على الشيفرة منظمةً بدقة.
حدّث البرنامج الآن بحيث نستخدم لاحقات اسم الملف بدلًا من وسوم البناء، وأعد تسمية ملف "path.go" و "windows.go" لاستخدام الاصطلاح المستخدم في حزمةos
:
$ mv path.go path_unix.go $ mv windows.go path_windows.go
بعد تعديل اسم الملف أصبح بإمكانك حذف وسم البناء من ملف "path_windows.go":
$ nano path_windows.go
بعد حذف build windows+ //
سيكون ملفك كما يلي:
package main const PathSeparator = "\\"
احفظ الملف واخرج منه.
بما أن unix
هي قيمة غير صالحة للمتغير GOOS
، فإن اللاحقة unix.go_ ليس لها أي معنى لمُصرّف جو، وبالرغم من ذلك، فإنه ينقل الغاية المرجوة من الملف. لا يزال مثلًا ملف os/path_unix.go بحاجة إلى استخدام وسوم البناء، لذا يجب الاحتفاظ بهذا الملف دون تغيير.
نستنتج أنه من خلال الاصطلاحات استطعنا التخلص من وسوم البناء غير الضرورية التي أضفناها إلى شيفرات التطبيق، كما جعلنا نظام الملفات أكثر تنظيمًا ووضوحًا.
الخاتمة
لا تحتاج لغة جو إلى أدوات إضافية لدعم فكرة المنصات المتعددة، وهذه ميزة قوية في جو.
تعلمنا في هذه المقالة استخدام هذه الإمكانية عن طريق إضافة وسوم البناء واللواحق لاسم الملف، وذلك لتحديد الشيفرات البرمجية التي يجب تنفيذها وفقًا للمنصة المطلوب العمل عليه. أنشأنا تطبيقًا متعدد المنصات وتعلمنا كيفية التعامل مع متغيرات البيئة GOOS
و GOARCH
لبناء ملفات تنفيذية لمنصات أخرى تختلف عن المنصة الحالية. تعدد المنصات أمر مهم جدًا، فهو يُضيف ميزةً مهمة لتطبيقك، إذ تُمكنه من التصريف وفقًا للمنصة المطلوبة من خلال متغيرات البيئة هذه.
ترجمة -وبتصرف- للمقال Building Go Applications for Different Operating Systems and Architectures لصاحبه Gopher Guides.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.