Hướng dẫn văn phong .bzl

Trang này trình bày các nguyên tắc tạo kiểu cơ bản 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ữ định nghĩa cách xây dựng phần mềm, và do đó vừa là ngôn ngữ lập trình vừa là ngôn ngữ cấu hình.

Bạn sẽ sử dụng Starlark để ghi các tệp BUILD, macro và quy tắc tạo bản dựng. Về cơ bản, macro và quy tắc là siêu ngôn ngữ – chúng xác định cách viết tệp BUILD. BUILD tệp nên đơ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 nội dung viết ra. Điều này đặc biệt đúng với Starlark, vì các kỹ sư đọc các tệp BUILD để hiểu được các phần phụ thuộc của mục tiêu cũng như thông tin chi tiết về bản dựng. Kết quả đọc này thường diễn ra khi đang chạy, khi đang vội hoặc song song với khi hoàn thành một số nhiệm vụ khác. Do đó, tính đơn giản và dễ đọc là rất quan trọng để người dùng có thể phân tích cú pháp và hiểu rõ các tệp BUILD một cách nhanh chóng.

Khi mở tệp BUILD, người dùng sẽ nhanh chóng muốn biết danh sách 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 xóa 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 những tác vụ đó hơn.

Các tệp BUILD cũng được phân tích và cập nhật bằng nhiều công cụ. 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ẽ cho phép bạn sử dụng công cụ hiệu quả hơn. Khi cơ sở mã phát triển, việ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 thường xuyên hơn.

Lời khuyên chung

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 dùng 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.

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 thực hiện việc so sánh với singleton bằng is, không phải là toán tử trong Starlark.

Chuỗi tài liệu

Ghi lại tệp và hàm bằng cách sử dụng docstring. 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 tương ứng, cũng như trình cung cấp và trường của chúng, phải được ghi lại bằng đối số doc.

Quy ước đặt tên

  • Biến và tên hàm dùng chữ thường với 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.
  • 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 không thể dùng các giá trị riêng tư qua các tệp khá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

Như trong tệp BUILD, không có giới hạn độ dài dòng nghiêm ngặt 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 (theo hướng dẫn quy tắc 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à người dùng không nên mất thời gian phân tách các dòng đã đọc được.

Đối số từ khóa

Trong đối số từ khoá, khoảng trắng 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ị TrueFalse (thay vì 10) 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).

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 nội dung rác cho tất cả người dùng trực tiếp và gián tiếp trong 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ể bật bằng cách chỉnh sửa nguồn – ví dụ: nếu tất cả việc sử dụng print() được bảo vệ bởi if DEBUG:, trong đó DEBUG được mã hoá cứng vào False. Hãy lưu ý xem những câu lệnh này có đủ hữu ích để chứng minh tác động của chúng đối với 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, hãy 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ỳ phân tích biểu đồ bản dựng nào.

Do đó, khi xảy ra sự cố, 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ừ tính năng mở rộng vĩ mô. Cuối cùng, các khía cạnh không nhận biết được macro, vì vậy, việc xử lý công cụ phụ 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 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 các mục tiêu đó mới cần biết về các mục tiêu đó và mọi sự cố về bản dựng do macro tạo ra luôn ảnh hưởng đến cách sử dụng của chúng.

Đối với các macro xác định mục tiêu đã tạo (thông tin 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 macro đó tạo thực thể), hãy làm theo các phương pháp hay nhất sau:

  • Macro phải nhận đối số name và xác định 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 đã tạo (tức là tất cả các mục tiêu khác do macro xác định) nên:
    • Có tiền tố <name> hoặc _<name>. Ví dụ: sử dụng name = '%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.).
  • 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 nên cho bất kỳ mục đích nào khác. Ví dụ: đừng sử dụng tên này để lấy phần phụ thuộc hoặc tệp đầu vào không do chính macro tạo ra.
  • Tất cả các mục tiêu tạo trong macro phải được ghép nối theo một cách nào đó với mục tiêu chính.
  • Đảm bảo tên thông số trong macro nhất quán. Nếu một tham số được chuyể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 phục vụ cùng mục đích với thuộc tính quy tắc chung, chẳng hạn như deps, hãy đặt tên giống như 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 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 liên quan không đủ cho trường hợp sử dụng cụ thể, 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 để đạt được 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 thì càng tốt.

Hãy 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 viết thường ("kiểu 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ừ quan điểm của các phần phụ thuộc (hoặc đối với quy tắc lá là người dùng). Đây không nhất thiết là một hậu tố của tệp. Ví dụ: quy tắc tạo cấu phần phần mềm C++ dùng làm phần mở rộng 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 hay "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ục tiêu thử nghiệm. Quá trình này có thể bao gồm nhiều hoạt động kiểm thử. Dự kiến tất cả các lượt kiểm thử trong một mục tiêu *_test là các biến thể trên cùng một giao diện, chẳng hạn như 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à kiểu nhất quán cho các thuộc tính. Một số thuộc tính thường có thể á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.
    • 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 quá trình 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ơ chế thay thế đặc biệt hoặc các công cụ được gọi khi có 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 nội dung 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à 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 cho myrule.
  • Truyền thông tin giữa các quy tắc bằng giao diện trình cung cấp được xác định rõ ràng. Các trường khai báo và trình cung cấp tài liệu.
  • Thiết kế quy tắc có lưu ý đến khả năng mở rộng. Hãy cân nhắc rằng các quy tắc khác nên tương tác với quy tắc, truy cập vào trình cung cấp và sử dụng lại thao tác 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.