این صفحه اصول استفاده از ماکروها را پوشش می دهد و شامل موارد استفاده معمولی، اشکال زدایی و قراردادها می شود.
ماکرو تابعی است که از فایل BUILD
فراخوانی می شود که می تواند قوانین را نمونه سازی کند. ماکروها عمدتاً برای کپسولهسازی و استفاده مجدد از کد قوانین موجود و سایر ماکروها استفاده میشوند. در پایان مرحله بارگذاری ، ماکروها دیگر وجود ندارند و Bazel تنها مجموعه مشخصی از قوانین نمونه را می بیند.
استفاده
مورد معمول استفاده برای یک ماکرو زمانی است که می خواهید از یک قانون استفاده مجدد کنید.
به عنوان مثال، genrule در یک فایل 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()
نیاز ندارند) را می توان از ماژول بومی نمونه سازی کرد:
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")
. بازل را اجرا کنید. هنگامی که قانونfoo
ایجاد می شود (به دلیل تضاد نام)، یک استثنا دریافت خواهید کرد که ردیابی کامل پشته را به شما نشان می دهد.همچنین می توانید از چاپ برای اشکال زدایی استفاده کنید. این پیام را به عنوان یک خط ورود به سیستم
DEBUG
در مرحله بارگذاری نمایش می دهد. به جز در موارد نادر، قبل از ارسال کد به انبار، تماسهایprint
را حذف کنید یا آنها را تحت یک پارامترdebugging
که پیشفرضFalse
است، مشروط کنید.
خطاها
اگر می خواهید خطا ایجاد کنید، از تابع fail استفاده کنید. به طور واضح برای کاربر توضیح دهید که چه مشکلی رخ داده است و چگونه فایل BUILD
خود را تعمیر کند. امکان گرفتن خطا وجود ندارد.
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
کنوانسیون ها
همه توابع عمومی (توابعی که با خط زیر شروع نمی شوند) که قوانین را نمونه می کنند باید آرگومان
name
داشته باشند. این آرگومان نباید اختیاری باشد (مقدار پیش فرض ندهید).توابع عمومی باید از یک docstring پیرو قراردادهای پایتون استفاده کنند.
در فایل های
BUILD
، آرگومانname
ماکروها باید آرگومان کلمه کلیدی باشد (نه آرگومان موقعیتی).ویژگی
name
قوانین تولید شده توسط یک ماکرو باید آرگومان نام را به عنوان پیشوند شامل شود. به عنوان مثال،macro(name = "foo")
می تواند یکcc_library
foo
و یک genrulefoo_gen
کند.در بیشتر موارد، پارامترهای اختیاری باید مقدار پیشفرض
None
داشته باشند.None
را نمیتوان مستقیماً به قوانین بومی منتقل کرد، که با آن بهگونهای برخورد میکنند که انگار در هیچ استدلالی تصویب نکردهاید. بنابراین، برای این منظور نیازی به جایگزینی آن با0
،False
یا[]
نیست. در عوض، ماکرو باید قوانینی را که ایجاد میکند، رعایت کند، زیرا پیشفرضهای آنها ممکن است پیچیده باشند یا در طول زمان تغییر کنند. علاوه بر این، پارامتری که صراحتاً روی مقدار پیشفرض آن تنظیم شده است با پارامتری که هرگز تنظیم نشده است (یا رویNone
تنظیم نشده است) زمانی که از طریق زبان پرس و جو یا قسمتهای داخلی سیستم ساخت مورد دسترسی قرار میگیرد متفاوت به نظر میرسد.ماکروها باید آرگومان
visibility
اختیاری داشته باشند.