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

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

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

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

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

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