در این صفحه اصول و مزایای استفاده از جنبه ها توضیح داده شده و نمونه های ساده و پیشرفته ارائه شده است.
جنبه ها اجازه می دهد تا نمودارهای وابستگی ساخت را با اطلاعات و اقدامات اضافی تقویت کنید. برخی از سناریوهای معمولی که جنبه ها می توانند مفید باشند:
- IDE هایی که Bazel را یکپارچه می کنند می توانند از جنبه هایی برای جمع آوری اطلاعات در مورد پروژه استفاده کنند.
- ابزارهای تولید کد میتوانند جنبههایی را برای اجرا در ورودیهای خود به شیوهای هدفمند به کار گیرند. به عنوان مثال، فایلهای
BUILD
میتوانند سلسله مراتبی از تعاریف کتابخانه پروتوباف را مشخص کنند، و قوانین خاص زبان میتوانند از جنبههایی برای پیوست کردن اقداماتی که کد پشتیبانی پروتوباف را برای یک زبان خاص ایجاد میکنند، استفاده کنند.
مبانی جنبه
فایلهای BUILD
شرحی از کد منبع پروژه ارائه میدهند: فایلهای منبع بخشی از پروژه، چه مصنوعاتی ( هدفها ) باید از آن فایلها ساخته شوند، چه وابستگیهایی بین آن فایلها وجود دارد، و غیره. Bazel از این اطلاعات برای اجرای یک ساخت استفاده میکند. ، یعنی مجموعه ای از اقدامات مورد نیاز برای تولید مصنوعات (مانند اجرای کامپایلر یا لینکر) را مشخص می کند و آن اقدامات را اجرا می کند. Bazel این کار را با ساختن یک نمودار وابستگی بین اهداف و بازدید از این نمودار برای جمع آوری آن اقدامات انجام می دهد.
فایل BUILD
زیر را در نظر بگیرید:
java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)
این فایل BUILD
یک نمودار وابستگی را که در شکل زیر نشان داده شده است تعریف می کند:
شکل 1. ایجاد نمودار BUILD
فایل.
Bazel این نمودار وابستگی را با فراخوانی تابع پیاده سازی قانون مربوطه (در این مورد "java_library") برای هر هدف در مثال بالا تجزیه و تحلیل می کند. توابع اجرای قانون اقداماتی را ایجاد میکنند که مصنوعهایی مانند فایلهای .jar
را ایجاد میکنند و اطلاعاتی مانند مکانها و نام آن مصنوعات را به وابستگیهای معکوس آن اهداف در ارائهدهندگان منتقل میکنند.
جنبه ها شبیه به قوانین هستند زیرا دارای یک تابع پیاده سازی هستند که اقدامات را ایجاد می کند و ارائه دهندگان را برمی گرداند. با این حال، قدرت آنها ناشی از نحوه ساخت نمودار وابستگی برای آنهاست. یک جنبه دارای یک پیاده سازی و فهرستی از تمام ویژگی هایی است که در کنار آن منتشر می شود. یک جنبه A را در نظر بگیرید که در امتداد ویژگی هایی به نام "deps" منتشر می شود. این جنبه را می توان برای یک هدف X اعمال کرد و یک گره کاربردی جنبه A(X) ایجاد کرد. در طول کاربرد، جنبه A به صورت بازگشتی به تمام اهدافی که X در ویژگی "deps" خود به آنها اشاره می کند (همه ویژگی های موجود در لیست انتشار A) اعمال می شود.
بنابراین یک عمل منفرد از اعمال جنبه A به یک هدف X یک "گراف سایه" از نمودار وابستگی اصلی اهداف نشان داده شده در شکل زیر را به دست می دهد:
شکل 2. ساختن نمودار با جنبه ها.
تنها لبه هایی که سایه می زنند لبه های امتداد ویژگی های مجموعه انتشار هستند، بنابراین لبه runtime_deps
در این مثال سایه نمی اندازد. سپس یک تابع پیادهسازی جنبه بر روی تمام گرهها در گراف سایه فراخوانی میشود، مشابه نحوه فراخوانی اجرای قوانین بر روی گرههای گراف اصلی.
مثال ساده
این مثال نحوه چاپ بازگشتی فایل های منبع یک قانون و همه وابستگی های آن را که دارای ویژگی deps
هستند را نشان می دهد. اجرای یک جنبه، تعریف جنبه و نحوه فراخوانی جنبه از خط فرمان Bazel را نشان می دهد.
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
بیایید مثال را به قسمت های آن تقسیم کنیم و هر کدام را جداگانه بررسی کنیم.
تعریف جنبه
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
تعاریف جنبه شبیه به تعاریف قوانین هستند و با استفاده از تابع aspect
تعریف می شوند.
درست مانند یک قانون، یک جنبه دارای یک تابع پیاده سازی است که در این مورد _print_aspect_impl
است.
attr_aspects
لیستی از ویژگی های قانون است که در امتداد آن جنبه منتشر می شود. در این حالت، جنبه در امتداد ویژگی deps
قوانینی که روی آن اعمال میشود، منتشر میشود.
یکی دیگر از استدلال های رایج برای attr_aspects
['*']
است که جنبه را به همه ویژگی های یک قانون منتشر می کند.
اجرای جنبه
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
توابع اجرای جنبه مشابه توابع اجرای قانون هستند. آنها ارائه دهندگان را برمی گردانند، می توانند اقدامات ایجاد کنند، و دو آرگومان دریافت کنند:
-
target
: هدفی که جنبه به آن اعمال می شود. -
ctx
: شیctx
که می تواند برای دسترسی به ویژگی ها و تولید خروجی ها و اقدامات استفاده شود.
تابع پیاده سازی می تواند از طریق ctx.rule.attr
به ویژگی های قانون هدف دسترسی داشته باشد. میتواند ارائهدهندگانی را که توسط هدفی که به آن اعمال میشود (از طریق آرگومان target
) ارائه شده است، بررسی کند.
برای بازگرداندن فهرستی از ارائه دهندگان، جنبه ها مورد نیاز است. در این مثال، جنبه چیزی ارائه نمی دهد، بنابراین یک لیست خالی را برمی گرداند.
فراخوانی جنبه با استفاده از خط فرمان
ساده ترین راه برای اعمال یک جنبه از طریق خط فرمان با استفاده از آرگومان --aspects
است. با فرض اینکه جنبه فوق در فایلی به نام print.bzl
تعریف شده باشد:
bazel build //MyExample:example --aspects print.bzl%print_aspect
print_aspect
را به example
هدف و تمام قوانین هدف که به صورت بازگشتی از طریق ویژگی deps
قابل دسترسی هستند اعمال می کند.
پرچم --aspects
یک آرگومان می گیرد که مشخصه ای از جنبه در قالب <extension file label>%<aspect top-level name>
است.
نمونه پیشرفته
مثال زیر استفاده از جنبهای از یک قانون هدف را نشان میدهد که فایلها را در اهداف شمارش میکند و احتمالاً آنها را با پسوند فیلتر میکند. نحوه استفاده از ارائه دهنده برای برگرداندن مقادیر، نحوه استفاده از پارامترها برای ارسال آرگومان به اجرای یک جنبه، و نحوه فراخوانی یک جنبه از یک قانون را نشان می دهد.
فایل file_count.bzl
:
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
فایل BUILD.bazel
:
load('//:file_count.bzl', 'file_count_rule')
cc_library(
name = 'lib',
srcs = [
'lib.h',
'lib.cc',
],
)
cc_binary(
name = 'app',
srcs = [
'app.h',
'app.cc',
'main.cc',
],
deps = ['lib'],
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
تعریف جنبه
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
این مثال نحوه انتشار aspect از طریق ویژگی deps
را نشان می دهد.
attrs
مجموعه ای از ویژگی ها را برای یک جنبه تعریف می کند. ویژگی های جنبه عمومی از نوع string
هستند و پارامتر نامیده می شوند. پارامترها باید دارای ویژگی values
مشخص شده روی آنها باشند. این مثال دارای پارامتری به نام extension
است که مجاز است « *
»، « h
» یا « cc
» را به عنوان مقدار داشته باشد.
مقادیر پارامتر برای جنبه از ویژگی رشته با همان نام قانون درخواست کننده جنبه گرفته شده است (به تعریف file_count_rule
). جنبه های دارای پارامتر را نمی توان از طریق خط فرمان استفاده کرد زیرا هیچ نحوی برای تعریف پارامترها وجود ندارد.
جنبه ها همچنین مجاز به داشتن ویژگی های خصوصی انواع label
یا label_list
هستند. ویژگیهای برچسب خصوصی را میتوان برای تعیین وابستگیها به ابزارها یا کتابخانههایی که برای اقدامات تولید شده توسط جنبهها مورد نیاز است، استفاده کرد. در این مثال یک ویژگی خصوصی تعریف نشده است، اما قطعه کد زیر نشان می دهد که چگونه می توانید یک ابزار را به یک جنبه منتقل کنید:
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
اجرای جنبه
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
درست مانند یک تابع اجرای قانون، یک تابع پیادهسازی جنبه ساختاری از ارائهدهندگان را برمیگرداند که برای وابستگیهای آن قابل دسترسی هستند.
در این مثال، FileCountInfo
به عنوان ارائهدهندهای تعریف میشود که یک count
فیلد دارد. بهترین کار این است که فیلدهای یک ارائه دهنده را با استفاده از ویژگی fields
به صراحت تعریف کنید.
مجموعه ارائه دهندگان برای یک برنامه کاربردی A(X) اتحادیه ارائه دهندگانی است که از اجرای یک قانون برای هدف X و از اجرای جنبه A به دست می آیند. ارائه دهندگانی که اجرای یک قانون منتشر می کند ایجاد شده و قبل از جنبه ها منجمد می شوند. اعمال می شود و نمی توان آن را از یک جنبه تغییر داد. اگر هدف و جنبهای که روی آن اعمال میشود، هر کدام یک نوع ارائهدهنده ارائه دهند، به استثنای OutputGroupInfo
(که ادغام میشود، تا زمانی که قانون و جنبه گروههای خروجی متفاوتی را مشخص کند) و InstrumentedFilesInfo
(که ادغام میشود) خطا است. از جنبه گرفته شده است). این بدان معناست که پیادهسازیهای جنبه ممکن است هرگز DefaultInfo
را برنگردانند.
پارامترها و ویژگی های خصوصی در ویژگی های ctx
می شوند. این مثال به پارامتر extension
اشاره می کند و تعیین می کند که چه فایل هایی باید شمارش شوند.
برای ارائه دهندگان بازگشتی، مقادیر ویژگی هایی که جنبه در امتداد آنها منتشر می شود (از لیست attr_aspects
) با نتایج استفاده از جنبه برای آنها جایگزین می شود. برای مثال، اگر هدف X در عمق خود Y و Z داشته باشد، ctx.rule.attr.deps
برای A(X) [A(Y)، A(Z)] خواهد بود. در این مثال، ctx.rule.attr.deps
اشیاء هدف هستند که نتایج اعمال جنبه به 'deps' هدف اصلی است که جنبه به آن اعمال شده است.
در مثال، جنبه به ارائهدهنده FileCountInfo
از وابستگیهای هدف دسترسی پیدا میکند تا کل تعداد انتقالی فایلها را جمع کند.
استناد جنبه از یک قاعده
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
اجرای قانون نحوه دسترسی به FileCountInfo
را از طریق ctx.attr.deps
نشان می دهد.
تعریف قانون نشان می دهد که چگونه می توان یک پارامتر ( extension
) را تعریف کرد و به آن مقدار پیش فرض ( *
) داد. توجه داشته باشید که داشتن یک مقدار پیشفرض که یکی از « cc
»، « h
» یا « *
» نباشد، به دلیل محدودیتهای اعمال شده روی پارامتر در تعریف جنبه، یک خطا خواهد بود.
فراخوانی یک جنبه از طریق یک قانون هدف
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
این نشان می دهد که چگونه می توان پارامتر extension
را از طریق قانون به یک جنبه منتقل کرد. از آنجایی که پارامتر extension
دارای یک مقدار پیش فرض در اجرای قانون است، extension
یک پارامتر اختیاری در نظر گرفته می شود.
هنگامی که هدف file_count
ساخته شد، جنبه ما برای خود ارزیابی می شود و همه اهداف به صورت بازگشتی از طریق deps
قابل دسترسی هستند.