Định dạng tệp BUILD
tuân theo cùng một phương pháp như Go, trong đó một công cụ tiêu chuẩn hoá sẽ xử lý hầu hết các vấn đề về định dạng.
Buildifier là một công cụ phân tích cú pháp và phát ra mã nguồn theo một kiểu tiêu chuẩn. Do đó, mọi tệp BUILD
đều được định dạng theo cùng một cách tự động, giúp việc định dạng không còn là vấn đề trong quá trình xem xét mã. Điều này cũng giúp các công cụ dễ dàng hiểu, chỉnh sửa và tạo tệp BUILD
.
Định dạng tệp BUILD
phải khớp với đầu ra của buildifier
.
Ví dụ về định dạng
# 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",
],
)
Cấu trúc tệp
Đề xuất: Sử dụng thứ tự sau (mọi phần tử đều không bắt buộc):
Nội dung mô tả gói (một nhận xét)
Tất cả câu lệnh
load()
Hàm
package()
.Gọi các quy tắc và macro
Buildifier phân biệt giữa một nhận xét độc lập và một nhận xét được đính kèm vào một phần tử. Nếu một nhận xét không được đính kèm vào một phần tử cụ thể, hãy sử dụng một dòng trống sau nhận xét đó. Sự khác biệt này rất quan trọng khi thực hiện các thay đổi tự động (ví dụ: để giữ lại hoặc xoá một bình luận khi xoá một quy tắc).
# Standalone comment (such as to make a section in a file)
# Comment for the cc_library below
cc_library(name = "cc")
Tham chiếu đến các mục tiêu trong gói hiện tại
Các tệp phải được tham chiếu theo đường dẫn tương ứng với thư mục gói (không bao giờ sử dụng các tham chiếu lên, chẳng hạn như ..
). Các tệp được tạo phải có tiền tố ":
" để cho biết rằng chúng không phải là nguồn. Tệp nguồn không được có tiền tố là :
. Các quy tắc phải có tiền tố là :
. Ví dụ: giả sử x.cc
là một tệp nguồn:
cc_library(
name = "lib",
srcs = ["x.cc"],
hdrs = [":gen_header"],
)
genrule(
name = "gen_header",
srcs = [],
outs = ["x.h"],
cmd = "echo 'int x();' > $@",
)
Đặt tên mục tiêu
Tên mục tiêu phải mang tính mô tả. Nếu một mục tiêu chứa một tệp nguồn, thì mục tiêu đó thường phải có tên bắt nguồn từ nguồn đó (ví dụ: cc_library
cho chat.cc
có thể được đặt tên là chat
hoặc java_library
cho DirectMessage.java
có thể được đặt tên là direct_message
).
Mục tiêu cùng tên cho một gói (mục tiêu có cùng tên với thư mục chứa) phải cung cấp chức năng được mô tả theo tên thư mục. Nếu không có mục tiêu nào như vậy, đừng tạo mục tiêu trùng tên.
Nên sử dụng tên ngắn khi đề cập đến mục tiêu cùng tên (//x
thay vì //x:x
). Nếu bạn ở trong cùng một gói, hãy ưu tiên tham chiếu cục bộ (:x
thay vì //x
).
Tránh sử dụng tên mục tiêu "đã đặt trước" có ý nghĩa đặc biệt. Điều này bao gồm all
, __pkg__
và __subpackages__
. Các tên này có ngữ nghĩa đặc biệt và có thể gây nhầm lẫn cũng như hành vi không mong muốn khi được sử dụng.
Trong trường hợp không có quy ước chung của nhóm, sau đây là một số đề xuất không bắt buộc thường được sử dụng tại Google:
- Nhìn chung, hãy sử dụng "snake_case"
- Đối với một
java_library
có mộtsrc
, điều này có nghĩa là bạn phải sử dụng một tên không giống với tên tệp mà không có đuôi - Đối với các quy tắc
*_binary
và*_test
của Java, hãy sử dụng "Upper CamelCase". Điều này cho phép tên mục tiêu khớp với một trong cácsrc
. Đối vớijava_test
, điều này giúp bạn có thể suy luận thuộc tínhtest_class
từ tên của mục tiêu.
- Đối với một
- Nếu có nhiều biến thể của một mục tiêu cụ thể, hãy thêm một hậu tố để phân biệt (chẳng hạn như.
:foo_dev
,:foo_prod
hoặc:bar_x86
,:bar_x64
) - Thêm hậu tố cho
_test
mục tiêu bằng_test
,_unittest
,Test
hoặcTests
- Tránh sử dụng các hậu tố vô nghĩa như
_lib
hoặc_library
(trừ phi cần thiết để tránh xung đột giữa mục tiêu_library
và_binary
tương ứng) - Đối với các mục tiêu liên quan đến proto:
proto_library
mục tiêu phải có tên kết thúc bằng_proto
- Các quy tắc cụ thể về ngôn ngữ
*_proto_library
phải khớp với proto cơ bản nhưng thay thế_proto
bằng một hậu tố cụ thể về ngôn ngữ, chẳng hạn như:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
Chế độ hiển thị
Khả năng hiển thị phải được giới hạn chặt chẽ nhất có thể, trong khi vẫn cho phép các kiểm thử và các phần phụ thuộc đảo ngược truy cập. Hãy sử dụng __pkg__
và __subpackages__
khi thích hợp.
Tránh đặt gói default_visibility
thành //visibility:public
.
//visibility:public
chỉ được đặt riêng cho các mục tiêu trong API công khai của dự án. Đây có thể là những thư viện được thiết kế để các dự án hoặc tệp nhị phân bên ngoài phụ thuộc vào, hoặc những thư viện mà quy trình tạo của dự án bên ngoài có thể sử dụng.
Phần phụ thuộc
Các phần phụ thuộc phải được giới hạn ở các phần phụ thuộc trực tiếp (các phần phụ thuộc cần thiết cho các nguồn được liệt kê trong quy tắc). Không liệt kê các phần phụ thuộc bắc cầu.
Các phần phụ thuộc cục bộ của gói phải được liệt kê trước và được tham chiếu theo cách tương thích với phần Tham chiếu đến các mục tiêu trong gói hiện tại ở trên (không phải bằng tên gói tuyệt đối).
Bạn nên liệt kê các phần phụ thuộc trực tiếp dưới dạng một danh sách duy nhất. Việc đưa các phần phụ thuộc "chung" của một số mục tiêu vào một biến sẽ làm giảm khả năng duy trì, khiến các công cụ không thể thay đổi các phần phụ thuộc của một mục tiêu và có thể dẫn đến các phần phụ thuộc không dùng đến.
Globs
Cho biết "không có mục tiêu" bằng []
. Không sử dụng một mẫu chung không khớp với bất kỳ nội dung nào: mẫu này dễ xảy ra lỗi hơn và ít rõ ràng hơn so với một danh sách trống.
Đệ quy
Không sử dụng các glob đệ quy để so khớp tệp nguồn (ví dụ: glob(["**/*.java"])
).
Các glob đệ quy khiến cho việc suy luận về các tệp BUILD
trở nên khó khăn vì chúng bỏ qua các thư mục con chứa tệp BUILD
.
Các glob đệ quy thường kém hiệu quả hơn so với việc có một tệp BUILD
cho mỗi thư mục với một biểu đồ phần phụ thuộc được xác định giữa chúng vì điều này cho phép lưu vào bộ nhớ đệm từ xa và tính song song tốt hơn.
Bạn nên tạo một tệp BUILD
trong mỗi thư mục và xác định biểu đồ phần phụ thuộc giữa các thư mục đó.
Không đệ quy
Các glob không đệ quy thường được chấp nhận.
Quy ước khác
Sử dụng chữ in hoa và dấu gạch dưới để khai báo hằng số (chẳng hạn như
GLOBAL_CONSTANT
), sử dụng chữ in thường và dấu gạch dưới để khai báo biến (chẳng hạn nhưmy_variable
).Bạn không bao giờ được chia nhãn, ngay cả khi nhãn dài hơn 79 ký tự. Nhãn phải là chuỗi ký tự theo nghĩa đen bất cứ khi nào có thể. Lý do: Giúp bạn dễ dàng tìm và thay thế. Việc này cũng giúp nội dung dễ đọc hơn.
Giá trị của thuộc tính name phải là một chuỗi hằng số theo nghĩa đen (ngoại trừ trong macro). Lý do: Các công cụ bên ngoài sử dụng thuộc tính name để tham chiếu đến một quy tắc. Họ cần tìm các quy tắc mà không cần phải diễn giải mã.
Khi đặt các thuộc tính thuộc loại boolean, hãy sử dụng giá trị boolean chứ không phải giá trị số nguyên. Vì lý do cũ, các quy tắc vẫn chuyển đổi số nguyên thành giá trị boolean khi cần, nhưng bạn không nên làm như vậy. Lý do:
flaky = 1
có thể bị đọc nhầm thành "deflake this target by rerunning it once" (giảm số lần thất bại của mục tiêu này bằng cách chạy lại một lần).flaky = True
nói rõ ràng rằng "thử nghiệm này không ổn định".
Điểm khác biệt với hướng dẫn về kiểu Python
Mặc dù mục tiêu là tương thích với hướng dẫn về kiểu Python, nhưng vẫn có một số điểm khác biệt:
Không có giới hạn nghiêm ngặt về độ dài dòng. Các bình luận và chuỗi dài thường được chia thành 79 cột, nhưng không bắt buộc. Bạn không nên thực thi quy tắc này trong các quy trình đánh giá mã hoặc tập lệnh trước khi gửi. Lý do: Nhãn có thể dài và vượt quá giới hạn này. Các tệp
BUILD
thường được tạo hoặc chỉnh sửa bằng các công cụ, điều này không phù hợp với giới hạn độ dài dòng.Không hỗ trợ việc nối chuỗi ngầm định. Sử dụng toán tử
+
. Lý do: TệpBUILD
chứa nhiều danh sách chuỗi. Bạn rất dễ quên dấu phẩy, dẫn đến kết quả hoàn toàn khác. Điều này đã gây ra nhiều lỗi trong quá khứ. Xem thêm cuộc thảo luận này.Sử dụng khoảng trắng xung quanh dấu
=
cho các đối số từ khoá trong quy tắc. Lý do: Các đối số được đặt tên thường xuyên hơn nhiều so với trong Python và luôn nằm trên một dòng riêng. Dấu cách giúp cải thiện khả năng đọc. Quy ước này đã tồn tại từ lâu và không đáng để sửa đổi tất cả các tệpBUILD
hiện có.Theo mặc định, hãy sử dụng dấu ngoặc kép cho các chuỗi. Lý do: Điều này không được chỉ định trong hướng dẫn về phong cách Python, nhưng hướng dẫn này khuyến nghị tính nhất quán. Vì vậy, chúng tôi quyết định chỉ sử dụng các chuỗi trong dấu ngoặc kép. Nhiều ngôn ngữ sử dụng dấu ngoặc kép cho các chuỗi ký tự.
Sử dụng một dòng trống duy nhất giữa hai định nghĩa cấp cao nhất. Lý do: Cấu trúc của tệp
BUILD
không giống như một tệp Python thông thường. Nó chỉ có các câu lệnh cấp cao nhất. Việc sử dụng một dòng trống sẽ giúp các tệpBUILD
ngắn hơn.