پیکربندی

این صفحه مزایا و استفاده اساسی از تنظیمات Starlark، API Bazel برای سفارشی کردن نحوه ساخت پروژه شما را پوشش می دهد. این شامل نحوه تعریف تنظیمات ساخت و نمونه هایی است.

این امکان را فراهم می کند:

  • پرچم های سفارشی را برای پروژه خود تعریف کنید و نیاز به --define را منسوخ کنید
  • برای پیکربندی دپ‌ها در پیکربندی‌های متفاوتی نسبت به والدینشان، ترانزیشن‌ها را بنویسید (مانند --compilation_mode=opt یا --cpu=arm )
  • ایجاد پیش فرض های بهتر در قوانین (مانند ساخت خودکار //my:android_app با یک SDK مشخص)

و بیشتر، همه به طور کامل از فایل های bzl. (بدون نیاز به انتشار Bazel). برای مثال به مخزن bazelbuild/examples مراجعه کنید.

تنظیمات ساخت تعریف شده توسط کاربر

تنظیمات ساخت یک تکه اطلاعات پیکربندی است. یک پیکربندی را به عنوان یک نقشه کلید/مقدار در نظر بگیرید. با تنظیم --cpu=ppc و --copt="-DFoo" پیکربندی شبیه {cpu: ppc, copt: "-DFoo"} ایجاد می شود. هر ورودی یک تنظیم ساخت است.

پرچم های سنتی مانند cpu و copt تنظیمات بومی هستند - کلیدهای آنها تعریف شده و مقادیر آنها در کد بومی bazel java تنظیم می شود. کاربران Bazel فقط می توانند آنها را از طریق خط فرمان و سایر APIهایی که به صورت بومی نگهداری می شوند بخوانند و بنویسند. تغییر پرچم‌های بومی و APIهایی که آن‌ها را در معرض دید قرار می‌دهند، نیازمند انتشار بازل است. تنظیمات ساخت تعریف شده توسط کاربر در فایل‌های .bzl . تعریف می‌شوند (و بنابراین، برای ثبت تغییرات نیازی به انتشار bazel نیست). آنها همچنین می توانند از طریق خط فرمان تنظیم شوند (اگر آنها به عنوان flags تعیین شده اند، بیشتر در زیر مشاهده کنید)، اما همچنین می توانند از طریق انتقال های تعریف شده توسط کاربر تنظیم شوند.

تعریف تنظیمات ساخت

مثال پایان به پایان

build_setting rule()

تنظیمات بیلد مانند هر قانون دیگری قوانینی هستند و با استفاده از ویژگی build_setting تابع Starlark rule() build_setting می شوند.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

ویژگی build_setting را می گیرد که نوع تنظیم ساخت را مشخص می کند. نوع به مجموعه ای از انواع اصلی Starlark مانند bool و string محدود می شود. برای جزئیات به مستندات ماژول config مراجعه کنید. تایپ پیچیده تر را می توان در عملکرد اجرای قانون انجام داد. بیشتر در این مورد در زیر.

توابع ماژول config یک پارامتر بولی اختیاری به نام flag می گیرد که به طور پیش فرض روی false تنظیم شده است. اگر flag روی true تنظیم شود، تنظیمات ساخت را می توان در خط فرمان توسط کاربران و همچنین به صورت داخلی توسط قوانین نویسان از طریق مقادیر پیش فرض و انتقال تنظیم کرد. همه تنظیمات نباید توسط کاربران قابل تنظیم باشند. برای مثال، اگر شما به عنوان یک قانون‌نویس حالت اشکال‌زدایی دارید که می‌خواهید در قوانین تست آن را روشن کنید، نمی‌خواهید به کاربران این امکان را بدهید که به‌طور بی‌رویه آن ویژگی را در قوانین غیر آزمایشی دیگر روشن کنند.

با استفاده از ctx.build_setting_value

مانند همه قوانین، قوانین تنظیم ساخت دارای توابع پیاده سازی هستند . از طریق روش ctx.build_setting_value می توان به مقدار اولیه Starlark نوع تنظیمات ساخت دسترسی پیدا کرد. این روش فقط برای اشیاء ctx قوانین تنظیم ساخت در دسترس است. این روش‌های پیاده‌سازی می‌توانند مستقیماً مقدار تنظیمات ساخت را ارسال کنند یا کارهای اضافی روی آن انجام دهند، مانند بررسی نوع یا ایجاد ساختار پیچیده‌تر. در اینجا نحوه اجرای تنظیمات ساخت enum -typed آورده شده است:

# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])

temperatures = ["HOT", "LUKEWARM", "ICED"]

def _impl(ctx):
    raw_temperature = ctx.build_setting_value
    if raw_temperature not in temperatures:
        fail(str(ctx.label) + " build setting allowed to take values {"
             + ", ".join(temperatures) + "} but was set to unallowed value "
             + raw_temperature)
    return TemperatureProvider(type = raw_temperature)

temperature = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

تعریف پرچم رشته های چند مجموعه ای

تنظیمات رشته دارای یک پارامتر allow_multiple اضافی هستند که اجازه می دهد پرچم چندین بار در خط فرمان یا در bazelrcs تنظیم شود. مقدار پیش‌فرض آنها همچنان با یک ویژگی رشته‌ای تنظیم می‌شود:

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
    name = "roasts",
    build_setting_default = "medium"
)

هر تنظیم پرچم به عنوان یک مقدار واحد در نظر گرفته می شود:

$ bazel build //my/target --//example:roasts=blonde \
    --//example:roasts=medium,dark

موارد فوق به {"//example:roasts": ["blonde", "medium,dark"]} تجزیه می شود و ctx.build_setting_value لیست ["blonde", "medium,dark"] برمی گرداند.

نمونه سازی تنظیمات ساخت

قوانین تعریف شده با پارامتر build_setting دارای یک ویژگی اجباری ضمنی build_setting_default هستند. این ویژگی همان نوع را دارد که توسط build_setting اعلام شده است.

# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])

def _impl(ctx):
    return FlavorProvider(type = ctx.build_setting_value)

flavor = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

تنظیمات از پیش تعریف شده

مثال پایان به پایان

کتابخانه Skylib شامل مجموعه ای از تنظیمات از پیش تعریف شده است که می توانید بدون نیاز به نوشتن Starlark سفارشی، نمونه سازی کنید.

به عنوان مثال، برای تعریف یک تنظیم که مجموعه محدودی از مقادیر رشته را می پذیرد:

# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
    name = "myflag",
    values = ["a", "b", "c"],
    build_setting_default = "a",
)

برای فهرست کامل، قوانین تنظیم ساخت متداول را ببینید.

با استفاده از تنظیمات ساخت

بسته به تنظیمات ساخت

اگر هدفی بخواهد بخشی از اطلاعات پیکربندی را بخواند، می‌تواند مستقیماً از طریق یک وابستگی مشخصه معمولی به تنظیمات ساخت بستگی داشته باشد.

# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
    if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
        ...

drink_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "flavor": attr.label()
    }
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)
drink_rule(
    name = "my_drink",
    flavor = ":favorite_flavor",
)

ممکن است زبان ها بخواهند مجموعه ای متعارف از تنظیمات ساخت را ایجاد کنند که همه قوانین آن زبان به آن بستگی دارد. اگرچه مفهوم اصلی fragments دیگر به عنوان یک شیء رمزگذاری شده در دنیای پیکربندی Starlark وجود ندارد، یک راه برای ترجمه این مفهوم استفاده از مجموعه‌ای از ویژگی‌های ضمنی مشترک است. مثلا:

# kotlin/rules.bzl
_KOTLIN_CONFIG = {
    "_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
    "_mode": attr.label(default = "//kotlin/config:mode-flag"),
    ...
}

...

kotlin_library = rule(
    implementation = _rule_impl,
    attrs = dicts.add({
        "library-attr": attr.string()
    }, _KOTLIN_CONFIG)
)

kotlin_binary = rule(
    implementation = _binary_impl,
    attrs = dicts.add({
        "binary-attr": attr.label()
    }, _KOTLIN_CONFIG)

با استفاده از تنظیمات ساخت در خط فرمان

مشابه بیشتر پرچم‌های بومی، می‌توانید از خط فرمان برای تنظیم تنظیمات ساخت که به عنوان پرچم علامت‌گذاری شده‌اند استفاده کنید. نام تنظیمات ساخت، مسیر کامل هدف آن با استفاده از نحو name=value :

$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed

نحو بولی ویژه پشتیبانی می شود:

$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag

استفاده از نام مستعار تنظیمات ساخت

می‌توانید برای مسیر هدف تنظیم ساخت خود یک نام مستعار تنظیم کنید تا خواندن آن در خط فرمان آسان‌تر شود. نام مستعار مشابه پرچم های بومی عمل می کند و همچنین از دستور گزینه دو خط تیره استفاده می کند.

با افزودن --flag_alias=ALIAS_NAME=TARGET_PATH به .bazelrc خود، یک نام مستعار تنظیم کنید. به عنوان مثال، برای تنظیم یک نام مستعار برای coffee :

# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp

بهترین روش: تعیین نام مستعار چندین بار منجر به اولویت گرفتن جدیدترین نام مستعار می شود. برای جلوگیری از نتایج تجزیه ناخواسته از نام های مستعار منحصر به فرد استفاده کنید.

برای استفاده از نام مستعار، آن را به جای مسیر هدف تنظیم ساخت تایپ کنید. با مثال بالا از مجموعه coffee در .bazelrc کاربر:

$ bazel build //my/target --coffee=ICED

بجای

$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED

بهترین روش: در حالی که امکان تنظیم نام مستعار در خط فرمان وجود دارد، باقی گذاشتن آنها در یک .bazelrc باعث کاهش شلوغی خط فرمان می شود.

تنظیمات ساخت برچسب تایپ شده

مثال پایان به پایان

بر خلاف سایر تنظیمات ساخت، تنظیمات برچسب تایپ شده را نمی توان با استفاده از پارامتر قانون build_setting تعریف کرد. در عوض، bazel دو قانون داخلی دارد: label_flag و label_setting . این قوانین ارائه دهندگان هدف واقعی را که تنظیمات ساخت به آن تنظیم شده است، ارسال می کند. label_flag و label_setting را می توان با انتقال ها خواند/نوشتن کرد و label_flag را می توان توسط کاربر مانند سایر قوانین build_setting تنظیم کرد. تنها تفاوت آنها این است که نمی توانند به طور سفارشی تعریف شوند.

تنظیمات برچسب تایپ شده در نهایت جایگزین عملکرد پیش‌فرض‌های با محدودیت دیرهنگام خواهند شد. ویژگی‌های پیش‌فرض با کران آخر، ویژگی‌های برچسب‌دار هستند که مقادیر نهایی آن‌ها را می‌توان تحت تأثیر پیکربندی قرار داد. در Starlark، این جایگزین API configuration_field خواهد شد.

# example/rules.bzl
MyProvider = provider(fields = ["my_field"])

def _dep_impl(ctx):
    return MyProvider(my_field = "yeehaw")

dep_rule = rule(
    implementation = _dep_impl
)

def _parent_impl(ctx):
    if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
        ...

parent_rule = rule(
    implementation = _parent_impl,
    attrs = { "my_field_provider": attr.label() }
)

# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")

dep_rule(name = "dep")

parent_rule(name = "parent", my_field_provider = ":my_field_provider")

label_flag(
    name = "my_field_provider",
    build_setting_default = ":dep"
)

تنظیمات را بسازید و () را انتخاب کنید

مثال پایان به پایان

کاربران می توانند با استفاده از select() ویژگی ها را در تنظیمات ساخت پیکربندی کنند. اهداف تنظیم ساخت را می توان به ویژگی flag_values config_setting کرد. مقدار مطابق با پیکربندی به عنوان یک String ارسال می شود و سپس به نوع تنظیم ساخت برای تطبیق تجزیه می شود.

config_setting(
    name = "my_config",
    flag_values = {
        "//example:favorite_flavor": "MANGO"
    }
)

انتقال های تعریف شده توسط کاربر

یک انتقال پیکربندی، تبدیل از یک هدف پیکربندی شده به هدف دیگر را در نمودار ساخت، ترسیم می کند.

قوانینی که آنها را تنظیم می کنند باید دارای یک ویژگی خاص باشند:

  "_allowlist_function_transition": attr.label(
      default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
  )

با افزودن ترانزیشن ها می توانید به راحتی اندازه نمودار ساخت خود را منفجر کنید. این یک لیست مجاز روی بسته هایی تنظیم می کند که در آن می توانید اهداف این قانون را ایجاد کنید. مقدار پیش‌فرض در بلوک کد بالا همه چیز را فهرست می‌کند. اما اگر می‌خواهید افرادی را که از قانون شما استفاده می‌کنند محدود کنید، می‌توانید آن ویژگی را طوری تنظیم کنید که به لیست مجاز سفارشی شما اشاره کند. اگر می‌خواهید در مورد اینکه چگونه انتقال‌ها بر عملکرد ساخت شما تأثیر می‌گذارند، راهنمایی یا کمک کنید، با bazel-discuss@googlegroups.com تماس بگیرید.

تعریف کردن

Transitionها تغییرات پیکربندی بین قوانین را تعریف می کنند. به عنوان مثال، درخواستی مانند "وابستگی من برای یک CPU متفاوت از والد آن کامپایل شود" توسط یک انتقال مدیریت می شود.

به طور رسمی، انتقال تابعی از یک پیکربندی ورودی به یک یا چند پیکربندی خروجی است. اکثر انتقال‌ها 1:1 هستند، مانند "پیکربندی ورودی با --cpu=ppc لغو شود". انتقال 1:2+ نیز می تواند وجود داشته باشد اما با محدودیت های خاصی همراه باشد.

در Starlark، انتقال ها بسیار شبیه به قوانین، با یک تابع transition() تعریف و یک تابع پیاده سازی تعریف می شوند.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//example:favorite_flavor" : "MINT"}

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

تابع transition() یک تابع پیاده سازی، مجموعه ای از تنظیمات ساخت برای خواندن ( inputs )، و مجموعه ای از تنظیمات ساخت برای نوشتن ( outputs ) را می گیرد. تابع پیاده سازی دارای دو پارامتر settings و attr است. settings یک فرهنگ لغت { String : Object } از تمام تنظیمات اعلام شده در پارامتر inputs به transition() است.

attr یک فرهنگ لغت از ویژگی ها و مقادیر قاعده ای است که انتقال به آن پیوست شده است. هنگامی که به عنوان یک انتقال لبه خروجی متصل می‌شود، مقادیر این ویژگی‌ها همه با وضوح () post-select پیکربندی می‌شوند. هنگامی که به عنوان یک انتقال لبه ورودی متصل می شود، attr هیچ ویژگی را شامل نمی شود که از یک انتخابگر برای تعیین مقدار خود استفاده می کند. اگر یک انتقال لبه ورودی در --foo bar صفت را می خواند و سپس روی --foo را نیز برای تنظیم bar ویژگی انتخاب می کند، در این صورت این شانس وجود دارد که انتقال لبه ورودی مقدار اشتباه bar را در انتقال بخواند.

تابع پیاده سازی باید یک فرهنگ لغت (یا فهرست فرهنگ لغت ها، در مورد انتقال با تنظیمات خروجی متعدد) از مقادیر تنظیمات ساخت جدید را برای اعمال بازگرداند. مجموعه کلید(های) فرهنگ لغت برگردانده شده باید دقیقاً شامل مجموعه تنظیمات ساختنی باشد که به پارامتر outputs تابع انتقال داده شده است. این درست است حتی اگر یک تنظیم ساخت در طول دوره انتقال تغییر نکرده باشد - مقدار اصلی آن باید به صراحت در فرهنگ لغت بازگشتی ارسال شود.

تعریف انتقال 1:2+

مثال پایان به پایان

انتقال لبه خروجی می تواند یک پیکربندی ورودی واحد را به دو یا چند پیکربندی خروجی ترسیم کند. این برای تعریف قوانینی که کدهای چند معماری را بسته بندی می کنند مفید است.

انتقال های 1:2+ با بازگرداندن فهرستی از فرهنگ لغت ها در تابع پیاده سازی انتقال تعریف می شوند.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return [
        {"//example:favorite_flavor" : "LATTE"},
        {"//example:favorite_flavor" : "MOCHA"},
    ]

coffee_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

آنها همچنین می توانند کلیدهای سفارشی را تنظیم کنند که تابع اجرای قانون می تواند از آنها برای خواندن وابستگی های فردی استفاده کند:

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

پیوست کردن انتقال

مثال پایان به پایان

انتقال ها را می توان در دو مکان متصل کرد: لبه های ورودی و لبه های خروجی. عملاً این بدان معنی است که قوانین می توانند پیکربندی خود را (انتقال لبه ورودی) و پیکربندی های وابستگی خود (انتقال لبه خروجی) را تغییر دهند.

توجه: در حال حاضر هیچ راهی برای پیوست کردن انتقال Starlark به قوانین بومی وجود ندارد. اگر نیاز به انجام این کار دارید، با bazel-discuss@googlegroups.com تماس بگیرید تا در یافتن راه‌حل‌ها کمک کنید.

انتقال لبه های ورودی

انتقال‌های لبه ورودی با پیوست کردن یک شیء transition (که توسط transition() ایجاد شده است) به پارامتر cfg rule() فعال می‌شوند:

# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
    implementation = _impl,
    cfg = hot_chocolate_transition,
    ...

انتقال لبه های ورودی باید انتقال 1:1 باشد.

انتقال لبه های خروجی

انتقال‌های لبه خروجی با پیوست کردن یک شیء transition (ایجاد شده توسط transition() ) به پارامتر cfg یک ویژگی فعال می‌شوند:

# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
    implementation = _impl,
    attrs = { "dep": attr.label(cfg = coffee_transition)}
    ...

انتقال لبه های خروجی می تواند 1:1 یا 1:2+ باشد.

برای نحوه خواندن این کلیدها به دسترسی به ویژگی ها با انتقال مراجعه کنید.

انتقال در گزینه های بومی

مثال پایان به پایان

انتقال های Starlark همچنین می توانند خواندن و نوشتن را در گزینه های پیکربندی ساخت بومی از طریق یک پیشوند خاص برای نام گزینه اعلام کنند.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//command_line_option:cpu": "k8"}

cpu_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]

گزینه های بومی پشتیبانی نشده

Bazel از انتقال در --define با "//command_line_option:define" پشتیبانی نمی کند. در عوض، از یک تنظیم ساخت سفارشی استفاده کنید. به طور کلی، استفاده های جدید از --define به نفع تنظیمات ساخت منع می شود.

--config از انتقال در --config پشتیبانی نمی کند. این به این دلیل است که --config یک پرچم "بسط" است که به پرچم های دیگر گسترش می یابد.

مهمتر از همه، --config ممکن است شامل پرچم هایی باشد که بر پیکربندی ساخت تأثیر نمی گذارد، مانند --spawn_strategy . Bazel، از نظر طراحی، نمی تواند چنین پرچم هایی را به اهداف فردی متصل کند. این بدان معناست که هیچ راه منسجمی برای اعمال آنها در انتقال وجود ندارد.

به عنوان یک راه حل، می توانید به صراحت پرچم هایی را که بخشی از پیکربندی در انتقال شما هستند، مشخص کنید. این امر مستلزم حفظ --config در دو مکان است که یک نقص شناخته شده UI است.

انتقال‌های روشن به تنظیمات ساخت چندگانه اجازه می‌دهند

هنگام تنظیم تنظیمات ساخت که اجازه چندین مقدار را می دهد، مقدار تنظیم باید با یک لیست تنظیم شود.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    # Using a value of just "dark" here will throw an error
    return {"//example:roasts" : ["dark"]},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:roasts"]
)

انتقال بدون عملیات

اگر یک انتقال {} ، [] ، یا None را برمی گرداند، این مختصر برای حفظ تمام تنظیمات در مقادیر اصلی است. این می تواند راحت تر از تنظیم صریح هر خروجی برای خودش باشد.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (attr)
    if settings["//example:already_chosen"] is True:
      return {}
    return {
      "//example:favorite_flavor": "dark chocolate",
      "//example:include_marshmallows": "yes",
      "//example:desired_temperature": "38C",
    }

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = ["//example:already_chosen"],
    outputs = [
        "//example:favorite_flavor",
        "//example:include_marshmallows",
        "//example:desired_temperature",
    ]
)

دسترسی به ویژگی ها با انتقال

مثال پایان به پایان

هنگام پیوست کردن یک انتقال به لبه خروجی (صرف نظر از اینکه انتقال یک انتقال 1:1 یا 1:2+ باشد)، ctx.attr مجبور است که یک لیست باشد، اگر قبلاً نبوده است. ترتیب عناصر در این لیست نامشخص است.

# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    return {"//example:favorite_flavor" : "LATTE"},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

def _rule_impl(ctx):
    # Note: List access even though "dep" is not declared as list
    transitioned_dep = ctx.attr.dep[0]

    # Note: Access doesn't change, other_deps was already a list
    for other dep in ctx.attr.other_deps:
      # ...


coffee_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = coffee_transition)
        "other_deps": attr.label_list(cfg = coffee_transition)
    })

اگر انتقال 1:2+ است و کلیدهای سفارشی را تنظیم می کند، ctx.split_attr می تواند برای خواندن عمق های جداگانه برای هر کلید استفاده شود:

# example/transitions/rules.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

def _rule_impl(ctx):
    apple_dep = ctx.split_attr.dep["Apple deps"]
    linux_dep = ctx.split_attr.dep["Linux deps"]
    # ctx.attr has a list of all deps for all keys. Order is not guaranteed.
    all_deps = ctx.attr.dep

multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = multi_arch_transition)
    })

مثال کامل را اینجا ببینید.

ادغام با پلتفرم ها و زنجیره های ابزار

امروزه بسیاری از پرچم‌های بومی مانند --cpu و --crosstool_top به وضوح زنجیره ابزار مرتبط هستند. در آینده، انتقال‌های صریح روی این نوع پرچم‌ها احتمالاً با انتقال در پلتفرم هدف جایگزین خواهند شد.

ملاحظات حافظه و عملکرد

افزودن ترانزیشن‌ها و در نتیجه پیکربندی‌های جدید به ساخت شما هزینه دارد: نمودارهای ساخت بزرگتر، نمودارهای ساخت کمتر قابل درک، و ساخت‌های کندتر. هنگام در نظر گرفتن استفاده از انتقال در قوانین ساخت، ارزش آن را دارد که این هزینه ها را در نظر بگیرید. در زیر مثالی از این که چگونه یک انتقال ممکن است رشد تصاعدی گراف ساخت شما را ایجاد کند، آورده شده است.

ساختارهای بد رفتار: مطالعه موردی

Scalability graph

شکل 1. نمودار مقیاس پذیری که یک هدف سطح بالا و وابستگی های آن را نشان می دهد.

این نمودار یک هدف سطح بالا، //pkg:app را نشان می‌دهد که به دو هدف بستگی دارد: //pkg:1_0 و //pkg:1_1. هر دو این اهداف به دو هدف بستگی دارند، //pkg:2_0 و //pkg:2_1. هر دو این اهداف به دو هدف بستگی دارند، //pkg:3_0 و //pkg:3_1. این کار تا //pkg:n_0 و //pkg:n_1 ادامه می‌یابد که هر دو به یک هدف، //pkg:dep بستگی دارند.

ساخت //pkg:app به اهداف \(2n+2\) نیاز دارد:

  • //pkg:app
  • //pkg:dep
  • //pkg:i_0 و //pkg:i_1 برای \(i\) در \([1..n]\)

تصور کنید ) یک پرچم --//foo:owner=<STRING> و //pkg:i_b اعمال می شود

depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"

به عبارت دیگر، //pkg:i_b b را به مقدار قدیمی --owner برای تمام عمق های آن اضافه می کند.

این اهداف پیکربندی شده زیر را تولید می کند:

//pkg:app                              //foo:owner=""
//pkg:1_0                              //foo:owner=""
//pkg:1_1                              //foo:owner=""
//pkg:2_0 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_0 (via //pkg:1_1)              //foo:owner="1"
//pkg:2_1 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_1 (via //pkg:1_1)              //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0)  //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1)  //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0)  //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1)  //foo:owner="11"
...

//pkg:dep اهداف پیکربندی شده \(2^n\) را تولید می کند: config.owner= "\(b_0b_1...b_n\)" برای همه \(b_i\) در \(\{0,1\}\).

این باعث می شود که نمودار ساخت به طور تصاعدی بزرگتر از گراف هدف باشد، با پیامدهای حافظه و عملکرد مربوطه.

TODO: استراتژی هایی برای اندازه گیری و کاهش این مسائل اضافه کنید.

بیشتر خواندن

برای جزئیات بیشتر در مورد اصلاح تنظیمات ساخت، نگاه کنید به: