कॉन्फ़िगर किए जा सकने वाले बिल्ड एट्रिब्यूट

समस्या की शिकायत करें सोर्स देखें ठीक

कॉन्फ़िगरेशन किए जा सकने वाले एट्रिब्यूट, आम तौर पर select() के नाम से जाना जाता है. यह बेज़ल फ़ीचर है. इसकी मदद से उपयोगकर्ता, कमांड लाइन पर नियम के एट्रिब्यूट की वैल्यू को टॉगल कर सकते हैं.

उदाहरण के लिए, इसका इस्तेमाल ऐसी मल्टीप्लैटफ़ॉर्म लाइब्रेरी के लिए किया जा सकता है जो आर्किटेक्चर के लिए, अपने-आप सही लागू करने का विकल्प चुनती है. इसके अलावा, इसका इस्तेमाल ऐसी सुविधा के लिए कॉन्फ़िगर की जा सकने वाली बाइनरी के लिए भी किया जा सकता है जिसे बिल्ड के समय अपनी पसंद के मुताबिक बनाया जा सकता है.

उदाहरण

# 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"} साफ़ तौर पर दिखता है. ऐसा इसलिए, क्योंकि दोनों ब्रांच "हैलो" पर सेट होती हैं. * एक का values, अन्य सभी का सख्त सुपरसेट है. उदाहरण के लिए, values = {"cpu": "x86", "compilation_mode": "dbg"}, values = {"cpu": "x86"} की विशेषज्ञता है.

बिल्ट-इन शर्त //conditions:default अपने-आप तब मैच होती है, जब कोई और चीज़ नहीं करती है.

इस उदाहरण में deps का इस्तेमाल किया गया है, लेकिन select() srcs, resources, cmd, और ज़्यादातर दूसरे एट्रिब्यूट की तरह ही काम करता है. कुछ ही एट्रिब्यूट को कॉन्फ़िगर नहीं किया जा सकता और इनके बारे में साफ़ तौर पर जानकारी दी जाती है. उदाहरण के लिए, config_setting के values एट्रिब्यूट को कॉन्फ़िगर नहीं किया जा सकता.

select() और डिपेंडेंसी

कुछ एट्रिब्यूट, टारगेट के तहत सभी ट्रांज़िटिव डिपेंडेंसी के लिए बिल्ड पैरामीटर बदल देते हैं. उदाहरण के लिए, genrule का tools, Basel का इस्तेमाल करने वाली मशीन के सीपीयू के --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 से बाइंड किया जाता है. my_genrule के साथ अटैच किए गए दोनों select में, my_genrule के बिल्ड पैरामीटर का इस्तेमाल किया जाता है. इन बिल्ड पैरामीटर में --cpu=arm शामिल होता है. tool1 और इसकी ट्रांज़िटिव डिपेंडेंसी के लिए tools एट्रिब्यूट, --cpu को x86 में बदल देता है. tool1 पर मौजूद select, tool1 के बिल्ड पैरामीटर का इस्तेमाल करता है. इन पैरामीटर में --cpu=x86 शामिल होता है.

कॉन्फ़िगरेशन की शर्तें

कॉन्फ़िगर किए जा सकने वाले एट्रिब्यूट में मौजूद हर कुंजी, config_setting या constraint_value के लिए लेबल रेफ़रंस होती है.

config_setting, कमांड लाइन फ़्लैग की सेटिंग का सिर्फ़ एक कलेक्शन है. इन्हें एक टारगेट में एन्क्रिप्ट करके, "स्टैंडर्ड" स्थितियों को बनाए रखना आसान हो जाता है. उपयोगकर्ता कई जगहों से इनका रेफ़रंस ले सकते हैं.

constraint_value एक से ज़्यादा प्लैटफ़ॉर्म के व्यवहार के लिए सहायता देता है.

पहले से मौजूद फ़्लैग

--cpu जैसे फ़्लैग बेज़ल में पहले से मौजूद होते हैं: बिल्ड टूल मूल रूप से इन फ़्लैग को सभी प्रोजेक्ट में मौजूद सभी बिल्ड के लिए समझता है. इनकी जानकारी config_setting के values एट्रिब्यूट का इस्तेमाल करके दी जाती है:

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 की अनुमति नहीं है, क्योंकि इससे सिर्फ़ इस बात पर असर पड़ता है कि Basel ने उपयोगकर्ता को प्रोग्रेस कैसे रिपोर्ट की. टारगेट, अपने नतीजे बनाने के लिए उस फ़्लैग का इस्तेमाल नहीं कर सकते. इस्तेमाल किए जा सकने वाले फ़्लैग के सटीक सेट का दस्तावेज़ नहीं है. व्यावहारिक तौर पर, "काम की जानकारी देने वाले" ज़्यादातर फ़्लैग काम करते हैं.

पसंद के मुताबिक बनाए गए फ़्लैग

Starlark की बिल्ड सेटिंग की मदद से, अपने प्रोजेक्ट के हिसाब से फ़्लैग किए जा सकते हैं. बिल्ट-इन फ़्लैग के उलट, इन्हें बिल्ड टारगेट के तौर पर तय किया जाता है. इसलिए, Baze इनके पर टारगेट लेबल का इस्तेमाल करता है.

ये 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 की ज़रूरत है, तो और चेन का इस्तेमाल करें.

या चेन बनाना

इसके लिए, इन्हें आज़माएं:

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"] बार-बार दोहराने की ज़रूरत नहीं है, तो अच्छा होगा.

वैल्यू को बिल्ड वैरिएबल के रूप में पहले से तय करना एक विकल्प है:

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 मैक्रो, ORselect के अंदर सीधे तौर पर 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 मैक्रो में ORकई config_setting का इस्तेमाल किया जा सकता है:

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 मैक्रो 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"],
    }),
)

या चेन बनाने के उलट, मौजूदा 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 कौनसा पाथ चुनेगा काम नहीं कर सकता क्योंकि मैक्रो का आकलन बेज़ेल के लोड होने के चरण में किया जाता है. यह चरण, फ़्लैग की वैल्यू के पता चलने से पहले होता है. यह Baज़ल डिज़ाइन से जुड़ी मुख्य पाबंदी है. जल्द ही, इसे बदलने की संभावना कम है.

दूसरा, तकनीकी तौर पर मुमकिन होने के साथ-साथ, मैक्रो को सभी select पाथ में दोहराना होता है, लेकिन उनमें एक अच्छा यूज़र इंटरफ़ेस (यूआई) नहीं होता. इसे बदलने के लिए और डिज़ाइन ज़रूरी है.

बेज़ल क्वेरी और cquery

Basel query, Basel के लोडिंग फ़ेज़ पर काम करता है. इसका मतलब है कि इसे इस बात की जानकारी नहीं होती कि टारगेट किस कमांड लाइन का इस्तेमाल करता है, क्योंकि बिल्ड (विश्लेषण के चरण में) में इन फ़्लैग का आकलन किए जाने तक इनका आकलन नहीं किया जाता. इसलिए, यह तय नहीं किया जा सकता कि कौनसी select() ब्रांच चुनी गई हैं.

Baज़र, cquery का विश्लेषण, उसके विश्लेषण के बाद होता है. इसलिए, उसके पास यह पूरी जानकारी मौजूद होती है और यह 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({
        "//third_party/bazel_platforms/cpu:x86_32": "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({
        "//third_party/bazel_platforms/cpu:x86_32": "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({
        "//third_party/bazel_platforms/cpu:x86_32": 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 डिज़ाइन स्टैंडर्ड के मुताबिक, बहुत कम अपवादों को छोड़कर बाकी सभी ऑब्जेक्ट अपने-आप 'सही' के तौर पर दिखते हैं.

क्या किसी निर्देश की तरह, Select() को पढ़ा जा सकता है?

मैक्रो चुने गए(चुनें) का मूल्यांकन नहीं कर सकते क्योंकि इससे पहले कि बैकेल को बिल्ड के कमांड लाइन पैरामीटर के बारे में पता चलने से पहले मैक्रो का मूल्यांकन किया जाता है. उदाहरण के लिए, क्या वे कम से कम 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 = {
        "//third_party/bazel_platforms/cpu:x86_32": "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() रेपो नियम है, न कि बिल्ड नियम.

रेपो नियमों का कोई खास कॉन्फ़िगरेशन नहीं होता है और इनका आकलन 'बिल्ड नियमों' की तरह नहीं किया जाता. इसलिए, bind() में मौजूद select() का किसी ब्रांच का आकलन नहीं किया जा सकता.

इसके बजाय, इस तरह की रनटाइम तय करने के लिए, आपको actual एट्रिब्यूट में select() के साथ alias() का इस्तेमाल करना चाहिए. यह सही तरीके से काम करता है, क्योंकि alias() 'बिल्ड' नियम है. इसका आकलन खास कॉन्फ़िगरेशन की मदद से किया जाता है.

ज़रूरत पड़ने पर, आपके पास alias() के लिए bind() का टारगेट पॉइंट भी हो सकता है.

$ 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": [],
    }),
)

इस BUILD फ़ाइल में, अगर टारगेट प्लैटफ़ॉर्म में @platforms//cpu:x86 और @platforms//os:linux, दोनों कंस्ट्रेंट हैं, लेकिन यहां :x86_linux_platform के बारे में नहीं बताया गया है, तो किस 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 करना हो, तो क्या करना होगा?

अगर बिल्ड की ज़रूरी शर्तों के लिए खास तौर पर प्लैटफ़ॉर्म की जांच करना ज़रूरी हो, तो config_setting में --platforms फ़्लैग की वैल्यू को बदला जा सकता है:

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 की टीम ऐसा करने का सुझाव नहीं देती है. यह आपके बिल्ड को बहुत ज़्यादा सीमित करती है. साथ ही, जब ज़रूरी शर्तें पूरी नहीं होती हैं, तब उपयोगकर्ताओं को भ्रम हो सकता है.