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

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

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

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

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

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

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

सामान्य सलाह

शैली

Python की शैली

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

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

डॉकस्ट्रिंग

डॉकस्ट्रिंग का इस्तेमाल करके, फ़ाइलों और फ़ंक्शन के बारे में जानकारी दें. हर .bzl फ़ाइल के सबसे ऊपर, डॉकस्ट्रिंग का इस्तेमाल करें. साथ ही, हर सार्वजनिक फ़ंक्शन के लिए भी डॉकस्ट्रिंग का इस्तेमाल करें.

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

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

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

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

लाइन की लंबाई

BUILD फ़ाइलों की तरह, लाइन की लंबाई की कोई तय सीमा नहीं होती, क्योंकि लेबल लंबे हो सकते हैं. जहां तक हो सके, हर लाइन में ज़्यादा से ज़्यादा 79 वर्णों का इस्तेमाल करें. इसके लिए, Python's स्टाइल गाइड, पीईपी 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 सीएलआई या BUILD फ़ाइलों में सीधे तौर पर रेफ़रंस किए जाने वाले अतिरिक्त टारगेट तय करने के लिए किया जाता है. ऐसे में, उन टारगेट के असली उपयोगकर्ताओं को ही उनके बारे में जानने की ज़रूरत होती है. साथ ही, मैक्रो की वजह से होने वाली बिल्ड की समस्याएं, उनके इस्तेमाल से कभी दूर नहीं होतीं.

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

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

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

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

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

नियम

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