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

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

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

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

  • अपने प्रोजेक्ट के लिए कस्टम फ़्लैग तय करें. इससे --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 एट्रिब्यूट एक ऐसा फ़ंक्शन लेता है जो यह बताता है कि बिल्ड सेटिंग किस तरह की है. टाइप, Starlark के बेसिक टाइप के लिए है. जैसे, bool और string. ज़्यादा जानकारी के लिए, config मॉड्यूल दस्तावेज़ देखें. नियम के लागू करने वाले फ़ंक्शन में, ज़्यादा जटिल टाइपिंग को पूरा किया जा सकता है. इस बारे में ज़्यादा जानकारी यहां दी गई है.

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

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

सभी नियमों की तरह, बिल्ड सेटिंग के नियमों में भी लागू करने के फ़ंक्शन होते हैं. बिल्ड सेटिंग की स्टारलार्क-टाइप वैल्यू को 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 पैरामीटर ने बताया है.

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

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

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

उपयोगकर्ता, 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() फ़ंक्शन और उसे लागू करने वाला फ़ंक्शन होता है.

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

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

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

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

ध्यान दें: फ़िलहाल, 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 को पहले से सूची बनने के लिए ही मज़बूर किया जाता है. इस सूची में एलिमेंट के क्रम की जानकारी नहीं है.

# 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

मान लें कि आपने एक फ़्लैग 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_0b_1...b_n\)" सभी \(b_i\) इन \(\{0,1\}\)में.

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

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

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

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