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

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

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

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

एक्सटेंशन का इस्तेमाल

एक्सटेंशन, Bazel मॉड्यूल में होस्ट किए जाते हैं. किसी मॉड्यूल में एक्सटेंशन का इस्तेमाल करने के लिए, सबसे पहले उस मॉड्यूल में bazel_dep जोड़ें जिसमें एक्सटेंशन होस्ट किया जा रहा है. इसके बाद, उसे स्कोप में लाने के लिए, पहले से मौजूद use_extension फ़ंक्शन को कॉल करें. इस उदाहरण पर विचार करें — rules_jvm_external मॉड्यूल में तय किए गए "मेवेन" एक्सटेंशन का इस्तेमाल करने के लिए, 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" एक्सटेंशन से जनरेट किए गए repo पर ले जाया जा सके.

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

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

# @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)

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

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

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

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

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

मॉड्यूल एक्सटेंशन रिपोज़ को बदलना और इंजेक्ट करना

मॉड्यूल एक्सटेंशन रिपोज़ को बदलने या इंजेक्ट करने के लिए, रूट मॉड्यूल override_repo और inject_repo का इस्तेमाल कर सकता है.

उदाहरण: rules_java के java_tools को वेंडर की कॉपी से बदलना

# MODULE.bazel
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
local_repository(
  name = "my_java_tools",
  path = "vendor/java_tools",
)

bazel_dep(name = "rules_java", version = "7.11.1")
java_toolchains = use_extension("@rules_java//java:extension.bzl", "toolchains")

override_repo(java_toolchains, remote_java_tools = "my_java_tools")

उदाहरण: सिस्टम zlib के बजाय @zlib पर निर्भर रहने के लिए Go डिपेंडेंसी को पैच करें

# MODULE.bazel
bazel_dep(name = "gazelle", version = "0.38.0")
bazel_dep(name = "zlib", version = "1.3.1.bcr.3")

go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
go_deps.module_override(
  patches = [
    "//patches:my_module_zlib.patch",
  ],
  path = "example.com/my_module",
)
use_repo(go_deps, ...)

inject_repo(go_deps, "zlib")
# patches/my_module_zlib.patch
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,6 +1,6 @@
 go_binary(
     name = "my_module",
     importpath = "example.com/my_module",
     srcs = ["my_module.go"],
-    copts = ["-lz"],
+    cdeps = ["@zlib"],
 )

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

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

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

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

दोहराए जाने की जानकारी देना

अगर आपका एक्सटेंशन, एक जैसे इनपुट (एक्सटेंशन टैग, पढ़ी जाने वाली फ़ाइलें वगैरह) के लिए हमेशा एक ही रिपॉज़िटरी तय करता है और खास तौर पर, ऐसे किसी भी डाउनलोड पर भरोसा नहीं करता जिसे चेकसम से सुरक्षित नहीं किया गया है, तो reproducible = True के साथ extension_metadata दिखाएं. इससे Bazel, लॉकफ़ाइल में लिखते समय इस एक्सटेंशन को छोड़ सकता है.

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

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

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

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

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

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

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