بازيل التعليمية: إنشاء مشروع جافا

يتناول هذا البرنامج التعليمي أساسيات إنشاء تطبيقات باستخدام لغة Java. ستتمكّن من إعداد مساحة العمل وإنشاء مشروع Java بسيط يشرح مفاهيم Bazel الرئيسية، مثل الأهداف وملفات BUILD.

المدة المقدّرة للاكتمال: 30 دقيقة.

ما ستتعرَّف عليه

في هذا البرنامج التعليمي، ستتعرّف على كيفية:

  • إنشاء هدف
  • عرض العناصر التابعة للمشروع
  • تقسيم المشروع إلى استهدافات وحِزم متعددة
  • التحكّم في مستوى العرض المستهدَف في جميع الحِزم
  • الأهداف المرجعية من خلال التصنيفات
  • نشر هدف

قبل البدء

تثبيت Bazel

للاستعداد للبرنامج التعليمي، عليك أولاً تثبيت Bazel إذا لم يسبق لك تثبيته.

تثبيت JDK

  1. ثبِّت Java JDK (الإصدار المفضّل هو 11، ولكن تتوفّر الإصدارات بين 8 و15).

  2. اضبط متغير بيئة JAVA_HOME للتوجيه إلى JDK.

    • على نظام التشغيل Linux/macOS:

      export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
      
    • في نظام التشغيل Windows:

      1. افتح Control Panel (لوحة التحكم).
      2. الانتقال إلى "نظام التشغيل" و"نظام التشغيل" و"الأمان" & ‎
      3. ضمن قائمة &quot،المتغيّرات والمستخدِمين" (القائمة في أعلى الصفحة)، انقر على "جديد...&quot،.
      4. في الحقل "الاسم &المتغيّر"،&أدخِل JAVA_HOME.
      5. انقر على &عرض الدليل، &سجلّ؛؛
      6. انتقِل إلى دليل JDK (مثل C:\Program Files\Java\jdk1.8.0_152).
      7. انقر على "OK"جميع نوافذ الحوار.

الحصول على نموذج المشروع

يمكنك استرداد نموذج المشروع من مستودع Bazel' GitHub:

git clone https://github.com/bazelbuild/examples

يتوفر نموذج المشروع لهذا البرنامج التعليمي في الدليل examples/java-tutorial، وهو منظم على النحو التالي:

java-tutorial
├── BUILD
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── cmdline
│                   │   ├── BUILD
│                   │   └── Runner.java
│                   ├── Greeting.java
│                   └── ProjectRunner.java
└── WORKSPACE

إنشاء المحتوى باستخدام Bazel

إعداد مساحة العمل

قبل إنشاء مشروع، عليك إعداد مساحة عمل له. مساحة العمل هي دليل يضم ملفات المصدر الخاصة بمشروعك وملفات Bazel&#39s، ويتم إنشاؤها بسهولة. يتضمّن الملف أيضًا ملفات يعتبرها Bazel خاصةً:

  • يعرّف الملف WORKSPACE، الذي يعرّف الدليل ومحتواه كمساحة عمل في Bazel، ويتألف من جذر بنية الدليل.

  • توفّر هذه اللعبة ملفًا واحدًا أو أكثر على BUILD لإعلام منشئ المحتوى بكيفية إنشاء أجزاء مختلفة من المشروع. (الدليل ضمن مساحة العمل التي تحتوي على ملف BUILD هو حزمة. ستتعرّف على مزيد من المعلومات عن الحِزم لاحقًا في هذا البرنامج التعليمي.)

لتخصيص دليل كمساحة عمل من Bazel، يمكنك إنشاء ملف فارغ باسم WORKSPACE في هذا الدليل.

عندما ينشئ Bazel المشروع، يجب أن تكون جميع الإدخالات والاعتماديات في مساحة العمل نفسها. وتكون الملفات المتوفرة في مساحات عمل مختلفة مستقلة عن بعضها البعض ما لم تكن مرتبطة، وهذا خارج نطاق هذا البرنامج التعليمي.

فهم ملف BUILD

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

يُرجى إلقاء نظرة على ملف java-tutorial/BUILD:

java_binary(
    name = "ProjectRunner",
    srcs = glob(["src/main/java/com/example/*.java"]),
)

في المثال هنا، ينشئ هدف ProjectRunner مثيل Bazel's المضمّن java_binary. تطلب القاعدة من Bazel إنشاء ملف .jar ونص برمجي لبرنامج تضمين البرنامج الوكيل (كلا منهما تمت تسميته بعد الهدف).

تشير السمات في الهدف بشكلٍ صريح إلى تبعياتها وخياراتها. السمة name إلزامية، ولكن العديد منها اختياري. على سبيل المثال، في قاعدة القاعدة ProjectRunner، name هو اسم الهدف، يحدد srcs الملفات المصدر التي يستخدمها Bazel لإنشاء الهدف، ويحدد main_class الفئة التي تحتوي على الطريقة الرئيسية. (ربما لاحظت أن المثال الذي نستخدمه يستخدم glob لتمرير مجموعة من ملفات المصدر إلى Bazel بدلاً من إدراجها واحدًا تلو الآخر).

إنشاء المشروع

لإنشاء نموذج لمشروعك، انتقل إلى دليل java-tutorial وشغّل ما يلي:

bazel build //:ProjectRunner

في التصنيف المستهدف، يكون الجزء // هو موقع الملف BUILD المرتبط بجذر مساحة العمل (في هذه الحالة، الجذر نفسه)، وProjectRunner هو الاسم المستهدف في الملف BUILD. (ستتعرف أكثر على التصنيفات المستهدفة في نهاية هذا البرنامج التعليمي.)

تنتج Bazel نتائج مشابهة لما يلي:

   INFO: Found 1 target...
   Target //:ProjectRunner up-to-date:
      bazel-bin/ProjectRunner.jar
      bazel-bin/ProjectRunner
   INFO: Elapsed time: 1.021s, Critical Path: 0.83s

تهانينا، لقد أنشأت للتو هدف Bazel الأول تُنشئ أماكن Bazel النتائج في دليل bazel-bin في جذر مساحة العمل. تصفَّح محتوى القناة للحصول على فكرة عن بنية Bazel's للعرض.

يمكنك الآن اختبار برنامجك الثنائي الذي تم إنشاؤه حديثًا:

bazel-bin/ProjectRunner

مراجعة الرسم البياني للاعتمادية

يتطلب Bazel اعتمادية الإصدار صراحةً في ملفات BUILD. يستخدم Bazel هذه العبارات لإنشاء الرسم البياني للاعتمادية على المشروع، والذي يُمكّن إصدارات متزايدة دقيقة.

لوضع تمثيل بصري لاعتماديات المشروع، يمكنك إنشاء تمثيل نصي للرسم البياني للاعتمادية من خلال تشغيل هذا الأمر في جذر مساحة العمل:

bazel query  --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph

يوجّه الأمر السابق "بازل" البحث عن جميع تبعيات الهدف //:ProjectRunner (باستثناء المضيف والتبعيات الضمنية) وتنسيق الإخراج كرسم بياني.

بعد ذلك، الصق النص في GraphViz.

كما ترى، يحتوي المشروع على هدف واحد يعمل على إنشاء ملفَي مصدر بدون تبعيات إضافية:

الرسم البياني للمهام التابعة للهدف و#39;ProjectRunner'

بعد إعداد مساحة العمل وإنشاء المشروع وفحص مهامها، يمكنك إضافة بعض التعقيد.

تحسين إصدار Bazel

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

تحديد أهداف تصميم متعددة

يمكنك تقسيم نموذج المشروع إلى هدفين. استبدِل محتوى ملف java-tutorial/BUILD بما يلي:

java_binary(
    name = "ProjectRunner",
    srcs = ["src/main/java/com/example/ProjectRunner.java"],
    main_class = "com.example.ProjectRunner",
    deps = [":greeter"],
)

java_library(
    name = "greeter",
    srcs = ["src/main/java/com/example/Greeting.java"],
)

باستخدام هذه الإعدادات، ينشئ Bazel مكتبة greeter أولاً، ثم البرنامج الثنائي ProjectRunner. تخبر السمة deps في java_binary Bazel أن مكتبة greeter مطلوبة لإنشاء برنامج ProjectRunner ثنائي.

لإنشاء هذا الإصدار الجديد من المشروع، شغِّل الأمر التالي:

bazel build //:ProjectRunner

تنتج Bazel نتائج مشابهة لما يلي:

INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
  bazel-bin/ProjectRunner.jar
  bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s

يمكنك الآن اختبار برنامجك الثنائي الذي تم إنشاؤه حديثًا:

bazel-bin/ProjectRunner

إذا عدّلت ProjectRunner.java الآن وأعدت بناء المشروع، سيعمل Bazel على إعادة تجميع هذا الملف فقط.

بعد الاطّلاع على الرسم البياني للاعتمادية، يمكنك ملاحظة أنّ ProjectRunner يعتمد على المدخلات نفسها التي كانت عليه من قبل، ولكن تختلف بنية الإصدار:

الرسم البياني للمهام التابعة للهدف و#39;ProjectRunner' بعد إضافة تبعية

لقد أنشأت المشروع الآن باستخدام هدفين. ينشئ الهدف ProjectRunner ملفَين مصدر، ويعتمد على هدف آخر واحد (:greeter)، ما يؤدي إلى إنشاء ملف مصدر واحد آخر.

استخدام حِزم متعدّدة

لنقسِّم المشروع الآن إلى عدة حِزم. عند الاطّلاع على دليل src/main/java/com/example/cmdline، يمكنك العثور على الملف BUILD أيضًا، بالإضافة إلى بعض الملفات المصدر. وبالتالي، في مساحة العمل، تحتوي مساحة العمل الآن على حزمتَين، //src/main/java/com/example/cmdline و// (بما أن هناك ملف BUILD في جذر مساحة العمل).

يُرجى إلقاء نظرة على ملف src/main/java/com/example/cmdline/BUILD:

java_binary(
    name = "runner",
    srcs = ["Runner.java"],
    main_class = "com.example.cmdline.Runner",
    deps = ["//:greeter"],
)

يعتمد الاستهداف runner على الهدف greeter في الحزمة // (بالتالي التصنيف المستهدف //:greeter) - يعرف Bazel ذلك من خلال السمة deps. إلقاء نظرة على الرسم البياني للاعتمادية:

الرسم البياني للمهام التابعة للهدف 'runner'

ومع نجاح هذا الإصدار، يجب منح الهدف runner صراحةً في نطاق مستوى الرؤية //src/main/java/com/example/cmdline/BUILD للاستهداف في //BUILD باستخدام السمة visibility. ويرجع ذلك إلى أنّ الأهداف التلقائية لا تظهر إلا للأهداف الأخرى في ملف BUILD نفسه. (يستخدم Bazel مستوى الرؤية المستهدف لمنع حدوث مشاكل مثل المكتبات التي تحتوي على تفاصيل التنفيذ (تسرب البيانات إلى واجهات برمجة التطبيقات العامة).

ولإجراء ذلك، أضِف السمة visibility إلى الفئة المستهدفة greeter في java-tutorial/BUILD كما هو موضّح أدناه:

java_library(
    name = "greeter",
    srcs = ["src/main/java/com/example/Greeting.java"],
    visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)

يمكنك الآن إنشاء حزمة جديدة من خلال تشغيل الأمر التالي على جذر مساحة العمل:

bazel build //src/main/java/com/example/cmdline:runner

تنتج Bazel نتائج مشابهة لما يلي:

INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
  bazel-bin/src/main/java/com/example/cmdline/runner.jar
  bazel-bin/src/main/java/com/example/cmdline/runner
  INFO: Elapsed time: 1.576s, Critical Path: 0.81s

يمكنك الآن اختبار برنامجك الثنائي الذي تم إنشاؤه حديثًا:

./bazel-bin/src/main/java/com/example/cmdline/runner

لقد عدَّلت الآن المشروع لإنشاء حزمتين، تحتوي كل منهما على هدف واحد، وفهم التبعيات بينهما.

استخدام التصنيفات للإشارة إلى الأهداف

في ملفات BUILD وفي سطر الأوامر، يستخدم Bazel التصنيفات المستهدَفة للإشارة إلى الأهداف المستهدفة، على سبيل المثال //:ProjectRunner أو //src/main/java/com/example/cmdline:runner. وتكون البنية على النحو التالي:

//path/to/package:target-name

إذا كان الهدف هدف قاعدة، يكون path/to/package هو المسار إلى الدليل الذي يحتوي على الملف BUILD، وtarget-name هو ما أسماه الهدف في الملف BUILD (السمة name). إذا كان الهدف استهداف ملف، يكون path/to/package هو مسار جذر الجذر، ويمثل target-name اسم الملف المستهدف، بما في ذلك المسار الكامل له.

عند الإشارة إلى الأهداف في جذر المستودع، يكون مسار الحزمة فارغًا، ما عليك سوى استخدام //:target-name. عند الإشارة إلى الأهداف في ملف BUILD نفسه، يمكنك تخطي معرّف جذر // لمساحة العمل واستخدام :target-name فقط.

على سبيل المثال، بالنسبة إلى الأهداف في ملف java-tutorial/BUILD، لم تكن مضطرًا إلى تحديد مسار الحزمة، بما أن جذر مساحة العمل هو الحزمة (//)، وكان التصنيفان المستهدفان هما //:ProjectRunner و//:greeter.

ومع ذلك، بالنسبة إلى الأهداف في ملف //src/main/java/com/example/cmdline/BUILD، كان عليك تحديد مسار الحزمة الكامل من //src/main/java/com/example/cmdline وكان تصنيفك المستهدف هو //src/main/java/com/example/cmdline:runner.

إرسال هدف Java للنشر

والآن، يمكننا تجميع هدف Java لنشره من خلال إنشاء برنامج ثنائي يتضمن جميع تبعيات وقت التشغيل. وهذا يتيح لك تشغيل البرنامج الثنائي خارج بيئة التطوير.

كما تتذكر، تُنشئ القاعدة java_binary نصًّا برمجيًا .jar والنص البرمجي لبرنامج Shell. يمكنك الاطّلاع على محتوى runner.jar باستخدام هذا الأمر:

jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar

المحتوى هو:

META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class

كما ترى، يحتوي runner.jar على Runner.class، ولكن لا يعتمد على تحكمه، Greeting.class. يضيف النص البرمجي runner الذي أنشأه Bazel greeter.jar إلى مسار الصف، لذلك إذا تركته على هذا النحو، سيتم تشغيله محليًا، ولكن لن يتم تشغيله بشكل مستقل على جهاز آخر. تسمح لك قاعدة java_binary بإنشاء برنامج ثنائي مستقل وقابل للنشر. ولإنشائها، أضِف _deploy.jar إلى الاسم الهدف:

bazel build //src/main/java/com/example/cmdline:runner_deploy.jar

تنتج Bazel نتائج مشابهة لما يلي:

INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
  bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s

لقد صمَّمت للتو runner_deploy.jar الذي يمكنك تشغيله بشكل مستقل عن بيئة التطوير نظرًا لاحتوائه على العناصر التابعة وقت التشغيل المطلوبة. يمكنك الاطّلاع على محتوى ملف JAR المستقل هذا باستخدام الأمر نفسه كما في السابق:

jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar

يتضمن المحتوى كل الفئات المطلوبة لتشغيلها:

META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class

المزيد من القراءة

لمزيد من التفاصيل، يُرجى الاطّلاع على:

نتمنى لك مبنى سعيدًا!