Đị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ụ được chuẩn hoá giải quyết hầu hết các vấn đề về định dạng.
Hàm dựng là một công cụ phân tích cú pháp và phát ra mã nguồn theo kiểu chuẩn. Do đó, mọi tệp BUILD
đều được định dạng theo cách tự động, điều này giúp việc định dạng trở thành vấn đề không cần giải quyết 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 kết quả 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):
Mô tả gói (nhận xét)
Tất cả câu lệnh
load()
Hàm
package()
.Lệnh gọi đến các quy tắc và macro
Trình tạo phân biệt giữa nhận xét độc lập và nhận xét đính kèm với một phần tử. Nếu nhận xét không được đính kèm vào một phần tử cụ thể, hãy sử dụng dòng trống sau đó. Sự khác biệt quan trọng khi thực hiện các thay đổi tự động (ví dụ: để giữ lại hoặc xóa nhận xét khi xóa 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 mục tiêu trong gói hiện tại
Tệp phải được tham chiếu bằng đường dẫn tương ứng với thư mục gói (mà không bao giờ sử dụng tệp tham chiếu lên, chẳng hạn như ..
). Tệp đã tạo phải có tiền tố là ":
" để cho biết đó không phải là nguồn. Các 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ụ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 với 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 như vậy, đừng tạo mục tiêu cùng tên.
Ưu tiên sử dụng tên ngắn khi tham chiếu đến mục tiêu cùng tên (//x
thay vì //x:x
). Nếu bạn đang sử dụng cùng một gói, hãy ưu tiên tệp tham chiếu cục bộ (:x
thay vì //x
).
Tránh sử dụng các tên mục tiêu "đã đặt trước" có ý nghĩa đặc biệt. Các tên này bao gồm
all
, __pkg__
và __subpackages__
, những tên này có ngữ nghĩa
đặc biệt và có thể gây ra nhầm lẫn cũng như hành vi không mong muốn khi sử dụng.
Trong trường hợp không có quy ước chung cho nhóm, dưới đây là một số đề xuất không ràng buộc được sử dụng rộng rãi tại Google:
- Nói chung, hãy sử dụng "snake_case"
- Đối với
java_library
cósrc
, đ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 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
, thuộc tính này có thể giúp suy luận thuộc tínhtest_class
từ tên của mục tiêu.
- Đối với
- Nếu có nhiều biến thể của một mục tiêu cụ thể, hãy thêm hậu tố để phân biệt (chẳng hạn như.
:foo_dev
,:foo_prod
hoặc:bar_x86
,:bar_x64
) - Tước hiệu các mục tiêu
_test
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
*_proto_library
dành riêng cho ngôn ngữ phải khớp với nguyên mẫu cơ bản nhưng thay thế_proto
bằng một hậu tố cụ thể cho ngôn ngữ như:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
Chế độ hiển thị
Mức độ hiển thị nên được giới hạn ở phạm vi chặt chẽ nhất có thể, đồng thời vẫn cho phép truy cập bằng các quy trình kiểm thử và phần phụ thuộc ngược. Sử dụng __pkg__
và __subpackages__
khi thích hợp.
Tránh đặt gói default_visibility
thành //visibility:public
.
Bạn chỉ nên đặt riêng //visibility:public
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ế để phụ thuộc vào các dự án hoặc tệp nhị phân bên ngoài có thể sử dụng trong quá trình xây dựng của một dự án bên ngoài.
Phần phụ thuộc
Các phần phụ thuộc phải được hạn chế theo 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). Đừng liệt kê các phần phụ thuộc bắc cầu.
Các phần phụ thuộc gói gói cục bộ nên được liệt kê trước và tham chiếu theo cách tương thích với Tài liệu tham chiếu đến mục tiêu trong gói hiện tại ở trên (không phải theo tên gói tuyệt đối).
Ưu tiê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 đặt các phần phụ thuộc "phổ biến" của một số mục tiêu vào một biến sẽ làm giảm khả năng bảo trì, khiến các công cụ không thể thay đổi các phần phụ thuộc của mục tiêu và có thể dẫn đến các phần phụ thuộc không sử dụng.
Găng tay
Biểu thị "không có mục tiêu" bằng []
. Không sử dụng trục trặc không khớp với bất kỳ giá trị nào: nó dễ xảy ra lỗi và ít rõ ràng hơn so với danh sách trống.
Định kỳ
Không sử dụng cầu nối đệ quy để khớp với tệp nguồn (ví dụ: glob(["**/*.java"])
).
Các trục trặc định kỳ khiến các tệp BUILD
trở nên khó giải thích vì chúng bỏ qua các thư mục con chứa các tệp BUILD
.
Các hình cầu lặp lại 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 biểu đồ phần phụ thuộc được xác định giữa các định dạng này vì tính năng lưu vào bộ nhớ đệm từ xa và 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ụ thuộc giữa các thư mục đó.
Không định kỳ
Các cầu cầu không đệ quy thường được chấp nhận.
Quy ước khác
Sử dụng chữ 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ữ thường và dấu gạch dưới để khai báo biến (chẳng hạn nhưmy_variable
).Nhãn không bao giờ được chia nhỏ, ngay cả khi nhãn dài hơn 79 ký tự. Nhãn phải là giá trị cố định kiểu chuỗi bất cứ khi nào có thể. Rationale: Giúp bạn dễ dàng tìm và thay thế. Đồng thời, nó cũng cải thiện khả năng đọc.
Giá trị của thuộc tính tên phải là một chuỗi hằng số cố định (ngoại trừ trong macro). Rationale: 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 quy tắc mà không cần phải hiểu mã.
Khi đặt thuộc tính loại boolean, hãy sử dụng giá trị boolean, không phải giá trị số nguyên. Vì các lý do cũ, các quy tắc vẫn chuyển đổi số nguyên thành boolean nếu cần, nhưng bạn không nên làm như vậy. Rationale:
flaky = 1
có thể hiểu sai bằng cách nói rằng "xoá mục tiêu này bằng cách chạy lại mục tiêu một lần".flaky = True
nói rõ ràng rằng "kiểm thử này không ổn định".
Điểm khác biệt với hướng dẫn kiểu Python
Mặc dù tính năng tương thích với hướng dẫn về kiểu Python là mục tiêu, nhưng 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 nhận xét dài và chuỗi dài thường được chia thành 79 cột, nhưng điều này là không bắt buộc. Bạn không nên thực thi mã này trong quá trình xem xét mã hoặc gửi trước tập lệnh. Rationale: Nhãn có thể dài và vượt quá giới hạn này. Thông thường, các công cụ sẽ tạo hoặc chỉnh sửa tệp
BUILD
, tệp này không tuân thủ giới hạn độ dài dòng.Việc nối chuỗi ngầm ẩn không được hỗ trợ. Sử dụng toán tử
+
. Rationale: TệpBUILD
chứa nhiều danh sách chuỗi. Dễ quên một dấu phẩy, dẫn đến một kết quả hoàn chỉnh khác. Điều này đã tạo ra nhiều lỗi trong quá khứ. Xem thêm cuộc thảo luận này.Sử dụng dấu cách quanh dấu
=
cho các đối số từ khoá trong quy tắc. Rationale: 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 biệt. Không gian cải thiện khả năng đọc. Quy ước này đã có từ lâu và bạn không nên 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 chuỗi. Rationale: Thuộc tính này không được chỉ định trong hướng dẫn về kiểu Python nhưng phù hợp nhất. 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 giá trị cố định kiểu chuỗi.
Sử dụng một dòng trống duy nhất giữa hai định nghĩa cấp cao nhất. Rationale: 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ẽ làm choBUILD
tệp ngắn hơn.