कॉन्फ़िगरेशन किए जा सकने वाले एट्रिब्यूट, आम तौर पर select()
के नाम से जाना जाता है. यह एक BaZ सुविधा है. इसकी मदद से उपयोगकर्ता, कमांड लाइन पर नियम के एट्रिब्यूट की वैल्यू को टॉगल कर सकते हैं.
उदाहरण के लिए, इसका इस्तेमाल किसी ऐसी मल्टी-प्लैटफ़ॉर्म लाइब्रेरी के लिए किया जा सकता है जो आर्किटेक्चर के लिए सही तरीके को अपने-आप चुनती है. इसके अलावा, इसका इस्तेमाल ऐसी सुविधा-कॉन्फ़िगरेशन वाली बाइनरी के लिए भी किया जा सकता है जिसे बिल्ड के समय पसंद के मुताबिक बनाया जा सकता है.
उदाहरण
# 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
इस तरह दिखेगा:
कमांड | 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()
का इस्तेमाल करने पर, अलग-अलग शर्तें लागू होने पर एट्रिब्यूट, अलग-अलग वैल्यू को असरदार तरीके से अपना लेता है.
मैच साफ़ तौर पर होने चाहिए: अगर कई शर्तें मैच करती हैं, तो इनमें से कोई एक होना चाहिए
* वे सभी एक ही वैल्यू पर रिज़ॉल्व होते हैं. उदाहरण के लिए, linux x86 पर चलने पर, यह साफ़ तौर पर पता चलता है कि {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
, दोनों शाखाओं से "hello" पर पहुंचती है.
* किसी व्यक्ति का values
, बाकी सभी लोगों के values
का सख्त सुपरसेट होता है. उदाहरण के लिए, values = {"cpu": "x86", "compilation_mode": "dbg"}
, values = {"cpu": "x86"}
की विशेषज्ञता है.
अगर कोई दूसरी शर्त मैच नहीं होती है, तो पहले से मौजूद शर्त //conditions:default
अपने-आप मैच हो जाती है.
इस उदाहरण में deps
का इस्तेमाल किया गया है, लेकिन select()
srcs
, resources
, cmd
, और ज़्यादातर दूसरे एट्रिब्यूट की तरह ही काम करता है. सिर्फ़ कुछ एट्रिब्यूट कॉन्फ़िगर नहीं किए जा सकते. इन पर साफ़ तौर पर एनोटेशन दिया गया है. उदाहरण के लिए, config_setting
के values
एट्रिब्यूट को कॉन्फ़िगर नहीं किया जा सकता.
select()
और डिपेंडेंसी
कुछ एट्रिब्यूट, टारगेट के तहत सभी ट्रांज़िटिव डिपेंडेंसी के लिए बिल्ड पैरामीटर बदल देते हैं. उदाहरण के लिए, genrule
का tools
, --cpu
को Bazel चलाने वाली मशीन के सीपीयू में बदल देता है. क्रॉस-कंपाइलेशन की मदद से, यह सीपीयू उस सीपीयू से अलग हो सकता है जिसके लिए टारगेट बनाया गया है. इसे कॉन्फ़िगरेशन ट्रांज़िशन कहा जाता है.
दिया गया
#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
के लिए बंडल बनाता है. my_genrule
से जुड़े दोनों select
, my_genrule
के बिल्ड पैरामीटर का इस्तेमाल करते हैं. इन पैरामीटर में --cpu=arm
भी शामिल है. tools
एट्रिब्यूट की वैल्यू, tool1
और उससे जुड़ी ट्रांज़िशन डिपेंडेंसी के लिए, --cpu
से x86
में बदल जाती है. select
पर
tool1
, tool1
के बिल्ड पैरामीटर का इस्तेमाल करता है. इनमें --cpu=x86
भी शामिल है.
कॉन्फ़िगरेशन की शर्तें
कॉन्फ़िगर किए जा सकने वाले एट्रिब्यूट में मौजूद हर बटन, config_setting
या constraint_value
का लेबल रेफ़रंस होता है.
config_setting
, कमांड लाइन फ़्लैग की सेटिंग का सिर्फ़ एक कलेक्शन है. इन्हें टारगेट में शामिल करके, "स्टैंडर्ड" शर्तों को बनाए रखना आसान हो जाता है. उपयोगकर्ता इन शर्तों का रेफ़रंस कई जगहों से दे सकते हैं.
constraint_value
, मल्टी-प्लैटफ़ॉर्म पर काम करने के तरीके के लिए सहायता उपलब्ध कराता है.
पहले से मौजूद फ़्लैग
--cpu
जैसे फ़्लैग, Bazel में पहले से मौजूद होते हैं: सभी प्रोजेक्ट के सभी बिल्ड के लिए, बिल्ड टूल उन्हें नेटिव तौर पर समझता है. इनकी जानकारी, config_setting
के 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
के 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
ज़्यादा साफ़ तौर पर दिखने वाली गड़बड़ियों के लिए, select()
के no_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()
एक्सप्रेशन में मैच करने की अनुमति मिलती है.
उदाहरण के लिए, my_rocks
के srcs
एट्रिब्यूट को 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
को मैच करना है, तो AND का इस्तेमाल करके चेन बनाएं.
या चेन बनाना
इसके लिए, इन्हें आज़माएं:
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
Skylib के selects
मॉड्यूल में मौजूद with_or मैक्रो, OR
select
के अंदर सीधे तौर पर OR
स्टेटस के साथ काम करता है:
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
Skylib के selects
मॉड्यूल में, config_setting_group मैक्रो की मदद से एक से ज़्यादा config_setting
को OR
किया जा सकता है:
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
शाखा चाहिए, तो Skylib मैक्रो 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 चेनिंग के उलट, मौजूदा 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
कौनसा पाथ चुनेगा काम नहीं कर सकता
क्योंकि मैक्रो का आकलन बेज़ेल के लोड होने के चरण में किया जाता है. यह चरण, फ़्लैग की वैल्यू के पता चलने से पहले होता है.
यह Basel के डिज़ाइन से जुड़ी मुख्य पाबंदी है. जल्द ही, इसे बदला नहीं जा सकता.
दूसरा, तकनीकी तौर पर मुमकिन होने के साथ-साथ, मैक्रो को सभी select
पाथ में दोहराना होता है, लेकिन उनमें एक अच्छा यूज़र इंटरफ़ेस (यूआई) नहीं होता. इसे बदलने के लिए, डिज़ाइन में और बदलाव करना ज़रूरी है.
Bazel क्वेरी और cquery
Baज़ल query
, Basel के लोडिंग फ़ेज़ पर काम करता है.
इसका मतलब है कि यह नहीं पता कि कोई टारगेट कौनसे कमांड लाइन फ़्लैग का इस्तेमाल करता है, क्योंकि उन फ़्लैग का आकलन, बिल्ड के बाद (विश्लेषण के चरण में) किया जाता है.
इसलिए, यह तय नहीं किया जा सकता कि कौनसी select()
शाखाएं चुनी गई हैं.
Basel cquery
की सुविधा, Basel के विश्लेषण के चरण के बाद काम करती है. इसलिए, उसके पास
यह पूरी जानकारी मौजूद होती है और यह select()
का सटीक तरीके से समाधान कर सकता है.
आकलन करते समय इन बातों का ध्यान रखें:
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",
"//third_party/bazel_platforms/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",
"//third_party/bazel_platforms/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.
इसे बदलना मुमकिन नहीं है, क्योंकि परिभाषा के मुताबिक मैक्रो का आकलन, बिल्ड के कमांड लाइन फ़्लैग को पढ़ने से पहले Baze चैनल की तरफ़ से किया जाता है. इसका मतलब है कि Select() का आकलन करने के लिए, ज़रूरत के मुताबिक जानकारी उपलब्ध नहीं है.
हालांकि, मैक्रो 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,
"//third_party/bazel_platforms/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 डिज़ाइन स्टैंडर्ड के मुताबिक, कुछ अपवादों को छोड़कर सभी ऑब्जेक्ट अपने-आप True दिखाते हैं.
क्या select() को डिक्शनरी की तरह पढ़ा जा सकता है?
मैक्रो चुने गए(चुनें) का मूल्यांकन नहीं कर सकते, क्योंकि इससे पहले कि Baज़ल, बिल्ड के कमांड लाइन पैरामीटर के बारे में जान जाता है, मैक्रो से आकलन किया जाता है. क्या वे कम से कम select()
की डिक्शनरी पढ़ सकते हैं, ताकि वे हर वैल्यू में सफ़िक्स जोड़ सकें?
सैद्धांतिक तौर पर ऐसा किया जा सकता है, लेकिन यह अब तक Basel की सुविधा नहीं है.
आज आपके पास एक सामान्य डिक्शनरी बनाने का विकल्प है. इसके बाद, उसे 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 + "> $@",
)
select() फ़ंक्शन, bind() फ़ंक्शन के साथ क्यों काम नहीं करता?
सबसे पहले, bind()
का इस्तेमाल न करें. इसे बंद कर दिया गया है. इसकी जगह अब alias()
का इस्तेमाल किया जाता है.
तकनीकी जवाब यह है कि bind()
एक repo
नियम है, न कि BUILD नियम.
रिपॉज़िटरी के नियमों का कोई खास कॉन्फ़िगरेशन नहीं होता. साथ ही, इनका आकलन, BUILD नियमों के जैसे नहीं किया जाता. इसलिए, bind()
में मौजूद select()
का आकलन, असल में किसी खास शाखा के लिए नहीं किया जा सकता.
इसके बजाय, इस तरह की रनटाइम तय करने के लिए, आपको actual
एट्रिब्यूट में select()
के साथ alias()
का इस्तेमाल करना चाहिए. यह ठीक से काम करता है, क्योंकि 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
पर मौजूद वैकल्पिक वैल्यू दिखेगी.
bind()
का इस्तेमाल बंद कर दें.
मेरा 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)
//myapp:foo
के आगे मौजूद (12e23b9a2b534a)
, उस कॉन्फ़िगरेशन का हैश है जो //myapp:foo
के 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 Docs देखें.
select()
, प्लैटफ़ॉर्म के साथ काम क्यों नहीं करता?
Baज़ल, कॉन्फ़िगर किए जा सकने वाले एट्रिब्यूट के साथ यह पुष्टि नहीं करता कि दिया गया प्लैटफ़ॉर्म, टारगेट प्लैटफ़ॉर्म है या नहीं, क्योंकि सिमैंटिक की जानकारी साफ़ तौर पर नहीं दी गई है.
उदाहरण के लिए:
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": [],
}),
)
अगर टारगेट प्लैटफ़ॉर्म पर @platforms//cpu:x86
और @platforms//os:linux
, दोनों तरह की पाबंदियां हैं, लेकिन यहां बताए गए :x86_linux_platform
के बजाय कोई दूसरा :x86_linux_platform
है, तो इस BUILD
फ़ाइल में किस select()
का इस्तेमाल किया जाना चाहिए? 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": [],
}),
)
Basel की टीम ऐसा करने का सुझाव नहीं देती है. यह आपके बिल्ड को बहुत ज़्यादा सीमित करती है. साथ ही, जब ज़रूरी स्थिति मेल नहीं खाती है, तब उपयोगकर्ताओं को भ्रम हो सकता है.