Trang này trình bày các nguyên tắc cơ bản về kiểu cho Starlark cũng như thông tin về macro và quy tắc.
Starlark là một ngôn ngữ xác định cách xây dựng phần mềm, do đó, ngôn ngữ nà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 để ghi tệp BUILD
, macro và quy tắc bản dựng. Về cơ bản, macro và quy tắc là ngôn ngữ siêu dữ liệu – chúng xác định cách viết các tệp BUILD
.
Tệp BUILD
có thiết kế đơn giản và lặp lại.
Mọi phần mềm được đọc thường xuyên hơn so với được viết ra. Điều này đặc biệt đúng đối với Starlark, khi 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. Hoạt động đọc này thường sẽ xảy ra khi đang lướt qua, vội vàng hoặc song song để hoàn thành một số nhiệm vụ khác. Do đó, tính đơn giản và dễ đọc là điều rất quan trọng để người dùng có thể phân tích cú pháp và nhanh chóng hiểu được tệp BUILD
.
Khi mở tệp BUILD
, người dùng sẽ nhanh chóng muốn biết danh sách các mục tiêu trong tệp; hoặc xem lại 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.
Các tệp BUILD
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 đối tượng trừu tượng. Việc đơn giản hoá các tệp BUILD
sẽ giúp bạn có công cụ tốt hơn. Khi cơ sở mã phát triển, việc thực hiện các thay đổi trên nhiều tệp BUILD
để cập nhật thư viện hoặc dọn dẹp ngày càng trở nên thường xuyên hơn.
Lời khuyên chung
- Sử dụng Buildifier (Trình định dạng) làm trình định dạng và linter (tìm lỗi mã nguồn).
- Tuân thủ nguyên tắc kiểm thử.
Phong cách
Kiểu Python
Khi nghi ngờ, hãy làm theo hướng dẫn quy tắc dành cho Người có yếu tố chính trị (PEP 8) nếu có thể. Cụ thể, hãy sử dụng 4 thay vì 2 dấu cách để 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 không áp dụng được. Ví dụ: PEP 8 khuyên bạn nên thực hiện việc so sánh với các singleton bằng is
, vì đây không phải là toán tử trong Starlark.
Chuỗi tài liệu
Ghi lại các tệp và hàm bằng chuỗi tài liệu (docstrings).
Sử dụng một chuỗi tài liệu ở đầu mỗi tệp .bzl
và một chuỗi tài liệu cho mỗi hàm công khai.
Các 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 thuộc tính, cũng như trình cung cấp và trường của các quy tắc đó, phải được ghi lại bằng cách sử dụng đối số doc
.
Quy ước đặt tên
- Tên biến và hàm sử dụng chữ viết thường, trong đó 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 việc không thể dùng các giá trị riêng tư trong các tệp khác. Biến cục bộ không nên sử dụng tiền tố dấu gạch dưới.
Độ dài của đường kẻ
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ự mỗi dòng (làm 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 phải 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 sẽ không tốn thời gian để phân tách các dòng đã đọc được.
Đối số từ khoá
Trong đối số từ khoá, dấu cách xung quanh dấu bằng được ưu tiên:
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 thuộc tính boolean trong một quy tắc).
Chỉ sử dụng bản in để gỡ lỗi
Không dùng hàm print()
trong mã phát hành chính thức; hàm này chỉ dùng để gỡ lỗi và sẽ gửi thư 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ã này bị tắt 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 cách sử dụng print()
đều được bảo vệ bằng if DEBUG:
, trong đó DEBUG
được mã cứng vào False
. Hãy lưu ý xem các câu lệnh này có đủ hữu ích để chứng minh tác động của chúng đến khả năng đọc hay không.
Macro
Macro là một hàm tạo thực thể cho một hoặc nhiều quy tắc trong giai đoạn tải. Nói chung, sử dụng quy tắc bất cứ khi nào có thể thay vì macro. 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 bản dựng – các macro được mở rộng trước khi Bazel thực hiện bất kỳ hoạt động phân tích biểu đồ bản dựng nào.
Do đó, khi xảy ra lỗi, người dùng sẽ cần hiểu cách triển khai macro của bạn để khắc phục sự cố về bản dựng. Ngoài ra, kết quả bazel
query
có thể khó diễn giải vì các mục tiêu hiển thị 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ông cụ tuỳ thuộc vào các khía cạnh (IDE và các khía cạnh khác) có thể không thành công.
Cách sử dụng an toàn cho macro 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 tệp BUILD: Trong trường hợp đó, chỉ người dùng cuối của các mục tiêu đó mới cần biết về các mục tiêu đó và mọi sự cố bản dựng do macro gây ra không bao giờ vượt quá mức sử dụng của macro.
Đố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 đến tại CLI hoặc phụ thuộc vào các mục tiêu không được macro đó tạo thực thể), 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 đó trở thành mục tiêu chính của macro đó. - Các mục tiêu được tạo (là tất cả các mục tiêu khác do macro xác định) phải:
- Có tiền tố tên là
<name>
hoặc_<name>
. Ví dụ: sử dụngname = '%s_bar' % (name)
. - Bị hạn chế hiển thị (
//visibility:private
) và - Có thẻ
manual
để tránh mở rộng trong các mục tiêu ký tự đại diện (:all
,...
,:*
, v.v.).
- Có tiền tố tên là
- Bạn chỉ nên sử dụng
name
để lấy tên của các mục tiêu do macro xác định, chứ không phải cho bất kỳ mục tiêu nào khác. Ví dụ: không dùng tên này để lấy phần phụ thuộc hoặc tệp đầu vào không phải do chính macro tạo ra. - Tất cả các mục tiêu được tạo trong macro phải được kết hợp theo cách nào đó với mục tiêu chính.
- 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 cho mục tiêu chính, hãy giữ nguyên tên của tham số đó. Nếu một tham số macro có cùng mục đích với thuộc tính quy tắc chung, chẳng hạn như
deps
, hãy dùng tên giống như cách bạn sử dụng thuộc tính đó (xem bên dưới). - Khi gọi macro, chỉ sử dụng các đối số từ khoá. Điều này phù hợp 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 Starlark API của các quy tắc có liên quan là 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 trong mã gốc hay trong Starlark. Nếu bạn 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 để hoàn thành mục tiêu của bạn hay không.
Theo quy tắc chung, càng nhiều macro giống với quy tắc càng tốt.
Bạn cũng có thể xem 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 viết thường ("chữ viết hoa rắn").
- Tên quy tắc là danh từ mô tả loại cấu phần phần mềm chính do quy tắc tạo ra, từ góc độ các phần phụ thuộc của quy tắc đó (hoặc người dùng) trong quy tắc lá. Đây không nhất thiết là hậu tố của tệp. Ví dụ: một quy tắc tạo ra cấu phần phần mềm C++ dùng làm tiện ích Python có thể được gọi là
py_extension
. Đối với hầu hết ngôn ngữ, các quy tắc thông thường bao gồm:*_library
– một đơn vị biên dịch hoặc "mô-đun".*_binary
– một mục tiêu tạo tệp thực thi hoặc đơn vị triển khai.*_test
– một mục tiêu thử nghiệm. Bạn có thể bao gồm nhiều bài kiểm thử. Tất cả các lượt kiểm thử trong mục tiêu*_test
đều là biến thể của cùng một chủ đề, chẳng hạn như kiểm thử một thư viện.*_import
: một mục tiêu đóng gói cấu phần phần mềm được biên dịch trước, chẳng hạn như.jar
hoặc.dll
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 là do con người tạo.deps
:label_list
, thường không cho phép tệp: phần phụ thuộc 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 thời gian chạy không cần thiết cho việc 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ó lựa chọn thay thế đặc biệt hoặc công cụ được gọi với 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 phần khai báo của 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 là các 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
_myrule_impl
cho hàm triển khai chomyrule
. - Truyền thông tin giữa các quy tắc bằng giao diện provider được xác định rõ ràng. Khai báo và ghi nhận các trường của trình cung cấp.
- Thiết kế quy tắc chú trọng đến khả năng mở rộng. Hãy lưu ý 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 trình cung cấp 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 quy tắc của bạn.