DRY के बजाय DAMP BUILD फ़ाइलों को प्राथमिकता दें
डीआरवाई सिद्धांत — "खुद को दोहराएं नहीं" — कोड में एक जैसी जानकारी को बार-बार इस्तेमाल करने से बचने के लिए, वैरिएबल और फ़ंक्शन जैसे ऐब्स्ट्रैक्शन का इस्तेमाल करने का सुझाव देता है. इससे कोड में यूनीकनेस बढ़ती है.
इसके उलट, DAMP सिद्धांत — "Descriptive and Meaningful Phrases" — फ़ाइलों को समझने और बनाए रखने के लिए, यूनीक होने के बजाय पढ़ने लायक होने पर ज़ोर देता है.
BUILD
फ़ाइलें कोड नहीं हैं, बल्कि कॉन्फ़िगरेशन हैं. इनकी जांच कोड की तरह नहीं की जाती. हालांकि, इन्हें लोगों और टूल की मदद से बनाए रखना ज़रूरी है. इसलिए, उनके लिए DRY की तुलना में DAMP बेहतर है.
BUILD.bazel फ़ाइल का फ़ॉर्मैट
BUILD
फ़ाइल फ़ॉर्मैटिंग, Go के तरीके को फ़ॉलो करती है. इसमें एक स्टैंडर्ड टूल, फ़ॉर्मैटिंग से जुड़ी ज़्यादातर समस्याओं को ठीक करता है.
Buildifier एक ऐसा टूल है जो सोर्स कोड को पार्स करता है और उसे स्टैंडर्ड स्टाइल में दिखाता है. इसलिए, हर BUILD
फ़ाइल को एक ही तरीके से अपने-आप फ़ॉर्मैट किया जाता है. इससे कोड की समीक्षा के दौरान, फ़ॉर्मैटिंग की समस्या नहीं होती. इससे टूल के लिए, BUILD
फ़ाइलों को समझना, उनमें बदलाव करना, और उन्हें जनरेट करना भी आसान हो जाता है.
BUILD
फ़ाइल का फ़ॉर्मैट, buildifier
के आउटपुट से मेल खाना चाहिए.
फ़ॉर्मैटिंग का उदाहरण
# Test code implementing the Foo controller.
package(default_testonly = True)
py_test(
name = "foo_test",
srcs = glob(["*.py"]),
data = [
"//data/production/foo:startfoo",
"//foo",
"//third_party/java/jdk:jdk-k8",
],
flaky = True,
deps = [
":check_bar_lib",
":foo_data_check",
":pick_foo_port",
"//pyglib",
"//testing/pybase",
],
)
फ़ाइल स्ट्रक्चर
सुझाव: इस क्रम का इस्तेमाल करें. हर एलिमेंट का इस्तेमाल करना ज़रूरी नहीं है:
पैकेज की जानकारी (टिप्पणी)
load()
के सभी स्टेटमेंटpackage()
फ़ंक्शन.नियमों और मैक्रो को कॉल करना
Buildifier, स्टैंडअलोन टिप्पणी और किसी एलिमेंट से जुड़ी टिप्पणी के बीच अंतर करता है. अगर कोई टिप्पणी किसी खास एलिमेंट से जुड़ी नहीं है, तो उसके बाद एक खाली लाइन का इस्तेमाल करें. अपने-आप होने वाले बदलावों के लिए, यह अंतर जानना ज़रूरी है. उदाहरण के लिए, किसी नियम को मिटाते समय किसी टिप्पणी को बनाए रखने या हटाने के लिए.
# Standalone comment (such as to make a section in a file)
# Comment for the cc_library below
cc_library(name = "cc")
मौजूदा पैकेज में टारगेट के रेफ़रंस
फ़ाइलों को पैकेज डायरेक्ट्री के हिसाब से उनके पाथ से रेफ़र किया जाना चाहिए. साथ ही, अप-रेफ़रंस (जैसे, ..
) का इस्तेमाल कभी नहीं किया जाना चाहिए. जनरेट की गई फ़ाइलों के नाम के आगे ":
" प्रीफ़िक्स होना चाहिए, ताकि यह पता चल सके कि वे सोर्स नहीं हैं. सोर्स फ़ाइलों के नाम की शुरुआत :
से नहीं होनी चाहिए. नियमों के पहले :
होना चाहिए. उदाहरण के लिए, मान लें कि x.cc
एक सोर्स फ़ाइल है:
cc_library(
name = "lib",
srcs = ["x.cc"],
hdrs = [":gen_header"],
)
genrule(
name = "gen_header",
srcs = [],
outs = ["x.h"],
cmd = "echo 'int x();' > $@",
)
टारगेट का नामकरण
टारगेट के नाम, कम शब्दों में ज़्यादा जानकारी देने वाले होने चाहिए. अगर किसी टारगेट में एक सोर्स फ़ाइल है, तो टारगेट का नाम आम तौर पर उस सोर्स से लिया जाना चाहिए. उदाहरण के लिए, chat.cc
के लिए cc_library
का नाम chat
रखा जा सकता है या DirectMessage.java
के लिए java_library
का नाम direct_message
रखा जा सकता है.
किसी पैकेज के लिए, उसी नाम का टारगेट (जिस टारगेट का नाम, पैकेज वाली डायरेक्ट्री के नाम से मिलता-जुलता हो) को डायरेक्ट्री के नाम से बताई गई सुविधा देनी चाहिए. अगर ऐसा कोई टारगेट मौजूद नहीं है, तो उसी नाम का टारगेट न बनाएं.
एक ही नाम वाले टारगेट (//x
के बजाय //x:x
) का रेफ़रंस देते समय, छोटे नाम का इस्तेमाल करें. अगर आप एक ही पैकेज में हैं, तो लोकल रेफ़रंस (:x
के बजाय //x
) का इस्तेमाल करें.
"आरक्षित" टारगेट के नामों का इस्तेमाल न करें, क्योंकि इनका खास मतलब होता है. इनमें all
, __pkg__
, और __subpackages__
शामिल हैं. इन नामों के खास मायने होते हैं. इनका इस्तेमाल करने पर, भ्रम पैदा हो सकता है और अनचाहे नतीजे मिल सकते हैं.
टीम के मौजूदा नियमों के न होने पर, यहां कुछ ऐसी सलाह दी गई हैं जिनका पालन करना ज़रूरी नहीं है. हालांकि, Google में इनका इस्तेमाल बड़े पैमाने पर किया जाता है:
- आम तौर पर, "snake_case" का इस्तेमाल करें
- एक
src
वालेjava_library
के लिए, इसका मतलब है कि ऐसे नाम का इस्तेमाल करना जो एक्सटेंशन के बिना फ़ाइल के नाम जैसा न हो - Java
*_binary
और*_test
नियमों के लिए, "Upper CamelCase" का इस्तेमाल करें. इससे टारगेट किए गए नाम का मिलान,src
में से किसी एक से किया जा सकता है.java_test
के लिए, इससेtest_class
एट्रिब्यूट को टारगेट के नाम से अनुमानित किया जा सकता है.
- एक
- अगर किसी टारगेट के एक से ज़्यादा वैरिएंट हैं, तो उन्हें अलग-अलग दिखाने के लिए, सफ़िक्स जोड़ें. जैसे,
:foo_dev
,:foo_prod
या:bar_x86
,:bar_x64
) - सफ़िक्स
_test
,_test
,_unittest
,Test
याTests
को टारगेट करता है _lib
या_library
जैसे बेमतलब के सफ़िक्स इस्तेमाल न करें. हालांकि, अगर_library
टारगेट और उससे जुड़े_binary
के बीच टकराव से बचने के लिए ऐसा करना ज़रूरी हो, तो किया जा सकता है- प्रोटो से जुड़े टारगेट के लिए:
proto_library
टारगेट के नाम_proto
से खत्म होने चाहिए- भाषा के हिसाब से
*_proto_library
के नियम, मूल प्रोटो से मेल खाने चाहिए. हालांकि,*_proto_library
को भाषा के हिसाब से सफ़िक्स से बदलें. जैसे:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
_proto
किसको दिखे
विज़िबिलिटी को जितना हो सके उतना सीमित किया जाना चाहिए. हालांकि, टेस्ट और रिवर्स डिपेंडेंसी के ज़रिए ऐक्सेस करने की अनुमति दी जानी चाहिए. ज़रूरत के हिसाब से __pkg__
और __subpackages__
का इस्तेमाल करें.
पैकेज default_visibility
को //visibility:public
पर सेट न करें.
//visibility:public
को सिर्फ़ प्रोजेक्ट के सार्वजनिक एपीआई में मौजूद टारगेट के लिए अलग-अलग सेट किया जाना चाहिए. ये ऐसी लाइब्रेरी हो सकती हैं जिन्हें बाहरी प्रोजेक्ट या बाइनरी पर निर्भर रहने के लिए डिज़ाइन किया गया है. साथ ही, इनका इस्तेमाल बाहरी प्रोजेक्ट की बिल्ड प्रोसेस में किया जा सकता है.
डिपेंडेंसी
डिपेंडेंसी सिर्फ़ डायरेक्ट डिपेंडेंसी तक सीमित होनी चाहिए. डायरेक्ट डिपेंडेंसी का मतलब है कि नियम में शामिल सोर्स के लिए ज़रूरी डिपेंडेंसी. ट्रांज़िटिव डिपेंडेंसी की सूची न बनाएं.
पैकेज-लोकल डिपेंडेंसी को सबसे पहले लिस्ट किया जाना चाहिए. साथ ही, उन्हें ऊपर दिए गए मौजूदा पैकेज में टारगेट के रेफ़रंस सेक्शन के साथ काम करने वाले तरीके से रेफ़र किया जाना चाहिए. इसके लिए, उनके पूरे पैकेज के नाम का इस्तेमाल नहीं किया जाना चाहिए.
डिपेंडेंसी को सीधे तौर पर एक ही सूची के तौर पर दिखाएं. कई टारगेट की "सामान्य" डिपेंडेंसी को किसी वैरिएबल में डालने से, उन्हें मैनेज करना मुश्किल हो जाता है. साथ ही, टूल के लिए किसी टारगेट की डिपेंडेंसी को बदलना नामुमकिन हो जाता है. इससे ऐसी डिपेंडेंसी भी बन सकती हैं जिनका इस्तेमाल नहीं किया जाता.
Globs
[]
की मदद से, "कोई टारगेट नहीं" बताएं. ऐसे ग्लोब का इस्तेमाल न करें जो किसी भी फ़ाइल से मेल न खाता हो: इसमें गड़बड़ी होने की संभावना ज़्यादा होती है और यह खाली सूची से कम साफ़ तौर पर दिखता है.
बार-बार होने वाला
सोर्स फ़ाइलों को मैच करने के लिए, रिकर्सिव ग्लोब का इस्तेमाल न करें. उदाहरण के लिए,
glob(["**/*.java"])
.
रिकर्सिव ग्लोब की वजह से, BUILD
फ़ाइलों के बारे में तर्क देना मुश्किल हो जाता है, क्योंकि ये BUILD
फ़ाइलों वाली सबडायरेक्ट्री को छोड़ देते हैं.
आम तौर पर, रिकर्सिव ग्लोब, हर डायरेक्ट्री के लिए BUILD
फ़ाइल से कम असरदार होते हैं. साथ ही, इनके बीच डिपेंडेंसी ग्राफ़ तय किया जाता है. ऐसा इसलिए, क्योंकि इससे रिमोट कैशिंग और पैरललिज़्म को बेहतर तरीके से इस्तेमाल किया जा सकता है.
हर डायरेक्ट्री में BUILD
फ़ाइल बनाना और उनके बीच डिपेंडेंसी ग्राफ़ तय करना एक अच्छा तरीका है.
नॉन-रिकर्सिव
आम तौर पर, नॉन-रिकर्सिव ग्लोब स्वीकार किए जाते हैं.
सूची की समझ से बचें
BUILD.bazel
फ़ाइल के टॉप लेवल पर, लिस्ट कंप्रीहेंशन का इस्तेमाल न करें.
बार-बार किए जाने वाले कॉल को अपने-आप पूरा करने के लिए, हर टारगेट को अलग-अलग टॉप-लेवल के नियम या मैक्रो कॉल के साथ बनाएं. हर एक को समझने में आसानी हो, इसके लिए छोटा name
पैरामीटर जोड़ें.
लिस्ट कंप्रीहेंशन से, इन चीज़ों में कमी आती है:
- रखरखाव में आसानी. मैन्युअल तरीके से रखरखाव करने वालों और बड़े पैमाने पर अपने-आप होने वाले बदलावों के लिए, लिस्ट कंप्रीहेंशन को सही तरीके से अपडेट करना मुश्किल होता है.
- कुछ इस तरह से वीडियो बनाना कि सही दर्शक उन्हें आसानी से खोज सकें. पैटर्न में
name
पैरामीटर मौजूद नहीं हैं. इसलिए, नाम के हिसाब से नियम ढूंढना मुश्किल है.
लिस्ट कंप्रीहेंशन पैटर्न का इस्तेमाल, आम तौर पर टेस्ट जनरेट करने के लिए किया जाता है. उदाहरण के लिए:
[[java_test(
name = "test_%s_%s" % (backend, count),
srcs = [ ... ],
deps = [ ... ],
...
) for backend in [
"fake",
"mock",
]] for count in [
1,
10,
]]
हमारा सुझाव है कि आप आसान विकल्पों का इस्तेमाल करें. उदाहरण के लिए, ऐसा मैक्रो तय करें जो एक टेस्ट जनरेट करता है और उसे हर टॉप-लेवल name
के लिए लागू करता है:
my_java_test(name = "test_fake_1",
...)
my_java_test(name = "test_fake_10",
...)
...
डिपेंडेंसी वैरिएबल का इस्तेमाल न करें
सामान्य डिपेंडेंसी को शामिल करने के लिए, लिस्ट वैरिएबल का इस्तेमाल न करें:
COMMON_DEPS = [
"//d:e",
"//x/y:z",
]
cc_library(name = "a",
srcs = ["a.cc"],
deps = COMMON_DEPS + [ ... ],
)
cc_library(name = "b",
srcs = ["b.cc"],
deps = COMMON_DEPS + [ ... ],
)
इसी तरह, डिपेंडेंसी को ग्रुप करने के लिए, exports
के साथ लाइब्रेरी टारगेट का इस्तेमाल न करें.
इसके बजाय, हर टारगेट के लिए डिपेंडेंसी अलग-अलग लिस्ट करें:
cc_library(name = "a",
srcs = ["a.cc"],
deps = [
"//a:b",
"//x/y:z",
...
],
)
cc_library(name = "b",
srcs = ["b.cc"],
deps = [
"//a:b",
"//x/y:z",
...
],
)
इन्हें Gazelle और अन्य टूल मैनेज करने दें. इसमें दोहराव होगा, लेकिन आपको यह नहीं सोचना होगा कि डिपेंडेंसी को कैसे मैनेज किया जाए.
लिटरल स्ट्रिंग को प्राथमिकता दें
Starlark, स्ट्रिंग जोड़ने (+
) और फ़ॉर्मैट करने (%
) के लिए ऑपरेटर उपलब्ध कराता है. हालांकि, इनका इस्तेमाल सावधानी से करें. एक्सप्रेशन को ज़्यादा छोटा बनाने या लंबी लाइनों को तोड़ने के लिए, स्ट्रिंग के सामान्य हिस्सों को अलग करना आसान होता है. फिर भी,
स्ट्रिंग वैल्यू को अलग-अलग हिस्सों में बांटने पर, उन्हें एक नज़र में पढ़ना मुश्किल हो जाता है.
buildozer और Code Search जैसे ऑटोमेटेड टूल को वैल्यू ढूंढने और उन्हें सही तरीके से अपडेट करने में समस्या होती है. ऐसा तब होता है, जब वैल्यू को अलग-अलग हिस्सों में बांटा जाता है.
BUILD
फ़ाइलों में, दोहराव से बचने के बजाय आसानी से समझ में आने वाली भाषा का इस्तेमाल करना ज़्यादा ज़रूरी है (DAMP बनाम DRY देखें).इस स्टाइल गाइड में, लेबल वाली वैल्यू की स्ट्रिंग को अलग-अलग करने के ख़िलाफ़ चेतावनी दी गई है. साथ ही, लंबी लाइनों को इस्तेमाल करने की अनुमति दी गई है.
Buildifier, जोड़ी गई स्ट्रिंग को अपने-आप जोड़ देता है. ऐसा तब होता है, जब उसे पता चलता है कि वे लेबल हैं.
इसलिए, जोड़ी गई या फ़ॉर्मैट की गई स्ट्रिंग के बजाय, साफ़ तौर पर बताई गई स्ट्रिंग का इस्तेमाल करें. खास तौर पर, लेबल वाले एट्रिब्यूट में, जैसे कि name
और deps
. उदाहरण के लिए, यह BUILD
फ़्रैगमेंट:
NAME = "foo"
PACKAGE = "//a/b"
proto_library(
name = "%s_proto" % NAME,
deps = [PACKAGE + ":other_proto"],
alt_dep = "//surprisingly/long/chain/of/package/names:" +
"extravagantly_long_target_name",
)
को बेहतर तरीके से ऐसे लिखा जा सकता है
proto_library(
name = "foo_proto",
deps = ["//a/b:other_proto"],
alt_dep = "//surprisingly/long/chain/of/package/names:extravagantly_long_target_name",
)
हर .bzl
फ़ाइल से एक्सपोर्ट किए जाने वाले सिंबल की संख्या सीमित करें
हर सार्वजनिक .bzl
(Starlark) फ़ाइल से एक्सपोर्ट किए गए सिंबल (नियम, मैक्रो, कॉन्स्टेंट, फ़ंक्शन) की संख्या कम करें. हमारा सुझाव है कि किसी फ़ाइल में एक से ज़्यादा सिंबल सिर्फ़ तब एक्सपोर्ट किए जाने चाहिए, जब उन्हें एक साथ इस्तेमाल किया जाना हो. इसके अलावा, इसे कई .bzl
फ़ाइलों में बांटें. हर फ़ाइल का अपना bzl_library होना चाहिए.
बहुत ज़्यादा सिंबल होने की वजह से, .bzl
फ़ाइलें सिंबल की बड़ी "लाइब्रेरी" में बदल सकती हैं. इससे, किसी एक फ़ाइल में बदलाव करने पर, Bazel को कई टारगेट फिर से बनाने पड़ सकते हैं.
अन्य कन्वेंशन
कॉन्स्टेंट (जैसे,
GLOBAL_CONSTANT
) को तय करने के लिए, बड़े अक्षरों और अंडरस्कोर का इस्तेमाल करें. वैरिएबल (जैसे,my_variable
) को तय करने के लिए, छोटे अक्षरों और अंडरस्कोर का इस्तेमाल करें.लेबल को कभी भी अलग-अलग नहीं किया जाना चाहिए, भले ही उनमें 79 से ज़्यादा वर्ण हों. जहां तक हो सके, लेबल स्ट्रिंग लिटरल होने चाहिए. वजह: इससे ढूंढने और बदलने की प्रोसेस आसान हो जाती है. इससे पढ़ने में आसानी होती है.
name एट्रिब्यूट की वैल्यू, लिटरल कॉन्स्टेंट स्ट्रिंग होनी चाहिए. हालांकि, मैक्रो में ऐसा नहीं होता. वजह: बाहरी टूल, किसी नियम को रेफ़र करने के लिए, नाम एट्रिब्यूट का इस्तेमाल करते हैं. उन्हें कोड को समझे बिना नियमों का पता लगाना होता है.
बूलियन टाइप के एट्रिब्यूट सेट करते समय, पूर्णांक वैल्यू के बजाय बूलियन वैल्यू का इस्तेमाल करें. लेगसी की वजह से, नियमों में अब भी ज़रूरत के मुताबिक पूर्णांकों को बूलियन में बदला जाता है. हालांकि, ऐसा न करने का सुझाव दिया जाता है. वजह:
flaky = 1
को गलत तरीके से पढ़ा जा सकता है. ऐसा लग सकता है कि इसमें कहा गया है कि "इस टारगेट को एक बार फिर से चलाकर, इसमें मौजूद गड़बड़ी को ठीक करें".flaky = True
में साफ़ तौर पर बताया गया है कि "यह टेस्ट फ़्लेकी है".
Python स्टाइल गाइड में अंतर
हालांकि, Python स्टाइल गाइड के साथ काम करना एक लक्ष्य है, लेकिन इसमें कुछ अंतर हैं:
लाइन की लंबाई की कोई तय सीमा नहीं है. लंबी टिप्पणियों और लंबी स्ट्रिंग को अक्सर 79 कॉलम में बांटा जाता है. हालांकि, ऐसा करना ज़रूरी नहीं है. इसे कोड की समीक्षा या सबमिट करने से पहले की स्क्रिप्ट में लागू नहीं किया जाना चाहिए. वजह: लेबल लंबे हो सकते हैं और इस सीमा से ज़्यादा हो सकते हैं. आम तौर पर,
BUILD
फ़ाइलों को टूल से जनरेट या उनमें बदलाव किया जाता है. इसलिए, लाइन की लंबाई की सीमा तय करना सही नहीं है.स्ट्रिंग को अपने-आप जोड़ने की सुविधा काम नहीं करती.
+
ऑपरेटर का इस्तेमाल करें. वजह:BUILD
फ़ाइलों में स्ट्रिंग की कई सूचियां शामिल हैं. कॉमा लगाना भूलना आम बात है. इससे नतीजे पूरी तरह से अलग हो जाते हैं. इस वजह से, पहले कई गड़बड़ियां हुई हैं. यह बातचीत भी देखें.नियमों में कीवर्ड आर्ग्युमेंट के लिए,
=
के आस-पास स्पेस का इस्तेमाल करें. वजह: Python की तुलना में, नाम वाले आर्ग्युमेंट का इस्तेमाल ज़्यादा बार किया जाता है. साथ ही, ये हमेशा अलग लाइन में होते हैं. स्पेस का इस्तेमाल करने से, कॉन्टेंट को आसानी से पढ़ा जा सकता है. यह तरीका लंबे समय से इस्तेमाल किया जा रहा है. इसलिए, सभी मौजूदाBUILD
फ़ाइलों में बदलाव करने की ज़रूरत नहीं है.डिफ़ॉल्ट रूप से, स्ट्रिंग के लिए डबल कोटेशन मार्क का इस्तेमाल करें. वजह: Python की स्टाइल गाइड में इसके बारे में नहीं बताया गया है. हालांकि, इसमें एक जैसे तरीके का इस्तेमाल करने का सुझाव दिया गया है. इसलिए, हमने सिर्फ़ डबल कोट वाली स्ट्रिंग का इस्तेमाल करने का फ़ैसला किया है. कई भाषाओं में स्ट्रिंग लिटरल के लिए, डबल-कोट का इस्तेमाल किया जाता है.
टॉप-लेवल की दो परिभाषाओं के बीच एक खाली लाइन का इस्तेमाल करें. वजह:
BUILD
फ़ाइल का स्ट्रक्चर, सामान्य Python फ़ाइल जैसा नहीं होता. इसमें सिर्फ़ टॉप-लेवल के स्टेटमेंट होते हैं. एक खाली लाइन का इस्तेमाल करने से,BUILD
फ़ाइलें छोटी हो जाती हैं.