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

समस्या की शिकायत करें सोर्स देखें Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

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

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

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

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

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

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

एंड-टू-एंड उदाहरण

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

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

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

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

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

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 पैरामीटर होता है. इसकी मदद से, कमांड लाइन या bazelrcs पर फ़्लैग को कई बार सेट किया जा सकता है. उनकी डिफ़ॉल्ट वैल्यू अब भी स्ट्रिंग टाइप वाले एट्रिब्यूट के साथ सेट है:

# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
    implementation = _impl,
    build_setting = config.string(flag = True, allow_multiple = True)
)
# example/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 param एट्रिब्यूट में तय की गई वैल्यू के जैसी होती है.

# 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/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",
)

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

# 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"
)

Build settings and select()

एंड-टू-एंड उदाहरण

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

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

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

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

डेफ़िनिशन

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

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

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

# 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, transition() के लिए inputs पैरामीटर में तय की गई सभी सेटिंग की डिक्शनरी {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"]

नेटिव विज्ञापन के ऐसे विकल्प जिनका इस्तेमाल नहीं किया जा सकता

Bazel, --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 को सूची के तौर पर इस्तेमाल करना ज़रूरी है. ऐसा तब होता है, जब 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: Add strategies for measurement and mitigation of these issues.

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

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