DERLEME Stil Kılavuzu

Sorun bildir Kaynağı görüntüle Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

DAMP BUILD dosyalarını DRY dosyalarına tercih et

"Kendini Tekrar Etme" (DRY) ilkesi, kodda gereksiz tekrarları önlemek için değişkenler ve işlevler gibi soyutlamalar sunarak benzersizliği teşvik eder.

Buna karşılık, "Açıklayıcı ve Anlamlı İfadeler" anlamına gelen DAMP ilkesi, dosyaların daha kolay anlaşılması ve korunması için okunabilirliği benzersizliğe tercih eder.

BUILD dosyaları kod değil, yapılandırmadır. Bunlar kod gibi test edilmez ancak insanlar ve araçlar tarafından korunması gerekir. Bu nedenle DAMP, DRY'den daha iyi bir seçenektir.

BUILD.bazel dosya biçimlendirmesi

BUILD dosya biçimlendirmesi, standartlaştırılmış bir aracın çoğu biçimlendirme sorununu ele aldığı Go ile aynı yaklaşımı izler. Buildifier, kaynak kodu ayrıştırıp standart bir stilde yayınlayan bir araçtır. Bu nedenle, her BUILD dosyası aynı otomatik şekilde biçimlendirilir. Böylece, biçimlendirme işlemi kod incelemeleri sırasında sorun oluşturmaz. Ayrıca araçların BUILD dosyalarını anlamasını, düzenlemesini ve oluşturmasını da kolaylaştırır.

BUILD dosya biçimi, buildifier çıkışıyla 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() ekstreleri

  • package() işlevi.

  • Kurallara ve makrolara yapılan çağrılar

Buildifier, bağımsız yorum ile bir öğeye eklenmiş yorum 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) 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")

Geçerli paketteki hedeflere yapılan referanslar

Dosyalara, paket dizinine göre yollarıyla (.. gibi yukarı referanslar kullanılmadan) başvurulmalıdır. Oluşturulan dosyalar, kaynak olmadıklarını belirtmek için ":" ile öneklenmelidir. Kaynak dosyaların başına : eklenmemelidir. Kuralların önüne : eklenmelidir. Örneğin, x.cc dosyasının kaynak dosya olduğunu varsayarsak:

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 dosyası içeriyorsa hedef genellikle bu kaynaktan türetilmiş bir ada sahip olmalıdır (örneğin, chat.cc için cc_library, chat olarak adlandırılabilir veya DirectMessage.java için java_library, direct_message olarak adlandırılabilir).

Bir paketin adaş hedefi (içeren dizinle aynı ada sahip hedef), dizin adıyla açıklanan işlevselliği sağlamalıdır. Böyle bir hedef yoksa aynı adlı bir hedef oluşturmayın.

Adını taşıyan bir hedefi belirtirken kısa adı kullanın (//x yerine //x:x). Aynı paketteyseniz yerel referansı (:x yerine //x) kullanın.

Özel anlamı olan "ayrılmış" hedef adları kullanmaktan kaçının. all, __pkg__ ve __subpackages__ gibi adlar özel semantiğe sahiptir ve kullanıldıklarında kafa karışıklığına ve beklenmedik davranışlara neden olabilir.

Geçerli bir ekip kuralı yoksa bunlar Google'da yaygın olarak kullanılan, bağlayıcı olmayan bazı önerilerdir:

  • Genel olarak "snake_case" kullanın.
    • Bir java_library için bu, uzantısız dosya adıyla aynı olmayan bir ad kullanmak anlamına gelir.src
    • Java *_binary ve *_test kuralları için "Upper CamelCase" kullanın. Bu, hedef adının src'lardan biriyle eşleşmesini sağlar. java_test için bu, test_class özelliğinin hedefin adından çıkarılmasını sağlar.
  • Belirli bir hedefin birden fazla varyantı varsa ayırt etmek için bir sonek ekleyin (ör. :foo_dev, :foo_prod veya :bar_x86, :bar_x64)
  • _test hedeflerine _test, _unittest, Test veya Tests ekleyin
  • _lib veya _library gibi anlamsız eklerden kaçının (_library hedefi ile karşılık gelen _binary arasındaki çakışmaları önlemek için gerekli olmadığı sürece)
  • Proto ile ilgili hedefler için:
    • proto_library hedeflerinin adları _proto ile bitmelidir
    • Dile özgü *_proto_library kuralları, temel protokolle eşleşmeli ancak _proto, dile özgü bir sonek ile değiştirilmelidir. Örneğin:
      • 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 verecek şekilde mümkün olduğunca dar kapsamlı olmalıdır. Uygun şekilde __pkg__ ve __subpackages__ özelliğini kullanın.

default_visibility paketini //visibility:public olarak ayarlamaktan kaçının. //visibility:public yalnızca projenin herkese açık API'sindeki hedefler için ayrı ayrı ayarlanmalıdır. Bunlar, harici projelerin bağımlı olması için tasarlanmış kitaplıklar veya harici bir projenin derleme sürecinde kullanılabilecek ikili dosyalar olabilir.

Bağımlılıklar

Bağımlılıklar doğrudan bağımlılıklarla (kuralda listelenen kaynaklar için gereken bağımlılıklar) sınırlı olmalıdır. Geçişli bağımlılıkları listelemeyin.

Pakete özel bağımlılıklar önce listelenmeli ve yukarıdaki References to targets in the current package (Mevcut paketteki hedeflere referanslar) bölümüyle uyumlu bir şekilde (mutlak paket adlarıyla değil) ele alınmalıdır.

Bağımlılıkları tek bir liste olarak doğrudan listelemeyi tercih edin. Çeşitli hedeflerin "ortak" bağımlılıklarını bir değişkene yerleştirmek, sürdürülebilirliği azaltır, araçların bir hedefin bağımlılıklarını değiştirmesini imkansız hâle getirir ve kullanılmayan bağımlılıklara yol açabilir.

Globs

[] ile "hedef yok" ifadesini belirtin. Hiçbir şeyle eşleşmeyen bir glob kullanmayın. Bu, boş bir listeden daha fazla hataya neden olur ve daha az belirgindir.

Yinelemeli

Kaynak dosyaları eşleştirmek için yinelemeli glob'lar kullanmayın (örneğin, glob(["**/*.java"])).

Yinelemeli glob'lar, BUILD dosyalarını içeren alt dizinleri atladıkları için BUILD dosyaları hakkında akıl yürütmeyi zorlaştırır.

Özyinelemeli globlar, genellikle dizin başına bir BUILD dosyası oluşturmaktan daha az verimlidir. Bu dosyalarda, daha iyi uzaktan önbelleğe alma ve paralellik sağlayan bir bağımlılık grafiği tanımlanır.

Her dizinde bir BUILD dosyası oluşturmak ve bunlar arasında bir bağımlılık grafiği tanımlamak iyi bir uygulamadır.

Özyinelemeli olmayan

Genel olarak, özyinelemeli olmayan glob'lar kabul edilebilir.

Liste kavrayışlarından kaçının

BUILD.bazel dosyasının üst düzeyinde liste kavrayışlarını kullanmaktan kaçının. Her bir adlandırılmış hedefi ayrı bir üst düzey kural veya makro çağrısıyla oluşturarak tekrarlayan çağrıları otomatikleştirin. Anlaşılır olması için her birine kısa bir name parametresi verin.

Liste kavrama, aşağıdakileri azaltır:

  • Bakım yapılabilirlik. İnsan bakıcıların ve büyük ölçekli otomatik değişikliklerin liste kapsamlarını doğru şekilde güncellemesi zor veya imkansızdır.
  • Bulunabilirlik. Kalıpta name parametresi olmadığından kuralı ada göre bulmak zordur.

Liste kavrama 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 alternatifler kullanmanızı öneririz. Örneğin, bir test oluşturan bir makro tanımlayın ve bunu her üst düzey name için çağırı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ı kapsüllemek için 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 ile bir kitaplık hedefi kullanmayın.

Bunun yerine, her hedef için bağımlılıkları 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ı korumasına izin verin. Tekrarlar olabilir ancak bağımlılıkları nasıl yöneteceğinizi düşünmeniz gerekmez.

Tam anlamıyla dizeleri tercih et

Starlark, 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 bölümlerini ayırmak cazip gelebilir. Ancak,

Bu nedenle, özellikle name ve deps gibi etiket türü özelliklerde birleştirilmiş veya biçimlendirilmiş dizeler yerine açık ve tam dizeleri tercih edin. Örneğin, şu 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",
)

daha iyi bir şekilde ş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 genel .bzl (Starlark) dosyası tarafından dışa aktarılan sembol (kural, makro, sabit, işlev) sayısını en aza indirin. Bir dosyanın yalnızca birlikte kullanılacağından emin olunan birden fazla sembolü dışa aktarması önerilir. Aksi takdirde, her biri kendi bzl_library'sine sahip olacak şekilde birden fazla .bzl dosyasına bölün.

Aşırı sembol kullanımı, .bzl dosyalarının geniş sembol "kitaplıklarına" dönüşmesine neden olabilir. Bu da tek bir dosyada yapılan değişikliklerin Bazel'in birçok hedefi yeniden oluşturmasına yol açar.

Diğer kurallar

  • Sabitleri (ör. GLOBAL_CONSTANT) tanımlamak için büyük harf ve alt çizgi, değişkenleri (ör. my_variable) tanımlamak için küçük harf ve alt çizgi kullanın.

  • Etiketler 79 karakterden uzun olsa bile asla bölünmemelidir. Etiketler mümkün olduğunda dize değişmezleri olmalıdır. Gerekçe: Bul ve değiştir işlemini kolaylaştırır. Ayrıca okunabilirliği de artırır.

  • Ad özelliğinin değeri, değişmez bir sabit dize olmalıdır (makrolar hariç). Gerekçe: Harici araçlar, bir kurala başvurmak için ad özelliğini kullanır. Kullanıcıların, kodu yorumlamadan kuralları bulması gerekir.

  • Boole türü özellikler ayarlanırken tam sayı değerleri değil, Boole değerleri kullanın. Eski nedenlerden dolayı kurallar, gerektiğinde tam sayıları hâlâ boole değerlerine dönüştürmektedir ancak bu durum önerilmez. Gerekçe: flaky = 1, "Bu hedefi bir kez daha çalıştırarak temizle" şeklinde yanlış okunabilir. flaky = True, "bu test güvenilir değil" ifadesini net bir şekilde belirtiyor.

Python stil kılavuzuyla farklılıklar

Python stil kılavuzu ile uyumluluk hedefimiz olsa da birkaç fark vardır:

  • Kesin bir satır uzunluğu 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 çalıştırılan komut dosyalarında zorunlu tutulmamalıdır. Gerekçe: 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 durum, satır uzunluğu sınırı ile iyi gitmez.

  • Örtülü dize birleştirme desteklenmez. + operatörünü kullanın. Gerekçe: BUILD dosyaları birçok dize listesi içeriyor. Virgülün unutulması kolaydır ve bu da tamamen farklı bir sonuç verir. Bu durum geçmişte birçok hataya neden oldu. 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. Gerekçe: Adlandırılmış bağımsız değişkenler, Python'da olduğundan ç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 süredir kullanılmaktadır ve mevcut tüm BUILD dosyalarını değiştirmeye değmez.

  • Varsayılan olarak dizeler için çift tırnak işareti kullanın. Gerekçe: Bu, Python stil kılavuzunda belirtilmemiştir ancak tutarlılık önerilir. Bu nedenle, yalnızca çift tırnaklı dizeler kullanmaya karar verdik. Birçok dilde, dize değişmezleri için çift tırnak kullanılır.

  • İki üst düzey tanım arasında tek bir boş satır kullanın. Gerekçe: BUILD dosyasının yapısı, normal bir Python dosyasına benzemez. Yalnızca üst düzey ifadeler içerir. Tek bir boş satır kullanmak BUILD dosyalarını kısaltır.