टूलचेन

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

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

वजह

आइए, पहले उन टूलचेन पर नज़र डालते हैं जिन्हें हल करने के लिए डिज़ाइन किया गया है. मान लें कि "bar" प्रोग्रामिंग भाषा को सपोर्ट करने के लिए नियम लिखे जा रहे हैं. आपका bar_binary नियम, barc कंपाइलर का इस्तेमाल करके *.bar फ़ाइलों को कंपाइल करेगा. यह एक ऐसा टूल है जो आपके फ़ाइल फ़ोल्डर में दूसरे टारगेट के तौर पर बनाया गया है. bar_binary टारगेट लिखने वाले उपयोगकर्ताओं के लिए, कंपाइलर पर डिपेंडेंसी तय करने की ज़रूरत नहीं होनी चाहिए. इसलिए, नियम की डेफ़िनिशन में निजी एट्रिब्यूट के तौर पर जोड़कर, इसे इंप्लिसिट डिपेंडेंसी बना दिया जाता है.

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        "_compiler": attr.label(
            default = "//bar_tools:barc_linux",  # the compiler running on linux
            providers = [BarcInfo],
        ),
    },
)

//bar_tools:barc_linux अब हर bar_binary टारगेट पर निर्भर है. इसलिए, इसे किसी भी bar_binary टारगेट से पहले बनाया जाएगा. इसे किसी भी दूसरे एट्रिब्यूट की तरह ही, नियम लागू करने वाले फ़ंक्शन से ऐक्सेस किया जा सकता है:

BarcInfo = provider(
    doc = "Information about how to invoke the barc compiler.",
    # In the real world, compiler_path and system_lib might hold File objects,
    # but for simplicity they are strings for this example. arch_flags is a list
    # of strings.
    fields = ["compiler_path", "system_lib", "arch_flags"],
)

def _bar_binary_impl(ctx):
    ...
    info = ctx.attr._compiler[BarcInfo]
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

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

अगर _compiler एट्रिब्यूट को 'निजी' के तौर पर सेट नहीं किया जाता है, तो इसे सही विकल्प यह साबित हो सकता है कि इस बोझ को उपयोगकर्ताओं पर बताया जाए. इसके बाद एक प्लैटफ़ॉर्म या दूसरे प्लैटफ़ॉर्म के लिए बनाने के लिए, अलग-अलग टारगेट को हार्डकोड किया जा सकता है.

bar_binary(
    name = "myprog_on_linux",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_linux",
)

bar_binary(
    name = "myprog_on_windows",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_windows",
)

इस समाधान को बेहतर बनाने के लिए, select का इस्तेमाल करके, प्लैटफ़ॉर्म के हिसाब से compiler चुनें:

config_setting(
    name = "on_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "on_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

bar_binary(
    name = "myprog",
    srcs = ["mysrc.bar"],
    compiler = select({
        ":on_linux": "//bar_tools:barc_linux",
        ":on_windows": "//bar_tools:barc_windows",
    }),
)

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

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

टूलचेन का इस्तेमाल करने वाले लिखने के नियम

टूलचेन फ़्रेमवर्क के तहत, नियम सीधे टूल पर निर्भर होने के बजाय, टूलचेन के टाइप पर निर्भर होते हैं. टूलचेन टाइप एक आसान टारगेट है, जो टूल की ऐसी कैटगरी को दिखाता है जो अलग-अलग प्लैटफ़ॉर्म पर एक ही भूमिका में काम करते हैं. उदाहरण के लिए, आपके पास ऐसा टाइप बताने का विकल्प है जो बार कंपाइलर को दिखाता है:

# By convention, toolchain_type targets are named "toolchain_type" and
# distinguished by their package path. So the full path for this would be
# //bar_tools:toolchain_type.
toolchain_type(name = "toolchain_type")

पिछले सेक्शन में, नियम की परिभाषा में बदलाव किया गया है, ताकि कंपाइलर को एट्रिब्यूट के तौर पर लेने के बजाय, यह एलान करे कि यह //bar_tools:toolchain_type टूलचेन का इस्तेमाल करता है.

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        # No `_compiler` attribute anymore.
    },
    toolchains = ["//bar_tools:toolchain_type"],
)

लागू करने वाला फ़ंक्शन, अब ctx.attr के बजाय ctx.toolchains के तहत इस डिपेंडेंसी को ऐक्सेस करता है. साथ ही, इसके लिए टूलचेन टाइप का इस्तेमाल कुंजी के तौर पर करता है.

def _bar_binary_impl(ctx):
    ...
    info = ctx.toolchains["//bar_tools:toolchain_type"].barcinfo
    # The rest is unchanged.
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

ctx.toolchains["//bar_tools:toolchain_type"], ToolchainInfo प्रोवाइडर को दिखाता है जो Bazel के टारगेट से जुड़ा होता है. इससे, टूलचेन डिपेंडेंसी को हल करने में मदद मिलती है. ToolchainInfo ऑब्जेक्ट के फ़ील्ड, टूल के नियम के तहत सेट किए जाते हैं. अगले सेक्शन में, इस नियम को इस तरह से तय किया गया है कि barcinfo फ़ील्ड में BarcInfo ऑब्जेक्ट को रैप किया जाता है.

टारगेट से टूलचेन की समस्या हल करने के लिए, Bazel की प्रोसेस यहां बताई गई है. सिर्फ़ टूलचेन टारगेट का समाधान हो चुका है, लेकिन यह bar_binary टारगेट के आधार पर तय होती है, न कि पूरी तरह से कैंडिडेट टूलचेन के हिसाब से.

ज़रूरी और वैकल्पिक टूलचेन

डिफ़ॉल्ट रूप से, जब कोई नियम किसी बेअस लेबल (जैसा कि ऊपर दिखाया गया है) का इस्तेमाल करके, टूलचेन टाइप की डिपेंडेंसी दिखाता है, तो टूलचेन टाइप को ज़रूरी माना जाता है. अगर ज़रूरी टूलचेन टाइप के लिए, Bazel को कोई मिलता-जुलता टूलचेन (यहां टूलचेन रिज़ॉल्यूशन देखें) नहीं मिलता, तो यह एक गड़बड़ी है और इसका विश्लेषण रोक दिया जाता है.

इसके बजाय, नीचे बताए गए तरीके से ज़रूरी नहीं टूलचेन टाइप डिपेंडेंसी का एलान किया जा सकता है:

bar_binary = rule(
    ...
    toolchains = [
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

जब वैकल्पिक टूलचेन टाइप का समाधान नहीं किया जा सकता, तब विश्लेषण जारी रहता है और ctx.toolchains["//bar_tools:toolchain_type"] का नतीजा None होता है.

config_common.toolchain_type फ़ंक्शन, डिफ़ॉल्ट रूप से 'ज़रूरी' पर सेट होता है.

नीचे दिए गए फ़ॉर्म इस्तेमाल किए जा सकते हैं:

  • ज़रूरी टूलचेन टाइप:
    • toolchains = ["//bar_tools:toolchain_type"]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type")]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = True)]
  • टूलचेन के वैकल्पिक टाइप:
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False)]
bar_binary = rule(
    ...
    toolchains = [
        "//foo_tools:toolchain_type",
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

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

टूलचेन का इस्तेमाल करने वाले लिखने के पहलू

दोनों पक्षों के पास नियमों की तरह, एक ही टूलचेन एपीआई का ऐक्सेस होता है: ज़रूरी टूलचेन के टाइप तय किए जा सकते हैं, कॉन्टेक्स्ट के हिसाब से टूलचेन को ऐक्सेस किया जा सकता है, और टूलचेन का इस्तेमाल करके नए ऐक्शन जनरेट करने के लिए उनका इस्तेमाल किया जा सकता है.

bar_aspect = aspect(
    implementation = _bar_aspect_impl,
    attrs = {},
    toolchains = ['//bar_tools:toolchain_type'],
)

def _bar_aspect_impl(target, ctx):
  toolchain = ctx.toolchains['//bar_tools:toolchain_type']
  # Use the toolchain provider like in a rule.
  return []

टूलचेन की परिभाषा

किसी दिए गए टूलचेन टाइप के लिए कुछ टूलचेन तय करने के लिए, आपके पास तीन चीज़ों की ज़रूरत होती है:

  1. टूल या टूल सुइट के टाइप को दिखाने वाला भाषा का खास नियम. पारंपरिक तरीके के मुताबिक, इस नियम का नाम "_toolchain" जोड़ दिया जाता है.

    1. ध्यान दें: \_toolchain नियम कोई बिल्ड ऐक्शन नहीं बना सकता. इसके बजाय, यह दूसरे नियमों से आर्टफ़ैक्ट इकट्ठा करता है और उन्हें उस नियम पर भेजता है जो टूलचेन का इस्तेमाल करता है. यह नियम सभी बिल्ड ऐक्शन बनाने के लिए ज़िम्मेदार है.
  2. इस नियम प्रकार के कई टारगेट, जो अलग-अलग प्लैटफ़ॉर्म के लिए टूल या टूल सुइट के वर्शन दिखाते हैं.

  3. ऐसे हर टारगेट के लिए, टूलचेन फ़्रेमवर्क में इस्तेमाल किया गया मेटाडेटा उपलब्ध कराने के लिए, सामान्य toolchain नियम से जुड़ा टारगेट. यह toolchain टारगेट, इस टूलचेन से जुड़े toolchain_type को भी दिखाता है. इसका मतलब है कि यह _toolchain नियम किसी भी toolchain_type से जुड़ा हो सकता है. साथ ही, ऐसा toolchain इंस्टेंस में ही हो सकता है जो इस _toolchain नियम का इस्तेमाल करता हो कि यह नियम toolchain_type से जुड़ा है.

उदाहरण के लिए, bar_toolchain नियम की परिभाषा यहां दी गई है. हमारे उदाहरण में सिर्फ़ कंपाइलर है, लेकिन लिंकर जैसे दूसरे टूल को भी इसके नीचे ग्रुप किया जा सकता है.

def _bar_toolchain_impl(ctx):
    toolchain_info = platform_common.ToolchainInfo(
        barcinfo = BarcInfo(
            compiler_path = ctx.attr.compiler_path,
            system_lib = ctx.attr.system_lib,
            arch_flags = ctx.attr.arch_flags,
        ),
    )
    return [toolchain_info]

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler_path": attr.string(),
        "system_lib": attr.string(),
        "arch_flags": attr.string_list(),
    },
)

नियम के लिए, ToolchainInfo की सेवा देने वाली कंपनी ज़रूर दिखेगी. यह एक ऐसा ऑब्जेक्ट बन जाएगी जिसे इस्तेमाल करने वाला नियम, ctx.toolchains और टूलचेन टाइप के लेबल का इस्तेमाल करके हासिल करता है. struct की तरह ToolchainInfo, फ़ील्ड की वैल्यू के हिसाब से अपने-आप जोड़े गए पेयर को होल्ड कर सकता है. ToolchainInfo में कौनसे फ़ील्ड जोड़े जाते हैं, इसके बारे में खास जानकारी को टूलचेन टाइप में साफ़ तौर पर दिखाना चाहिए. इस उदाहरण में, ऊपर बताए गए स्कीमा का फिर से इस्तेमाल करने के लिए वैल्यू, BarcInfo ऑब्जेक्ट में रैप की जाती हैं; यह स्टाइल पुष्टि करने और कोड का दोबारा इस्तेमाल करने के लिए काम आ सकती है.

अब अलग-अलग barc कंपाइलर के लिए, टारगेट तय किए जा सकते हैं.

bar_toolchain(
    name = "barc_linux",
    arch_flags = [
        "--arch=Linux",
        "--debug_everything",
    ],
    compiler_path = "/path/to/barc/on/linux",
    system_lib = "/usr/lib/libbarc.so",
)

bar_toolchain(
    name = "barc_windows",
    arch_flags = [
        "--arch=Windows",
        # Different flags, no debug support on windows.
    ],
    compiler_path = "C:\\path\\on\\windows\\barc.exe",
    system_lib = "C:\\path\\on\\windows\\barclib.dll",
)

आखिर में, आप bar_toolchain के दो टारगेट के लिए toolchain परिभाषाएं बनाते हैं. ये परिभाषाएं, भाषा के हिसाब से टारगेट को टूलचेन टाइप से जोड़ती हैं. साथ ही, कंस्ट्रेंट की जानकारी देती हैं, जिससे Bazel को यह पता चलता है कि किसी प्लैटफ़ॉर्म के लिए टूलचेन सही है या नहीं.

toolchain(
    name = "barc_linux_toolchain",
    exec_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_linux",
    toolchain_type = ":toolchain_type",
)

toolchain(
    name = "barc_windows_toolchain",
    exec_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_windows",
    toolchain_type = ":toolchain_type",
)

ऊपर दिए गए रिलेटिव पाथ सिंटैक्स का इस्तेमाल करने से पता चलता है कि ये सभी डेफ़िनिशन एक ही पैकेज में हैं. हालांकि, इसकी कोई वजह नहीं है कि टूलचेन टाइप, भाषा के हिसाब से टूलचेन टारगेट, और toolchain डेफ़िनिशन वाले टारगेट अलग-अलग पैकेज में नहीं रखे जा सकते.

उदाहरण के लिए, go_toolchain देखें.

टूलचेन और कॉन्फ़िगरेशन

नियम बनाने वाले लोगों के लिए एक अहम सवाल यह है कि जब किसी bar_toolchain टारगेट का विश्लेषण किया जाता है, तो वह कौनसा कॉन्फ़िगरेशन दिखता है. साथ ही, डिपेंडेंसी के लिए कौनसे ट्रांज़िशन इस्तेमाल किए जाने चाहिए? ऊपर दिए गए उदाहरण में स्ट्रिंग एट्रिब्यूट का इस्तेमाल किया गया है. हालांकि, Bazel डेटा स्टोर करने की जगह के अन्य टारगेट पर निर्भर ज़्यादा जटिल टूलचेन का क्या होगा?

चलिए, bar_toolchain का ज़्यादा मुश्किल वर्शन देखते हैं:

def _bar_toolchain_impl(ctx):
    # The implementation is mostly the same as above, so skipping.
    pass

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler": attr.label(
            executable = True,
            mandatory = True,
            cfg = "exec",
        ),
        "system_lib": attr.label(
            mandatory = True,
            cfg = "target",
        ),
        "arch_flags": attr.string_list(),
    },
)

attr.label का इस्तेमाल, स्टैंडर्ड नियम की तरह ही होता है, लेकिन cfg पैरामीटर का मतलब थोड़ा अलग होता है.

टूलचेन रिज़ॉल्यूशन के ज़रिए टारगेट (जिसे "पैरंट" कहा जाता है) से टूलचेन तक पहुंचने के लिए, डिपेंडेंसी एक खास कॉन्फ़िगरेशन ट्रांज़िशन का इस्तेमाल करती है. इसे "टूलचेन ट्रांज़िशन" कहते हैं. टूलचेन ट्रांज़िशन, कॉन्फ़िगरेशन को एक जैसा बनाए रखता है. हालांकि, यह एक्ज़ीक्यूशन प्लैटफ़ॉर्म के लिए टूलचेन के लिए एक जैसा प्लैटफ़ॉर्म लागू करने के लिए बाध्य करता है. इससे, टूलचेन की किसी भी exec डिपेंडेंसी को पैरंट के बिल्ड ऐक्शन के लिए एक्ज़ीक्यूट करने की अनुमति मिलती है. टूलचेन की कोई भी डिपेंडेंसी, जो cfg = "target" का इस्तेमाल करती है (या जिसमें cfg के बारे में नहीं बताया जाता, क्योंकि "टारगेट" डिफ़ॉल्ट है). इन्हें पैरंट के टारगेट प्लैटफ़ॉर्म के लिए बनाया जाता है. इससे टूलचेन नियमों को लाइब्रेरी (ऊपर दिया गया system_lib एट्रिब्यूट) और टूल (compiler एट्रिब्यूट), दोनों का इस्तेमाल उन बिल्ड नियमों में करने में मदद मिलती है जिनके लिए इन नियमों की ज़रूरत है. सिस्टम लाइब्रेरी, फ़ाइनल आर्टफ़ैक्ट में जुड़ी होती हैं, इसलिए इसको उसी प्लैटफ़ॉर्म के लिए बनाया जाना चाहिए. वहीं, कंपाइलर एक टूल है जिसे बिल्ड के दौरान शुरू किया जाता है और यह एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चलने के लिए ज़रूरी होता है.

टूलचेन के साथ रजिस्टर करना और बनाना

इस चरण में सभी बिल्डिंग ब्लॉक असेंबल हो जाते हैं और आपको सिर्फ़ टूलचेन को Bazel की रिज़ॉल्यूशन प्रोसेस के लिए उपलब्ध कराना है. इसके लिए, टूलचेन को register_toolchains() का इस्तेमाल करके MODULE.bazel फ़ाइल में रजिस्टर किया जाता है. इसके अलावा, --extra_toolchains फ़्लैग का इस्तेमाल करके, टूलचेन के लेबल को कमांड लाइन पर पास किया जाता है.

register_toolchains(
    "//bar_tools:barc_linux_toolchain",
    "//bar_tools:barc_windows_toolchain",
    # Target patterns are also permitted, so you could have also written:
    # "//bar_tools:all",
    # or even
    # "//bar_tools/...",
)

टूलचेन को रजिस्टर करने के लिए टारगेट पैटर्न का इस्तेमाल करते समय, अलग-अलग टूलचेन को रजिस्टर करने का क्रम नीचे दिए गए नियमों से तय होता है:

  • किसी पैकेज के सबपैकेज में तय किए गए टूलचेन को, पैकेज में बताई गई टूलचेन से पहले रजिस्टर किया जाता है.
  • एक पैकेज में, टूलचेन को उनके नाम के लेक्सिकोलॉजिकल क्रम में रजिस्टर किया जाता है.

अब जब टूलचेन टाइप के आधार पर कोई टारगेट बनाया जाता है, तो टारगेट और एक्ज़ीक्यूशन प्लैटफ़ॉर्म के आधार पर एक सही टूलचेन चुनी जाएगी.

# my_pkg/BUILD

platform(
    name = "my_target_platform",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

bar_binary(
    name = "my_bar_binary",
    ...
)
bazel build //my_pkg:my_bar_binary --platforms=//my_pkg:my_target_platform

Bazel को दिखेगा कि //my_pkg:my_bar_binary को ऐसे प्लैटफ़ॉर्म की मदद से बनाया जा रहा है जिसमें @platforms//os:linux है. इसलिए, //bar_tools:barc_linux_toolchain के लिए //bar_tools:toolchain_type रेफ़रंस का इस्तेमाल करें. इससे //bar_tools:barc_linux बन जाएगा, लेकिन //bar_tools:barc_windows नहीं.

टूलचेन का रिज़ॉल्यूशन

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

एक्ज़ीक्यूशन के लिए उपलब्ध प्लैटफ़ॉर्म और टूलचेन, register_execution_platforms और register_toolchains कॉल इन MODULE.bazel फ़ाइलों के ज़रिए, बाहरी डिपेंडेंसी ग्राफ़ से इकट्ठा किए जाते हैं. --extra_execution_platforms और --extra_toolchains के ज़रिए, कमांड लाइन पर अन्य एक्ज़ीक्यूशन प्लैटफ़ॉर्म और टूलचेन की जानकारी भी दी जा सकती है. होस्ट प्लैटफ़ॉर्म, एक्ज़ीक्यूशन के उपलब्ध प्लैटफ़ॉर्म के तौर पर अपने-आप शामिल हो जाता है. उपलब्ध प्लैटफ़ॉर्म और टूलचेन को डिटर्मिनिज़्म के लिए, क्रम वाली सूचियों के तौर पर ट्रैक किया जाता है. इस सूची में पहले के आइटम को प्राथमिकता दी जाती है.

प्राथमिकता के क्रम में उपलब्ध टूलचेन का सेट, --extra_toolchains और register_toolchains से बनाया जाता है:

  1. --extra_toolchains का इस्तेमाल करके रजिस्टर की गई टूलचेन को पहले जोड़ा जाता है. (इनमें, आखिरी टूलचेन को सबसे ज़्यादा प्राथमिकता दी जाती है.)
  2. ट्रांज़िटिव एक्सटर्नल डिपेंडेंसी ग्राफ़ में register_toolchains का इस्तेमाल करके रजिस्टर की गई टूलचेन, इस क्रम में होती हैं: (इनमें, सबसे पहले बताए गए टूलचेन को सबसे ज़्यादा प्राथमिकता दी जाती है.)
    1. रूट मॉड्यूल की मदद से रजिस्टर की गई टूलचेन (जैसे, फ़ाइल फ़ोल्डर रूट में मौजूद MODULE.bazel);
    2. उपयोगकर्ता की WORKSPACE फ़ाइल में रजिस्टर की गई टूलचेन, जिसमें वहां से शुरू किए गए सभी मैक्रो शामिल हैं;
    3. नॉन-रूट मॉड्यूल से रजिस्टर की गई टूलचेन (जैसे, रूट मॉड्यूल में तय की गई डिपेंडेंसी, उनकी डिपेंडेंसी वगैरह);
    4. "वर्कSPACE सफ़िक्स" में रजिस्टर की गई टूलचेन टेक्नोलॉजी का इस्तेमाल किया जाता है. इसका इस्तेमाल सिर्फ़ उन स्थानीय नियमों के लिए किया जाता है जो Bazel इंस्टॉलेशन के साथ बंडल किए गए होते हैं.

ध्यान दें: :all, :*, और/... जैसे सूडो टारगेट को Bazel के पैकेज लोड करने के तरीके के हिसाब से क्रम में लगाया जाता है. यह तकनीक, लेक्सिकोग्राफ़िक ऑर्डर में इस्तेमाल की जाती है.

समस्या को हल करने का तरीका नीचे दिया गया है.

  1. target_compatible_with या exec_compatible_with क्लॉज़ किसी प्लैटफ़ॉर्म से मेल खाता है, अगर सूची में मौजूद हर constraint_value के लिए, प्लैटफ़ॉर्म में वह constraint_value भी मौजूद हो (साफ़ तौर पर या डिफ़ॉल्ट के तौर पर).

    अगर प्लैटफ़ॉर्म में constraint_setting के constraint_value ऐसे हैं जिनका इस्तेमाल क्लॉज़ में नहीं किया गया है, तो इनसे मिलान करने की प्रोसेस पर कोई असर नहीं पड़ता है.

  2. अगर टारगेट बनाया जा रहा है, exec_compatible_with एट्रिब्यूट या इसके नियम की परिभाषा exec_compatible_with आर्ग्युमेंट के बारे में बताती है, तो एक्ज़ीक्यूशन की शर्तों से मेल न खाने वाले किसी भी प्लैटफ़ॉर्म को हटाने के लिए, उपलब्ध एक्ज़ीक्यूशन प्लैटफ़ॉर्म की सूची फ़िल्टर की जाती है.

  3. उपलब्ध टूलचेन की सूची, ऐसे किसी भी टूलचेन को हटाने के लिए फ़िल्टर की जाती है जो target_settings के बारे में बताती है जो मौजूदा कॉन्फ़िगरेशन से मेल नहीं खाता.

  4. हर उपलब्ध एक्ज़ीक्यूशन प्लैटफ़ॉर्म के लिए, हर टूलचेन टाइप को उस पहले उपलब्ध टूलचेन से जोड़ा जाता है जो इस प्रोग्राम और टारगेट प्लैटफ़ॉर्म के साथ काम करती हो.

  5. अगर कोई एक्ज़ीक्यूशन प्लैटफ़ॉर्म अपने किसी एक टूलचेन टाइप के लिए, साथ काम करने वाला ज़रूरी टूलचेन नहीं ढूंढ पाता, तो उसे लागू नहीं किया जाएगा. बाकी बचे प्लैटफ़ॉर्म में से पहला प्लैटफ़ॉर्म, मौजूदा टारगेट के लिए, लागू करने वाला प्लैटफ़ॉर्म बन जाता है. साथ ही, उससे जुड़े टूलचेन (अगर कोई है), टारगेट के लिए डिपेंडेंसी बन जाते हैं.

प्रोग्राम चलाने के लिए चुने गए प्लैटफ़ॉर्म का इस्तेमाल, टारगेट से जनरेट होने वाली सभी कार्रवाइयों को पूरा करने के लिए किया जाता है.

ऐसे मामलों में जहां एक ही टारगेट को एक ही बिल्ड के अंदर कई कॉन्फ़िगरेशन (जैसे कि अलग-अलग सीपीयू के लिए) में बनाया जा सकता है, वहां रिज़ॉल्यूशन प्रोसेस को टारगेट के हर वर्शन पर अलग-अलग लागू किया जाता है.

अगर नियम एक्ज़ीक्यूशन ग्रुप का इस्तेमाल करता है, तो हर एक्ज़ीक्यूशन ग्रुप, टूलचेन रिज़ॉल्यूशन को अलग-अलग परफ़ॉर्म करता है. साथ ही, हर ग्रुप का अपना एक्ज़ीक्यूशन प्लैटफ़ॉर्म और टूलचेन होता है.

टूलचेन को डीबग करना

अगर किसी मौजूदा नियम में टूलचेन की सुविधा जोड़ी जा रही है, तो --toolchain_resolution_debug=regex फ़्लैग का इस्तेमाल करें. टूलचेन रिज़ॉल्यूशन के दौरान फ़्लैग, रेगुलर एक्सप्रेशन वैरिएबल से मैच होने वाले टूलचेन टाइप या टारगेट नामों के लिए वर्बोज़ आउटपुट देता है. पूरी जानकारी देने के लिए, .* का इस्तेमाल किया जा सकता है. रिज़ॉल्यूशन प्रोसेस के दौरान, Bazel उन टूलचेन के नाम दिखाएगा जिनकी जांच करता है और जिन्हें स्किप करता है.

अगर आपको यह देखना है कि टूलचेन रिज़ॉल्यूशन से कौनसी cquery डिपेंडेंसी है, तो cquery के --transitions फ़्लैग का इस्तेमाल करें:

# Find all direct dependencies of //cc:my_cc_lib. This includes explicitly
# declared dependencies, implicit dependencies, and toolchain dependencies.
$ bazel cquery 'deps(//cc:my_cc_lib, 1)'
//cc:my_cc_lib (96d6638)
@bazel_tools//tools/cpp:toolchain (96d6638)
@bazel_tools//tools/def_parser:def_parser (HOST)
//cc:my_cc_dep (96d6638)
@local_config_platform//:host (96d6638)
@bazel_tools//tools/cpp:toolchain_type (96d6638)
//:default_host_platform (96d6638)
@local_config_cc//:cc-compiler-k8 (HOST)
//cc:my_cc_lib.cc (null)
@bazel_tools//tools/cpp:grep-includes (HOST)

# Which of these are from toolchain resolution?
$ bazel cquery 'deps(//cc:my_cc_lib, 1)' --transitions=lite | grep "toolchain dependency"
  [toolchain dependency]#@local_config_cc//:cc-compiler-k8#HostTransition -> b6df211