मॉड्यूल एक्सटेंशन की मदद से, उपयोगकर्ता मॉड्यूल सिस्टम को बढ़ा सकते हैं. इसके लिए, वे डिपेंडेंसी ग्राफ़ में मौजूद मॉड्यूल से इनपुट डेटा पढ़ते हैं, डिपेंडेंसी को हल करने के लिए ज़रूरी लॉजिक लागू करते हैं, और आखिर में, रिपॉज़िटरी के नियम कॉल करके रिपॉज़िटरी बनाते हैं. इन एक्सटेंशन में, रिपॉज़िटरी के नियमों जैसी ही क्षमताएं होती हैं. इनकी मदद से, फ़ाइल I/O किया जा सकता है, नेटवर्क के अनुरोध भेजे जा सकते हैं वगैरह. इनकी मदद से, Bazel अन्य पैकेज मैनेजमेंट सिस्टम के साथ इंटरैक्ट कर सकता है. साथ ही, Bazel मॉड्यूल से बने डिपेंडेंसी ग्राफ़ का भी पालन किया जा सकता है.
रिपॉज़िटरी के नियमों की तरह, मॉड्यूल एक्सटेंशन को .bzl फ़ाइलों में तय किया जा सकता है. इन्हें सीधे तौर पर कॉल नहीं किया जाता. इसके बजाय, हर मॉड्यूल, डेटा के ऐसे हिस्से तय करता है जिन्हें एक्सटेंशन टैग कहते हैं और जिन्हें एक्सटेंशन पढ़ सकते हैं. Bazel, किसी भी एक्सटेंशन का आकलन करने से पहले, मॉड्यूल रिज़ॉल्यूशन चलाता है. एक्सटेंशन, पूरे डिपेंडेंसी ग्राफ़ में मौजूद अपने सभी टैग पढ़ता है.
एक्सटेंशन का इस्तेमाल करना
एक्सटेंशन, Bazel मॉड्यूल में ही होस्ट किए जाते हैं. किसी
मॉड्यूल में एक्सटेंशन का इस्तेमाल करने के लिए, सबसे पहले उस मॉड्यूल में bazel_dep जोड़ें जो एक्सटेंशन को होस्ट करता है. इसके बाद,
उसे स्कोप में लाने के लिए, use_extension बिल्ट-इन फ़ंक्शन
को कॉल करें. यहां एक उदाहरण दिया गया है. यह
MODULE.bazel फ़ाइल का एक स्निपेट है. यह
rules_jvm_external
मॉड्यूल में तय किए गए "maven" एक्सटेंशन का इस्तेमाल करने के लिए है:
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` फ़ंक्शन का इस्तेमाल करके, मॉड्यूल एक्सटेंशन को रिपॉज़िटरी के नियमों की तरह ही तय किया जा सकता है.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 टैग तय किए जा सकते हैं.
मॉड्यूल एक्सटेंशन के लागू करने वाले फ़ंक्शन, रिपॉज़िटरी के नियमों के फ़ंक्शन की तरह ही होते हैं. हालांकि, इन्हें 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 मॉड्यूल की रिपॉज़िटरी, अपनी
MODULE.bazelफ़ाइल में जोड़ी गई सभी रिपॉज़िटरी कोbazel_depऔरuse_repoके ज़रिए देख सकती है. - मॉड्यूल एक्सटेंशन से जनरेट हुई रिपॉज़िटरी, उस मॉड्यूल को दिखने वाली सभी रिपॉज़िटरी को देख सकती है जो एक्सटेंशन को होस्ट करता है. इसके अलावा, उसी मॉड्यूल एक्सटेंशन से जनरेट हुई अन्य सभी रिपॉज़िटरी को भी देखा जा सकता है. इसके लिए, रिपॉज़िटरी के नियम को कॉल करने पर तय किए गए नामों को उनके असली नामों के तौर पर इस्तेमाल किया जाता है.
- इससे टकराव हो सकता है. अगर मॉड्यूल रिपॉज़िटरी,
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"],
)
सबसे सही तरीके
इस सेक्शन में, एक्सटेंशन लिखते समय सबसे सही तरीके बताए गए हैं, ताकि उन्हें आसानी से इस्तेमाल किया जा सके, बनाए रखा जा सके, और समय के साथ होने वाले बदलावों के हिसाब से अडजस्ट किया जा सके.
हर एक्सटेंशन को अलग फ़ाइल में रखें
एक्सटेंशन को अलग-अलग फ़ाइलों में रखने से, एक एक्सटेंशन, दूसरे एक्सटेंशन से जनरेट हुई रिपॉज़िटरी को लोड कर सकता है. भले ही, इस सुविधा का इस्तेमाल न किया जाए, लेकिन एक्सटेंशन को अलग-अलग फ़ाइलों में रखना बेहतर है, ताकि बाद में ज़रूरत पड़ने पर इसका इस्तेमाल किया जा सके. ऐसा इसलिए, क्योंकि एक्सटेंशन की पहचान उसकी फ़ाइल पर आधारित होती है. इसलिए, बाद में एक्सटेंशन को किसी दूसरी फ़ाइल में ले जाने से, आपका सार्वजनिक एपीआई बदल जाता है. साथ ही, यह आपके उपयोगकर्ताओं के लिए, पीछे की ओर काम न करने वाला बदलाव होता है.
फिर से बनाए जा सकने की जानकारी दें और फ़ैक्ट का इस्तेमाल करें
अगर आपका एक्सटेंशन, एक जैसे इनपुट
(एक्सटेंशन टैग, पढ़ी जाने वाली फ़ाइलें वगैरह) के लिए हमेशा एक जैसी रिपॉज़िटरी तय करता है और खास तौर पर,
ऐसे डाउनलोड पर निर्भर नहीं रहता जिन्हें
चेकसम से सुरक्षित नहीं किया गया है, तो
extension_metadata को
reproducible = True के साथ वापस करने पर विचार करें. इससे Bazel, MODULE.bazel लॉकफ़ाइल में लिखते समय, इस एक्सटेंशन को छोड़ सकता है. इससे लॉकफ़ाइल का साइज़ छोटा रहता है और मर्ज के टकराव की संभावना कम हो जाती है. ध्यान दें कि Bazel, फिर से बनाए जा सकने वाले एक्सटेंशन के नतीजों को इस तरह से कैश करता है कि वे सर्वर रीस्टार्ट होने पर भी बने रहें. इसलिए, लंबे समय तक चलने वाले एक्सटेंशन को भी, परफ़ॉर्मेंस पर असर डाले बिना, फिर से बनाया जा सकने वाला एक्सटेंशन के तौर पर मार्क किया जा सकता है.
अगर आपका एक्सटेंशन, बिल्ड के बाहर से मिले ऐसे डेटा पर निर्भर करता है जो बदला नहीं जा सकता, तो आम तौर पर यह डेटा नेटवर्क से मिलता है. हालांकि, आपके पास डाउनलोड को सुरक्षित करने के लिए चेकसम उपलब्ध नहीं है, तो facts पैरामीटर का इस्तेमाल करके, ऐसे डेटा को लगातार रिकॉर्ड करने पर विचार करें. इससे आपका एक्सटेंशन, फिर से बनाया जा सकने वाला एक्सटेंशन बन सकता है.extension_metadata facts एक डिक्शनरी होनी चाहिए, जिसमें स्ट्रिंग की कुंजियां और
JSON जैसे Starlark की वैल्यू हों. इसे हमेशा लॉकफ़ाइल में सेव किया जाता है. साथ ही,
facts फ़ील्ड के ज़रिए, एक्सटेंशन के आने वाले समय के आकलन के लिए उपलब्ध कराया जाता है.
module_ctx
facts को तब भी अमान्य नहीं किया जाता, जब आपके मॉड्यूल एक्सटेंशन का कोड बदल जाता है. इसलिए, facts के स्ट्रक्चर में बदलाव होने पर, उसे मैनेज करने के लिए तैयार रहें.
Bazel यह भी मानता है कि एक ही एक्सटेंशन के दो अलग-अलग आकलन से जनरेट हुई दो अलग-अलग facts डिक्ट को शैलो तरीके से मर्ज किया जा सकता है. इसका मतलब है कि दो डिक्ट पर | ऑपरेटर का इस्तेमाल करके मर्ज किया जा सकता है. module_ctx.facts की एंट्री की गिनती की सुविधा उपलब्ध न होने से, इसे आंशिक तौर पर लागू किया जाता है. इसमें सिर्फ़ कुंजी के हिसाब से लुकअप किया जा सकता है.
facts का इस्तेमाल करने का एक उदाहरण यह है कि किसी SDK के वर्शन नंबर से, उस वर्शन के डाउनलोड यूआरएल और चेकसम वाले ऑब्जेक्ट की मैपिंग को रिकॉर्ड किया जाए. एक्सटेंशन का आकलन पहली बार होने पर, यह मैपिंग नेटवर्क से फ़ेच की जा सकती है. हालांकि, बाद के आकलन में, नेटवर्क के अनुरोधों से बचने के लिए, facts से मैपिंग का इस्तेमाल किया जा सकता है.
अगर आपको वैल्यू के स्कीमा में, पीछे की ओर काम न करने वाला बदलाव करना है
जो आपका एक्सटेंशन facts में सेव करता है, तो
facts_version
पैरामीटर को module_extension पर उसकी पिछली वैल्यू से ज़्यादा किसी पूर्णांक पर सेट करें.
Bazel, लॉकफ़ाइल में फ़ैक्ट के साथ facts_version को सेव करता है. साथ ही, रिकॉर्ड किए गए वर्शन के मौजूदा वर्शन से अलग होने पर, सेव किए गए सभी फ़ैक्ट को खारिज कर देता है. इससे यह पक्का होता है कि आपका एक्सटेंशन, सिर्फ़ एक ही स्कीमा से जनरेट हुए फ़ैक्ट को देखे.
ऑपरेटिंग सिस्टम और आर्किटेक्चर पर निर्भरता की जानकारी देना
अगर आपका एक्सटेंशन, ऑपरेटिंग सिस्टम या उसके आर्किटेक्चर टाइप पर निर्भर करता है, तो पक्का करें कि इसकी जानकारी, एक्सटेंशन की परिभाषा में दी गई हो. इसके लिए, os_dependent और arch_dependent बूलियन एट्रिब्यूट का इस्तेमाल करें. इससे यह पक्का होता है कि इनमें से किसी में भी बदलाव होने पर, Bazel को फिर से आकलन करने की ज़रूरत का पता चले.
सिर्फ़ रूट मॉड्यूल को सीधे तौर पर रिपॉज़िटरी के नामों पर असर डालना चाहिए
ध्यान रखें कि जब कोई एक्सटेंशन रिपॉज़िटरी बनाता है, तो वे एक्सटेंशन के नेमस्पेस में बनती हैं. इसका मतलब है कि अगर अलग-अलग मॉड्यूल, एक ही एक्सटेंशन का इस्तेमाल करते हैं और एक ही नाम से रिपॉज़िटरी बनाते हैं, तो टकराव हो सकता है. आम तौर पर, यह मॉड्यूल एक्सटेंशन की tag_class में name आर्ग्युमेंट के तौर पर दिखता है, जिसे रिपॉज़िटरी के नियम की name वैल्यू के तौर पर पास किया जाता है.
उदाहरण के लिए, मान लें कि रूट मॉड्यूल, A, मॉड्यूल B पर निर्भर है. दोनों मॉड्यूल, mylang मॉड्यूल पर निर्भर हैं. अगर A और B दोनों,
mylang.toolchain(name="foo") को कॉल करते हैं, तो दोनों mylang मॉड्यूल में
foo नाम की रिपॉज़िटरी बनाने की कोशिश करेंगे. इससे गड़बड़ी होगी.
इससे बचने के लिए, सीधे तौर पर रिपॉज़िटरी का नाम सेट करने की सुविधा हटाएं या सिर्फ़ रूट मॉड्यूल को ऐसा करने की अनुमति दें. रूट मॉड्यूल को यह सुविधा देने में कोई समस्या नहीं है, क्योंकि इस पर कुछ भी निर्भर नहीं होगा. इसलिए, उसे किसी दूसरे मॉड्यूल के टकराव वाले नाम बनाने की चिंता नहीं करनी होगी.