جنبه های

در این صفحه اصول و مزایای استفاده از جنبه ها توضیح داده شده و نمونه های ساده و پیشرفته ارائه شده است.

جنبه ها اجازه می دهد تا نمودارهای وابستگی ساخت را با اطلاعات و اقدامات اضافی تقویت کنید. برخی از سناریوهای معمولی که جنبه ها می توانند مفید باشند:

  • 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 یک نمودار وابستگی را که در شکل زیر نشان داده شده است تعریف می کند:

Build graph

شکل 1. ایجاد نمودار BUILD فایل.

Bazel این نمودار وابستگی را با فراخوانی تابع پیاده سازی قانون مربوطه (در این مورد "java_library") برای هر هدف در مثال بالا تجزیه و تحلیل می کند. توابع اجرای قانون اقداماتی را ایجاد می‌کنند که مصنوع‌هایی مانند فایل‌های .jar را ایجاد می‌کنند و اطلاعاتی مانند مکان‌ها و نام آن مصنوعات را به وابستگی‌های معکوس آن اهداف در ارائه‌دهندگان منتقل می‌کنند.

جنبه ها شبیه به قوانین هستند زیرا دارای یک تابع پیاده سازی هستند که اقدامات را ایجاد می کند و ارائه دهندگان را برمی گرداند. با این حال، قدرت آنها ناشی از نحوه ساخت نمودار وابستگی برای آنهاست. یک جنبه دارای یک پیاده سازی و فهرستی از تمام ویژگی هایی است که در کنار آن منتشر می شود. یک جنبه A را در نظر بگیرید که در امتداد ویژگی هایی به نام "deps" منتشر می شود. این جنبه را می توان برای یک هدف X اعمال کرد و یک گره کاربردی جنبه A(X) ایجاد کرد. در طول کاربرد، جنبه A به صورت بازگشتی به تمام اهدافی که X در ویژگی "deps" خود به آنها اشاره می کند (همه ویژگی های موجود در لیست انتشار A) اعمال می شود.

بنابراین یک عمل منفرد از اعمال جنبه A به یک هدف X یک "گراف سایه" از نمودار وابستگی اصلی اهداف نشان داده شده در شکل زیر را به دست می دهد:

Build Graph with Aspect

شکل 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 قابل دسترسی هستند.