Starlark هي لغة إعداد شبيهة بلغة Python ومُعدّة في الأصل للاستخدام في Bazel ومن ثمّ تم استخدامها من خلال أدوات أخرى. تتم كتابة الملفين BUILD
و.bzl
في Bazel بلهجة
Starstark المعروفة بشكل صحيح باسم "لغة الإنشاء"، على الرغم من أنه يُشار إليها ببساطة باسم "Starlark"، وخاصة عند التركيز على يتم التعبير عن هذه الميزة في لغة التصميم بدلاً من كونها مضمّنة في تطبيق Bazel. تعزز البازل اللغة الأساسية باستخدام العديد من الوظائف المتعلقة بالإصدار، مثل glob
وgenrule
وjava_binary
وغيرها.
ولمزيد من التفاصيل، يمكنك الاطّلاع على مستندات Bazel وStarlark للحصول على مزيد من التفاصيل ونموذج SIG لقواعد نقطة البداية لقواعد القواعد الجديدة.
القاعدة الفارغة
لإنشاء القاعدة الأولى، أنشئ الملف foo.bzl
:
def _foo_binary_impl(ctx):
pass
foo_binary = rule(
implementation = _foo_binary_impl,
)
عند استدعاء الدالة rule
، يجب تحديد دالة رد اتصال. سينتقل المنطق إلى هناك، لكن
يمكنك ترك الدالة فارغة الآن. وتوفّر الوسيطة ctx
معلومات عن الهدف.
يمكنك تحميل القاعدة واستخدامها من ملف BUILD
.
أنشئ ملف BUILD
في الدليل نفسه:
load(":foo.bzl", "foo_binary")
foo_binary(name = "bin")
أما الآن، فيمكن إنشاء الهدف:
$ bazel build bin
INFO: Analyzed target //:bin (2 packages loaded, 17 targets configured).
INFO: Found 1 target...
Target //:bin up-to-date (nothing to build)
على الرغم من أن القاعدة لا تتخذ أي إجراء، فإنها تتصرف مثل القواعد الأخرى: فهي تحمل اسمًا إلزاميًا، إلا أنها تدعم سمات شائعة مثل visibility
وtestonly
tags
.
نموذج التقييم
قبل المضي قدمًا، من المهم فهم كيفية تقييم الرمز.
يُرجى تعديل foo.bzl
من خلال إضافة بعض الصور المطبوعة:
def _foo_binary_impl(ctx):
print("analyzing", ctx.label)
foo_binary = rule(
implementation = _foo_binary_impl,
)
print("bzl file evaluation")
وتصميم:
load(":foo.bzl", "foo_binary")
print("BUILD file")
foo_binary(name = "bin1")
foo_binary(name = "bin2")
ctx.label
تتوافق مع تصنيف الهدف الذي يتم تحليله. ويحتوي الكائن ctx
على العديد من الحقول والأساليب المفيدة، يمكنك العثور على قائمة شاملة في
مرجع واجهة برمجة التطبيقات.
طلب البحث عن الرمز:
$ bazel query :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
//:bin2
//:bin1
إليك بعض الملاحظات:
- يتم طباعة "تقييم ملف bzl" أولاً. قبل تقييم ملف
BUILD
، يقيّم Baelel جميع الملفات التي يحمّلها. في حال تحميل ملفاتBUILD
متعدّدة، لن ترى سوى ورود واحد لـ "تقييم ملف bzl" لأن Google Barel تخزّن نتيجة التقييم مؤقتًا. - لا يتم استدعاء دالة رد الاتصال
_foo_binary_impl
. يحمّل طلب البحث Bazel ملفاتBUILD
، ولكنه لا يحلّل الأهداف.
لتحليل الأهداف، استخدِم الأمر cquery
("الطلب
الذي تم ضبطه") أو الأمر build
:
$ bazel build :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin1
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin2
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...
كما ترى، يُسمى _foo_binary_impl
الآن مرتين، مرة لكل هدف.
سيلاحظ بعض القراء أن "تقييم ملف bzl" تمت طباعته مرة أخرى، على الرغم من
تخزين نسخة foo.bzl مؤقتًا بعد الطلب إلى bazel query
. عدم إعادة تقييم Bazel للرمز، لكنه يُعيد تشغيل أحداث الطباعة فقط. وبغض النظر عن
حالة ذاكرة التخزين المؤقت، ستحصل على النتيجة نفسها.
إنشاء ملف
لجعل القاعدة أكثر فائدة، يُرجى تعديلها لإنشاء ملف. أولاً، يُرجى تحديد الملف وتحديد اسم له. في هذا المثال، أنشئ ملفًا بالاسم نفسه المستخدَم في الهدف:
ctx.actions.declare_file(ctx.label.name)
إذا شغّلت bazel build :all
الآن، ستظهر لك رسالة خطأ:
The following files have no generating action:
bin2
عندما تُعلن عن ملف، عليك أن تخبر بازل بكيفية إنشائه من خلال إنشاء إجراء. يمكنك استخدام ctx.actions.write
لإنشاء ملف يضم المحتوى المحدد.
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello\n",
)
الرمز صالح، ولكنه لن ينفذ أي إجراء:
$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)
سجّلت الدالة ctx.actions.write
إجراءً علّم البازل
كيفية إنشاء الملف. ولكن البازل لن ينشئ الملف إلا إذا طُلب منه ذلك. وأخيرًا، فإنّ آخر ما يجب فعله هو إخبار Bazel بأن الملف هو نتيجة للقاعدة، وليس ملفًا مؤقتًا مستخدَمًا داخل تنفيذ القاعدة.
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello!\n",
)
return [DefaultInfo(files = depset([out]))]
يُرجى الاطّلاع على وظائف DefaultInfo
وdepset
لاحقًا. لنفترض الآن أن السطر الأخير هو طريقة اختيار مخرجات القاعدة.
والآن، أريد تشغيل Bazel:
$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
bazel-bin/bin1
$ cat bazel-bin/bin1
Hello!
لقد أنشأت ملفًا بنجاح.
السمات
لزيادة فائدة القاعدة، أضف سمات جديدة باستخدام الوحدة attr
وعدِّل تعريف القاعدة.
أضِف سمة سلسلة تُسمى username
:
foo_binary = rule(
implementation = _foo_binary_impl,
attrs = {
"username": attr.string(),
},
)
ثم اضبطه في ملف BUILD
:
foo_binary(
name = "bin",
username = "Alice",
)
للوصول إلى القيمة في دالة رد الاتصال، استخدِم ctx.attr.username
. على سبيل المثال:
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello {}!\n".format(ctx.attr.username),
)
return [DefaultInfo(files = depset([out]))]
ملاحظة: يمكنك جعل السمة إلزامية أو ضبط قيمة تلقائية. انظر إلى مستندات attr.string
.
يمكنك أيضًا استخدام أنواع أخرى من السمات، مثل منطقي
أو قائمة بأعداد صحيحة.
تبعيات
تُعلن سمات التبعية، مثل attr.label
وattr.label_list
،
عن اعتمادية من الهدف الذي يملك السمة إلى الجمهور المستهدف الذي{ يظهر التصنيف 101} في قيمة السمة. ويشكّل هذا النوع من السمات أساس الرسم البياني المستهدف.
في الملف BUILD
، يظهر التصنيف الهدف كعنصر سلسلة، مثل //pkg:name
. في وظيفة التنفيذ، سيكون الوصول إلى الهدف متاحًا
كعنصر Target
. على سبيل المثال، يمكنك عرض الملفات التي يعرضها
الهدف باستخدام Target.files
.
ملفات متعدّدة
وفقًا للإعدادات التلقائية، قد تظهر فقط الأهداف التي تم إنشاؤها من خلال القواعد كتبعيات (مثل استهداف foo_library()
). وإذا أردت أن تقبل السمة الأهداف التي تكون ملفات الإدخال (مثل ملفات المصدر في المستودع)، يمكنك إجراء ذلك باستخدام
allow_files
وتحديد قائمة امتدادات الملفات المقبولة (أو)True
للسماح بأي امتداد للملف):
"srcs": attr.label_list(allow_files = [".java"]),
يمكن الوصول إلى قائمة الملفات باستخدام ctx.files.<attribute name>
. على سبيل المثال، يمكن الوصول إلى قائمة الملفات في السمة srcs
من خلال
ctx.files.srcs
ملف واحد
في حال كنت بحاجة إلى ملف واحد فقط، استخدِم allow_single_file
:
"src": attr.label(allow_single_file = [".java"])
يمكن الوصول إلى هذا الملف بعد ذلك ضمن ctx.file.<attribute name>
:
ctx.file.src
إنشاء ملف باستخدام نموذج
يمكنك إنشاء قاعدة تنشئ ملف .cc استنادًا إلى نموذج. ويمكنك أيضًا استخدام ctx.actions.write
لإخراج سلسلة تم إنشاؤها في دالة تنفيذ القاعدة، ولكن هذه المشكلة بها مشكلتان. أولاً، كلما زاد حجم النموذج، أصبح كفاءة الذاكرة أكبر لوضعه في ملف منفصل وتجنُّب إنشاء سلاسل كبيرة أثناء مرحلة التحليل. ثانيًا، يُعدّ استخدام ملف منفصل أكثر ملاءمة للمستخدم. بدلاً من ذلك، يمكنك استخدام
ctx.actions.expand_template
الذي يؤدي إلى إجراء استبدالات في ملف نموذج.
أنشئ سمة template
للإقرار باعتمادية على ملف النموذج:
def _hello_world_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name + ".cc")
ctx.actions.expand_template(
output = out,
template = ctx.file.template,
substitutions = {"{NAME}": ctx.attr.username},
)
return [DefaultInfo(files = depset([out]))]
hello_world = rule(
implementation = _hello_world_impl,
attrs = {
"username": attr.string(default = "unknown person"),
"template": attr.label(
allow_single_file = [".cc.tpl"],
mandatory = True,
),
},
)
يمكن للمستخدمين استخدام القاعدة كما يلي:
hello_world(
name = "hello",
username = "Alice",
template = "file.cc.tpl",
)
cc_binary(
name = "hello_bin",
srcs = [":hello"],
)
إذا كنت لا تريد عرض النموذج للمستخدم النهائي واستخدام النموذج نفسه دائمًا، يمكنك ضبط قيمة تلقائية وجعل السمة خاصة:
"_template": attr.label(
allow_single_file = True,
default = "file.cc.tpl",
),
تكون السمات التي تبدأ بشرطة سفلية خاصة، ولا يمكن ضبطها في ملف BUILD
. أصبح النموذج الآن اعتمادية ضمنية: يعتمد كل هدف hello_world
على هذا الملف. لا تنسَ أن تجعل هذا الملف مرئيًا للحزم الأخرى من خلال تحديث ملف BUILD
واستخدام
exports_files
:
exports_files(["file.cc.tpl"])
المضي قدمًا
- يمكنك الاطّلاع على المستندات المرجعية للقواعد.
- اطّلِع على معلومات العمق.
- يمكنك الاطّلاع على أمثلة عن المستودع الذي يتضمّن أمثلة إضافية على القواعد.