Hướng dẫn XÂY DỰNG phong cách

Việc định dạng tệp BUILD tuân theo cách tiếp cận tương tự như Go, trong đó, một công cụ chuẩn hoá sẽ giải quyết hầu hết các vấn đề về định dạng. Buildifier là công cụ phân tích cú pháp và phát hành mã nguồn theo kiểu chuẩn. Do đó, mỗi tệp BUILD sẽ được định dạng theo cùng một cách tự động. Điều này giúp việc định dạng sẽ không gặp vấn đề trong quá trình xem xét mã. Điều này cũng giúp các công cụ hiểu, chỉnh sửa và tạo tệp BUILD dễ dàng hơn.

Đị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 thành phần là không bắt buộc):

  • Nội dung 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 bản dựng tạo sự khác 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ới 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 phần 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á nhận xét khi xoá 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 đến bằng đường dẫn tương ứng với thư mục gói (không bao giờ sử dụng các tệp tham chiếu lên, chẳng hạn như ..). Các tệp đã tạo phải có tiền tố ":" để cho biết rằng các tệp đó không phải là nguồn. Tệp nguồn không được có tiền tố :. 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 cùng tên.

Ưu tiên 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 nằm 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 "dành riêng" có ý nghĩa đặc biệt. Bao gồm cả all, __pkg____subpackages__. Những tên này có ngữ nghĩa đặc biệt và có thể gây nhầm lẫn và các hành vi không mong muốn khi 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 mang tính ràng buộc vẫn được sử dụng rộng rãi tại Google:

  • Nói chung, hãy sử dụng "solid_case"
    • Đối với java_library có một src, điều này có nghĩa là sử dụng tên không giống với tên tệp không có đuôi tệp
    • Đối với các quy tắc *_binary*_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ác src. Đối với java_test, việc này giúp thuộc tính test_class có thể suy ra từ tên của mục tiêu.
  • 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)
  • Hậu tố mục tiêu _test với _test, _unittest, Test hoặc Tests
  • Tránh 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_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 cụ thể của ngôn ngữ phải khớp với proto cơ bản nhưng thay thế _proto bằng một hậu tố cụ thể theo 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ị

Chế độ hiển thị phải được thu hẹp nhất có thể, trong khi vẫn cho phép truy cập thông qua các hoạt động kiểm thử và các phần phụ thuộc đảo ngược. Hãy sử dụng __pkg____subpackages__ nếu phù 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. Đó có thể là các thư viện được thiết kế để dựa vào các dự án bên ngoài hoặc tệp nhị phân mà quy trình xây dựng của dự án bên ngoài có thể sử dụng.

Phần phụ thuộc

Bạn nên hạn chế các phần phụ thuộc ở 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 cần được liệt kê trước và tham chiếu theo cách tương thích với phần 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).

Muốn liệt kê trực tiếp các phần phụ thuộc, 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 nhiều 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 được.

Khối cầu

Chỉ ra "không có mục tiêu" bằng []. Không sử dụng một bóng không khớp với giá trị nào: vì điề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 khối toàn cầu đệ quy để khớp với các tệp nguồn (ví dụ: glob(["**/*.java"])).

Cụm cầu đệ quy khiến tệp BUILD khó giải thích vì chúng bỏ qua các thư mục con chứa tệp BUILD.

Cụm cầu đệ quy thường kém hiệu quả hơn so với việc sử dụng một tệp BUILD trên mỗi thư mục với một biểu đồ phần phụ thuộc được xác định giữa các tệp đó vì điều này cho phép lưu song song và lưu vào bộ nhớ đệm từ xa hiệu quả hơn.

Bạn nên tạo tệp BUILD trong mỗi thư mục và xác định một biểu đồ phần phụ thuộc giữa các thư mục đó.

Không đệ quy

Nhìn chung, các khối cầu không đệ quy được chấp nhận.

Quy ước khác

  • 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 các biến (chẳng hạn như my_variable).

  • Tuyệt đối không được tách nhãn, ngay cả khi nhãn dài hơn 79 ký tự. Nhãn phải là giá trị cố định dạng chuỗi bất cứ khi nào có thể. Rationale: Giúp bạn dễ dàng tìm và thay thế. Hàm này 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ác công cụ bên ngoài sử dụng thuộc tính tên để tham chiếu một quy tắc. Chúng cần tìm các quy tắc mà không phải diễn giải mã.

  • Khi thiết lập các thuộc tính kiểu boolean, hãy dùng giá trị boolean chứ 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 khi cần, nhưng bạn không nên làm vậy. Rationale: flaky = 1 có thể bị đọc sai khi nói rằng "bỏ qua mục tiêu này bằng cách chạy lại mục tiêu một lần". flaky = True tuyên bố rõ ràng "quy trình kiểm thử này không ổn định".

Điểm khác biệt với hướng dẫn về quy tắc Python

Mặc dù mục tiêu về khả năng tương thích với hướng dẫn quy tắc Python, nhưng vẫn sẽ có một số điểm khác biệt:

  • Không có giới hạn độ dài dòng nghiêm ngặt. Các ghi chú 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 chính sách này trong quá trình xem xét mã hoặc tập lệnh gửi trước. Rationale: Nhãn có thể dài và vượt quá giới hạn này. Thông thường, các tệp BUILD sẽ được các công cụ tạo hoặc chỉnh sửa, chúng không hoạt động tốt với giới hạn độ dài dòng.

  • Không hỗ trợ nối chuỗi ngầm. Dùng toán tử +. Rationale: Các tệp BUILD chứa nhiều danh sách chuỗi. Rất dễ quên dấu phẩy, dẫn đến một kết quả khác hoàn toàn. Việc này đã tạo ra nhiều lỗi trước đây. Xem thêm cuộc thảo luận này.

  • Sử dụng dấu cách xung quanh dấu = cho đối số từ khoá trong quy tắc. Rationale: Đố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. Dấu cách giúp dễ đọc hơn. Quy ước này đã có từ lâu và bạn không nên sửa đổi tất cả các tệp BUILD hiện có.

  • Theo mặc định, hãy sử dụng dấu ngoặc kép cho chuỗi. Rationale: Lớp này không được chỉ định trong hướng dẫn quy tắc Python, nhưng 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 được trích dẫn kép. Nhiều ngôn ngữ sử dụng dấu ngoặc kép cho giá trị cố định dạng chuỗi.

  • Sử dụng một dòng trống 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ư tệp Python thông thường. Lớp này chỉ có các câu lệnh cấp cao nhất. Việc sử dụng một dòng trống duy nhất sẽ giúp tệp BUILD ngắn hơn.