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

समस्या की शिकायत करें सोर्स देखें

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

bazel रन कमांड

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

इसके बजाय, bazel run पर भरोसा करें: यह वह टास्क है जिसका खराब असर आप चाहते हैं. Bazel के उपयोगकर्ता, एक्ज़ीक्यूटेबल बनाने के नियमों को समझते हैं. नियम बनाने वाले लोग, पैटर्न के सामान्य सेट का पालन करके इसे "कस्टम क्रिया" तक बढ़ा सकते हैं.

जंगल में: नियम_k8s

उदाहरण के लिए, rules_k8s पर विचार करें, जिसमें Bazel के लिए Cubernetes नियम हैं. मान लें कि आपके पास नीचे दिया गया टारगेट है:

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

अगर staging टारगेट पर bazel build का इस्तेमाल किया जाता है, तो k8s_object नियम एक स्टैंडर्ड Cubernetes YAML फ़ाइल बनाता है. हालांकि, staging.apply और :staging.delete जैसे नामों के साथ k8s_object मैक्रो भी अतिरिक्त टारगेट बनाते हैं. ये कार्रवाइयां करने के लिए स्क्रिप्ट बनाती हैं. 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 के साथ, मैक्रो के साथ वेबसाइट जनरेट करता है. इससे एक और टारगेट बनाने के लिए, उपयोगकर्ता उसे तैयार होने पर पब्लिश कर सकता है. Sling की मदद से वेबसाइट जनरेट करने के लिए, यहां दिए गए मौजूदा नियम पर विचार करें:

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

आखिर में, ऊपर दिए गए दोनों नियमों के लिए एक साथ टारगेट बनाने के लिए नीचे दिया गया मैक्रो तय करें:

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" टारगेट बनाया गया है, ठीक उसी तरह जैसे कि मैक्रो एक मानक, सिंगल बैजल नियम था. बनाए जाने के बाद, नियम कुछ कॉन्फ़िगरेशन जनरेट करता है और Sphinx को एक एचटीएमएल साइट बनाने के लिए चलाता है, जो मैन्युअल जांच के लिए तैयार है. हालांकि, एक और "docs.publish" टारगेट भी बनाया जाता है, जो साइट को पब्लिश करने के लिए एक स्क्रिप्ट बनाता है. प्राइमरी टारगेट के आउटपुट की जांच करने के बाद, किसी काल्पनिक bazel publish कमांड की तरह ही, bazel run :docs.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 निर्देश मिलता है.