टूलचेन

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

The 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_type से सिर्फ़ उस toolchain इंस्टेंस में जोड़ा जाता है जो इस _toolchain नियम का इस्तेमाल करता है.

हमारे उदाहरण के लिए, यहां 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 और टूलचेन टाइप के लेबल का इस्तेमाल करके वापस पाता है. ToolchainInfo, struct की तरह, फ़ील्ड-वैल्यू के जोड़े को सेव कर सकता है. 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",
)

आखिर में, toolchain के दो bar_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 तय नहीं करती, क्योंकि "target" डिफ़ॉल्ट होता है), उसे पैरंट के जैसे ही टारगेट प्लैटफ़ॉर्म के लिए बनाया जाता है. इससे, टूलचेन के नियम, बिल्ड के उन नियमों में लाइब्रेरी (system_lib एट्रिब्यूट) और टूल (compiler एट्रिब्यूट) दोनों जोड़ सकते हैं जिन्हें इनकी ज़रूरत होती है. सिस्टम लाइब्रेरी को फ़ाइनल आर्टफ़ैक्ट में लिंक किया जाता है. इसलिए, इन्हें एक ही प्लैटफ़ॉर्म के लिए बनाया जाना चाहिए. वहीं, कंपाइलर एक ऐसा टूल है जिसे बिल्ड के दौरान इस्तेमाल किया जाता है. इसलिए, यह एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चल सकता है.

टूलचेन रजिस्टर करना और उनकी मदद से बिल्ड करना

इस समय, सभी बिल्डिंग ब्लॉक इकट्ठा हो गए हैं. अब आपको सिर्फ़ Bazel की रिज़ॉल्यूशन प्रोसेस के लिए टूलचेन उपलब्ध कराने हैं. इसके लिए, टूलचेन को रजिस्टर करें. इसके लिए, MODULE.bazel फ़ाइल में register_toolchains() का इस्तेमाल करें या --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:toolchain_type रेफ़रंस को //bar_tools:barc_linux_toolchain पर रिज़ॉल्व किया जाएगा. इससे //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. "WORKSPACE सफ़िक्स" में रजिस्टर किए गए टूलचेन. इसका इस्तेमाल सिर्फ़ Bazel इंस्टॉलेशन के साथ बंडल किए गए कुछ नेटिव नियमों से किया जाता है.

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

रिज़ॉल्यूशन के चरण यहां दिए गए हैं.

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

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

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

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

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

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

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

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

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

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

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

उदाहरण के लिए, //my:target से सीधे तौर पर बनाए गए सभी ऐक्शन के लिए, टूलचेन के चुनाव को डीबग करने के लिए:

$ bazel build //my:all --toolchain_resolution_debug=//my:target

सभी बिल्ड टारगेट के सभी ऐक्शन के लिए, टूलचेन के चुनाव को डीबग करने के लिए:

$ bazel build //my:all --toolchain_resolution_debug=.*

अगर आपको यह देखना है कि cquery की कौनसी डिपेंडेंसी, टूलचेन रिज़ॉल्यूशन से हैं, तो cquery's --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