این صفحه اصول استفاده از ماکروها را پوشش می دهد و شامل موارد استفاده معمولی، اشکال زدایی و قراردادها می شود.
ماکرو تابعی است که از فایل 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_libraryfooو یک genrulefoo_genکند.در بیشتر موارد، پارامترهای اختیاری باید مقدار پیشفرض
Noneداشته باشند.Noneرا نمیتوان مستقیماً به قوانین بومی منتقل کرد، که با آن بهگونهای برخورد میکنند که انگار در هیچ استدلالی تصویب نکردهاید. بنابراین، برای این منظور نیازی به جایگزینی آن با0،Falseیا[]نیست. در عوض، ماکرو باید قوانینی را که ایجاد میکند، رعایت کند، زیرا پیشفرضهای آنها ممکن است پیچیده باشند یا در طول زمان تغییر کنند. علاوه بر این، پارامتری که صراحتاً روی مقدار پیشفرض آن تنظیم شده است با پارامتری که هرگز تنظیم نشده است (یا رویNoneتنظیم نشده است) زمانی که از طریق زبان پرس و جو یا قسمتهای داخلی سیستم ساخت مورد دسترسی قرار میگیرد متفاوت به نظر میرسد.ماکروها باید آرگومان
visibilityاختیاری داشته باشند.