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

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

कॉन्फ़िगर करने लायक एट्रिब्यूट, जिन्हें आम तौर पर 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:

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() का इस्तेमाल करने से, यह एट्रिब्यूट अलग-अलग स्थितियों के लिए अलग-अलग वैल्यू का इस्तेमाल करता है.

मैच आसानी से समझ में आने चाहिए: अगर एक से ज़्यादा शर्तें मेल खाती हैं, तो * उन सभी का मान एक ही होता है. उदाहरण के लिए, linux x86 पर चलते समय, यह आसानी से समझ में नहीं आता {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"} क्योंकि दोनों ब्रांच "hello" पर जाती हैं. * values दूसरों के मुकाबले बहुत सख्त है. उदाहरण के लिए, values = {"cpu": "x86", "compilation_mode": "dbg"} एक अच्छी विशेषज्ञता है values = {"cpu": "x86"}.

पहले से मौजूद शर्त //conditions:default अपने-आप मैच करती है, जब कुछ और नहीं होता.

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

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

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

कस्टम फ़्लैग

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

इन्हें config_setting के flag_values एट्रिब्यूट से ट्रिगर किया जाता है:

config_setting(
    name = "meaningful_condition_name",
    flag_values = {
        "//myflags:flag1": "value1",
        "//myflags:flag2": "value2",
        ...
    },
)

पहले से मौजूद फ़्लैग की तरह ही इसका इस्तेमाल किया जाता है. उदाहरण के लिए, यहां देखें.

--define कस्टम फ़्लैग (उदाहरण के लिए, --define foo=bar) के लिए एक वैकल्पिक लेगसी सिंटैक्स है. इसे values एट्रिब्यूट (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",
    ],
)

प्लैटफ़ॉर्म को कमांड लाइन के बारे में बताया जा सकता है. यह प्लैटफ़ॉर्म के constraint_values के सबसेट वाले config_setting को चालू करता है. इससे, 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 की ज़रूरत है, तो और चेनिंग के बारे में सोचें.

OR चेन

नीचे दी गई चीज़ों पर विचार करें:

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 में इस्तेमाल करता है:

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

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

Bazel क्वेरी और cquery

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

Bazel का विश्लेषण चरण पूरा होने के बाद, Bazel 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() नियमों में काम करता है! ज़्यादा जानकारी के लिए, नियम के साथ काम करने की सुविधा देखें.

आम तौर पर, इस सवाल का मुख्य मुद्दा यह है कि चुनें() मैक्रो में काम नहीं करता है. ये नियमों से अलग हैं. अंतर को समझने के लिए, नियम और मैक्रो से जुड़े दस्तावेज़ देखें. यहां एक एंड-टू-एंड उदाहरण दिया गया है:

नियम और मैक्रो तय करें:

# 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.

इसे बदलना मुमकिन नहीं है, क्योंकि परिभाषा के हिसाब से मैक्रो का आकलन बेज़ल के कमांड लाइन फ़्लैग को पढ़ने से पहले किया जाता है. इसका मतलब है कि Select() का आकलन करने के लिए, ज़रूरत के मुताबिक जानकारी मौजूद नहीं है.

हालांकि, मैक्रो नियमों को अपारदर्शी BLOBs के रूप में 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() ऑब्जेक्ट है. Python के डिज़ाइन स्टैंडर्ड के मुताबिक, बहुत कम अपवादों को छोड़कर सभी ऑब्जेक्ट अपने-आप 'सही' दिखाते हैं.

क्या मेरे पास, चुनें() को लिखवाने का विकल्प होता है?

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

सैद्धांतिक तौर पर, ऐसा हो सकता है, लेकिन अभी तक यह बेज़ल सुविधा नहीं है. आज आप जो कर सकते हैं सीधा शब्दकोश तैयार कर सकते हैं, उसे 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 + "> $@",
    )

चुने गए() को Bin() के साथ क्यों काम नहीं करता?

क्योंकि bind() WORKSPACE का नियम है, बिल बनाने का नियम नहीं है.

Workspace के नियमों का कोई खास कॉन्फ़िगरेशन नहीं होता है. इनका आकलन भी BUILD के नियमों की तरह ही किया जाता है. इसलिए, bind() में select() का असल में किसी खास ब्रांच पर मूल्यांकन करना संभव नहीं होता.

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

ज़रूरत पड़ने पर, आपके पास 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 पर मौजूद विकल्प देख पाएगा.

मेरे Select() में, मेरी उम्मीद के मुताबिक वैल्यू क्यों नहीं चुनी जाती?

अगर //myapp:foo में select() है, जो आपकी उम्मीद के मुताबिक शर्त नहीं चुनता है, तो डीबग करने के लिए क्वेरी और 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 का इस्तेमाल करने के बारे में जानने के लिए क्वेरी दस्तावेज़ देखें.

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() का इस्तेमाल किया जाना चाहिए, लेकिन not :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 करना चाहूं, तो क्या होगा?

अगर आपकी बिल्ड ज़रूरतों के लिए खास तौर पर प्लैटफ़ॉर्म की जांच करना ज़रूरी है, तो आप 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": [],
    }),
)

Bazel टीम ऐसा करने का समर्थन नहीं करती है. यह आपके बिल को सीमित कर देती है और अनुमानित शर्त के मेल नहीं खाने पर उपयोगकर्ताओं को भ्रम की स्थिति में डाल देती है.