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

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

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",
)

staging टारगेट पर bazel build का इस्तेमाल करने पर, k8s_object नियम एक स्टैंडर्ड 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 टारगेट भी उपलब्ध कराता है, जो गोल्डन फ़ाइल की तुलना करने के बजाय उसे अपडेट करता है. असल में, एक ही टेस्ट स्क्रिप्ट को "पुष्टि करें" या "स्वीकार करें" मोड में चलाने के लिए लिखा जा सकता है. यह इस बात पर निर्भर करता है कि इसे कैसे शुरू किया गया है. यह उसी पैटर्न का पालन करता है जिसे आपने पहले ही सीख लिया है: इसमें कोई नेटिव 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 के आधार पर, एक बहुत ही आसान Bash स्क्रिप्ट लिखता है, जो मुख्य टारगेट के आउटपुट के साथ kubectl चलाता है. इसके बाद, इस स्क्रिप्ट को bazel run :staging.apply के साथ बनाया और चलाया जा सकता है. इससे k8s_object टारगेट के लिए k8s-apply निर्देश असरदार तरीके से मिलता है.