DRY yerine DAMP BUILD dosyalarını tercih edin
DRY (Don't Repeat Yourself - Kendinizi Tekrarlamayın) ilkesi, kodda gereksiz tekrarları önlemek için değişkenler ve işlevler gibi soyutlamalar ekleyerek benzersizliği teşvik eder.
Buna karşılık, "Açıklayıcı ve Anlamlı Kelime Öbekleri" anlamına gelen DAMP ilkesi, dosyaların anlaşılmasını ve korunmasını kolaylaştırmak için benzersizlik yerine okunabilirliği teşvik eder.
BUILD
dosyaları kod değil, yapılandırmalardır. Bunlar kod gibi test edilmez ancak insanlar ve araçlar tarafından bakımı yapılmalıdır. Bu nedenle, NEMLİ kuru olana göre daha iyidir.
BUILD.bazel dosyası biçimlendirmesi
BUILD
dosya biçimlendirmesi, Go ile aynı yaklaşımı izler. Bu yaklaşımda, standartlaştırılmış bir araç çoğu biçimlendirme sorununu çözer.
Buildifier, kaynak kodunu standart bir stille ayrıştırıp yayınlayan bir araçtır. Bu nedenle, her BUILD
dosyası aynı otomatik şekilde biçimlendirilir. Bu da kod incelemeleri sırasında biçimlendirmenin sorun olmasını önler. Ayrıca, araçların BUILD
dosyalarını anlamasını, düzenlemesini ve oluşturmasını kolaylaştırır.
BUILD
dosya biçimi, buildifier
sonucuyla eşleşmelidir.
Biçimlendirme örneği
# 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",
],
)
Dosya yapısı
Öneri: Aşağıdaki sırayı kullanın (her öğe isteğe bağlıdır):
Paket açıklaması (yorum)
Tüm
load()
hesap özetleripackage()
işlevi.Kural ve makro çağrıları
Buildifier, bağımsız yorumlar ile bir öğeye eklenmiş yorumlar arasında ayrım yapar. Bir yorum belirli bir öğeye eklenmemişse yorumdan sonra boş bir satır kullanın. Otomatik değişiklikler yaparken (ör. bir kuralı silerken yorumu tutmak veya kaldırmak için) bu ayrım önemlidir.
# Standalone comment (such as to make a section in a file)
# Comment for the cc_library below
cc_library(name = "cc")
Mevcut paketteki hedeflere referanslar
Dosyalara, paket dizine göreli yollarıyla referans verilmelidir (..
gibi üst referanslar kullanılmamalıdır). Oluşturulan dosyaların ön eklerine, kaynak olmadıklarını belirtmek için ":
" eklenmelidir. Kaynak dosyaların önüne :
eklenmemelidir. Kurallar :
ile başlamalıdır. Örneğin, x.cc
bir kaynak dosya olduğu varsayıldığında:
cc_library(
name = "lib",
srcs = ["x.cc"],
hdrs = [":gen_header"],
)
genrule(
name = "gen_header",
srcs = [],
outs = ["x.h"],
cmd = "echo 'int x();' > $@",
)
Hedef adlandırma
Hedef adları açıklayıcı olmalıdır. Bir hedef tek bir kaynak dosya içeriyorsa hedefin adı genellikle bu kaynaktan türetilmelidir (örneğin, chat.cc
için bir cc_library
chat
olarak adlandırılabilir veya DirectMessage.java
için bir java_library
direct_message
olarak adlandırılabilir).
Bir paketin aynı ada sahip hedefi (içeren dizinle aynı ada sahip hedef), dizin adıyla açıklanan işlevi sağlamalıdır. Böyle bir hedef yoksa aynı ada sahip bir hedef oluşturmayın.
Aynı ada sahip bir hedeften bahsederken kısa adı tercih edin (//x:x
yerine //x
). Aynı paketteyseniz yerel referansı tercih edin (//x
yerine :x
).
Özel anlamı olan "ayrılmış" hedef adlar kullanmaktan kaçının. all
, __pkg__
ve __subpackages__
de bu kapsamdadır. Bu adlar özel anlamlara sahiptir ve kullanıldığında karışıklıklara ve beklenmedik davranışlara neden olabilir.
Geçerli bir ekip kuralı olmadığında, Google'da yaygın olarak kullanılan ve bağlayıcı olmayan bazı öneriler aşağıda verilmiştir:
- Genel olarak "snake_case" değerini kullanın
- Bir
src
içerenjava_library
için bu, uzantı olmadan dosya adıyla aynı olmayan bir ad kullanmak anlamına gelir. - Java
*_binary
ve*_test
kuralları için "Büyük CamelCase" kullanın. Bu, hedef adınınsrc
değerlerinden biriyle eşleşmesini sağlar.java_test
için bu,test_class
özelliğinin hedefin adından çıkarılmasını sağlar.
- Bir
- Belirli bir hedefin birden fazla varyantı varsa anlam karışıklığını önlemek için bir son ek ekleyin (ör.
:foo_dev
,:foo_prod
veya:bar_x86
,:bar_x64
) _test
,_unittest
,Test
veyaTests
ile_test
son eki hedefleri_lib
veya_library
gibi anlamsız son ekler kullanmaktan kaçının (_library
hedefi ile ilgili_binary
arasında çakışmaları önlemek için gerekli olmadığı sürece)- Proto ile ilgili hedefler için:
proto_library
hedeflerinin adları_proto
ile bitmelidir- Dillere özgü
*_proto_library
kuralları, temel proto ile eşleşmelidir ancak_proto
, aşağıdaki gibi dile özgü bir son ek ile değiştirilir:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
Görünürlük
Görünürlük, testler ve ters bağımlılıklar tarafından erişime izin verirken mümkün olduğunca dar kapsamlı olmalıdır. Uygun durumlarda __pkg__
ve __subpackages__
özelliğini kullanın.
default_visibility
paketini //visibility:public
olarak ayarlamamaya çalışın.
//visibility:public
yalnızca projenin herkese açık API'sindeki hedefler için ayrı ayrı ayarlanmalıdır. Bunlar, harici projeler tarafından bağımlı olacak şekilde tasarlanmış kitaplıklar veya harici bir projenin derleme süreci tarafından kullanılabilecek ikili dosyalar olabilir.
Bağımlılıklar
Bağımlılıklar, doğrudan bağımlılıklarla (kuraldaki kaynaklar tarafından ihtiyaç duyulan bağımlılıklar) sınırlandırılmalıdır. Geçişli bağımlılıkları listelemeyin.
Paket yerel bağımlılıkları önce listelenmeli ve yukarıdaki Mevcut paketteki hedeflere referanslar bölümüyle uyumlu bir şekilde referans verilmelidir (mutlak paket adlarıyla değil).
Bağımlılıkları doğrudan tek bir liste halinde listelemeyi tercih edin. Birkaç hedefin "ortak" bağımlılıkları bir değişkene yerleştirildiğinde bakım kolaylığı azalır, araçların bir hedefin bağımlılıkları değiştirmesi imkansız hale gelir ve kullanılmayan bağımlılıklara yol açabilir.
Baloncuklar
"Hedef yok"u []
ile belirtin. Hiçbir şeyle eşleşmeyen bir glob kullanmayın: Boş bir listeden daha hatalara açık ve daha belirsizdir.
Yinelemeli
Kaynak dosyaları eşleştirmek için yinelenen glob'lar kullanmayın (örneğin, glob(["**/*.java"])
).
Yinelenen glob'lar, BUILD
dosyaları içeren alt dizinleri atladığından BUILD
dosyalarının anlaşılmasını zorlaştırır.
Yinelenen glob'lar, daha iyi uzaktan önbelleğe alma ve paralellik sağladığı için genellikle dizin başına bir BUILD
dosyası ve aralarında tanımlanmış bir bağımlılık grafiği bulundurmaktan daha az verimlidir.
Her dizinde bir BUILD
dosyası oluşturmak ve bunlar arasında bir bağımlılık grafiği tanımlamak iyi bir uygulamadır.
Yinelemesiz
Yinelenen olmayan globlar genellikle kabul edilir.
Liste kapsamları kullanmaktan kaçının
BUILD.bazel
dosyasının üst düzeyinde liste kapsamları kullanmaktan kaçının.
Her adlandırılmış hedefi ayrı bir üst düzey kural veya makro çağrısıyla oluşturarak tekrarlanan çağrıları otomatikleştirin. Anlaşılır olması için her birine kısa bir name
parametresi verin.
Liste kapsamı aşağıdakileri azaltır:
- Bakım yapılabilirlik. Liste kapsamlarının doğru şekilde güncellenmesi, gerçek kişilerden oluşan bakım ekiplerinin ve büyük ölçekli otomatik değişikliklerin yapması için zor veya imkansızdır.
- Bulunabilirlik. Kalıbın
name
parametreleri olmadığından kuralın adını kullanarak bulmak zordur.
Liste kapsamı kalıbının yaygın bir uygulaması, test oluşturmaktır. Örneğin:
[[java_test(
name = "test_%s_%s" % (backend, count),
srcs = [ ... ],
deps = [ ... ],
...
) for backend in [
"fake",
"mock",
]] for count in [
1,
10,
]]
Daha basit alternatifleri kullanmanızı öneririz. Örneğin, bir test oluşturan ve her üst düzey name
için çağıran bir makro tanımlayın:
my_java_test(name = "test_fake_1",
...)
my_java_test(name = "test_fake_10",
...)
...
deps değişkenlerini kullanmayın
Ortak bağımlılıkları kapsayacak şekilde liste değişkenleri kullanmayın:
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 + [ ... ],
)
Benzer şekilde, bağımlılıkları gruplandırmak için exports
içeren bir kitaplık hedefi kullanmayın.
Bunun yerine, bağımlılıkları her hedef için ayrı ayrı listeleyin:
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 ve diğer araçların bunları yönetmesine izin verin. Bazı kısımlar tekrarlanacaktır ancak bağımlılıkları nasıl yöneteceğinizi düşünmeniz gerekmez.
Söz dizimi dizelerini tercih edin
Starlark, dize birleştirme (+
) ve biçimlendirme (%
) için dize operatörleri sağlasa da bunları dikkatli kullanın. İfadeleri daha kısa hale getirmek veya uzun satırları bölmek için ortak dize parçalarını çıkarmak cazip gelebilir. Ancak,
Bölünmüş dize değerlerinin bir bakışta okunması daha zordur.
buildozer ve Code Search gibi otomatik araçlar, değerler bölündüğünde değerleri bulup doğru şekilde güncellemede sorun yaşar.
BUILD
dosyalarında okunabilirlik, tekrardan kaçınmadan daha önemlidir (DAMP ve DRY karşılaştırması bölümüne bakın).Bu stil kılavuzu, etiket değerine sahip dizeleri bölmemeye karşı uyarıyor ve uzun satırlara açıkça izin veriyor.
Buildifier, etiket olduklarını algıladığında birleştirilmiş dizeleri otomatik olarak birleştirir.
Bu nedenle, özellikle name
ve deps
gibi etiket türündeki özelliklerde, birleştirilmiş veya biçimlendirilmiş dizeler yerine açık, değişmez dizeleri tercih edin. Örneğin, bu BUILD
parçası:
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",
)
şu şekilde yeniden yazılabilir:
proto_library(
name = "foo_proto",
deps = ["//a/b:other_proto"],
alt_dep = "//surprisingly/long/chain/of/package/names:extravagantly_long_target_name",
)
Her .bzl
dosyası tarafından dışa aktarılan sembolleri sınırlama
Her herkese açık .bzl
(Starlark) dosyası tarafından dışa aktarılan simgelerin (kurallar, makrolar, sabitler, işlevler) sayısını en aza indirin. Bir dosyada birden fazla sembolün yalnızca birlikte kullanılacağından eminseniz dışa aktarılmasını öneririz. Aksi takdirde, her biri kendi bzl_library dosyasına sahip olacak şekilde birden fazla .bzl
dosyasına bölün.
Aşırı sayıda simge, .bzl
dosyalarının geniş simge "kütüphanelerine" dönüşmesine neden olabilir. Bu da tek dosyalardaki değişikliklerin Bazel'i birçok hedefi yeniden oluşturmaya zorlamasına yol açar.
Diğer sözleşmeler
Sabitler için büyük harf ve alt çizgi (
GLOBAL_CONSTANT
gibi), değişkenler için küçük harf ve alt çizgi (my_variable
gibi) kullanın.Etiketler 79 karakterden uzun olsa bile hiçbir zaman bölünmemelidir. Etiketler mümkün olduğunda dize değişmezleri olmalıdır. Açıklama: Bul ve değiştir özelliğini kolaylaştırır. Okunabilirliği de artırır.
Ad özelliğinin değeri, değişmez bir dize olmalıdır (makrolarda hariç). Mantık: Harici araçlar, bir kurala atıfta bulunmak için ad özelliğini kullanır. Kodu yorumlamak zorunda kalmadan kuralları bulmaları gerekir.
Boole türündeki özellikleri ayarlarken tam sayı değerleri yerine boole değerleri kullanın. Eskiden olduğu gibi kurallar, gerektiğinde tam sayıları doğru/yanlış değerlere dönüştürmeye devam eder ancak bu işlemden kaçınılması önerilir. Açıklama:
flaky = 1
, "bu hedefi bir kez yeniden çalıştırarak deflake et" şeklinde yanlış anlaşılabilir.flaky = True
, "bu test kararsız" olduğunu açıkça belirtiyor.
Python stil kılavuzuyla arasındaki farklar
Python stil kılavuzu ile uyumluluk hedef olsa da birkaç fark vardır:
Satır uzunluğu için katı bir sınır yoktur. Uzun yorumlar ve uzun dizeler genellikle 79 sütuna bölünür ancak bu zorunlu değildir. Kod incelemelerinde veya göndermeden önce komut dosyalarında zorunlu kılınmamalıdır. Nedeni: Etiketler uzun olabilir ve bu sınırı aşabilir.
BUILD
dosyalarının araçlar tarafından oluşturulması veya düzenlenmesi yaygın bir durumdur. Bu da satır uzunluğu sınırıyla uyumlu değildir.Örtülü dize birleştirme desteklenmez.
+
operatörünü kullanın. Mantık:BUILD
dosyaları birçok dize listesi içerir. Virgülü unutmak kolaydır ve bu da tamamen farklı bir sonuca yol açar. Bu durum geçmişte birçok hataya yol açmıştır. Bu tartışmaya da göz atın.Kurallardaki anahtar kelime bağımsız değişkenleri için
=
işaretinin etrafında boşluk kullanın. Mantık: Adlandırılmış bağımsız değişkenler, Python'a kıyasla çok daha sık kullanılır ve her zaman ayrı bir satırda yer alır. Boşluklar okunabilirliği artırır. Bu kural uzun zamandır kullanılıyor ve mevcut tümBUILD
dosyalarını değiştirmeye değmez.Varsayılan olarak, dizelerde çift tırnak kullanın. Mantık: Bu, Python stil kılavuzunda belirtilmemiştir ancak tutarlılık önerilir. Bu nedenle, yalnızca çift tırnak içine alınmış dizeleri kullanmaya karar verdik. Birçok dil, dize değişmezleri için çift tırnak kullanır.
İki üst düzey tanım arasında tek bir boş satır kullanın. Mantık:
BUILD
dosyasının yapısı, tipik bir Python dosyasına benzemez. Yalnızca üst düzey ifadeler içerir. Tek boş satır kullanmakBUILD
dosyalarını kısaltır.