इस पेज में Starlark कॉन्फ़िगरेशन के फ़ायदों और बुनियादी इस्तेमाल के बारे में बताया गया है. यह अपने प्रोजेक्ट को बनाने के तरीके को पसंद के मुताबिक बनाने के लिए, Bazel का एपीआई है. इसमें बिल्ड सेटिंग तय करने का तरीका बताया गया है और उदाहरण दिए गए हैं.
इसकी मदद से, ये काम किए जा सकते हैं:
- अपने प्रोजेक्ट के लिए कस्टम फ़्लैग तय करें. इससे
--define
की ज़रूरत अब खत्म हो जाएगी - डिप को उनके पैरंट से अलग कॉन्फ़िगरेशन में कॉन्फ़िगर करने के लिए
ट्रांज़िशन लिखें
(जैसे कि
--compilation_mode=opt
या--cpu=arm
) - नियमों में बेहतर तरीके से डिफ़ॉल्ट बनाएं (जैसे, किसी खास SDK टूल की मदद से
//my:android_app
अपने-आप बनाना)
और बहुत कुछ, पूरी तरह से .bzl फ़ाइलों से (Bzel रिलीज़ की ज़रूरत नहीं). उदाहरण के लिए, bazelbuild/examples
रेपो देखें.
उपयोगकर्ता के लिए तय की गई बिल्ड सेटिंग
बिल्ड सेटिंग, कॉन्फ़िगरेशन की जानकारी का एक हिस्सा होती है. कॉन्फ़िगरेशन को की/वैल्यू मैप के तौर पर देखें. --cpu=ppc
और --copt="-DFoo"
को सेट करने पर, {cpu: ppc, copt: "-DFoo"}
जैसा कॉन्फ़िगरेशन दिखता है. हर एंट्री एक बिल्ड सेटिंग होती है.
cpu
और copt
जैसे परंपरागत फ़्लैग नेटिव सेटिंग होते हैं —
उनकी कुंजियां तय की जाती हैं और उनकी वैल्यू नेटिव बाज़ेल जावा कोड में सेट होती हैं.
Bazel उपयोगकर्ता, उन्हें सिर्फ़ कमांड लाइन और मूल रूप से मैनेज किए जाने वाले अन्य एपीआई के ज़रिए पढ़ और लिख सकते हैं. नेटिव फ़्लैग और उन्हें उपलब्ध कराने वाले एपीआई को बदलने के लिए, बैजल रिलीज़ की ज़रूरत होती है. उपयोगकर्ता के तय किए गए बिल्ड सेटिंग, .bzl
फ़ाइलों में तय की जाती हैं. इसलिए, बदलावों को रजिस्टर करने के लिए, बैजल रिलीज़ की ज़रूरत नहीं है. इन्हें कमांड लाइन की मदद से भी सेट किया जा सकता है
(अगर उन्हें 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
जैसे बेसिक स्टारलार्क टाइप के सेट तक सीमित है. ज़्यादा जानकारी के लिए, 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"
)
पहले से तय सेटिंग
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
सबसे सही तरीका: किसी उपनाम को कई बार सेट करने से, सबसे हाल ही में इस्तेमाल किए जाने वाले उपनाम को प्राथमिकता दी जाती है. अनचाहे पार्सिंग नतीजों से बचने के लिए, यूनीक उपनाम वाले नामों का इस्तेमाल करें.
उपनाम का इस्तेमाल करने के लिए, इसे बिल्ड सेटिंग के टारगेट पाथ की जगह टाइप करें.
ऊपर दिए गए coffee
के उदाहरण को उपयोगकर्ता के .bazelrc
में सेट करने से:
$ 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
, transition()
में inputs
पैरामीटर में बताई गई सभी सेटिंग का {String
:Object
} डिक्शनरी है.
attr
, उस नियम के एट्रिब्यूट और वैल्यू का शब्दकोश है जिससे ट्रांज़िशन जुड़ा है. आउटगोइंग एज ट्रांज़िशन के तौर पर अटैच करने पर, इन
एट्रिब्यूट की सभी वैल्यू, पोस्ट-select() रिज़ॉल्यूशन में कॉन्फ़िगर की जाती हैं. इनकमिंग एज ट्रांज़िशन के रूप में अटैच किए जाने पर, attr
में ऐसे एट्रिब्यूट शामिल नहीं होते हैं जो अपनी वैल्यू का समाधान करने के लिए, सिलेक्टर का इस्तेमाल करते हैं. अगर --foo
पर किसी इनिंग एज ट्रांज़िशन को bar
एट्रिब्यूट को सेट करने के बाद, --foo
को भी चुना जाता है, तो एट्रिब्यूट bar
को सेट किया जा सकता है. ऐसे में, इस बात की संभावना होती है कि इनकमिंग एज ट्रांज़िशन, ट्रांज़िशन के दौरान 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"]
)
ट्रांज़िशन अटैच किया जा रहा है
ट्रांज़िशन दो जगहों पर जोड़े जा सकते हैं: आने वाले किनारों और आउटगोइंग किनारों. इसका मतलब यह है कि नियम अपने खुद के कॉन्फ़िगरेशन (आने वाले एज ट्रांज़िशन) को बदल सकते हैं और अपनी डिपेंडेंसी के कॉन्फ़िगरेशन (आउटगोइंग एज ट्रांज़िशन) का ट्रांज़िशन कर सकते हैं.
ध्यान दें: फ़िलहाल, स्टारलार्क ट्रांज़िशन को स्थानीय नियमों में अटैच करने का कोई तरीका मौजूद नहीं है. अगर आपको ऐसा करना है, तो 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+ हो सकते हैं.
इन कुंजियों को पढ़ने का तरीका जानने के लिए, ट्रांज़िशन के साथ एट्रिब्यूट ऐक्सेस करना देखें.
नेटिव विकल्पों पर ट्रांज़िशन
स्टारलार्क ट्रांज़िशन, विकल्प के नाम के लिए एक खास प्रीफ़िक्स का इस्तेमाल करके, नेटिव बिल्ड कॉन्फ़िगरेशन विकल्पों पर रीड और राइट का एलान भी कर सकता है.
# 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
को पहले से सूची न होने पर, हर हाल में सूची बनाया जाता है. इस सूची में एलिमेंट का क्रम तय नहीं है.
# 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
- \([1..n]\)में \(i\) के लिए
//pkg:i_0
और//pkg:i_1
मान लें कि आपने फ़्लैग implement) किया है
--//foo:owner=<STRING>
और //pkg:i_b
लागू होता है
depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"
दूसरे शब्दों में, //pkg:i_b
अपने सभी डेप के लिए, b
को --owner
की पुरानी वैल्यू में जोड़ता है.
इससे, कॉन्फ़िगर किए गए ये टारगेट मिलते हैं:
//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=
\(b_i\) में \(\{0,1\}\)सभी के लिए "\(b_0b_1...b_n\)".
इससे, बिल्ड ग्राफ़, टारगेट ग्राफ़ से ज़्यादा बड़ा हो जाता है. साथ ही, वही मेमोरी और परफ़ॉर्मेंस से जुड़े नतीजे दिखाता है.
काम की सूची: इन समस्याओं के आकलन और उन्हें कम करने के लिए रणनीतियां जोड़ें.
इसके बारे में और पढ़ें
बिल्ड कॉन्फ़िगरेशन में बदलाव करने के बारे में ज़्यादा जानने के लिए, यहां देखें:
- Starlark बिल्ड कॉन्फ़िगरेशन
- Bazel Configurability रोडमैप
- शुरू से लेकर आखिर तक के उदाहरणों का पूरा सेट