السمات القابلة للضبط، المعروفة أيضًا باسم 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
عن "Choose;" الانخفاضات استنادًا إلى العلامات في سطر الأوامر. على وجه التحديد، يصبح deps
:
Command | 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()
أيضًا على srcs
وresources
وcmd
ومعظم السمات الأخرى. هناك عدد قليل فقط من السمات تكون غير قابلة للإعداد، ويتم وضع تعليقات توضيحية عليها بوضوح. على سبيل المثال، لا يمكن ضبط السمة config_setting
'svalues
.
select()
والاعتماديات
تغيّر بعض السمات معلمات الإصدار لجميع التبعيات الانتقالية ضمن هدف. على سبيل المثال، genrule
tools
تغيّر --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
على
tool1
معلمات الإصدار tool1
's التي تتضمن --cpu=x86
.
شروط الإعداد
يشير كل مفتاح في سمة قابلة للضبط إلى مرجع تصنيف إلى
config_setting
أو
constraint_value
.
config_setting
هي مجموعة من إعدادات علامات سطر الأوامر المتوقعة. ومن خلال تضمين هذه المصطلحات في هدف، سيكون من السهل
الحفاظ على &الشروط والتعبير العادي"؛ بحيث يمكن للمستخدمين الإشارة إليها من أماكن متعددة.
يقدم constraint_value
توافقًا مع السلوك من عدّة منصات.
علامات المضمّنة
تُدمج العلامات، مثل --cpu
، في Bazel، وهي أداة تُعنى بفهم جميع الإصدارات في جميع المشاريع. ويتم تحديدها باستخدام السمة
config_setting
's
values
:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
هو اسم علم (بدون --
، لذلك يكون "cpu"
بدلاً من "--cpu"
). valueN
القيمة المتوقّعة لتلك العلامة. يتطابق :meaningful_condition_name
إذا تطابق
كل إدخال في values
. الطلب غير ذي صلة.
يتم تحليل 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
'sflag_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
ولتوفير أخطاء أكثر وضوحًا، يمكنك ضبط رسائل مخصَّصة باستخدام السمة select()
'sno_match_error
.
الأنظمة الأساسية
على الرغم من أنّ إمكانية تحديد علامات متعددة في سطر الأوامر توفّر المرونة، قد يكون من الصعب أيضًا وضع كل علامة بشكل فردي في كل مرة تريد إنشاء هدف. تتيح لك الأنظمة الأساسية دمج هذه المجموعات في حِزم بسيطة.
# 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
التي تحتوي على مجموعة فرعية من 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
مباشرةً:
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()
يمكن أن يظهر 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
للمطابقة عندما تتطابق عدة شروط، ننصحك و
التسلسل.
سلسلة أو
ننصحك باتّباع الخطوات التالية:
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
في Skylib's
selects
استخدام OR
ing الشروط مباشرةً في 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
في Skylib's
selects
استخدام OR
config_setting
s العديدة:
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
في سمات مختلفة.
خطأ في مطابقة العديد من الشروط ما لم يكن أحدها غير واضح وخاص بالشروط الأخرى. انظر هنا للحصول على تفاصيل.
AND سلسلة
إذا كنت بحاجة إلى فرع select
لمطابقة عدة شروط، استخدِم
ماكرو وحدة ماكرو
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"، لا يمكن AND
config_setting
مباشرةً
داخل select
. يجب تضمينها بوضوح في 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'، والتي تحدث قبل أن تكون قيم العلامات معروفة.
ويُعد ذلك أحد القيود الأساسية لتصميم البازلاء والذي من غير المحتمل أن يتغيّر في أي وقت قريب.
ثانيًا، تفتقر وحدات الماكرو التي تحتاج إلى تكرار عبر جميع مسارات select
، على الرغم من أنها مجدية من الناحية التقنية، إلى واجهة مستخدم مترابطة. المزيد من التصميم ضروري لتغيير هذا.
طلب بحث Bazel وcquery
يعمل البازيل query
فوق مرحلة التحميل في Bazel's.
وهذا يعني أنها لا تتعرّف على سطر الأوامر الذي تضعه الأهداف المستهدفة نظرًا لعدم تقييم هذه العلامات حتى وقت لاحق في الإصدار (في مرحلة التحليل).
لذلك، لا يمكن تحديد فروع 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
الأسئلة الشائعة
لماذا لا يعمل't 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 علامات سطر الأوامر Build’#39;s. وهذا يعني أنه لا تتوفر معلومات كافية لتقييم select(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() القيمة "صحيح" دائمًا؟
نظرًا لأن وحدات الماكرو (وليس القواعد) بتعريفها يمكن'لتقييم select()
، فإن أي محاولة لتنفيذ ذلك عادةً ما تظهر خطأ:
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().
المنطق المنطقية هي حالة خاصة تعذّر تنفيذ الإجراء بها تلقائيًا، لذا يجب توخي الحذر الشديد بشأنها:
$ 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()
نفسه. وفقًا لمعايير التصميم Pythonic، يتم عرض جميع العناصر باستثناء عدد قليل جدًا من الاستثناءات.
هل يمكنني قراءة select() مثل إملاء؟
يمكن لوحدات الماكرو الماكرو تقييم الاختيارات لأنّ وحدات الماكرو تُقيِّم قبل أن تعرف البازل معلَمات سطر الأوامر Build'#. هل يمكنهم على الأقل قراءة قاموس select()
's، على سبيل المثال، لإضافة لاحقة إلى كل قيمة؟
من الناحية النظرية، يمكن تحقيق ذلك، ولكنها ليست ميزة 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()
وأنواع الإعلانات المدمجة مع المحتوى، يمكنك إجراء ما يلي:
$ 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 + "> $@",
)
لماذا لا يعمل't select() مع bind()؟
لأن bind()
هي قاعدة WORKSPACE، وليست قاعدة BUILD.
لا تتضمّن قواعد Workspace إعدادات محدَّدة، ولا يتم تقييمها بالطريقة نفسها التي يتم بها تقييم قواعد 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
.
لماذا لا يختار't Select() ما أتوقعه؟
إذا كان لدى //myapp:foo
select()
لا يختار الشرط الذي تتوقعه، يمكنك استخدام cquery وbazel config
لتصحيح الأخطاء:
إذا كان //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
بتنسيقات مختلفة في الإصدار نفسه. راجِع مستندات cquery للحصول على إرشادات حول استخدام somepath
للحصول على المستند الصحيح.
لماذا لا يعمل 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
هذا، أي select()
يجب استخدامه إذا كان النظام الأساسي المستهدَف يشتمل على كلٍّ من القيود @platforms//cpu:x86
و@platforms//os:linux
، ولم يتم تعريف :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": [],
}),
)
لا يصادق فريق Bazel على هذا الأمر، لأنه يقيد مشروعك بشكل زائد ويخلط بين المستخدمين عندما لا يتطابق الشرط المتوقع.