ویژگیهای قابل تنظیم ، که معمولاً با نام select()
شناخته میشود، یک ویژگی Bazel است که به کاربران اجازه میدهد مقادیر ویژگیهای قانون ساخت را در خط فرمان تغییر دهند.
این را می توان به عنوان مثال برای یک کتابخانه چند پلتفرمی که به طور خودکار پیاده سازی مناسب را برای معماری انتخاب می کند، یا برای یک باینری با قابلیت تنظیم ویژگی که می تواند در زمان ساخت سفارشی شود، استفاده شود.
مثال
# myapp/BUILD
cc_binary(
name = "mybinary",
srcs = ["main.cc"],
deps = select({
":arm_build": [":arm_lib"],
":x86_debug_build": [":x86_dev_lib"],
"//conditions:default": [":generic_lib"],
}),
)
config_setting(
name = "arm_build",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_debug_build",
values = {
"cpu": "x86",
"compilation_mode": "dbg",
},
)
این یک cc_binary
را اعلام می کند که دپ های خود را بر اساس پرچم های خط فرمان "انتخاب" می کند. به طور خاص، deps
تبدیل می شود:
فرمان | عمق = |
bazel build //myapp:mybinary --cpu=arm | [":arm_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=x86 | [":x86_dev_lib"] |
bazel build //myapp:mybinary --cpu=ppc | [":generic_lib"] |
bazel build //myapp:mybinary -c dbg --cpu=ppc | [":generic_lib"] |
select()
به عنوان یک مکان نگهدار برای مقداری عمل می کند که بر اساس شرایط پیکربندی انتخاب می شود، که برچسب هایی هستند که به اهداف config_setting
ارجاع می دهند. با استفاده از select()
در یک ویژگی قابل تنظیم، ویژگی به طور موثر مقادیر متفاوتی را زمانی که شرایط مختلف برقرار است، اتخاذ می کند.
منطبق ها باید بدون ابهام باشند: یا دقیقاً یک شرط باید مطابقت داشته باشد یا اگر چندین شرط مطابقت داشته باشند، values
یک فرد باید ابرمجموعه دقیقی از همه شرط های دیگر باشد. به عنوان مثال، values = {"cpu": "x86", "compilation_mode": "dbg"}
یک تخصص بدون ابهام از values = {"cpu": "x86"}
. شرط داخلی //conditions:default
به طور خودکار مطابقت دارد زمانی که هیچ چیز دیگری انجام نمی دهد.
در حالی که این مثال از deps
استفاده می کند، select()
select روی srcs
، resources
، cmd
و بسیاری از ویژگی های دیگر به خوبی کار می کند. فقط تعداد کمی از ویژگیها غیرقابل تنظیم هستند و به وضوح مشروح شدهاند. برای مثال، ویژگی values
خود config_setting
غیر قابل تنظیم است.
select()
و وابستگی ها
ویژگی های خاص پارامترهای ساخت را برای همه وابستگی های گذرا تحت یک هدف تغییر می دهند. برای مثال، tools
genrule
--cpu
را به CPU دستگاهی که Bazel را اجرا می کند تغییر می دهد (که به لطف کامپایل متقابل ممکن است با CPU مورد نظر متفاوت باشد). این به عنوان یک انتقال پیکربندی شناخته می شود.
داده شده
#myapp/BUILD
config_setting(
name = "arm_cpu",
values = {"cpu": "arm"},
)
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
genrule(
name = "my_genrule",
srcs = select({
":arm_cpu": ["g_arm.src"],
":x86_cpu": ["g_x86.src"],
}),
tools = select({
":arm_cpu": [":tool1"],
":x86_cpu": [":tool2"],
}),
)
cc_binary(
name = "tool1",
srcs = select({
":arm_cpu": ["armtool.cc"],
":x86_cpu": ["x86tool.cc"],
}),
)
در حال اجرا
$ bazel build //myapp:my_genrule --cpu=arm
در یک ماشین توسعه دهنده x86
بیلد را به g_arm.src
، tool1
و x86tool.cc
می کند. هر دو select
متصل به my_genrule
از پارامترهای ساخت my_genrule
استفاده میکنند که شامل --cpu=arm
است. ویژگی tools
--cpu
را به x86
برای tool1
و وابستگی های انتقالی آن تغییر می دهد. select
on tool1
از پارامترهای ساخت tool1
استفاده می کند که شامل --cpu=x86
است.
شرایط پیکربندی
هر کلید در یک ویژگی قابل تنظیم یک مرجع برچسب به config_setting
یا constraint_value
است.
config_setting
فقط مجموعه ای از تنظیمات پرچم خط فرمان مورد انتظار است. با کپسوله کردن آنها در یک هدف، حفظ شرایط "استاندارد" که کاربران می توانند از چندین مکان به آنها مراجعه کنند، آسان است.
constraint_value
از رفتار چند پلتفرمی پشتیبانی می کند.
پرچم های داخلی
پرچم هایی مانند --cpu
در Bazel تعبیه شده اند: ابزار ساخت به طور بومی آنها را برای همه بیلدها در همه پروژه ها درک می کند. اینها با ویژگی values
config_setting
مشخص می شوند:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
یک نام پرچم است (بدون --
، بنابراین "cpu"
به جای "--cpu"
). valueN
مقدار مورد انتظار برای آن پرچم است. اگر هر ورودی در values
مطابقت داشته باشد :meaningful_condition_name
مطابقت دارد. ترتیب بی ربط است
valueN
به گونه ای تجزیه می شود که گویی در خط فرمان تنظیم شده است. این یعنی:
-
values = { "compilation_mode": "opt" }
مطابق باbazel build -c opt
-
values = { "force_pic": "true" }
باbazel build --force_pic=1
-
values = { "force_pic": "0" }
باbazel build --noforce_pic
config_setting
فقط از پرچم هایی پشتیبانی می کند که بر رفتار هدف تأثیر می گذارد. به عنوان مثال، --show_progress
مجاز نیست زیرا تنها بر نحوه گزارش پیشرفت Bazel به کاربر تأثیر می گذارد. اهداف نمی توانند از آن پرچم برای ساختن نتایج خود استفاده کنند. مجموعه دقیق پرچمهای پشتیبانی شده مستند نشده است. در عمل، بیشتر پرچم هایی که «معنا دارند» کار می کنند.
پرچم های سفارشی
شما می توانید پرچم های خاص پروژه خود را با تنظیمات ساخت Starlark مدل کنید. برخلاف پرچمهای داخلی، این پرچمها به عنوان اهداف ساخت تعریف میشوند، بنابراین Bazel آنها را با برچسبهای هدف ارجاع میدهد.
اینها با ویژگی config_setting
flag_values
می شوند:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
رفتار مانند پرچم های داخلی است. برای نمونه کار اینجا را ببینید.
--define
یک نحو جایگزین جایگزین برای پرچم های سفارشی است (به عنوان مثال --define foo=bar
). این را می توان در ویژگی مقادیر ( values = {"define": "foo=bar"}
) یا ویژگی define_values ( define_values = {"foo": "bar"}
کرد. --define
فقط برای سازگاری با عقب پشتیبانی می شود. هر زمان که ممکن است تنظیمات ساخت Starlark را ترجیح دهید.
values
، flag_values
، و define_values
به طور مستقل ارزیابی می شوند. اگر همه مقادیر در همه آنها مطابقت داشته باشند، config_setting
مطابقت دارد.
شرط پیش فرض
شرط داخلی //conditions:default
زمانی مطابقت دارد که هیچ شرط دیگری مطابقت نداشته باشد.
به دلیل قانون «دقیقاً یک تطبیق»، یک ویژگی قابل تنظیم بدون تطابق و بدون شرط پیشفرض، خطای "no matching conditions"
را منتشر میکند. این می تواند در برابر خرابی های بی صدا ناشی از تنظیمات غیرمنتظره محافظت کند:
# myapp/BUILD
config_setting(
name = "x86_cpu",
values = {"cpu": "x86"},
)
cc_library(
name = "x86_only_lib",
srcs = select({
":x86_cpu": ["lib.cc"],
}),
)
$ bazel build //myapp:x86_only_lib --cpu=arm
ERROR: Configurable attribute "srcs" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//myapp:x86_cpu
برای خطاهای حتی واضحتر، میتوانید پیامهای سفارشی را با ویژگی no_match_error
در select()
تنظیم کنید.
بستر، زمینه
در حالی که توانایی تعیین چند پرچم در خط فرمان انعطافپذیری را فراهم میکند، تنظیم جداگانه هر یک از آنها هر بار که میخواهید هدفی را بسازید نیز میتواند دشوار باشد. پلتفرمها به شما امکان میدهند این موارد را در بستههای ساده ادغام کنید.
# myapp/BUILD
sh_binary(
name = "my_rocks",
srcs = select({
":basalt": ["pyroxene.sh"],
":marble": ["calcite.sh"],
"//conditions:default": ["feldspar.sh"],
}),
)
config_setting(
name = "basalt",
constraint_values = [
":black",
":igneous",
],
)
config_setting(
name = "marble",
constraint_values = [
":white",
":metamorphic",
],
)
# constraint_setting acts as an enum type, and constraint_value as an enum value.
constraint_setting(name = "color")
constraint_value(name = "black", constraint_setting = "color")
constraint_value(name = "white", constraint_setting = "color")
constraint_setting(name = "texture")
constraint_value(name = "smooth", constraint_setting = "texture")
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
platform(
name = "basalt_platform",
constraint_values = [
":black",
":igneous",
],
)
platform(
name = "marble_platform",
constraint_values = [
":white",
":smooth",
":metamorphic",
],
)
پلتفرم را می توان در خط فرمان مشخص کرد. این config_setting
s را فعال می کند که حاوی زیرمجموعه ای از constraint_values
پلتفرم است و به آن config_setting
اجازه می دهد تا در عبارت select()
مطابقت داشته باشند.
به عنوان مثال، برای تنظیم ویژگی srcs
my_rocks
به calcite.sh
، می توانید به سادگی اجرا کنید.
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
بدون پلتفرم، ممکن است چیزی شبیه به این باشد
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
همچنین میتواند مستقیماً constraint_value
s را بخواند:
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
name = "my_rocks",
srcs = select({
":igneous": ["igneous.sh"],
":metamorphic" ["metamorphic.sh"],
}),
)
این امر نیاز به config_setting
های صفحه دیگ را در زمانی که شما فقط باید مقادیر منفرد را بررسی کنید، کاهش می دهد.
پلتفرم ها هنوز در دست توسعه هستند. برای جزئیات به مستندات مراجعه کنید.
ترکیب select()
s
select
می تواند چندین بار در یک ویژگی ظاهر شود:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"] +
select({
":armeabi_mode": ["armeabi_src.sh"],
":x86_mode": ["x86_src.sh"],
}) +
select({
":opt_mode": ["opt_extras.sh"],
":dbg_mode": ["dbg_extras.sh"],
}),
)
select
نمی تواند در select
دیگری ظاهر شود. اگر میخواهید انتخابها را در selects
قرار دهید و مشخصه شما اهداف دیگری را به عنوان مقادیر میگیرد، از یک هدف میانی استفاده کنید:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":armeabi_mode": [":armeabi_lib"],
...
}),
)
sh_library(
name = "armeabi_lib",
srcs = select({
":opt_mode": ["armeabi_with_opt.sh"],
...
}),
)
اگر به select
برای مطابقت با شرایط متعدد نیاز دارید، AND chaining را در نظر بگیرید.
یا زنجیر زدن
موارد زیر را در نظر بگیرید:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
اکثر شرایط به همان عمق ارزیابی می شوند. اما خواندن و حفظ این نحو دشوار است. خوب است که مجبور نباشید [":standard_lib"]
چندین بار تکرار کنید.
یک گزینه این است که مقدار را به عنوان یک متغیر BUILD از پیش تعریف کنید:
STANDARD_DEP = [":standard_lib"]
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": STANDARD_DEP,
":config2": STANDARD_DEP,
":config3": STANDARD_DEP,
":config4": [":special_lib"],
}),
)
این امر مدیریت وابستگی را آسان تر می کند. اما همچنان باعث تکرارهای غیر ضروری می شود.
برای پشتیبانی مستقیم تر، از یکی از موارد زیر استفاده کنید:
selects.with_or
ماکرو with_or در ماژول Selects Skylib از شرایط OR
مستقیماً در یک selects
select
می کند:
load("@bazel_skylib//lib:selects.bzl", "selects")
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = selects.with_or({
(":config1", ":config2", ":config3"): [":standard_lib"],
":config4": [":special_lib"],
}),
)
selects.config_setting_group
ماکرو config_setting_group در ماژول config_setting
پشتیبانی OR
selects
:
load("@bazel_skylib//lib:selects.bzl", "selects")
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_or_2",
match_any = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_or_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
بر خلاف selects.with_or
، اهداف مختلف می توانند :config1_or_2
را در بین ویژگی های مختلف به اشتراک بگذارند.
تطابق چند شرط یک خطا است، مگر اینکه یکی «تخصص» واضح دیگری باشد. برای جزئیات اینجا را ببینید.
و زنجیر
اگر به یک شاخه select
برای مطابقت با شرایط متعدد نیاز دارید، از Skylib macro config_setting_group استفاده کنید:
config_setting(
name = "config1",
values = {"cpu": "arm"},
)
config_setting(
name = "config2",
values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
name = "config1_and_2",
match_all = [":config1", ":config2"],
)
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1_and_2": [":standard_lib"],
"//conditions:default": [":other_lib"],
}),
)
برخلاف OR chaining، config_setting
های موجود را نمی توان مستقیماً در یک select
AND
ویرایش کرد. شما باید به صراحت آنها را در یک config_setting_group
.
پیام های خطای سفارشی
به طور پیشفرض، زمانی که هیچ شرطی مطابقت نداشته باشد، هدفی که select()
به آن متصل است با خطای زیر شکست میخورد:
ERROR: Configurable attribute "deps" doesn't match this configuration (would
a default condition help?).
Conditions checked:
//tools/cc_target_os:darwin
//tools/cc_target_os:android
این را می توان با ویژگی no_match_error
سفارشی کرد:
cc_library(
name = "my_lib",
deps = select(
{
"//tools/cc_target_os:android": [":android_deps"],
"//tools/cc_target_os:windows": [":windows_deps"],
},
no_match_error = "Please build with an Android or Windows toolchain",
),
)
$ bazel build //myapp:my_lib
ERROR: Configurable attribute "deps" doesn't match this configuration: Please
build with an Android or Windows toolchain
سازگاری قوانین
پیاده سازی قوانین مقادیر حل شده از ویژگی های قابل تنظیم را دریافت می کنند. به عنوان مثال، با توجه به:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
کد اجرای قانون ctx.attr.some_attr
را به صورت [":foo"]
می بیند.
ماکروها میتوانند بندهای select()
را بپذیرند و آنها را به قوانین بومی منتقل کنند. اما آنها نمی توانند مستقیماً آنها را دستکاری کنند . برای مثال، هیچ راهی برای تبدیل ماکرو وجود ندارد
select({"foo": "val"}, ...)
به
select({"foo": "val_with_suffix"}, ...)
این به دو دلیل است.
اولاً، ماکروهایی که باید بدانند انتخابی کدام مسیر را select
میکند، نمیتوانند کار کنند ، زیرا ماکروها در مرحله بارگذاری Bazel ارزیابی میشوند، که قبل از مشخص شدن مقادیر پرچم اتفاق میافتد. این یک محدودیت اصلی طراحی Bazel است که بعید است به این زودی تغییر کند.
دوم، ماکروهایی که فقط باید روی همه مسیرهای select
تکرار شوند، در حالی که از نظر فنی امکان پذیر هستند، فاقد یک رابط کاربری منسجم هستند. طراحی بیشتر برای تغییر این امر ضروری است.
پرس و جو و استعلام بازل
query
Bazel در مرحله بارگیری Bazel عمل می کند. این بدان معنی است که نمی داند یک هدف از چه پرچم های خط فرمان استفاده می کند زیرا این پرچم ها تا مراحل بعدی ساخت (در مرحله تجزیه و تحلیل ) ارزیابی نمی شوند. بنابراین نمی تواند تعیین کند که کدام شاخه select()
انتخاب شده است.
cquery
بعد از مرحله تجزیه و تحلیل Bazel عمل میکند، بنابراین تمام این اطلاعات را دارد و میتواند به طور دقیق select()
s را حل کند.
در نظر گرفتن:
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
# myapp/BUILD
string_flag(
name = "dog_type",
build_setting_default = "cat"
)
cc_library(
name = "my_lib",
deps = select({
":long": [":foo_dep"],
":short": [":bar_dep"],
}),
)
config_setting(
name = "long",
flag_values = {":dog_type": "dachshund"},
)
config_setting(
name = "short",
flag_values = {":dog_type": "pug"},
)
query
بیش از حد تقریبی می کند وابستگی های :my_lib
:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
در حالی که cquery
وابستگی های دقیق آن را نشان می دهد:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
سوالات متداول
چرا select() در ماکروها کار نمی کند؟
Select() در قوانین کار می کند! برای جزئیات بیشتر به سازگاری قوانین مراجعه کنید.
مشکل اصلی این سوال معمولاً این است که () select در ماکروها کار نمی کند. اینها با قوانین فرق دارد. برای درک تفاوت، به مستندات قوانین و ماکروها مراجعه کنید. در اینجا یک مثال انتها به انتها آورده شده است:
تعریف قانون و کلان:
# myapp/defs.bzl
# Rule implementation: when an attribute is read, all select()s have already
# been resolved. So it looks like a plain old attribute just like any other.
def _impl(ctx):
name = ctx.attr.name
allcaps = ctx.attr.my_config_string.upper() # This works fine on all values.
print("My name is " + name + " with custom message: " + allcaps)
# Rule declaration:
my_custom_bazel_rule = rule(
implementation = _impl,
attrs = {"my_config_string": attr.string()},
)
# Macro declaration:
def my_custom_bazel_macro(name, my_config_string):
allcaps = my_config_string.upper() # This line won't work with select(s).
print("My name is " + name + " with custom message: " + allcaps)
قاعده و کلان را مثال بزنید:
# myapp/BUILD
load("//myapp:defs.bzl", "my_custom_bazel_rule")
load("//myapp:defs.bzl", "my_custom_bazel_macro")
my_custom_bazel_rule(
name = "happy_rule",
my_config_string = select({
"//tools/target_cpu:x86": "first string",
"//tools/target_cpu:ppc": "second string",
}),
)
my_custom_bazel_macro(
name = "happy_macro",
my_config_string = "fixed string",
)
my_custom_bazel_macro(
name = "sad_macro",
my_config_string = select({
"//tools/target_cpu:x86": "first string",
"//tools/target_cpu:ppc": "other string",
}),
)
ساختمان با شکست مواجه می شود زیرا sad_macro
نمی تواند select()
را پردازش کند:
$ bazel build //myapp:all
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
ERROR: error loading package 'myapp': Package 'myapp' contains errors.
وقتی که sad_macro
را کامنت می گذارید، ساختمان موفق می شود:
# Comment out sad_macro so it doesn't mess up the build.
$ bazel build //myapp:all
DEBUG: /myworkspace/myapp/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
DEBUG: /myworkspace/myapp/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.
تغییر این غیرممکن است زیرا طبق تعریف ماکروها قبل از خواندن پرچمهای خط فرمان ساخت توسط Bazel ارزیابی میشوند. این بدان معناست که اطلاعات کافی برای ارزیابی انتخاب()ها وجود ندارد.
با این حال، ماکروها می توانند s select()
را به عنوان حباب های مات به قوانین منتقل کنند:
# myapp/defs.bzl
def my_custom_bazel_macro(name, my_config_string):
print("Invoking macro " + name)
my_custom_bazel_rule(
name = name + "_as_target",
my_config_string = my_config_string,
)
$ bazel build //myapp:sad_macro_less_sad
DEBUG: /myworkspace/myapp/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
DEBUG: /myworkspace/myapp/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.
چرا () select همیشه true برمی گرداند؟
از آنجا که ماکروها (اما نه قوانین) بنا به تعریف نمی توانند select()
s را ارزیابی کنند، هر تلاشی برای انجام این کار معمولاً یک خطا ایجاد می کند:
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
(most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
Booleans یک مورد خاص است که بیصدا با شکست مواجه میشود، بنابراین باید مراقب آنها باشید:
$ cat myapp/defs.bzl
def my_boolean_macro(boolval):
print("TRUE" if boolval else "FALSE")
$ cat myapp/BUILD
load("//myapp:defs.bzl", "my_boolean_macro")
my_boolean_macro(
boolval = select({
"//tools/target_cpu:x86": True,
"//tools/target_cpu:ppc": False,
}),
)
$ bazel build //myapp:all --cpu=x86
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
$ bazel build //mypro:all --cpu=ppc
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
این به این دلیل اتفاق می افتد که ماکروها محتوای select()
را درک نمی کنند. بنابراین چیزی که آنها واقعاً ارزیابی می کنند، خود شی select()
است. طبق استانداردهای طراحی پایتونیک ، تمام اشیاء به غیر از تعداد بسیار کمی از استثناها به طور خودکار true را برمیگردانند.
آیا می توانم select() را مانند دیکت بخوانم؟
ماکروها نمیتوانند انتخاب(ها) را ارزیابی کنند، زیرا ماکروها قبل از اینکه Bazel بداند پارامترهای خط فرمان build چیست، ارزیابی میشوند. آیا حداقل می توانند فرهنگ لغت select()
را بخوانند تا مثلاً یک پسوند به هر مقدار اضافه کنند؟
از نظر مفهومی این امکان پذیر است، اما هنوز یکی از ویژگی های Bazel نیست. کاری که امروز می توانید انجام دهید این است که یک دیکشنری مستقیم تهیه کنید، سپس آن را به یک select()
وارد کنید:
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
+ " > $@"
)
$ cat myapp/BUILD
selecty_genrule(
name = "selecty",
select_cmd = {
"//tools/target_cpu:x86": "x86 mode",
},
)
$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX
اگر می خواهید از هر دو نوع select()
و native پشتیبانی کنید، می توانید این کار را انجام دهید:
$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
cmd_suffix = ""
if type(select_cmd) == "string":
cmd_suffix = select_cmd + " WITH SUFFIX"
elif type(select_cmd) == "dict":
for key in select_cmd.keys():
select_cmd[key] += " WITH SUFFIX"
cmd_suffix = select(select_cmd + {"//conditions:default": "default"})
native.genrule(
name = name,
outs = [name + ".out"],
srcs = [],
cmd = "echo " + cmd_suffix + "> $@",
)
چرا select() با bind() کار نمی کند؟
زیرا bind()
یک قانون WORKSPACE است نه یک قانون BUILD.
قوانین فضای کاری پیکربندی خاصی ندارند و مانند قوانین BUILD ارزیابی نمی شوند. بنابراین، یک select()
در bind()
در واقع نمی تواند برای هیچ شاخه خاصی ارزیابی شود.
در عوض، برای انجام این نوع تعیین زمان اجرا، باید از alias()
با یک select()
در ویژگی actual
استفاده کنید. این به درستی کار می کند، زیرا alias()
یک قانون BUILD است و با یک پیکربندی خاص ارزیابی می شود.
در صورت نیاز، حتی می توانید یک نقطه هدف bind()
به یک alias()
داشته باشید.
$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
http_archive(name = "alternative", ...)
http_archive(name = "boringssl", ...)
$ cat BUILD
config_setting(
name = "alt_ssl",
define_values = {
"ssl_library": "alternative",
},
)
alias(
name = "ssl",
actual = select({
"//:alt_ssl": "@alternative//:ssl",
"//conditions:default": "@boringssl//:ssl",
}),
)
با این تنظیمات، میتوانید --define ssl_library=alternative
را پاس کنید، و هر هدفی که به //:ssl
یا //external:ssl
بستگی دارد، جایگزین واقع در @alternative//:ssl
را میبیند.
چرا select() من چیزی را که انتظار دارم انتخاب نمی کند؟
اگر //myapp:foo
یک select()
دارد که شرایط مورد انتظار شما را انتخاب نمی کند، از cquery و bazel config
bazel برای اشکال زدایی استفاده کنید:
اگر //myapp:foo
هدف سطح بالایی است که میسازید، اجرا کنید:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
اگر هدف //bar
دیگری را می سازید که به //myapp:foo در جایی در زیرگراف آن بستگی دارد، اجرا کنید:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
(12e23b9a2b534a)
در کنار //myapp:foo
یک هش از پیکربندی است که //myapp:foo
's select()
حل می کند. می توانید مقادیر آن را با bazel config
بررسی کنید:
$ bazel config 12e23b9a2b534a
BuildConfigurationValue 12e23b9a2b534a
Fragment com.google.devtools.build.lib.analysis.config.CoreOptions {
cpu: darwin
compilation_mode: fastbuild
...
}
Fragment com.google.devtools.build.lib.rules.cpp.CppOptions {
linkopt: [-Dfoo=bar]
...
}
...
سپس این خروجی را با تنظیمات مورد انتظار هر config_setting
کنید.
//myapp:foo
ممکن است در پیکربندی های مختلف در یک بیلد وجود داشته باشد. برای راهنمایی در مورد استفاده از somepath
برای دریافت مسیر مناسب، به اسناد cquery مراجعه کنید.
چرا select()
با پلتفرم ها کار نمی کند؟
Bazel از ویژگیهای قابل تنظیم برای بررسی اینکه آیا یک پلتفرم معین پلتفرم هدف است یا خیر پشتیبانی نمیکند زیرا معنایی آن نامشخص است.
مثلا:
platform(
name = "x86_linux_platform",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
در این فایل BUILD
، اگر پلتفرم هدف دارای محدودیتهای @platforms//cpu:x86
و @platforms//os:linux
باشد، باید از کدام select()
استفاده شود، اما آیا :x86_linux_platform
در اینجا تعریف نشده است؟ نویسنده فایل BUILD
و کاربری که پلتفرم جداگانه را تعریف کرده است ممکن است ایده های متفاوتی داشته باشند.
به جای آن چه باید بکنم؟
در عوض، یک config_setting
تعریف کنید که با هر پلتفرمی با این محدودیت ها مطابقت داشته باشد:
config_setting(
name = "is_x86_linux",
constraint_values = [
"@platforms//cpu:x86",
"@platforms//os:linux",
],
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_x86_linux": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
این فرآیند معناشناسی خاصی را تعریف میکند و برای کاربران روشنتر میکند که چه پلتفرمهایی شرایط مورد نظر را برآورده میکنند.
اگر واقعاً بخواهم در پلتفرم select
کنم، چه؟
اگر الزامات ساخت شما به طور خاص نیاز به بررسی پلت فرم دارد، می توانید مقدار پرچم --platforms
را در یک config_setting
:
config_setting(
name = "is_specific_x86_linux_platform",
values = {
"platforms": ["//package:x86_linux_platform"],
},
)
cc_library(
name = "lib",
srcs = [...],
linkopts = select({
":is_specific_x86_linux_platform": ["--enable_x86_optimizations"],
"//conditions:default": [],
}),
)
تیم بازل انجام این کار را تایید نمی کند. بیش از حد ساخت شما را محدود می کند و در زمانی که شرایط مورد انتظار مطابقت ندارد، کاربران را گیج می کند.