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

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

मॉड्यूल एक्सटेंशन की मदद से उपयोगकर्ता, डिपेंडेंसी ग्राफ़ में मौजूद मॉड्यूल से इनपुट डेटा पढ़कर, डिपेंडेंसी को ठीक करने के लिए ज़रूरी लॉजिक का इस्तेमाल करके, और आखिर में रेपो नियमों को कॉल करके रिपॉज़िटरी बनाने की सुविधा का इस्तेमाल कर सकते हैं. इन एक्सटेंशन में रेपो नियमों जैसी क्षमताएं होती हैं, जो उन्हें फ़ाइल 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_class होते हैं. हर एक में कई एट्रिब्यूट होते हैं. टैग क्लास, इस एक्सटेंशन में इस्तेमाल किए जाने वाले टैग के लिए स्कीमा तय करती हैं. उदाहरण के लिए, ऊपर दिए गए "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 टैग के बारे में बताया जा सकता है.

मॉड्यूल एक्सटेंशन को लागू करने का काम रेपो नियमों की तरह ही होता है. हालांकि, इसमें एक 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)

एक्सटेंशन आइडेंटिटी

मॉड्यूल एक्सटेंशन की पहचान, use_extension को किए गए कॉल में दिखने वाली .bzl फ़ाइल और नाम से की जाती है. यहां दिए गए उदाहरण में, एक्सटेंशन 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 से बदल दिया जाता है. ध्यान दें कि कैननिकल नाम फ़ॉर्मैट वह एपीआई नहीं है जिस पर आपको भरोसा करना चाहिए — इसे किसी भी समय बदला जा सकता है.

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

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

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

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

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

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

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

ऑपरेटिंग सिस्टम और आर्किटेक्चर की जानकारी दें

अगर आपका एक्सटेंशन, ऑपरेटिंग सिस्टम या उसके आर्किटेक्चर टाइप पर निर्भर है, तो os_dependent और arch_dependent बूलियन एट्रिब्यूट का इस्तेमाल करके, एक्सटेंशन की परिभाषा में इसकी जानकारी देना न भूलें. इससे यह पक्का होता है कि अगर दोनों में से किसी में भी बदलाव होता है, तो बैजल को फिर से आकलन करने की ज़रूरत होगी.

सिर्फ़ रूट मॉड्यूल को डेटा स्टोर करने की जगह के नामों पर सीधे तौर पर असर डालना चाहिए

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

उदाहरण के लिए, मान लें कि रूट मॉड्यूल A, मॉड्यूल B पर निर्भर करता है. दोनों मॉड्यूल mylang मॉड्यूल पर निर्भर करते हैं. अगर A और B, दोनों mylang.toolchain(name="foo") को कॉल करते हैं, तो वे दोनों mylang मॉड्यूल में, foo नाम का डेटा स्टोर करने की कोशिश करेंगे और एक गड़बड़ी दिखेगी.

इससे बचने के लिए, या तो डेटा स्टोर करने की जगह के नाम को सीधे सेट करने की सुविधा हटाएं या रूट मॉड्यूल को ऐसा करने की अनुमति दें. रूट मॉड्यूल को इस क्षमता की अनुमति देना ठीक है क्योंकि इस पर कुछ भी निर्भर नहीं करेगा, इसलिए इसे विरोधी नाम बनाने के बारे में चिंता करने की ज़रूरत नहीं है.