.bzl स्टाइल गाइड

इस पेज पर, Starlark के लिए स्टाइल से जुड़े बुनियादी दिशा-निर्देश दिए गए हैं. इसमें मैक्रो और नियमों के बारे में भी जानकारी शामिल है.

Starlark एक ऐसी भाषा है जिससे यह तय किया जाता है कि सॉफ़्टवेयर कैसे बनाया जाता है. इसलिए, यह प्रोग्रामिंग और कॉन्फ़िगरेशन, दोनों के लिए इस्तेमाल की जाने वाली भाषा है.

BUILD फ़ाइलें, मैक्रो, और बिल्ड के नियम लिखने के लिए, Starlark का इस्तेमाल किया जाता है. मैक्रो और नियम, मेटा-लैंग्वेज होते हैं. इनसे यह तय किया जाता है कि BUILD फ़ाइलें कैसे लिखी जाती हैं. BUILD फ़ाइलें, आसान और दोहराव वाली होनी चाहिए.

सभी सॉफ़्टवेयर को लिखने के मुकाबले ज़्यादा बार पढ़ा जाता है. खास तौर पर, Starlark के लिए यह बात सही है, क्योंकि इंजीनियर अपने टारगेट की डिपेंडेंसी और बिल्ड की जानकारी समझने के लिए, BUILD फ़ाइलें पढ़ते हैं. इन्हें अक्सर जल्दबाज़ी में या कोई दूसरा काम करते समय पढ़ा जाता है. इसलिए, ये फ़ाइलें आसान और आसानी से पढ़ी जा सकने वाली होनी चाहिए, ताकि उपयोगकर्ता इन्हें तुरंत पार्स और समझ सकें.BUILD

जब कोई उपयोगकर्ता BUILD फ़ाइल खोलता है, तो वह तुरंत यह जानना चाहता है कि फ़ाइल में टारगेट की सूची क्या है. इसके अलावा, वह C++ लाइब्रेरी के सोर्स की सूची की समीक्षा करना चाहता है या Java बाइनरी से डिपेंडेंसी हटाना चाहता है. ऐब्स्ट्रैक्शन की एक लेयर जोड़ने पर, उपयोगकर्ता के लिए ये काम करना मुश्किल हो जाता है.

BUILD फ़ाइलों का विश्लेषण और अपडेट कई अलग-अलग टूल से किया जाता है. अगर आपकी BUILD फ़ाइल में ऐब्स्ट्रैक्शन का इस्तेमाल किया गया है, तो हो सकता है कि टूल उसमें बदलाव न कर पाएं. BUILD फ़ाइलों को आसान रखने से, आपको बेहतर टूलिंग मिल पाएगी. कोड बेस बढ़ने पर, किसी लाइब्रेरी को अपडेट करने या साफ़-सफ़ाई करने के लिए, कई BUILD फ़ाइलों में बदलाव करना आम बात है.

सामान्य सलाह

शैली

Python की शैली

किसी भी तरह का संदेह होने पर, जहां तक हो सके PEP 8 स्टाइल गाइड का पालन करें. खास तौर पर, Python के कन्वेंशन का पालन करने के लिए, इंडेंटेशन के लिए दो के बजाय चार स्पेस का इस्तेमाल करें.

Starlark, Python नहीं है. इसलिए, Python की शैली के कुछ पहलू लागू नहीं होते. उदाहरण के लिए, PEP 8 में सलाह दी जाती है कि सिंगलटन की तुलना is से की जाए. हालांकि, Starlark में यह ऑपरेटर नहीं है.

Docstring

Docstring का इस्तेमाल करके, फ़ाइलों और फ़ंक्शन के बारे में जानकारी दें. हर .bzl फ़ाइल के सबसे ऊपर एक docstring और हर सार्वजनिक फ़ंक्शन के लिए एक docstring का इस्तेमाल करें.

नियमों और आसपेक्ट के बारे में जानकारी देना

नियमों और आसपेक्ट के साथ-साथ उनके एट्रिब्यूट, प्रोवाइडर, और उनके फ़ील्ड के बारे में जानकारी देने के लिए, doc आर्ग्युमेंट का इस्तेमाल किया जाना चाहिए.

नेमिंग कन्वेंशन

  • वैरिएबल और फ़ंक्शन के नामों के लिए, लोअरकेस का इस्तेमाल किया जाता है. साथ ही, शब्दों को अलग-अलग करने के लिए अंडरस्कोर ([a-z][a-z0-9_]*) का इस्तेमाल किया जाता है. जैसे, cc_library.
  • टॉप-लेवल की निजी वैल्यू, एक अंडरस्कोर से शुरू होती हैं. Bazel यह पक्का करता है कि निजी वैल्यू का इस्तेमाल, दूसरी फ़ाइलों से न किया जा सके. स्थानीय वैरिएबल के लिए, अंडरस्कोर प्रीफ़िक्स का इस्तेमाल नहीं किया जाना चाहिए.

लाइन की लंबाई

BUILD फ़ाइलों की तरह, लाइन की लंबाई की कोई तय सीमा नहीं होती, क्योंकि लेबल लंबे हो सकते हैं. जहां तक हो सके, हर लाइन में ज़्यादा से ज़्यादा 79 वर्णों का इस्तेमाल करें. इसके लिए, Python की स्टाइल गाइड, PEP 8 का पालन करें. इस दिशा-निर्देश को सख्ती से लागू नहीं किया जाना चाहिए: एडिटर को 80 से ज़्यादा कॉलम दिखाने चाहिए. साथ ही, ऑटोमेटेड बदलावों से अक्सर लंबी लाइनें बन जाती हैं. इसके अलावा, लोगों को उन लाइनों को अलग-अलग करने में समय नहीं लगाना चाहिए जिन्हें आसानी से पढ़ा जा सकता है.

कीवर्ड आर्ग्युमेंट

कीवर्ड आर्ग्युमेंट में, बराबर के निशान के आस-पास स्पेस का इस्तेमाल करना बेहतर होता है:

def fct(name, srcs):
    filtered_srcs = my_filter(source = srcs)
    native.cc_library(
        name = name,
        srcs = filtered_srcs,
        testonly = True,
    )

बूलियन वैल्यू

बूलियन वैल्यू के लिए, True और False वैल्यू का इस्तेमाल करें. जैसे, किसी नियम में बूलियन एट्रिब्यूट का इस्तेमाल करते समय. इसके लिए, 1 और 0 वैल्यू का इस्तेमाल न करें.

प्रोडक्शन कोड में, print() फ़ंक्शन का इस्तेमाल न करें. इसका इस्तेमाल सिर्फ़ डीबग करने के लिए किया जाता है. साथ ही, यह आपकी .bzl फ़ाइल के सभी डायरेक्ट और इनडायरेक्ट उपयोगकर्ताओं को स्पैम करेगा. सिर्फ़ एक अपवाद है. अगर आपने ऐसा कोड सबमिट किया है जिसमें print() का इस्तेमाल किया गया है, तो यह डिफ़ॉल्ट रूप से बंद होना चाहिए. साथ ही, इसे सिर्फ़ सोर्स में बदलाव करके चालू किया जा सकता है. उदाहरण के लिए, अगर print() के सभी इस्तेमाल, if DEBUG: से सुरक्षित हैं, जहां DEBUG को False पर हार्डकोड किया गया है. इस बात का ध्यान रखें कि ये स्टेटमेंट, पढ़ने में आसानी पर पड़ने वाले असर को सही ठहराने के लिए काफ़ी काम के हैं या नहीं.

मैक्रो

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

इस वजह से, कोई गड़बड़ी होने पर, उपयोगकर्ता को बिल्ड की समस्याओं को हल करने के लिए, आपके मैक्रो के लागू करने के तरीके को समझना होगा. इसके अलावा, bazel query नतीजों को समझना मुश्किल हो सकता है, क्योंकि नतीजों में दिखाए गए टारगेट मैक्रो के एक्सपैंशन से मिलते हैं. आखिर में, आसपेक्ट को मैक्रो के बारे में पता नहीं होता. इसलिए, आसपेक्ट पर निर्भर रहने वाले टूल (आईडीई वगैरह) काम नहीं कर सकते.

मैक्रो का सुरक्षित इस्तेमाल, Bazel CLI या BUILD फ़ाइलों में सीधे तौर पर रेफ़रंस किए जाने वाले अतिरिक्त टारगेट तय करने के लिए किया जाता है. ऐसे में, उन टारगेट के असली उपयोगकर्ताओं को ही उनके बारे में जानने की ज़रूरत होती है. साथ ही, मैक्रो की वजह से होने वाली बिल्ड की समस्याएं, उनके इस्तेमाल से कभी दूर नहीं होतीं.

जनरेट किए गए टारगेट तय करने वाले मैक्रो के लिए, ये सबसे सही तरीके अपनाएं. मैक्रो के लागू करने की जानकारी को सीएलआई पर रेफ़र नहीं किया जाना चाहिए. साथ ही, उन टारगेट को इस पर निर्भर नहीं होना चाहिए जिन्हें उस मैक्रो से इंस्टैंशिएट नहीं किया गया है:

  • किसी मैक्रो में name आर्ग्युमेंट होना चाहिए. साथ ही, उसे उस नाम से टारगेट तय करना चाहिए. वह टारगेट, उस मैक्रो का मुख्य टारगेट बन जाता है.
  • जनरेट किए गए टारगेट, यानी किसी मैक्रो से तय किए गए अन्य सभी टारगेट के नाम में ये प्रीफ़िक्स होने चाहिए:
    • <name> या _<name>. उदाहरण के लिए, name = '%s_bar' % (name) का इस्तेमाल करना.
    • इनकी विज़िबिलिटी सीमित होनी चाहिए (//visibility:private).
    • इनमें manual टैग होना चाहिए, ताकि वाइल्डकार्ड टारगेट (:all, ..., :* वगैरह) में एक्सपैंशन न हो.
  • name का इस्तेमाल सिर्फ़ उन टारगेट के नाम पाने के लिए किया जाना चाहिए जिन्हें मैक्रो से तय किया गया है. इसका इस्तेमाल किसी और काम के लिए नहीं किया जाना चाहिए. उदाहरण के लिए, मैक्रो से जनरेट न होने वाली डिपेंडेंसी या इनपुट फ़ाइल का नाम पाने के लिए, नाम का इस्तेमाल न करें.
  • मैक्रो में बनाए गए सभी टारगेट, मुख्य टारगेट से किसी न किसी तरह से जुड़े होने चाहिए.
  • मैक्रो में पैरामीटर के नाम एक जैसे रखें. अगर किसी पैरामीटर को मुख्य टारगेट के एट्रिब्यूट वैल्यू के तौर पर पास किया जाता है, तो उसका नाम वही रखें. अगर कोई मैक्रो पैरामीटर, सामान्य नियम एट्रिब्यूट के जैसा ही काम करता है, तो उसका नाम एट्रिब्यूट के नाम जैसा ही रखें. जैसे, deps (नीचे देखें).
  • किसी मैक्रो को कॉल करते समय, सिर्फ़ कीवर्ड आर्ग्युमेंट का इस्तेमाल करें. यह नियमों के मुताबिक है. साथ ही, इससे पढ़ने में आसानी होती है.

इंजीनियर अक्सर मैक्रो तब लिखते हैं, जब काम के नियमों का Starlark एपीआई, उनके खास इस्तेमाल के लिए काफ़ी नहीं होता. भले ही, नियम को Bazel में नेटिव कोड में तय किया गया हो या Starlark में. अगर आपको यह समस्या आ रही है, तो नियम के लेखक से पूछें कि क्या वे आपके लक्ष्यों को पूरा करने के लिए, एपीआई को बढ़ा सकते हैं.

आम तौर पर, मैक्रो जितने नियमों से मिलते-जुलते होंगे, उतना ही बेहतर होगा.

मैक्रो के बारे में भी देखें.

नियम

  • नियमों, आसपेक्ट, और उनके एट्रिब्यूट के नामों के लिए, लोअर_केस ("स्नेक केस") का इस्तेमाल किया जाना चाहिए.
  • नियमों के नाम ऐसे संज्ञा शब्द होते हैं जिनसे नियम के ज़रिए बनाए गए मुख्य तरह के आर्टफ़ैक्ट के बारे में पता चलता है. यह जानकारी, उसकी डिपेंडेंसी के हिसाब से होती है. वहीं, लीफ़ नियमों के लिए, यह जानकारी उपयोगकर्ता के हिसाब से होती है. यह ज़रूरी नहीं है कि यह फ़ाइल का सफ़िक्स हो. उदाहरण के लिए, Python एक्सटेंशन के तौर पर इस्तेमाल किए जाने वाले C++ आर्टफ़ैक्ट बनाने वाले नियम को py_extension कहा जा सकता है. ज़्यादातर भाषाओं के लिए, सामान्य नियमों में ये शामिल हैं:
    • *_library - एक कंपाइलेशन यूनिट या "मॉड्यूल".
    • *_binary - एक ऐसा टारगेट जो एक्ज़ीक्यूटेबल या डिप्लॉयमेंट यूनिट बनाता है.
    • *_test - एक टेस्ट टारगेट. इसमें एक से ज़्यादा टेस्ट शामिल हो सकते हैं. *_test टारगेट में सभी टेस्ट, एक ही थीम पर आधारित होने चाहिए. उदाहरण के लिए, किसी एक लाइब्रेरी की जांच करना.
    • *_import: एक ऐसा टारगेट जो पहले से कंपाइल किए गए आर्टफ़ैक्ट को एनकैप्सुलेट करता है. जैसे, .jar या .dll, जिसका इस्तेमाल कंपाइलेशन के दौरान किया जाता है.
  • एट्रिब्यूट के लिए एक जैसे नाम और टाइप का इस्तेमाल करें. आम तौर पर लागू होने वाले कुछ एट्रिब्यूट में ये शामिल हैं:
    • srcs: label_list. इसमें ये फ़ाइलें शामिल हो सकती हैं: सोर्स फ़ाइलें, आम तौर पर लोगों ने लिखी होती हैं.
    • deps: label_list. इसमें आम तौर पर ये फ़ाइलें शामिल नहीं होतीं: कंपाइलेशन डिपेंडेंसी.
    • data: label_list. इसमें ये फ़ाइलें शामिल हो सकती हैं: डेटा फ़ाइलें, जैसे कि टेस्ट डेटा वगैरह.
    • runtime_deps: label_list. इसमें रनटाइम डिपेंडेंसी शामिल होती हैं, जिनकी ज़रूरत कंपाइलेशन के लिए नहीं होती.
  • ऐसे एट्रिब्यूट जिनके व्यवहार के बारे में साफ़ तौर पर पता नहीं चलता उनके लिए, doc कीवर्ड आर्ग्युमेंट का इस्तेमाल करके दस्तावेज़ उपलब्ध कराएं. उदाहरण के लिए, खास तौर पर सब्स्टिट्यूशन वाले स्ट्रिंग टेंप्लेट या खास ज़रूरी शर्तों के साथ लागू किए जाने वाले टूल. इसके लिए, एट्रिब्यूट के एलान (attr.label_list() या इसी तरह के किसी एलान) का इस्तेमाल करें.
  • नियम लागू करने वाले फ़ंक्शन, लगभग हमेशा निजी फ़ंक्शन होने चाहिए. इनके नाम की शुरुआत में अंडरस्कोर होना चाहिए. myrule के लिए, लागू करने वाले फ़ंक्शन को _myrule_impl नाम देना एक आम स्टाइल है.
  • अच्छी तरह से तय किए गए प्रोवाइडर इंटरफ़ेस का इस्तेमाल करके, अपने नियमों के बीच जानकारी पास करें. प्रोवाइडर फ़ील्ड का एलान करें और उनके बारे में जानकारी दें.
  • अपने नियम को इस तरह से डिज़ाइन करें कि उसे बढ़ाया जा सके. ध्यान रखें कि अन्य नियम, आपके नियम के साथ इंटरैक्ट कर सकते हैं, आपके प्रोवाइडर को ऐक्सेस कर सकते हैं, और आपके बनाए गए ऐक्शन को फिर से इस्तेमाल कर सकते हैं.
  • अपने नियमों में, परफ़ॉर्मेंस के दिशा-निर्देशों का पालन करें.