Tiện ích mô-đun cho phép người dùng mở rộng hệ thống mô-đun bằng cách đọc dữ liệu đầu vào từ các mô-đun trên biểu đồ phần phụ thuộc, thực hiện logic cần thiết để phân giải các phần phụ thuộc và cuối cùng là tạo kho lưu trữ bằng cách gọi các quy tắc kho lưu trữ. Các tiện ích này có các tính năng tương tự như quy tắc kho lưu trữ, cho phép thực hiện thao tác I/O tệp, gửi yêu cầu mạng, v.v. Ngoài ra, chúng cho phép Bazel tương tác với các hệ thống quản lý gói khác trong khi vẫn tôn trọng biểu đồ phần phụ thuộc được tạo từ các mô-đun Bazel.
Bạn có thể xác định các phần mở rộng của mô-đun trong các tệp .bzl
, giống như quy tắc kho lưu trữ. Các mô-đun này không được gọi trực tiếp; thay vào đó, mỗi mô-đun chỉ định các phần dữ liệu được gọi là thẻ để tiện ích đọc. Bazel chạy độ phân giải mô-đun trước khi đánh giá bất kỳ tiện ích nào. Tiện ích này đọc tất cả thẻ thuộc về tiện ích đó trên toàn bộ biểu đồ phần phụ thuộc.
Mức sử dụng tiện ích
Các tiện ích được lưu trữ trong chính các mô-đun Bazel. Để sử dụng tiện ích trong một mô-đun, trước tiên hãy thêm bazel_dep
trên mô-đun lưu trữ tiện ích, sau đó gọi hàm tích hợp use_extension
để đưa tiện ích đó vào phạm vi. Hãy xem xét ví dụ sau — một đoạn mã từ tệp MODULE.bazel
để sử dụng đuôi "maven" được xác định trong mô-đun rules_jvm_external
:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Thao tác này liên kết giá trị trả về của use_extension
với một biến, cho phép người dùng sử dụng cú pháp dấu chấm để chỉ định các thẻ cho tiện ích. Các thẻ phải tuân theo giản đồ do các lớp thẻ tương ứng xác định trong định nghĩa tiện ích. Xem ví dụ về cách chỉ định một số thẻ maven.install
và maven.artifact
:
maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = ["com.google.j2objc:j2objc-annotations"])
Sử dụng lệnh use_repo
để đưa các kho lưu trữ do tiện ích tạo vào phạm vi của mô-đun hiện tại.
use_repo(maven, "maven")
Các kho lưu trữ do một tiện ích tạo ra là một phần của API của tiện ích đó. Trong ví dụ này, tiện ích mô-đun "maven" hứa hẹn sẽ tạo một kho lưu trữ có tên là maven
. Với phần khai báo ở trên, tiện ích này sẽ phân giải đúng cách các nhãn như @maven//:org_junit_junit
để trỏ đến kho lưu trữ do tiện ích "maven" tạo ra.
Định nghĩa phần mở rộng
Bạn có thể xác định tiện ích mô-đun tương tự như quy tắc kho lưu trữ bằng cách sử dụng hàm module_extension
. Tuy nhiên, mặc dù các quy tắc repo có một số thuộc tính, nhưng phần mở rộng của mô-đun lại có tag_class
, mỗi phần mở rộng có một số thuộc tính. Các lớp thẻ xác định giản đồ cho các thẻ mà tiện ích này sử dụng. Ví dụ: bạn có thể xác định tiện ích "maven" ở trên như sau:
# @rules_jvm_external//:extensions.bzl
_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
implementation = _maven_impl,
tag_classes = {"install": _install, "artifact": _artifact},
)
Các nội dung khai báo này cho thấy bạn có thể chỉ định thẻ maven.install
và maven.artifact
bằng cách sử dụng giản đồ thuộc tính đã chỉ định.
Chức năng triển khai của các tiện ích mô-đun cũng tương tự như các quy tắc repo, ngoại trừ việc chúng nhận được đối tượng module_ctx
. Đối tượng này cấp quyền truy cập vào tất cả các mô-đun bằng tiện ích và tất cả thẻ liên quan.
Sau đó, hàm triển khai sẽ gọi các quy tắc kho lưu trữ để tạo kho lưu trữ.
# @rules_jvm_external//:extensions.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") # a repo rule
def _maven_impl(ctx):
# This is a fake implementation for demonstration purposes only
# collect artifacts from across the dependency graph
artifacts = []
for mod in ctx.modules:
for install in mod.tags.install:
artifacts += install.artifacts
artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]
# call out to the coursier CLI tool to resolve dependencies
output = ctx.execute(["coursier", "resolve", artifacts])
repo_attrs = _process_coursier_output(output)
# call repo rules to generate repos
for attrs in repo_attrs:
http_file(**attrs)
_generate_hub_repo(name = "maven", repo_attrs)
Thông tin nhận dạng của tiện ích
Tiện ích mô-đun được xác định bằng tên và tệp .bzl
xuất hiện trong lệnh gọi đến use_extension
. Trong ví dụ sau, phần mở rộng maven
được xác định bằng tệp .bzl
@rules_jvm_external//:extension.bzl
và tên maven
:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Việc xuất lại một tiện ích từ một tệp .bzl
khác sẽ cung cấp cho tiện ích một danh tính mới và nếu cả hai phiên bản của tiện ích đó được sử dụng trong biểu đồ mô-đun bắc cầu, thì các tiện ích đó sẽ được đánh giá riêng biệt và sẽ chỉ thấy các thẻ liên kết với danh tính cụ thể đó.
Là tác giả của tiện ích, bạn nên đảm bảo rằng người dùng sẽ chỉ sử dụng tiện ích mô-đun của bạn từ một tệp .bzl
duy nhất.
Tên và chế độ hiển thị kho lưu trữ
Các kho lưu trữ do các tiện ích tạo ra có tên chính tắc ở dạng module_repo_canonical_name~extension_name~repo_name
. Đối với các tiện ích được lưu trữ trong mô-đun gốc, phần module_repo_canonical_name
sẽ được thay thế bằng chuỗi _main
. Xin lưu ý rằng định dạng tên chính tắc không phải là API mà bạn nên phụ thuộc. API này có thể thay đổi bất cứ lúc nào.
Chính sách đặt tên này có nghĩa là mỗi tiện ích đều có "không gian tên kho lưu trữ" riêng; hai tiện ích riêng biệt có thể xác định một kho lưu trữ có cùng tên mà không có nguy cơ xảy ra xung đột. Điều này cũng có nghĩa là repository_ctx.name
báo cáo tên chuẩn hoá của kho lưu trữ, không giống với tên được chỉ định trong lệnh gọi quy tắc kho lưu trữ.
Xem xét các kho lưu trữ do các tiện ích mô-đun tạo ra, có một số quy tắc hiển thị kho lưu trữ:
- Kho lưu trữ mô-đun Bazel có thể xem tất cả kho lưu trữ được giới thiệu trong tệp
MODULE.bazel
thông quabazel_dep
vàuse_repo
. - Một kho lưu trữ do phần mở rộng mô-đun tạo ra có thể xem tất cả kho lưu trữ hiển thị với mô-đun lưu trữ phần mở rộng, cộng với tất cả kho lưu trữ khác do cùng một phần mở rộng mô-đun tạo ra (sử dụng tên được chỉ định trong lệnh gọi quy tắc kho lưu trữ làm tên hiển thị).
- Điều này có thể dẫn đến xung đột. Nếu kho lưu trữ mô-đun có thể xem một kho lưu trữ có tên hiển thị là
foo
và tiện ích tạo một kho lưu trữ có tên được chỉ định làfoo
, thì đối với tất cả kho lưu trữ do tiện ích đó tạo ra,foo
sẽ tham chiếu đến kho lưu trữ trước đó.
- Điều này có thể dẫn đến xung đột. Nếu kho lưu trữ mô-đun có thể xem một kho lưu trữ có tên hiển thị là
Các phương pháp hay nhất
Phần này mô tả các phương pháp hay nhất khi viết tiện ích để tiện ích đó dễ sử dụng, dễ bảo trì và thích ứng tốt với các thay đổi theo thời gian.
Đặt mỗi tiện ích vào một tệp riêng
Khi tiện ích nằm trong một tệp khác, tiện ích sẽ cho phép một tiện ích tải các kho lưu trữ do tiện ích khác tạo. Ngay cả khi bạn không sử dụng chức năng này, tốt nhất bạn vẫn nên đặt các tệp này vào các tệp riêng biệt phòng trường hợp sau này cần đến. Lý do là mã nhận dạng của tiện ích dựa trên tệp của tiện ích đó, vì vậy, việc di chuyển tiện ích vào một tệp khác sau này sẽ thay đổi API công khai của bạn và là một thay đổi không tương thích ngược cho người dùng.