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

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

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

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

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

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

  • बेज़ल मॉड्यूल रेपो, 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"],
 )

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

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

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

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

रीप्रॉड्यूसिबिलिटी के बारे में बताएं

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

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

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

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

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

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

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

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