मॉड्यूल एक्सटेंशन की मदद से उपयोगकर्ता, मॉड्यूल सिस्टम को बढ़ा सकते हैं. इसके लिए, वे डिपेंडेंसी ग्राफ़ में मौजूद मॉड्यूल से इनपुट डेटा पढ़ते हैं, डिपेंडेंसी को हल करने के लिए ज़रूरी लॉजिक लागू करते हैं, और आखिर में repo rules को कॉल करके रिपॉज़िटरी बनाते हैं. इन एक्सटेंशन में, रिपॉज़िटरी के नियमों जैसी सुविधाएं होती हैं. इनकी मदद से, फ़ाइल 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 फ़ंक्शन का इस्तेमाल करके, मॉड्यूल एक्सटेंशन को रिपॉज़िटरी के नियमों की तरह ही तय किया जा सकता है. हालांकि,
रिपो के नियमों में कई एट्रिब्यूट होते हैं, जबकि मॉड्यूल एक्सटेंशन में 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का मतलब पहली रिपो से होगा.
- इससे टकराव हो सकता है. अगर मॉड्यूल रिपो को
- इसी तरह, मॉड्यूल एक्सटेंशन के लागू करने वाले फ़ंक्शन में, एक्सटेंशन से बनाए गए रेपो, एट्रिब्यूट में अपने नाम से एक-दूसरे को रेफ़र कर सकते हैं. इससे कोई फ़र्क़ नहीं पड़ता कि उन्हें किस क्रम में बनाया गया है.
- अगर मॉड्यूल को दिखने वाली किसी रिपॉज़िटरी से कोई टकराव होता है, तो रिपॉज़िटरी के नियम वाले एट्रिब्यूट को पास किए गए लेबल को
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 को लॉकफ़ाइल में लिखते समय, इस एक्सटेंशन को छोड़ने की अनुमति मिलती है.
ऑपरेटिंग सिस्टम और आर्किटेक्चर के बारे में जानकारी देना
अगर आपका एक्सटेंशन, ऑपरेटिंग सिस्टम या उसके आर्किटेक्चर टाइप पर निर्भर करता है, तो os_dependent और arch_dependent बूलियन एट्रिब्यूट का इस्तेमाल करके, एक्सटेंशन की परिभाषा में इसकी जानकारी ज़रूर दें. इससे यह पक्का होता है कि अगर इनमें से किसी में भी बदलाव होता है, तो Bazel को दोबारा आकलन करने की ज़रूरत है.
इस तरह से होस्ट पर निर्भर रहने की वजह से, इस एक्सटेंशन के लिए लॉकफ़ाइल एंट्री को बनाए रखना ज़्यादा मुश्किल हो जाता है. इसलिए, अगर हो सके, तो एक्सटेंशन को फिर से बनाया जा सकने वाला एक्सटेंशन के तौर पर मार्क करें.
सिर्फ़ रूट मॉड्यूल से, सीधे तौर पर रिपॉज़िटरी के नामों पर असर पड़ना चाहिए
ध्यान रखें कि जब कोई एक्सटेंशन रिपॉज़िटरी बनाता है, तो वे एक्सटेंशन के नेमस्पेस में बनाई जाती हैं. इसका मतलब है कि अगर अलग-अलग मॉड्यूल एक ही एक्सटेंशन का इस्तेमाल करते हैं और एक ही नाम वाली रिपॉज़िटरी बनाते हैं, तो टकराव हो सकता है. ऐसा अक्सर तब होता है, जब मॉड्यूल एक्सटेंशन के tag_class में ऐसा name आर्ग्युमेंट होता है जिसे रिपॉज़िटरी के नियम की name वैल्यू के तौर पर पास किया जाता है.
उदाहरण के लिए, मान लें कि रूट मॉड्यूल A, मॉड्यूल B पर निर्भर करता है. दोनों मॉड्यूल, mylang मॉड्यूल पर निर्भर हैं. अगर A और B, दोनों mylang.toolchain(name="foo") को कॉल करते हैं, तो दोनों mylang मॉड्यूल में foo नाम की एक रिपॉज़िटरी बनाने की कोशिश करेंगे. इससे गड़बड़ी होगी.
इससे बचने के लिए, सीधे तौर पर रिपॉज़िटरी का नाम सेट करने की सुविधा हटा दें या सिर्फ़ रूट मॉड्यूल को ऐसा करने की अनुमति दें. रूट मॉड्यूल को यह सुविधा देने में कोई समस्या नहीं है, क्योंकि कुछ भी इस पर निर्भर नहीं करेगा. इसलिए, इसे किसी दूसरे मॉड्यूल के नाम से टकराव होने की चिंता नहीं करनी होगी.