اذهب إلى المحتوى

بناء تطبيقات لغة Go على أنظمة التشغيل والمعماريات المختلفة


هدى جبور

عند تطوير البرمجيات من المهم الأخذ بالحسبان نوع نظام التشغيل الذي تبني التطبيق عليه والمعمارية التي ستُصرّف تطبيقك من أجلها إلى ملف ثنائي تنفيذي Binary، وتكون عملية تشغيل التطبيق على نظام تشغيل مختلف أو معمارية مختلفة غالبًا عمليةً بطيئة أو مستحيلة أحيانًا، لذا من الممارسات العمليّة الشائعة بناء ملف تنفيذي يعمل على العديد من المنصات المختلفة، وذلك لزيادة شعبية وعدد مستخدمي تطبيقك، إلا أن ذلك غالبًا ما يكون صعبًا عندما تختلف المنصة التي تطوّر تطبيقك عليها عن المنصة التي تريد نشره عليها؛ إذ كان يتطلب مثلًا تطوير برنامج على ويندوز ونشره على لينوكس Linux أو ماك أو إس MacOS سابقًا إعدادات بناء مُحددة من أجل كل بيئة تُريد تصريف البرنامج من أجلها، ويتطلب الأمر أيضًا الحفاظ على مزامنة الأدوات، إضافةً إلى الاعتبارات الأخرى التي قد تضيف تكلفةً وتجعل الاختبار التعاوني Collaborative Testing والنشر أكثر صعوبة.

تحل لغة جو هذه المشكلة عن طريق بناء دعم لمنصّات متعددة مباشرةً من خلال الأداة go build وبقية أدوات اللغة. يمكنك باستخدام متغيرات البيئة ووسوم البناء التحكم في نظام التشغيل والمعمارية التي بُنى الثنائي النهائي من أجلها، إضافةً إلى وضع مُخطط لسير العمل يمكن من خلاله التبديل إلى التعليمات البرمجية المُضمّنة والمتوافقة مع المنصة المطلوب تشغيل التطبيق عليها دون تغيير الشيفرة البرمجية الأساسية.

ستُنشئ في هذا المقال تطبيقًا يربط السلاسل النصية مع بعضها في مسار ملف، إضافةً إلى كتابة شيفرات برمجية يمكن تضمينها اختياريًّا، يعتمد كلٌ منها على منصة مُحددة. ستُنشئ ملفات تنفيذية لأنظمة تشغيل ومعماريات مختلفة على نظامك الخاص، وسيُبيّن لك ذلك كم أن لغة جو قوية في هذا الجانب.

ملاحظة: في تكنولوجيا المعلومات، المنصة هي أي عتاد Hardware أو برمجية Software تُستخدم لاستضافة تطبيق أو خدمة. على سبيل المثال قد تتكون من عتاديات ونظام تشغيل وبرامج أخرى تستخدم مجموعة التعليمات الخاصة بالمعالج.

المتطلبات

المنصات التي يمكن أن تبني لها تطبيقك باستخدام لغة جو

قبل أن نعرض كيف يمكننا التحكم بعملية البناء من أجل بناء ملفات تنفيذية تتوافق مع منصات مختلفة، سنستعرض أولًا أنواع المنصات التي يمكن لجو البناء من أجلها، وكيف تشير جو إلى هذه المنصات باستخدام متغيرات البيئة 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.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...