דף זה עוסק בעקרונות השימוש בפקודות מאקרו, וכולל תרחישים נפוצים, ניפוי באגים ומוסכמות.
מאקרו הוא פונקציה שנקראת מתוך הקובץ BUILD
שיכולה ליצור כללים.
פקודות מאקרו משמשות בעיקר לאנקפסולות ולשימוש חוזר בקודים בכללים ובמאקרו אחרים. בסוף שלב הטעינה, פקודות המאקרו לא קיימות יותר, ו-Bazel רואה רק את קבוצת המוחשית של הכללים המוגדרים.
שימוש
תרחיש לדוגמה טיפוסי עבור מאקרו הוא כשרוצים לעשות שימוש חוזר בכלל.
לדוגמה, יצירה של ז'אנר בקובץ BUILD
יוצרת קובץ באמצעות//:generator
עם ארגומנט some_arg
בתוך הקוד בתוך הקוד:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
אם אתם רוצים ליצור עוד קבצים באמצעות ארגומנטים שונים, כדאי להוציא את הקוד הזה לפונקציית מאקרו. רוצה לקרוא למאקרו file_generator
? הוא מכיל name
ו-arg
פרמטרים. מחליפים את הז'אנר ב:
load("//path:generator.bzl", "file_generator")
file_generator(
name = "file",
arg = "some_arg",
)
file_generator(
name = "file-two",
arg = "some_arg_two",
)
file_generator(
name = "file-three",
arg = "some_arg_three",
)
כאן טוענים את הסמל של file_generator
מקובץ .bzl
שנמצא
בחבילה //path
. אם תוסיפו הגדרות של פונקציות מאקרו לקובץ נפרד מסוג .bzl
, קובצי BUILD
יישארו נקיים והצהרתיים, ואפשר יהיה לטעון את הקובץ .bzl
מכל חבילה בסביבת העבודה.
לבסוף, בpath/generator.bzl
, כותבים את הגדרת המאקרו כדי להבליט את הפרמטר הכללי הזה ולהגדיר אותו כפרמטר:
def file_generator(name, arg, visibility=None):
native.genrule(
name = name,
outs = [name + ".txt"],
cmd = "$(location //:generator) %s > $@" % arg,
tools = ["//:generator"],
visibility = visibility,
)
אפשר גם להשתמש ברכיבי מאקרו כדי לשרשר כללי רשת. בדוגמה הזו מוצגים כללים כלליים משורשרים, שבהם חוכמה להשתמש בפלט של ג'ונגל קודם בתור קלט:
def chained_genrules(name, visibility=None):
native.genrule(
name = name + "-one",
outs = [name + ".one"],
cmd = "$(location :tool-one) $@",
tools = [":tool-one"],
visibility = ["//visibility:private"],
)
native.genrule(
name = name + "-two",
srcs = [name + ".one"],
outs = [name + ".two"],
cmd = "$(location :tool-two) $< $@",
tools = [":tool-two"],
visibility = visibility,
)
הדוגמה מציינת רק את ערך החשיפה של הג'ל השני. ההגדרה הזו מאפשרת למחברים של מאקרו להסתיר את הפלט של כללי הביניים כדי שלא יתמכו ביעדים אחרים של סביבת העבודה.
רכיבי מאקרו מתרחבים
כדי לבדוק מה קורה במאקרו, השתמשו בפקודה query
עם
--output=build
כדי לראות את הטופס המורחב:
$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
name = "file",
tools = ["//:generator"],
outs = ["//test:file.txt"],
cmd = "$(location //:generator) some_arg > $@",
)
יצירת כללים מקומיים
ניתן ליצור מידית כללים מקומיים (כללים שלא צריכים הצהרה load()
) מהמודול native:
def my_macro(name, visibility=None):
native.cc_library(
name = name,
srcs = ["main.cc"],
visibility = visibility,
)
אם צריך לדעת את שם החבילה (לדוגמה, איזה קובץ BUILD
קורא למאקרו), יש להשתמש בפונקציה native.package_name() .
הערה: ניתן להשתמש ב-native
רק בקובצי .bzl
, ולא בקובצי WORKSPACE
או BUILD
.
רזולוציית תוויות בפקודות מאקרו
מכיוון שרכיבי מאקרו נמדדים בשלב הטעינה, המחרוזות של תווית כמו "//foo:bar"
שמתרחשות במאקרו מתפרשות ביחס לקובץ ה-BUILD
שבו נעשה שימוש במאקרו במקום ביחס לקובץ .bzl
שבו הוא מוגדר. באופן כללי, ההתנהגות הזו אינה רצויה בפקודות מאקרו, שאמורות לשמש במאגרים אחרים, למשל כי הן חלק מקבוצת כללים של Starlark שפורסמו.
כדי לשנות את ההתנהגות של כללי Starlark, צריך להקיף את מחרוזות התוויות בבונה
Label
:
# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
native.cc_library(
name = name,
deps = deps + select({
# Due to the use of Label, this label is resolved within @my_ruleset,
# regardless of its site of use.
Label("//config:needs_foo"): [
# Due to the use of Label, this label will resolve to the correct target
# even if the canonical name of @dep_of_my_ruleset should be different
# in the main workspace, such as due to repo mappings.
Label("@dep_of_my_ruleset//tools:foo"),
],
"//conditions:default": [],
}),
**kwargs,
)
ניפוי באגים
ב-
bazel query --output=build //my/path:all
תוכלו לראות איך הקובץBUILD
נראה אחרי ההערכה. כל פונקציות המאקרו, הגלובוסים והלולאות מורחבות. מגבלה ידועה:select
ביטויים לא מוצגים כרגע בפלט.אפשר לסנן את הפלט על סמך
generator_function
(הפונקציה שיצרה את הכללים) אוgenerator_name
(מאפיין השם של המאקרו):bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'
כדי לגלות בדיוק היכן הכלל
foo
נוצר בקובץBUILD
, אפשר לנסות את הטריק הבא. צריך להוסיף את השורה הזו בחלק העליון של קובץ ה-BUILD
:cc_library(name = "foo")
. מריצים את Bazel. יתקבל אישור חריג כש הכללfoo
נוצר (עקב התנגשות עם שם), שיציג את דוח הקריסות המלא.אפשר גם להשתמש בהדפסה לצורך ניפוי באגים. היא מציגה את ההודעה כשורת יומן
DEBUG
בשלב הטעינה. מלבד במקרים נדירים, צריך להסיר את הקריאות ל-print
או להפוך אותן לתנאי בעזרת פרמטרdebugging
שמוגדר כברירת מחדל ל-False
לפני שליחת הקוד למאגר.
שגיאות
אם רוצים להשליך שגיאה, אפשר להשתמש בפונקציה נכשל.
יש להסביר למשתמש בצורה ברורה מה השתבש ואיך לתקן את הקובץ BUILD
שלו.
לא ניתן לאתר שגיאה.
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
כנסים
כל הפונקציות הציבוריות (פונקציות שלא מתחילות בקו תחתון) שחייבות להתבצע באופן מיידי חייבות לכלול ארגומנט
name
. הארגומנט הזה לא יכול להיות אופציונלי (אין להגדיר ערך ברירת מחדל).הפונקציות הציבוריות צריכות להשתמש ב-docstring בהתאם למוסכמות של Python.
בקובצי
BUILD
, הארגומנטname
של המאקרו חייב להיות ארגומנט של מילת מפתח (לא ארגומנט של מיקום).מאפיין
name
של הכללים שנוצרים על ידי מאקרו צריך לכלול את שם הארגומנט כקידומת. לדוגמה,macro(name = "foo")
יכול ליצור אתcc_library
foo
ואת הוריאציה שלוfoo_gen
.ברוב המקרים, פרמטרים אופציונליים צריכים לקבל ערך ברירת מחדל של
None
. אפשר להעביר אתNone
ישירות לכללים מותאמים, שמטפלים בהם כאילו לא עברתם לארגומנט כלשהו. לכן אין צורך להחליף אותו ב-0
, ב-False
או ב-[]
למטרה זו. במקום זאת, רכיב המאקרו צריך להתאים לכללים שהוא יוצר, כי ברירות המחדל שלהם עשויות להיות מורכבות או משתנות לאורך זמן. בנוסף, פרמטר שמוגדר במפורש לערך ברירת המחדל שלו נראה שונה מפרמטר שמעולם לא הוגדר (או מוגדר ל-None
), כשניגשים אליו דרך שפת השאילתה או תוכן פנימי של מערכת ה-build.ברכיבי מאקרו צריך להיות ארגומנט
visibility
אופציונלי.