इस पेज पर, Starlark कॉन्फ़िगरेशन के फ़ायदों और बुनियादी इस्तेमाल के बारे में बताया गया है. यह Bazel का एपीआई है, जिसकी मदद से यह तय किया जा सकता है कि आपका प्रोजेक्ट कैसे बनाया जाए. इसमें, बिल्ड सेटिंग तय करने का तरीका बताया गया है. साथ ही, उदाहरण भी दिए गए हैं.
इससे ये काम किए जा सकते हैं:
- अपने प्रोजेक्ट के लिए कस्टम फ़्लैग तय करना, जिससे
--defineकी ज़रूरत खत्म हो जाती है - अपने पैरंट की तुलना में अलग-अलग कॉन्फ़िगरेशन में deps को कॉन्फ़िगर करने के लिए
ट्रांज़िशन लिखना.
जैसे,
--compilation_mode=optया--cpu=arm - नियमों में बेहतर डिफ़ॉल्ट वैल्यू सेट करना. जैसे, तय किए गए एसडीके के साथ
//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
एट्रिब्यूट का इस्तेमाल करके अलग किया जाता है.
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
build_setting एट्रिब्यूट, एक ऐसा फ़ंक्शन लेता है जो
बिल्ड सेटिंग का टाइप तय करता है. टाइप, Starlark के बुनियादी टाइप के सेट तक सीमित होता है. जैसे,
bool और string. ज़्यादा जानकारी के लिए, 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 पैरामीटर भी होता है. इसकी मदद से,
फ़्लैग को कमांड लाइन या bazelrcs में एक से ज़्यादा बार सेट किया जा सकता है. इसकी डिफ़ॉल्ट
वैल्यू अब भी स्ट्रिंग-टाइप के एट्रिब्यूट से सेट की जाती है:
# 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"
)
पहले से तय की गई सेटिंग
उदाहरण के लिए, ऐसी सेटिंग तय करने के लिए जो स्ट्रिंग वैल्यू के सीमित सेट को स्वीकार करती है:
# 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()
उपयोगकर्ता, बिल्ड सेटिंग पर एट्रिब्यूट कॉन्फ़िगर करने के लिए
select() का इस्तेमाल कर सकते हैं. बिल्ड सेटिंग के टारगेट को, flag_values एट्रिब्यूट में
config_setting पास किया जा सकता है. कॉन्फ़िगरेशन से मैच करने वाली वैल्यू को
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 एक डिक्शनरी {String:Object} होती है. इसमें, transition() के inputs पैरामीटर में तय की गई सभी सेटिंग शामिल होती हैं
.
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 पर संपर्क करें.
इनकमिंग एज ट्रांज़िशन
इनकमिंग एज ट्रांज़िशन को चालू करने के लिए, एक transition ऑब्जेक्ट
(transition() से बनाया गया) को rule()'s cfg पैरामीटर से अटैच करें:
# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
implementation = _impl,
cfg = hot_chocolate_transition,
...
इनकमिंग एज ट्रांज़िशन, 1:1 ट्रांज़िशन होने चाहिए.
आउटगोइंग एज ट्रांज़िशन
आउटगोइंग एज ट्रांज़िशन को चालू करने के लिए, transition ऑब्जेक्ट
(transition() से बनाया गया) को एट्रिब्यूट के cfg पैरामीटर से अटैच करें:
# 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,
"//command_line_option:define" के साथ --define पर ट्रांज़िशन करने की सुविधा नहीं देता. इसके बजाय, कस्टम
बिल्ड सेटिंग का इस्तेमाल करें. आम तौर पर, बिल्ड सेटिंग के लिए
--define के नए इस्तेमाल को बढ़ावा नहीं दिया जाता.
Bazel, --config पर ट्रांज़िशन करने की सुविधा नहीं देता. ऐसा इसलिए है, क्योंकि --config एक "एक्सपैंशन" फ़्लैग है. यह अन्य फ़्लैग के लिए एक्सपैंड होता है.
अहम बात यह है कि --config में ऐसे फ़्लैग शामिल हो सकते हैं जो बिल्ड कॉन्फ़िगरेशन पर असर नहीं डालते,
जैसे
--spawn_strategy
. Bazel, डिज़ाइन के हिसाब से, ऐसे फ़्लैग को अलग-अलग टारगेट से बाइंड नहीं कर सकता. इसका मतलब है
कि ट्रांज़िशन में इन्हें लागू करने का कोई तरीका नहीं है.
इसके बजाय, ट्रांज़िशन में उन फ़्लैग को साफ़ तौर पर शामिल किया जा सकता है जो कॉन्फ़िगरेशन का हिस्सा हैं. इसके लिए, --config's
एक्सपैंशन को दो जगहों पर बनाए रखना ज़रूरी है. यह यूज़र इंटरफ़ेस (यूआई) की एक समस्या है.
एक से ज़्यादा बिल्ड सेटिंग की अनुमति देने वाले ट्रांज़िशन
# 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",
]
)
ट्रांज़िशन के साथ एट्रिब्यूट ऐक्सेस करना
आउटगोइंग एज से ट्रांज़िशन अटैच करने पर,
को सूची के तौर पर फ़ोर्स किया जाता है. ctx.attr
भले ही, ट्रांज़िशन 1:1 हो या 1:2+. इस सूची में एलिमेंट का क्रम तय नहीं होता.
# 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 दिखाया गया है. यह दो टारगेट पर निर्भर करता है: a //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//pkg:i_0और//pkg:i_1सभी के लिए \(i\) और \([1..n]\)
मान लें कि आपने लागू किया है) फ़्लैग
--//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 से, कॉन्फ़िगर किए गए ये टारगेट मिलते हैं: सभी के लिए config.owner=
"\(b_0b_1...b_n\)" \(b_i\) in \(\{0,1\}\). \(2^n\)
इससे, बिल्ड ग्राफ़, टारगेट ग्राफ़ की तुलना में तेज़ी से बड़ा हो जाता है. साथ ही, मेमोरी और परफ़ॉर्मेंस पर भी असर पड़ता है.
अहम जानकारी: इन समस्याओं को मापने और कम करने के लिए, रणनीतियां जोड़ें.
इस बारे में और पढ़ें
बिल्ड कॉन्फ़िगरेशन में बदलाव करने के बारे में ज़्यादा जानने के लिए, ये लेख पढ़ें: