این صفحه مزایا و استفاده اساسی از تنظیمات 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
به وضوح زنجیره ابزار مرتبط هستند. در آینده، انتقالهای صریح روی این نوع پرچمها احتمالاً با انتقال در پلتفرم هدف جایگزین خواهند شد.
ملاحظات حافظه و عملکرد
افزودن ترانزیشنها و در نتیجه پیکربندیهای جدید به ساخت شما هزینه دارد: نمودارهای ساخت بزرگتر، نمودارهای ساخت کمتر قابل درک، و ساختهای کندتر. هنگام در نظر گرفتن استفاده از انتقال در قوانین ساخت، ارزش آن را دارد که این هزینه ها را در نظر بگیرید. در زیر مثالی از این که چگونه یک انتقال ممکن است رشد تصاعدی گراف ساخت شما را ایجاد کند، آورده شده است.
ساختارهای بد رفتار: مطالعه موردی
شکل 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: استراتژی هایی برای اندازه گیری و کاهش این مسائل اضافه کنید.
بیشتر خواندن
برای جزئیات بیشتر در مورد اصلاح تنظیمات ساخت، نگاه کنید به:
- پیکربندی ساخت Starlark
- نقشه راه پیکربندی Bazel
- مجموعه کاملی از نمونه های پایان به پایان