偏好使用 DAMP BUILD 檔案,而非 DRY
DRY 原則 (「不要重複」) 鼓勵採用獨特性,方法是導入變數和函式等抽象概念,避免程式碼出現多餘內容。
相較之下,DAMP 原則 (描述性且有意義的片語) 鼓勵使用易讀的名稱,而非獨特的名稱,讓檔案更容易理解及維護。
BUILD
檔案不是程式碼,而是設定。模型不會像程式碼一樣經過測試,但需要由人員和工具維護。因此對他們來說,DAMP 比 DRY 更適合。
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」
- 如果特定目標有多個變體,請新增後置字元來消除歧義 (例如
:foo_dev
、:foo_prod
或:bar_x86
,:bar_x64
) - 以
_test
、_unittest
、Test
或Tests
為目標的後置字串_test
- 避免使用無意義的後置字元,例如
_lib
或_library
(除非為了避免_library
目標與對應的_binary
發生衝突,否則請勿使用) - 適用於 proto 相關目標:
proto_library
目標的名稱結尾應為_proto
- 語言專屬的
*_proto_library
規則應與基礎原型相符,但要將_proto
替換為語言專屬的後置字串,例如:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
顯示設定
可見度應盡可能縮小範圍,同時仍允許測試和反向依附元件存取。視情況使用 __pkg__
和 __subpackages__
。
避免將套件 default_visibility
設為 //visibility:public
。
//visibility:public
只能針對專案公開 API 中的目標個別設定。這些可能是專為外部專案依附而設計的程式庫,或是可供外部專案建構程序使用的二進位檔。
依附元件
依附元件應僅限於直接依附元件 (規則中列出的來源所需依附元件)。請勿列出遞移依附元件。
套件本機的依附元件應列在最前面,並以與上方「參照目前套件中的目標」一節相容的方式參照 (而非使用絕對套件名稱)。
建議直接列出依附元件,做為單一清單。將多個目標的「通用」依附元件放入變數中,會降低可維護性,導致工具無法變更目標的依附元件,並可能導致未使用的依附元件。
Globs
使用 []
表示「沒有目標」。請勿使用不相符的 glob,因為這比空白清單更容易出錯,也較不明顯。
遞迴
請勿使用遞迴 glob 來比對來源檔案 (例如 glob(["**/*.java"])
)。
遞迴 glob 會略過含有 BUILD
檔案的子目錄,因此難以推斷 BUILD
檔案。
一般來說,遞迴 glob 的效率不如每個目錄都有 BUILD
檔案,且檔案之間定義了依附元件圖表,因為這樣可提升遠端快取和並行處理的效率。
建議您在每個目錄中撰寫 BUILD
檔案,並定義目錄之間的依附元件關係圖。
非遞迴
一般來說,非遞迴 glob 都可以接受。
避免使用清單理解
避免在 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",
...)
...
請勿使用 deps 變數
請勿使用清單變數封裝常見的依附元件:
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 提供字串運算子來串連 (+
) 和格式化 (%
),但請謹慎使用。您可能會想將常見的字串部分分解出來,讓運算式更簡潔或中斷長行。不過有時候
您較難一眼讀取中斷的字串值。
在
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 個字元,也不應分割。 標籤應盡可能為字串常值。理由:方便尋找及取代。同時提升可讀性。
名稱屬性的值應為常數字串 (巨集除外)。理由:外部工具會使用名稱屬性參照規則。他們需要找出規則,而不必解讀程式碼。
設定布林值類型的屬性時,請使用布林值,而非整數值。 基於舊版原因,規則仍會視需要將整數轉換為布林值,但我們不建議這麼做。理由:
flaky = 1
可能會誤解為「重新執行一次,即可解決這個目標的問題」。flaky = True
明確指出「這項測試不穩定」。
與 Python 樣式指南的差異
雖然目標是與 Python 樣式指南相容,但仍有幾項差異:
沒有嚴格的行長度限制。長註解和長字串通常會分割成 79 欄,但這並非必要。不應在程式碼審查或預先提交指令碼中強制執行。原因:標籤可能會很長,超出這個限制。工具通常會產生或編輯
BUILD
檔案,這與行長度限制不相容。系統不支援隱含字串串連。使用
+
運算子。 理由:BUILD
檔案包含許多字串清單。很容易忘記逗號,導致結果完全不同。這在過去造成許多錯誤。另請參閱這項討論。在規則中,關鍵字引數的
=
符號前後要加上空格。理由:具名引數比 Python 更常使用,且一律位於獨立一行。空格可提升可讀性。這項慣例已存在很長一段時間,不值得修改所有現有的BUILD
檔案。根據預設,字串會使用雙引號。理由:Python 樣式指南未指定此項目,但建議保持一致。因此我們決定只使用雙引號字串。許多語言會使用雙引號標註字串常值。
在兩個頂層定義之間使用單一空白行。理由:
BUILD
檔案的結構與一般 Python 檔案不同。當中只包含頂層陳述式。使用單一空白行可縮短BUILD
檔案。