कस्टम क्रिया बनाने के लिए मैक्रो का उपयोग करना

Bazel के साथ रोज़ाना इंटरैक्ट करने के लिए, मुख्य तौर पर इन कमांड का इस्तेमाल किया जाता है: build, test, और run. हालांकि, कभी-कभी ये कमांड सीमित लग सकती हैं. जैसे, हो सकता है कि आपको पैकेज को किसी रिपॉज़िटरी में पुश करना हो, एंड-यूज़र के लिए दस्तावेज़ पब्लिश करने हों या Kubernetes की मदद से कोई ऐप्लिकेशन डिप्लॉय करना हो. लेकिन Bazel में publish या deploy कमांड नहीं होती. ऐसे में, ये कार्रवाइयां कैसे की जा सकती हैं?

`bazel run` कमांड

Bazel, हर्मेटिसिटी, रीप्रोड्यूसिबिलिटी, और इंक्रीमेंटैलिटी पर फ़ोकस करता है. इसलिए, ऊपर दिए गए टास्क के लिए build और test कमांड काम की नहीं हैं. ये कार्रवाइयां सैंडबॉक्स में, सीमित नेटवर्क ऐक्सेस के साथ की जा सकती हैं. साथ ही, यह ज़रूरी नहीं है कि bazel build के साथ इन्हें फिर से चलाया जाए.

इसके बजाय, bazel run का इस्तेमाल करें. यह उन टास्क के लिए काम की है जिनके साइड इफ़ेक्ट हो सकते हैं. Bazel के उपयोगकर्ता, एक्ज़ीक्यूटेबल बनाने वाले नियमों के आदी हैं. साथ ही, नियम के लेखक, "कस्टम वर्ब" के लिए, पैटर्न के एक सामान्य सेट का इस्तेमाल कर सकते हैं.

असल में इस्तेमाल: rules_k8s

उदाहरण के लिए, rules_k8s, Bazel के लिए Kubernetes के नियम देखें. मान लें कि आपके पास यह टारगेट है:

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

k8s_object नियम staging टारगेट पर bazel build का इस्तेमाल करने पर, स्टैंडर्ड Kubernetes YAML फ़ाइल बनाता है. हालांकि, k8s_object मैक्रो से, staging.apply और :staging.delete जैसे नाम वाले अतिरिक्त टारगेट भी बनाए जाते हैं. ये टारगेट, उन कार्रवाइयों को करने के लिए स्क्रिप्ट बनाते हैं. साथ ही, bazel run staging.apply के साथ एक्ज़ीक्यूट करने पर, ये हमारी bazel k8s-apply या bazel k8s-delete कमांड की तरह काम करते हैं.

एक और उदाहरण: ts_api_guardian_test

यह पैटर्न, Angular प्रोजेक्ट में भी देखा जा सकता है. ts_api_guardian_test मैक्रो दो टारगेट बनाता है. पहला टारगेट, स्टैंडर्ड nodejs_test टारगेट है. यह जनरेट किए गए कुछ आउटपुट की तुलना "गोल्डन" फ़ाइल (यानी, अनुमानित आउटपुट वाली फ़ाइल) से करता है. इसे bazel test के सामान्य इनवोकेशन से बनाया और चलाया जा सकता है. angular-cli में, ऐसा कोई टारगेट bazel test //etc/api:angular_devkit_core_api की मदद से चलाया जा सकता है.

समय के साथ, सही वजहों से इस गोल्डन फ़ाइल को अपडेट करना पड़ सकता है. इसे मैन्युअल तरीके से अपडेट करना मुश्किल और गड़बड़ी वाला हो सकता है. इसलिए, यह मैक्रो एक nodejs_binary टारगेट भी उपलब्ध कराता है. यह टारगेट, गोल्डन फ़ाइल की तुलना करने के बजाय, उसे अपडेट करता है. असल में, एक ही टेस्ट स्क्रिप्ट को "verify" या "accept" मोड में चलाया जा सकता है. यह इस बात पर निर्भर करता है कि इसे कैसे इनवोक किया गया है. यह उसी पैटर्न पर काम करता है जिसके बारे में आपने पहले ही जान लिया है. इसमें, bazel test-accept कमांड नहीं होती. हालांकि, bazel run //etc/api:angular_devkit_core_api.accept की मदद से, वही काम किया जा सकता है.

यह पैटर्न काफ़ी काम का हो सकता है. साथ ही, इसे पहचानना सीख लेने के बाद, यह काफ़ी आम हो जाता है.

अपने नियमों को अडैप्ट करना

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

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

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

इसके बाद, इस तरह का कोई नियम देखें. यह नियम, एक स्क्रिप्ट बनाता है. इस स्क्रिप्ट को चलाने पर, जनरेट किए गए पेज पब्लिश हो जाते हैं:

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

आखिर में, ऊपर दिए गए दोनों नियमों के लिए टारगेट बनाने के लिए, यह सिंबॉलिक मैक्रो (Bazel 8 या इसके बाद के वर्शन में उपलब्ध) तय करें:

def _sphinx_site_impl(name, visibility, srcs, **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML. We
    # set `visibility = visibility` to make it visible to callers of the
    # macro.
    _sphinx_site(name = name, visibility = visibility, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above. We don't want it to be visible to callers of
    # our macro, so we omit visibility for it.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

sphinx_site = macro(
    implementation = _sphinx_site_impl,
    attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
    # Inherit common attributes like tags and testonly
    inherit_attrs = "common",
)

इसके अलावा, अगर आपको Bazel 8 से पुराने वर्शन के लिए सहायता देनी है, तो इसके बजाय, लेगसी मैक्रो तय करें:

def sphinx_site(name, srcs = [], **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML.
    _sphinx_site(name = name, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

BUILD फ़ाइलों में, मैक्रो का इस्तेमाल ऐसे करें जैसे कि यह सिर्फ़ मुख्य टारगेट बनाता है:

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

इस उदाहरण में, "docs" टारगेट बनाया गया है. यह ठीक वैसा ही है जैसे कि मैक्रो, Bazel का एक स्टैंडर्ड नियम हो. बिल्ड करने पर, यह नियम कुछ कॉन्फ़िगरेशन जनरेट करता है और Sphinx को चलाकर, एचटीएमएल साइट बनाता है. यह साइट, मैन्युअल तरीके से जांच के लिए तैयार होती है. हालांकि, "docs.publish" नाम का एक अतिरिक्त टारगेट भी बनाया जाता है. यह टारगेट, साइट को पब्लिश करने के लिए एक स्क्रिप्ट बनाता है. मुख्य टारगेट के आउटपुट की जांच करने के बाद, bazel run :docs.publish का इस्तेमाल करके, इसे सार्वजनिक तौर पर इस्तेमाल के लिए पब्लिश किया जा सकता है. यह ठीक वैसा ही है जैसे कि bazel publish कमांड का इस्तेमाल किया जाता है.

_sphinx_publisher नियम का लागू करने का तरीका तुरंत समझ में नहीं आता. अक्सर, इस तरह की कार्रवाइयों से लॉन्चर शेल स्क्रिप्ट लिखी जाती है. इस तरीके में आम तौर पर, ctx.actions.expand_template का इस्तेमाल करके, एक बहुत ही आसान शेल स्क्रिप्ट लिखी जाती है. इस मामले में, मुख्य टारगेट के आउटपुट के पाथ के साथ, पब्लिशर बाइनरी को इनवोक किया जाता है. इस तरह, पब्लिशर को लागू करने का तरीका सामान्य बना रहता है. _sphinx_site नियम सिर्फ़ एचटीएमएल जनरेट कर सकता है. साथ ही, इन दोनों को एक साथ इस्तेमाल करने के लिए, इस छोटी स्क्रिप्ट की ज़रूरत होती है.

rules_k8s में, .apply भी यही काम करता है: expand_template apply.sh.tpl के आधार पर, एक बहुत ही आसान बैश स्क्रिप्ट लिखता है. यह स्क्रिप्ट, मुख्य टारगेट के आउटपुट के साथ kubectl को चलाती है. इसके बाद, इस स्क्रिप्ट को bazel run :staging.apply की मदद से बनाया और चलाया जा सकता है. इससे k8s_object टारगेट के लिए, k8s-apply कमांड मिलती है.