कॉन्फ़िगरेशन

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

इस पेज पर, Starlark कॉन्फ़िगरेशन के फ़ायदों और बुनियादी इस्तेमाल के बारे में बताया गया है. साथ ही, प्रोजेक्ट के बिल्ड करने के तरीके को पसंद के मुताबिक बनाने के लिए, Bazel के एपीआई के बारे में भी बताया गया है. इसमें, बाइल्ड सेटिंग तय करने का तरीका और उदाहरण दिए गए हैं.

इससे ये काम किए जा सकते हैं:

  • अपने प्रोजेक्ट के लिए कस्टम फ़्लैग तय करें. इससे --define की ज़रूरत नहीं पड़ेगी
  • अपने पैरंट (जैसे, --compilation_mode=opt या --cpu=arm) के मुकाबले अलग-अलग कॉन्फ़िगरेशन में डिपेंडेंसी कॉन्फ़िगर करने के लिए, ट्रांज़िशन लिखें
  • नियमों में बेहतर डिफ़ॉल्ट वैल्यू जोड़ना. जैसे, किसी खास SDK टूल के साथ //my:android_app अपने-आप बिल्ड करना

और भी बहुत कुछ, जो पूरी तरह से .bzl फ़ाइलों से मिलता है. इसके लिए, Bazel रिलीज़ की ज़रूरत नहीं है. उदाहरण के लिए, bazelbuild/examples repo देखें.

उपयोगकर्ता के तय की गई बिल्ड सेटिंग

बिल्ड सेटिंग, कॉन्फ़िगरेशन की एक जानकारी होती है. कॉन्फ़िगरेशन को की/वैल्यू मैप के तौर पर देखें. --cpu=ppc और --copt="-DFoo" को सेट करने पर, ऐसा कॉन्फ़िगरेशन बनता है जो {cpu: ppc, copt: "-DFoo"} जैसा दिखता है. हर एंट्री एक बिल्ड सेटिंग है.

cpu और copt जैसे पारंपरिक फ़्लैग, नेटिव सेटिंग होते हैं — उनकी कुंजियां तय होती हैं और उनकी वैल्यू, नेटिव baज़ेन Java कोड में सेट होती हैं. Basel के उपयोगकर्ता, उन्हें सिर्फ़ कमांड लाइन और दूसरे एपीआई के ज़रिए पढ़ और लिख सकते हैं. इन एपीआई को मैन्युअल तौर पर मैनेज किया जाता है. नेटिव फ़्लैग और उन्हें एक्सपोज़ करने वाले एपीआई में बदलाव करने के लिए, bazel रिलीज़ की ज़रूरत होती है. उपयोगकर्ता की तय की गई बिल्ड सेटिंग, .bzl फ़ाइलों में तय की जाती हैं. इसलिए, बदलावों को रजिस्टर करने के लिए, bazel रिलीज़ की ज़रूरत नहीं होती. इन्हें कमांड लाइन की मदद से भी सेट किया जा सकता है. अगर इनका इस्तेमाल flags के तौर पर किया गया है, तो यहां ज़्यादा देखें. हालांकि, इन्हें उपयोगकर्ता के तय किए गए ट्रांज़िशन की मदद से भी सेट किया जा सकता है.

बिल्ड सेटिंग तय करना

पूरी प्रोसेस का उदाहरण

build_setting rule() पैरामीटर

बिल्ड सेटिंग, किसी भी दूसरे नियम की तरह ही नियम होते हैं. इनमें अंतर करने के लिए, Starlark rule() फ़ंक्शन के build_settingएट्रिब्यूट का इस्तेमाल किया जाता है.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

build_setting एट्रिब्यूट में एक फ़ंक्शन होता है, जो बिल्ड सेटिंग का टाइप तय करता है. यह टाइप, bool और string जैसे बुनियादी Starlark टाइप के सेट तक सीमित है. ज़्यादा जानकारी के लिए, config मॉड्यूल का दस्तावेज़ देखें. नियम लागू करने के फ़ंक्शन में, ज़्यादा मुश्किल टाइप किया जा सकता है. इस बारे में ज़्यादा जानकारी यहां दी गई है.

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

ctx.build_setting_value का इस्तेमाल करना

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

# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])

temperatures = ["HOT", "LUKEWARM", "ICED"]

def _impl(ctx):
    raw_temperature = ctx.build_setting_value
    if raw_temperature not in temperatures:
        fail(str(ctx.label) + " build setting allowed to take values {"
             + ", ".join(temperatures) + "} but was set to unallowed value "
             + raw_temperature)
    return TemperatureProvider(type = raw_temperature)

temperature = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)

एक से ज़्यादा सेट वाली स्ट्रिंग फ़्लैग तय करना

स्ट्रिंग सेटिंग में एक और allow_multiple पैरामीटर होता है, जिसकी मदद से फ़्लैग को कमांड लाइन या baज़लrc में, कई बार सेट किया जा सकता है. हालांकि, उनकी डिफ़ॉल्ट वैल्यू अब भी स्ट्रिंग टाइप वाले एट्रिब्यूट के साथ सेट है:

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
    name = "roasts",
    build_setting_default = "medium"
)

फ़्लैग की हर सेटिंग को एक वैल्यू माना जाता है:

$ bazel build //my/target --//example:roasts=blonde \
    --//example:roasts=medium,dark

ऊपर दिए गए डेटा को {"//example:roasts": ["blonde", "medium,dark"]} में पार्स किया गया है और ctx.build_setting_value, ["blonde", "medium,dark"] सूची दिखाता है.

बिल्ड की सेटिंग चालू की जा रही हैं

build_setting पैरामीटर की मदद से तय किए गए नियमों में, build_setting_default एट्रिब्यूट की वैल्यू अपने-आप लागू हो जाती है. यह एट्रिब्यूट उसी टाइप का होता है जैसा कि build_setting पैरामीटर में बताया गया है.

# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])

def _impl(ctx):
    return FlavorProvider(type = ctx.build_setting_value)

flavor = rule(
    implementation = _impl,
    build_setting = config.string(flag = True)
)
# example/buildsettings/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)

पहले से तय सेटिंग

पूरी प्रोसेस का उदाहरण

Skylib लाइब्रेरी में, पहले से तय की गई सेटिंग का एक सेट शामिल होता है. इनका इस्तेमाल, कस्टम Starlark लिखे बिना किया जा सकता है.

उदाहरण के लिए, ऐसी सेटिंग तय करने के लिए जो स्ट्रिंग वैल्यू के सीमित सेट को स्वीकार करती है:

# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
    name = "myflag",
    values = ["a", "b", "c"],
    build_setting_default = "a",
)

पूरी सूची के लिए, बिल्ड सेटिंग के सामान्य नियम देखें.

बिल्ड सेटिंग का इस्तेमाल करना

बिल्ड सेटिंग के आधार पर

अगर किसी टारगेट को कॉन्फ़िगरेशन की जानकारी पढ़नी है, तो वह सामान्य एट्रिब्यूट डिपेंडेंसी की मदद से, सीधे तौर पर बिल्ड सेटिंग पर निर्भर हो सकता है.

# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
    if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
        ...

drink_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "flavor": attr.label()
    }
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
    name = "favorite_flavor",
    build_setting_default = "APPLE"
)
drink_rule(
    name = "my_drink",
    flavor = ":favorite_flavor",
)

भाषाएं, बिल्ड सेटिंग का एक कैननिकल सेट बना सकती हैं. उस भाषा के सभी नियम, इस सेट पर निर्भर करते हैं. हालांकि, Starlark कॉन्फ़िगरेशन की दुनिया में fragments का नेटिव कॉन्सेप्ट, अब हार्डकोड किए गए ऑब्जेक्ट के तौर पर मौजूद नहीं है, लेकिन इस कॉन्सेप्ट को ट्रांसलेट करने का एक तरीका यह है कि सामान्य इम्प्लीसिट एट्रिब्यूट के सेट का इस्तेमाल किया जाए. उदाहरण के लिए:

# kotlin/rules.bzl
_KOTLIN_CONFIG = {
    "_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
    "_mode": attr.label(default = "//kotlin/config:mode-flag"),
    ...
}

...

kotlin_library = rule(
    implementation = _rule_impl,
    attrs = dicts.add({
        "library-attr": attr.string()
    }, _KOTLIN_CONFIG)
)

kotlin_binary = rule(
    implementation = _binary_impl,
    attrs = dicts.add({
        "binary-attr": attr.label()
    }, _KOTLIN_CONFIG)

कमांड लाइन पर बिल्ड सेटिंग का इस्तेमाल करना

ज़्यादातर नेटिव फ़्लैग की तरह ही, कमांड लाइन का इस्तेमाल करके, फ़्लैग के तौर पर मार्क की गई बिल्ड सेटिंग सेट की जा सकती हैं. name=value सिंटैक्स का इस्तेमाल करके, बिल्ड सेटिंग का नाम उसका पूरा टारगेट पाथ होता है:

$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed

खास बूलियन सिंटैक्स का इस्तेमाल किया जा सकता है:

$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag

बिल्ड सेटिंग के उपनामों का इस्तेमाल करना

आप अपनी बिल्ड सेटिंग के टारगेट पाथ के लिए उपनाम सेट कर सकते हैं, ताकि कमांड लाइन पर उसे आसानी से पढ़ा जा सके. उपनाम, नेटिव फ़्लैग की तरह ही काम करते हैं. साथ ही, ये डबल-डैश विकल्प सिंटैक्स का भी इस्तेमाल करते हैं.

अपने .bazelrc में --flag_alias=ALIAS_NAME=TARGET_PATH जोड़कर, दूसरा ईमेल पता सेट करें . उदाहरण के लिए, coffee के लिए कोई दूसरा नाम सेट करने के लिए:

# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp

सबसे सही तरीका: किसी ईमेल पते के लिए कई बार उपनाम सेट करने पर, सबसे नया उपनाम ही दिखता है. अनचाहे पार्सिंग नतीजों से बचने के लिए, यूनीक उपनाम का इस्तेमाल करें.

किसी दूसरे नाम का इस्तेमाल करने के लिए, उसे बिल्ड सेटिंग के टारगेट पाथ की जगह पर टाइप करें. उपयोगकर्ता के .bazelrc में सेट किए गए coffee के ऊपर दिए गए उदाहरण के साथ:

$ bazel build //my/target --coffee=ICED

के बजाय

$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED

सबसे सही तरीका: कमांड लाइन पर उपनाम सेट किए जा सकते हैं. हालांकि, उन्हें .bazelrc में छोड़ने से कमांड लाइन में गड़बड़ी कम होती है.

लेबल-टाइप की बिल्ड सेटिंग

पूरी प्रोसेस का उदाहरण

अन्य बिल्ड सेटिंग से अलग, लेबल टाइप की सेटिंग को build_setting नियम पैरामीटर का इस्तेमाल करके तय नहीं किया जा सकता. इसके बजाय, bazel में दो बुनियादी नियम होते हैं: label_flag और label_setting. ये नियम, उस टारगेट के प्रोवाइडर को फ़ॉरवर्ड करते हैं जिस पर बिल्ड सेटिंग सेट की गई है. label_flag और label_setting को ट्रांज़िशन से पढ़ा/लिखा जा सकता है. साथ ही, label_flag को उपयोगकर्ता की तरह ही सेट किया जा सकता है, जैसे कि build_setting के अन्य नियमों को सेट किया जा सकता है. इन दोनों में सिर्फ़ इतना अंतर है कि इन्हें अपनी पसंद के मुताबिक तय नहीं किया जा सकता.

लेबल-टाइप की सेटिंग, आखिर में लेट-बाउंड डिफ़ॉल्ट की सुविधा की जगह ले लेंगी. लेट-बाउंड डिफ़ॉल्ट एट्रिब्यूट, लेबल टाइप के एट्रिब्यूट होते हैं. इनकी आखिरी वैल्यू पर कॉन्फ़िगरेशन का असर पड़ सकता है. Starlark में, यह configuration_field एपीआई की जगह ले लेगा.

# example/rules.bzl
MyProvider = provider(fields = ["my_field"])

def _dep_impl(ctx):
    return MyProvider(my_field = "yeehaw")

dep_rule = rule(
    implementation = _dep_impl
)

def _parent_impl(ctx):
    if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
        ...

parent_rule = rule(
    implementation = _parent_impl,
    attrs = { "my_field_provider": attr.label() }
)

# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")

dep_rule(name = "dep")

parent_rule(name = "parent", my_field_provider = ":my_field_provider")

label_flag(
    name = "my_field_provider",
    build_setting_default = ":dep"
)

सेटिंग बनाएं और चुनें()

पूरी प्रोसेस का उदाहरण

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

config_setting(
    name = "my_config",
    flag_values = {
        "//example:favorite_flavor": "MANGO"
    }
)

उपयोगकर्ता के तय किए गए ट्रांज़िशन

कॉन्फ़िगरेशन ट्रांज़िशन, बिल्ड ग्राफ़ में कॉन्फ़िगर किए गए एक टारगेट से दूसरे टारगेट में बदलाव को मैप करता है.

उन्हें सेट करने वाले नियमों में एक खास एट्रिब्यूट शामिल होना चाहिए:

  "_allowlist_function_transition": attr.label(
      default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
  )

ट्रांज़िशन जोड़कर, अपने बिल्ड ग्राफ़ का साइज़ आसानी से बढ़ाया जा सकता है. इससे उन पैकेज पर अनुमति वाली सूची सेट हो जाती है जिनमें इस नियम के टारगेट बनाए जा सकते हैं. ऊपर दिए गए कोडब्लॉक में मौजूद डिफ़ॉल्ट वैल्यू, सभी को अनुमति देता है. हालांकि, अगर आपको यह तय करना है कि आपके नियम का इस्तेमाल कौन कर सकता है, तो उस एट्रिब्यूट को अपनी पसंद के मुताबिक बनाई गई अनुमति वाली सूची पर ले जाने के लिए सेट किया जा सकता है. अगर आपको ट्रांज़िशन से, बिल्ड की परफ़ॉर्मेंस पर पड़ने वाले असर को समझने के लिए सलाह या मदद चाहिए, तो bazel-discuss@googlegroups.com पर संपर्क करें.

तय करना

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

औपचारिक तौर पर, ट्रांज़िशन एक फ़ंक्शन है, जो इनपुट कॉन्फ़िगरेशन से एक या उससे ज़्यादा आउटपुट कॉन्फ़िगरेशन पर ले जाता है. ज़्यादातर ट्रांज़िशन 1:1 होते हैं, जैसे कि "इनपुट कॉन्फ़िगरेशन को --cpu=ppc से बदलें". 1:2+ ट्रांज़िशन भी मौजूद हो सकते हैं, लेकिन कुछ खास पाबंदियां भी लागू होती हैं.

Starlark में, ट्रांज़िशन को नियमों की तरह ही तय किया जाता है. इसमें, तय करने वाला transition() फ़ंक्शन और लागू करने वाला फ़ंक्शन होता है.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//example:favorite_flavor" : "MINT"}

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

transition() फ़ंक्शन में, लागू करने वाला फ़ंक्शन, पढ़ने के लिए बाइल्ड सेटिंग का एक सेट(inputs), और लिखने के लिए बाइल्ड सेटिंग का एक सेट (outputs) शामिल होता है. लागू करने वाले फ़ंक्शन में दो पैरामीटर होते हैं, settings और attr. settings, inputs से transition() तक के पैरामीटर में बताई गई सभी सेटिंग की एक डिक्शनरी {String:Object} है.

attr, उस नियम के एट्रिब्यूट और वैल्यू की डिक्शनरी है जिससे ट्रांज़िशन जुड़ा है. आउटगोइंग एज ट्रांज़िशन के तौर पर अटैच किए जाने पर, इन एट्रिब्यूट की वैल्यू को पोस्ट-select() रिज़ॉल्यूशन में कॉन्फ़िगर किया जाता है. इनकमिंग एज ट्रांज़िशन के तौर पर अटैच होने पर, attr में ऐसे किसी भी एट्रिब्यूट को शामिल नहीं किया जाता है जो अपनी वैल्यू को हल करने के लिए सिलेक्टर का इस्तेमाल करता है. अगर --foo पर आने वाला एज ट्रांज़िशन, एट्रिब्यूट bar को पढ़ता है और फिर एट्रिब्यूट bar को सेट करने के लिए --foo को भी चुनता है, तो आने वाले एज ट्रांज़िशन के ट्रांज़िशन में bar की गलत वैल्यू पढ़ने की संभावना होती है.

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

1:2 या उससे ज़्यादा के ट्रांज़िशन तय करना

शुरू से आखिर तक का उदाहरण

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

1:2+ ट्रांज़िशन को, ट्रांज़िशन लागू करने के फ़ंक्शन में डिक्शनरी की सूची दिखाकर तय किया जाता है.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return [
        {"//example:favorite_flavor" : "LATTE"},
        {"//example:favorite_flavor" : "MOCHA"},
    ]

coffee_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

वे कस्टम कुंजियां भी सेट कर सकते हैं जिनका इस्तेमाल, नियम लागू करने वाले फ़ंक्शन में अलग-अलग डिपेंडेंसी पढ़ने के लिए किया जा सकता है:

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

ट्रांज़िशन अटैच करना

पूरी प्रोसेस का उदाहरण

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

ध्यान दें: फ़िलहाल, Starlark ट्रांज़िशन को नेटिव नियमों से अटैच करने का कोई तरीका नहीं है. अगर आपको ऐसा करना है, तो समस्या हल करने के तरीकों के बारे में जानने के लिए, bazel-discuss@googlegroups.com पर संपर्क करें.

इनकमिंग एज ट्रांज़िशन

इनकमिंग किनारे के ट्रांज़िशन rule() के cfg पैरामीटर में transition ऑब्जेक्ट (transition() ने बनाया है) को अटैच करके चालू किए जाते हैं:

# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
    implementation = _impl,
    cfg = hot_chocolate_transition,
    ...

इनकमिंग एज ट्रांज़िशन, 1:1 ट्रांज़िशन होने चाहिए.

आउटगोइंग एज ट्रांज़िशन

आउटगोइंग एज ट्रांज़िशन, एट्रिब्यूट के cfg पैरामीटर में transition ऑब्जेक्ट (transition() से बनाया गया) अटैच करके चालू किए जाते हैं:

# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
    implementation = _impl,
    attrs = { "dep": attr.label(cfg = coffee_transition)}
    ...

आउटगोइंग एज ट्रांज़िशन 1:1 या 1:2+ हो सकते हैं.

इन कुंजियों को पढ़ने का तरीका जानने के लिए, ट्रांज़िशन की मदद से एट्रिब्यूट ऐक्सेस करना देखें.

नेटिव विकल्पों पर ट्रांज़िशन

पूरी प्रोसेस का उदाहरण

Starlark ट्रांज़िशन, विकल्प के नाम के साथ एक खास प्रीफ़िक्स का इस्तेमाल करके, नेटिव बिल्ड कॉन्फ़िगरेशन के विकल्पों पर रीड और राइट की जानकारी भी दे सकते हैं.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {"//command_line_option:cpu": "k8"}

cpu_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]

काम न करने वाले नेटिव विकल्प

Baज़ल, --define को "//command_line_option:define" के साथ ट्रांज़िशन करने की सुविधा नहीं देता. इसके बजाय, कस्टम बिल्ड सेटिंग का इस्तेमाल करें. आम तौर पर, --define के नए इस्तेमाल को बढ़ावा नहीं दिया जाता. इसके बजाय, बिल्ड सेटिंग का इस्तेमाल करने का सुझाव दिया जाता है.

Bazel, --config पर ट्रांज़िशन करने की सुविधा नहीं देता. ऐसा इसलिए है, क्योंकि --config एक "बड़ा करने वाला" फ़्लैग है, जो दूसरे फ़्लैग को बड़ा करता है.

खास तौर पर, --config में ऐसे फ़्लैग शामिल हो सकते हैं जिनसे बिल्ड कॉन्फ़िगरेशन पर कोई असर नहीं पड़ता, जैसे कि --spawn_strategy. डिज़ाइन के हिसाब से, Bazel ऐसे फ़्लैग को अलग-अलग टारगेट से नहीं जोड़ सकता. इसका मतलब है कि ट्रांज़िशन में, उन्हें लागू करने का कोई आसान तरीका नहीं है.

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

ट्रांज़िशन चालू होने पर, एक से ज़्यादा बिल्ड सेटिंग की अनुमति मिलती है

एक से ज़्यादा वैल्यू की अनुमति देने वाली बिल्ड सेटिंग को सेट करते समय, सेटिंग की वैल्यू को एक सूची के साथ सेट किया जाना चाहिए.

# example/buildsettings/build_settings.bzl
string_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    # Using a value of just "dark" here will throw an error
    return {"//example:roasts" : ["dark"]},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:roasts"]
)

नो-ऑप ट्रांज़िशन

अगर किसी ट्रांज़िशन से {}, [] या None वैल्यू मिलती है, तो यह सभी सेटिंग को उनकी मूल वैल्यू पर बनाए रखने का छोटा तरीका है. यह हर आउटपुट को साफ़ तौर पर सेट करने के मुकाबले ज़्यादा सुविधाजनक हो सकता है.

# example/transitions/transitions.bzl
def _impl(settings, attr):
    _ignore = (attr)
    if settings["//example:already_chosen"] is True:
      return {}
    return {
      "//example:favorite_flavor": "dark chocolate",
      "//example:include_marshmallows": "yes",
      "//example:desired_temperature": "38C",
    }

hot_chocolate_transition = transition(
    implementation = _impl,
    inputs = ["//example:already_chosen"],
    outputs = [
        "//example:favorite_flavor",
        "//example:include_marshmallows",
        "//example:desired_temperature",
    ]
)

ट्रांज़िशन वाले एट्रिब्यूट ऐक्सेस करना

पूरी प्रोसेस का उदाहरण

किसी आउटगोइंग किनारे में ट्रांज़िशन अटैच करते समय (चाहे ट्रांज़िशन 1:1 हो या 1:2+,), ctx.attr को सूची बनने के लिए मजबूर किया जाता है, अगर ऐसा पहले से नहीं हुआ है. इस सूची में एलिमेंट का क्रम तय नहीं है.

# example/transitions/rules.bzl
def _transition_impl(settings, attr):
    return {"//example:favorite_flavor" : "LATTE"},

coffee_transition = transition(
    implementation = _transition_impl,
    inputs = [],
    outputs = ["//example:favorite_flavor"]
)

def _rule_impl(ctx):
    # Note: List access even though "dep" is not declared as list
    transitioned_dep = ctx.attr.dep[0]

    # Note: Access doesn't change, other_deps was already a list
    for other dep in ctx.attr.other_deps:
      # ...


coffee_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = coffee_transition)
        "other_deps": attr.label_list(cfg = coffee_transition)
    })

अगर ट्रांज़िशन 1:2+ है और कस्टम कुंजियां सेट की जाती हैं, तो हर कुंजी के अलग-अलग डेस्क पढ़ने के लिए ctx.split_attr का इस्तेमाल किया जा सकता है:

# example/transitions/rules.bzl
def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Apple deps": {"//command_line_option:cpu": "ppc"},
        "Linux deps": {"//command_line_option:cpu": "x86"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:cpu"]
)

def _rule_impl(ctx):
    apple_dep = ctx.split_attr.dep["Apple deps"]
    linux_dep = ctx.split_attr.dep["Linux deps"]
    # ctx.attr has a list of all deps for all keys. Order is not guaranteed.
    all_deps = ctx.attr.dep

multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "dep": attr.label(cfg = multi_arch_transition)
    })

पूरा उदाहरण यहां देखें.

प्लैटफ़ॉर्म और टूलचेन के साथ इंटिग्रेशन

फ़िलहाल, --cpu और --crosstool_top जैसे कई नेटिव फ़्लैग, टूलचेन से जुड़े रिज़ॉल्यूशन से जुड़े होते हैं. आने वाले समय में, इस तरह के फ़्लैग पर साफ़ तौर पर दिखने वाले ट्रांज़िशन को, टारगेट प्लैटफ़ॉर्म पर ट्रांज़िशन से बदल दिया जाएगा.

मेमोरी और परफ़ॉर्मेंस से जुड़ी बातें

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

खराब तरीके से काम करने वाले बिल्ड: एक केस स्टडी

बढ़ाए जा सकने की योग्यता वाला ग्राफ़

पहली इमेज. बढ़ाए जा सकने की योग्यता वाला ग्राफ़, जो टॉप लेवल टारगेट और उसकी डिपेंडेंसी को दिखाता है.

यह ग्राफ़, टॉप लेवल का टारगेट, //pkg:app दिखाता है. यह दो टारगेट, //pkg:1_0 और //pkg:1_1 पर निर्भर करता है. ये दोनों लक्ष्य दो टारगेट पर निर्भर करते हैं, //pkg:2_0 और //pkg:2_1. ये दोनों लक्ष्य दो लक्ष्यों पर निर्भर करते हैं, //pkg:3_0 और //pkg:3_1. यह तब तक जारी रहता है, जब तक कि //pkg:n_0 और //pkg:n_1, दोनों एक ही टारगेट, //pkg:dep पर निर्भर न हो जाएं.

//pkg:app बनाने के लिए, इन लक्ष्यों को \(2n+2\) टारगेट करना ज़रूरी है:

  • //pkg:app
  • //pkg:dep
  • \([1..n]\)में \(i\) के लिए //pkg:i_0 और //pkg:i_1

मान लें कि आपने कोई फ़्लैग लागू किया है--//foo:owner=<STRING> और //pkg:i_b लागू होता है

depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"

दूसरे शब्दों में, //pkg:i_b अपने सभी डिपेंडेंसी के लिए, --owner की पुरानी वैल्यू में b जोड़ता है.

इससे ये कॉन्फ़िगर किए गए टारगेट बनते हैं:

//pkg:app                              //foo:owner=""
//pkg:1_0                              //foo:owner=""
//pkg:1_1                              //foo:owner=""
//pkg:2_0 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_0 (via //pkg:1_1)              //foo:owner="1"
//pkg:2_1 (via //pkg:1_0)              //foo:owner="0"
//pkg:2_1 (via //pkg:1_1)              //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0)  //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1)  //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0)  //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1)  //foo:owner="11"
...

//pkg:dep, कॉन्फ़िगर किए गए टारगेट \(2^n\) बनाता है: config.owner= \(\{0,1\}\)में मौजूद सभी \(b_i\) के लिए "\(b_0b_1...b_n\)".

इससे बिल्ड ग्राफ़, टारगेट ग्राफ़ की तुलना में काफ़ी बड़ा हो जाता है. साथ ही, इससे मेमोरी और परफ़ॉर्मेंस पर असर पड़ता है.

TODO: इन समस्याओं को मेज़र करने और कम करने के लिए रणनीतियां जोड़ें.

इसके बारे में और पढ़ें

बिल्ड कॉन्फ़िगरेशन में बदलाव करने के बारे में ज़्यादा जानने के लिए, ये लेख पढ़ें: