टूलचेन

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

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

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

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

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 और टूलचेन टाइप के लेबल का इस्तेमाल करके वापस पाता है. 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 एट्रिब्यूट) और टूल (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:toolchain_type के रेफ़रंस को //bar_tools:barc_linux_toolchain में बदल देगा. इससे //bar_tools:barc_linux बन जाएगा, लेकिन //bar_tools:barc_windows नहीं.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

उदाहरण के लिए, //my:target से सीधे तौर पर बनाई गई सभी कार्रवाइयों के लिए, टूलचेन चुनने की प्रोसेस में गड़बड़ी ठीक करने के लिए:

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

सभी बिल्ड टारगेट पर की गई सभी कार्रवाइयों के लिए, टूलचेन के चुनाव को डीबग करने के लिए:

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

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