Trang này trình bày các nguyên tắc cơ bản về kiểu cho Starlark, đồng thời cung cấp thông tin về các macro và quy tắc.
Starlark là một ngôn ngữ xác định cách tạo phần mềm, do đó, đây vừa là ngôn ngữ lập trình vừa là ngôn ngữ cấu hình.
Bạn sẽ dùng Starlark để viết các tệp BUILD
, macro và quy tắc xây dựng. Về cơ bản, macro và quy tắc là các ngôn ngữ meta – chúng xác định cách viết các tệp BUILD
.
BUILD
tệp được thiết kế để đơn giản và lặp lại.
Mọi phần mềm đều được đọc thường xuyên hơn là được viết. Điều này đặc biệt đúng với Starlark, vì các kỹ sư đọc tệp BUILD
để hiểu các phần phụ thuộc của mục tiêu và thông tin chi tiết về bản dựng. Việc đọc này thường diễn ra trong lúc rảnh, vội vàng hoặc song song với việc hoàn thành một số nhiệm vụ khác. Do đó, sự đơn giản và khả năng đọc là rất quan trọng để người dùng có thể phân tích cú pháp và hiểu nhanh các tệp BUILD
.
Khi mở một tệp BUILD
, người dùng muốn nhanh chóng biết danh sách các mục tiêu trong tệp; hoặc xem xét danh sách các nguồn của thư viện C++ đó; hoặc xoá một phần phụ thuộc khỏi tệp nhị phân Java đó. Mỗi lần thêm một lớp trừu tượng, bạn sẽ khiến người dùng khó thực hiện các tác vụ này hơn.
BUILD
tệp cũng được nhiều công cụ phân tích và cập nhật. Các công cụ có thể không chỉnh sửa được tệp BUILD
của bạn nếu tệp đó sử dụng các thành phần trừu tượng. Việc giữ cho các tệp BUILD
của bạn đơn giản sẽ giúp bạn có được các công cụ tốt hơn. Khi cơ sở mã phát triển, bạn sẽ ngày càng thường xuyên thực hiện các thay đổi trên nhiều tệp BUILD
để cập nhật một thư viện hoặc dọn dẹp.
Lời khuyên chung
- Sử dụng Buildifier làm trình định dạng và trình kiểm tra.
- Tuân thủ các nguyên tắc thử nghiệm.
Phong cách
Kiểu Python
Nếu nghi ngờ, hãy làm theo hướng dẫn về kiểu PEP 8 nếu có thể. Cụ thể, hãy dùng 4 thay vì 2 khoảng trắng để thụt lề theo quy ước của Python.
Vì Starlark không phải là Python, nên một số khía cạnh của kiểu Python sẽ không áp dụng. Ví dụ: PEP 8 khuyên bạn nên so sánh với các singleton bằng is
, đây không phải là một toán tử trong Starlark.
Chuỗi văn bản
Tệp và hàm tài liệu bằng cách sử dụng docstring.
Sử dụng chuỗi tài liệu ở đầu mỗi tệp .bzl
và chuỗi tài liệu cho từng hàm công khai.
Quy tắc và khía cạnh của tài liệu
Các quy tắc và khía cạnh, cùng với các thuộc tính của chúng, cũng như các nhà cung cấp và trường của họ, phải được ghi lại bằng đối số doc
.
Quy ước đặt tên
- Các biến và tên hàm sử dụng chữ thường, các từ được phân tách bằng dấu gạch dưới (
[a-z][a-z0-9_]*
), chẳng hạn nhưcc_library
. - Các giá trị riêng tư cấp cao nhất bắt đầu bằng một dấu gạch dưới. Bazel thực thi rằng các giá trị riêng tư không thể được dùng từ các tệp khác. Các biến cục bộ không được sử dụng tiền tố dấu gạch dưới.
Chiều dài dòng
Giống như trong tệp BUILD
, không có giới hạn nghiêm ngặt về độ dài dòng vì nhãn có thể dài.
Khi có thể, hãy cố gắng sử dụng tối đa 79 ký tự trên mỗi dòng (theo hướng dẫn về kiểu của Python, PEP 8). Không nên thực thi nghiêm ngặt nguyên tắc này: trình chỉnh sửa nên hiển thị hơn 80 cột, các thay đổi tự động sẽ thường xuyên đưa ra các dòng dài hơn và con người không nên mất thời gian để chia các dòng đã có thể đọc được.
Đối số từ khoá
Trong đối số từ khoá, bạn nên dùng khoảng trắng xung quanh dấu bằng:
def fct(name, srcs):
filtered_srcs = my_filter(source = srcs)
native.cc_library(
name = name,
srcs = filtered_srcs,
testonly = True,
)
Giá trị boolean
Ưu tiên các giá trị True
và False
(thay vì 1
và 0
) cho các giá trị boolean (chẳng hạn như khi sử dụng một thuộc tính boolean trong một quy tắc).
Chỉ dùng lệnh in để gỡ lỗi
Đừng sử dụng hàm print()
trong mã sản xuất; hàm này chỉ dùng để gỡ lỗi và sẽ gửi thông báo rác cho tất cả người dùng trực tiếp và gián tiếp của tệp .bzl
. Ngoại lệ duy nhất là bạn có thể gửi mã sử dụng print()
nếu mã đó bị vô hiệu hoá theo mặc định và chỉ có thể được bật bằng cách chỉnh sửa nguồn – ví dụ: nếu tất cả các lần sử dụng print()
đều được bảo vệ bằng if DEBUG:
trong đó DEBUG
được mã hoá cứng thành False
. Hãy cân nhắc xem những câu này có đủ hữu ích để biện minh cho tác động của chúng đối với khả năng đọc hay không.
Macro
Macro là một hàm khởi tạo một hoặc nhiều quy tắc trong giai đoạn tải. Nhìn chung, hãy sử dụng quy tắc thay vì macro bất cứ khi nào có thể. Biểu đồ bản dựng mà người dùng nhìn thấy không giống với biểu đồ mà Bazel sử dụng trong quá trình tạo – các macro được mở rộng trước khi Bazel phân tích bất kỳ biểu đồ bản dựng nào.
Do đó, khi có sự cố xảy ra, người dùng sẽ cần hiểu rõ cách triển khai macro của bạn để khắc phục các vấn đề về bản dựng. Ngoài ra, bạn có thể khó diễn giải kết quả bazel
query
vì các mục tiêu xuất hiện trong kết quả đến từ việc mở rộng macro. Cuối cùng, các khía cạnh không nhận biết được macro, vì vậy, các công cụ tuỳ thuộc vào các khía cạnh (IDE và những công cụ khác) có thể không hoạt động.
Một cách sử dụng macro an toàn là xác định các mục tiêu bổ sung dự kiến sẽ được tham chiếu trực tiếp tại Bazel CLI hoặc trong các tệp BUILD: Trong trường hợp đó, chỉ người dùng cuối của những mục tiêu đó mới cần biết về chúng và mọi vấn đề về bản dựng do macro gây ra sẽ không bao giờ xa cách với việc sử dụng chúng.
Đối với các macro xác định mục tiêu được tạo (chi tiết triển khai của macro không được tham chiếu tại CLI hoặc phụ thuộc vào các mục tiêu không được khởi tạo bởi macro đó), hãy làm theo các phương pháp hay nhất sau:
- Macro phải lấy đối số
name
và xác định một mục tiêu có tên đó. Mục tiêu đó sẽ trở thành mục tiêu chính của macro đó. - Các mục tiêu được tạo, tức là tất cả các mục tiêu khác do một macro xác định, phải:
- Có tên bắt đầu bằng
<name>
hoặc_<name>
. Ví dụ: sử dụngname = '%s_bar' % (name)
. - Bị hạn chế khả năng hiển thị (
//visibility:private
) và - Có thẻ
manual
để tránh việc mở rộng trong các mục tiêu có ký tự đại diện (:all
,...
,:*
, v.v.).
- Có tên bắt đầu bằng
- Bạn chỉ nên dùng
name
để lấy tên của các mục tiêu do macro xác định, chứ không dùng cho bất kỳ mục đích nào khác. Ví dụ: đừng sử dụng tên để lấy một phần phụ thuộc hoặc tệp đầu vào không do chính macro tạo. - Tất cả các mục tiêu được tạo trong macro phải được ghép nối theo cách nào đó với mục tiêu chính.
- Theo quy ước,
name
phải là đối số đầu tiên khi xác định một macro. - Giữ cho tên tham số trong macro nhất quán. Nếu một tham số được truyền dưới dạng giá trị thuộc tính đến mục tiêu chính, hãy giữ nguyên tên của tham số đó. Nếu một thông số macro có cùng mục đích với một thuộc tính quy tắc chung, chẳng hạn như
deps
, hãy đặt tên cho thông số đó như cách bạn đặt tên cho thuộc tính (xem bên dưới). - Khi gọi một macro, chỉ sử dụng các đối số từ khoá. Điều này nhất quán với các quy tắc và giúp cải thiện đáng kể khả năng đọc.
Các kỹ sư thường viết macro khi API Starlark của các quy tắc có liên quan không đủ cho trường hợp sử dụng cụ thể của họ, bất kể quy tắc đó được xác định trong Bazel bằng mã gốc hay trong Starlark. Nếu bạn đang gặp phải vấn đề này, hãy hỏi tác giả quy tắc xem họ có thể mở rộng API để đạt được mục tiêu của bạn hay không.
Theo quy tắc chung, macro càng giống với các quy tắc thì càng tốt.
Xem thêm về macro.
Quy tắc
- Các quy tắc, khía cạnh và thuộc tính của chúng phải sử dụng tên lower_case ("snake case").
- Tên quy tắc là danh từ mô tả loại chính của cấu phần phần mềm do quy tắc tạo ra, theo quan điểm về các phần phụ thuộc của quy tắc (hoặc đối với các quy tắc lá, người dùng). Đây không nhất thiết là hậu tố tệp. Ví dụ: một quy tắc tạo ra các cấu phần phần mềm C++ được dùng làm tiện ích Python có thể được gọi là
py_extension
. Đối với hầu hết các ngôn ngữ, các quy tắc điển hình bao gồm:*_library
– một đơn vị biên dịch hoặc "mô-đun".*_binary
– mục tiêu tạo ra một tệp thực thi hoặc một đơn vị triển khai.*_test
– mục tiêu kiểm thử. Việc này có thể bao gồm nhiều thử nghiệm. Dự kiến tất cả các kiểm thử trong một mục tiêu*_test
sẽ là các biến thể của cùng một chủ đề, ví dụ: kiểm thử một thư viện duy nhất.*_import
: một mục tiêu đóng gói một cấu phần phần mềm được biên dịch trước, chẳng hạn như.jar
hoặc.dll
được dùng trong quá trình biên dịch.
- Sử dụng tên và loại nhất quán cho các thuộc tính. Một số thuộc tính thường áp dụng bao gồm:
srcs
:label_list
, cho phép các tệp: tệp nguồn, thường do con người tạo ra.deps
:label_list
, thường không cho phép các tệp: các phần phụ thuộc của quá trình biên dịch.data
:label_list
, cho phép các tệp: tệp dữ liệu, chẳng hạn như dữ liệu kiểm thử, v.v.runtime_deps
:label_list
: các phần phụ thuộc trong thời gian chạy không cần thiết để biên dịch.
- Đối với mọi thuộc tính có hành vi không rõ ràng (ví dụ: mẫu chuỗi có các phép thay thế đặc biệt hoặc các công cụ được gọi theo các yêu cầu cụ thể), hãy cung cấp tài liệu bằng cách sử dụng đối số từ khoá
doc
cho khai báo thuộc tính (attr.label_list()
hoặc tương tự). - Các hàm triển khai quy tắc hầu như luôn phải là hàm riêng tư (được đặt tên bằng dấu gạch dưới ở đầu). Một kiểu phổ biến là đặt tên cho hàm triển khai cho
myrule
là_myrule_impl
. - Truyền thông tin giữa các quy tắc bằng cách sử dụng giao diện nhà cung cấp được xác định rõ. Khai báo và ghi lại các trường của nhà cung cấp.
- Thiết kế quy tắc sao cho có thể mở rộng. Hãy cân nhắc rằng các quy tắc khác có thể muốn tương tác với quy tắc của bạn, truy cập vào các nhà cung cấp của bạn và sử dụng lại các thao tác mà bạn tạo.
- Tuân thủ nguyên tắc về hiệu suất trong các quy tắc của bạn.