टूलचेन

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

वजह

सबसे पहले, उस समस्या के बारे में जानते हैं जिसे हल करने के लिए टूलचेन बनाए गए हैं. मान लें कि "बार" प्रोग्रामिंग भाषा के लिए नियम लिखे जा रहे हैं. आपका 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",
)

आखिर में, दो 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 तय नहीं करती, क्योंकि "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.bazelfiles. Additional execution platforms and toolchains may also be specified on the command line via [--extra_execution_platforms](/versions/7.5.0/reference/command-line-reference#flag--extra_execution_platforms) and [--extra_toolchains`](/versions/7.5.0/reference/command-line-reference#flag--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. हर उपलब्ध एक्ज़ीक्यूशन प्लैटफ़ॉर्म के लिए, हर टूलचेन टाइप को पहले उपलब्ध टूलचेन से जोड़ा जाता है. ऐसा तब किया जाता है, जब वह टूलचेन, इस एक्ज़ीक्यूशन प्लैटफ़ॉर्म और टारगेट प्लैटफ़ॉर्म के साथ काम करता हो.

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

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

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

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

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

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

अगर आपको यह देखना है कि 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