तारीख सेव करें: BazelCon 2023, 24 से 25 अक्टूबर तक Google म्यूनिख में होगा! ज़्यादा जानें

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

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

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

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

  • --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 जैसे पारंपरिक फ़्लैग, नेटिव सेटिंग के हिसाब से होते हैं — इनकी कुंजियों के बारे में बताया जाता है और इनकी वैल्यू, bazel java कोड में सेट होती है. Bazel के उपयोगकर्ता सिर्फ़ कमांड लाइन और नेटिव तौर पर बनाए गए अन्य एपीआई की मदद से, उन्हें पढ़ सकते हैं और लिख सकते हैं. स्थानीय फ़्लैग और उन्हें दिखाने वाले एपीआई में बदलाव करने के लिए, बेज़ल शैली की रिलीज़ ज़रूरी है. उपयोगकर्ता की बनाई गई बिल्ड सेटिंग .bzl फ़ाइलों में बताई गई हैं (और इसलिए, बदलावों को रजिस्टर करने के लिए बेज़ल रिलीज़ की ज़रूरत नहीं है). इन्हें कमांड लाइन के ज़रिए भी सेट किया जा सकता है (अगर उन्हें flags के तौर पर दिखाया गया है, तो नीचे देखें). हालांकि, इन्हें उपयोगकर्ता के तय किए गए ट्रांज़िशन के ज़रिए भी सेट किया जा सकता है.

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

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

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

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

# 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 पैरामीटर में बताया गया है.

# 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 लाइब्रेरी में, पहले से तय की गई सेटिंग का सेट शामिल है. इसके लिए, आपको कस्टम स्टारलार्क लिखने की ज़रूरत नहीं है.

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

# 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 नियम पैरामीटर का इस्तेमाल करके तय नहीं किया जा सकता. इसके बजाय, बेज़ल में दो मिलते-जुलते नियम होते हैं: 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() का इस्तेमाल करना होगा. बिल्ड सेटिंग के टारगेट को 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-consent@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} है, जिसके बारे में inputs पैरामीटर transition() में बताया गया है.

attr उस नियम के एट्रिब्यूट और मानों का शब्दकोश है जिससे ट्रांज़िशन अटैच किया गया है. आउटपुट एज ट्रांज़िशन के तौर पर अटैच करने पर, इन एट्रिब्यूट की वैल्यू कॉन्फ़िगर करने के बाद चुनी जाती है. आने वाले किनारे पर ट्रांज़िशन के तौर पर अटैच किए जाने पर, 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"]
)

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

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

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

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

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

आने वाले किनारे के ट्रांज़िशन को rule() के cfg पैरामीटर में 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 . बेज़ल, डिज़ाइन के हिसाब से ऐसे फ़्लैग को अलग-अलग टारगेट से नहीं जोड़ सकता. इसका मतलब है कि ट्रांज़िशन में उन्हें लागू करने का कोई सही तरीका नहीं है.

समाधान के तौर पर, आप उन झंडों को साफ़ तौर पर आइटम के तौर पर बता सकते हैं जो आपके ट्रांज़िशन में कॉन्फ़िगरेशन का हिस्सा हैं. इसके लिए, दो जगहों पर --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+ है और उसमें कस्टम बटन सेट किया जाता है, तो हर कुंजी की अलग-अलग Dep पढ़ने के लिए 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
  • \(i\) में \([1..n]\)के लिए //pkg:i_0 और //pkg:i_1

मान लें कि आपने कोई फ़्लैग लागू किया है --//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\)".

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

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

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

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