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