मॉड्यूल एक्सटेंशन

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

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

एक्सटेंशन के इस्तेमाल से जुड़ी जानकारी

एक्सटेंशन, Bazel मॉड्यूल में ही होस्ट किए जाते हैं. किसी मॉड्यूल में एक्सटेंशन का इस्तेमाल करने के लिए, सबसे पहले एक्सटेंशन को होस्ट करने वाले मॉड्यूल में bazel_dep जोड़ें. इसके बाद, इसे स्कोप में लाने के लिए, use_extension बिल्ट-इन फ़ंक्शन को कॉल करें. यहां दिए गए उदाहरण पर ध्यान दें. यह rules_jvm_external मॉड्यूल में तय किए गए "maven" एक्सटेंशन का इस्तेमाल करने के लिए, MODULE.bazel फ़ाइल का एक स्निपेट है:

bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

इससे use_extension की रिटर्न वैल्यू को किसी वैरिएबल से बाइंड किया जाता है. इससे उपयोगकर्ता, एक्सटेंशन के लिए टैग तय करने के लिए डॉट-सिंटैक्स का इस्तेमाल कर सकता है. टैग, एक्सटेंशन की परिभाषा में दिए गए, टैग क्लास के तय किए गए स्कीमा के मुताबिक होने चाहिए. maven.install और maven.artifact टैग के बारे में बताने वाले उदाहरण के लिए:

maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
               artifact = "guava",
               version = "27.0-jre",
               exclusions = ["com.google.j2objc:j2objc-annotations"])

एक्सटेंशन से जनरेट की गई रिपॉज़िटरी को मौजूदा मॉड्यूल के स्कोप में लाने के लिए, use_repo डायरेक्टिव का इस्तेमाल करें.

use_repo(maven, "maven")

किसी एक्सटेंशन से जनरेट किए गए रेपो, उसके एपीआई का हिस्सा होते हैं. इस उदाहरण में, "maven" मॉड्यूल एक्सटेंशन, maven नाम की एक रेपो जनरेट करने का वादा करता है. ऊपर दिए गए एलान की मदद से, एक्सटेंशन @maven//:org_junit_junit जैसे लेबल को सही तरीके से हल करता है, ताकि "maven" एक्सटेंशन से जनरेट किए गए रेपो की ओर इशारा किया जा सके.

एक्सटेंशन की परिभाषा

module_extension फ़ंक्शन का इस्तेमाल करके, मॉड्यूल एक्सटेंशन को रिपो के नियमों की तरह ही तय किया जा सकता है. हालांकि, रिपो के नियमों में कई एट्रिब्यूट होते हैं, जबकि मॉड्यूल एक्सटेंशन में tag_classes होते हैं. इनमें से हर एक में कई एट्रिब्यूट होते हैं. टैग क्लास, इस एक्सटेंशन के ज़रिए इस्तेमाल किए जाने वाले टैग के स्कीमा तय करती हैं. उदाहरण के लिए, ऊपर दिए गए "maven" एक्सटेंशन को इस तरह से तय किया जा सकता है:

# @rules_jvm_external//:extensions.bzl

_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
  implementation = _maven_impl,
  tag_classes = {"install": _install, "artifact": _artifact},
)

इन एलान से पता चलता है कि maven.install और maven.artifact टैग को, तय किए गए एट्रिब्यूट स्कीमा का इस्तेमाल करके तय किया जा सकता है.

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

# @rules_jvm_external//:extensions.bzl

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")  # a repo rule
def _maven_impl(ctx):
  # This is a fake implementation for demonstration purposes only

  # collect artifacts from across the dependency graph
  artifacts = []
  for mod in ctx.modules:
    for install in mod.tags.install:
      artifacts += install.artifacts
    artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]

  # call out to the coursier CLI tool to resolve dependencies
  output = ctx.execute(["coursier", "resolve", artifacts])
  repo_attrs = _process_coursier_output(output)

  # call repo rules to generate repos
  for attrs in repo_attrs:
    http_file(**attrs)
  _generate_hub_repo(name = "maven", repo_attrs)

एक्सटेंशन की पहचान

मॉड्यूल एक्सटेंशन की पहचान, नाम और .bzl फ़ाइल से होती है. यह फ़ाइल, use_extension को किए गए कॉल में दिखती है. यहां दिए गए उदाहरण में, एक्सटेंशन maven की पहचान .bzl फ़ाइल @rules_jvm_external//:extension.bzl और नाम maven से की गई है:

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

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

एक्सटेंशन के लेखक के तौर पर, आपको यह पक्का करना चाहिए कि उपयोगकर्ता सिर्फ़ एक .bzl फ़ाइल से आपके मॉड्यूल एक्सटेंशन का इस्तेमाल करें.

रिपॉज़िटरी के नाम और विज़िबिलिटी

एक्सटेंशन से जनरेट किए गए रिपो के कैननिकल नाम, module_repo_canonical_name~extension_name~repo_name के फ़ॉर्म में होते हैं. रूट मॉड्यूल में होस्ट किए गए एक्सटेंशन के लिए, module_repo_canonical_name हिस्से को _main स्ट्रिंग से बदल दिया जाता है. ध्यान दें कि कैननिकल नाम का फ़ॉर्मैट ऐसा एपीआई नहीं है जिस पर आपको भरोसा करना चाहिए. इसमें कभी भी बदलाव हो सकता है.

नाम रखने से जुड़ी इस नीति का मतलब है कि हर एक्सटेंशन का अपना "repo namespace" होता है. दो अलग-अलग एक्सटेंशन, एक ही नाम से repo तय कर सकते हैं. इससे कोई टकराव नहीं होगा. इसका यह भी मतलब है कि repository_ctx.name, रिपो का कैननिकल नाम रिपोर्ट करता है. यह रिपो के नियम को कॉल करने के लिए तय किए गए नाम से अलग होता है.

मॉड्यूल एक्सटेंशन से जनरेट की गई रीपो को ध्यान में रखते हुए, रीपो दिखने से जुड़े कई नियम हैं:

  • Bazel मॉड्यूल रेपो, bazel_dep और use_repo के ज़रिए, अपनी MODULE.bazel फ़ाइल में शामिल किए गए सभी रेपो देख सकता है.
  • मॉड्यूल एक्सटेंशन से जनरेट की गई रिपो, एक्सटेंशन को होस्ट करने वाले मॉड्यूल को दिखने वाली सभी रिपो देख सकती है. इसके अलावा, वह उसी मॉड्यूल एक्सटेंशन से जनरेट की गई अन्य सभी रिपो भी देख सकती है. इन रिपो के नाम, रिपो के नियम के कॉल में बताए गए नामों के हिसाब से होते हैं.
    • इस वजह से, टकराव हो सकता है. अगर मॉड्यूल रिपो को foo नाम वाली कोई रिपो दिखती है और एक्सटेंशन foo नाम वाली कोई रिपो जनरेट करता है, तो उस एक्सटेंशन से जनरेट की गई सभी रिपो के लिए, foo का मतलब पहली रिपो से होगा.

सबसे सही तरीके

इस सेक्शन में, एक्सटेंशन लिखने के सबसे सही तरीके बताए गए हैं, ताकि उन्हें आसानी से इस्तेमाल किया जा सके, बनाए रखा जा सके, और समय के साथ होने वाले बदलावों के हिसाब से बेहतर बनाया जा सके.

हर एक्सटेंशन को अलग फ़ाइल में रखें

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