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 repos 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 chức năng tương tự như các quy tắc kho lưu trữ, cho phép chúng thực hiện thao tác I/O đối với tệp, gửi yêu cầu mạng, v.v. Ngoài ra, các thư viện này 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 tuân thủ biểu đồ phần phụ thuộc được tạo từ các mô-đun Bazel.
Bạn có thể xác định đuôi mô-đun trong các tệp .bzl
, tương tự như các 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 sẽ đọc tất cả thẻ thuộc về nó 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 một tiện ích trong một mô-đun, trước tiên hãy thêm bazel_dep
vào 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 ví dụ sau – đ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, điều này 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 đồ được xác định bởi các lớp thẻ tương ứng được chỉ định trong định nghĩa tiện ích. 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 bản repos 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 tiện ích tạo là một phần của API. 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 nội dung khai báo ở trên, tiện ích này sẽ phân giải đúng cách các nhãn, chẳng hạn như @maven//:org_junit_junit
để trỏ đến kho lưu trữ do tiện ích "maven" tạo.
Định nghĩa phần mở rộng
Bạn có thể xác định tiện ích mô-đun tương tự như các quy tắc kho lưu trữ, bằng cách sử dụng hàm module_extension
. Tuy nhiên, mặc dù quy tắc kho lưu trữ có một số thuộc tính, tiện ích mô-đun có tag_class
es, mỗi hàm có một số thuộc tính. Các lớp thẻ xác định giản đồ cho những thẻ mà tiện ích này sử dụng. Ví dụ: phần mở rộng "maven" ở trên có thể được định nghĩa 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 phần mở rộng mô-đun cũng tương tự như chức năng của quy tắc kho lưu trữ, ngoại trừ việc chúng nhận được đối tượng module_ctx
, cấp quyền truy cập vào tất cả mô-đun sử dụng phần mở rộng và mọi thẻ thích hợp.
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)
Danh tính của tiện ích
Đuôi 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, tiện ích 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 phiên bản này sẽ được đánh giá riêng biệt và sẽ chỉ thấy các thẻ được liên kết với danh tính cụ thể đó.
Là tác giả tiện ích, bạn phải đảm bảo rằng người dùng chỉ sử dụng đuôi mô-đun từ một tệp .bzl
duy nhất.
Tên kho lưu trữ và chế độ hiển thị
Các kho lưu trữ do tiện ích tạo ra sẽ có tên chính tắc dưới 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
. Lưu ý rằng định dạng tên chuẩn không phải là API mà bạn nên dựa vào — định dạng 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 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ơ
xung đột. Điều này cũng có nghĩa là repository_ctx.name
báo cáo tên chính tắc của kho lưu trữ, tên này không giống với tên được chỉ định trong lệnh gọi quy tắc kho lưu trữ.
Khi xem xét các repos được tạo bởi tiện ích mô-đun, 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ả cá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 một tiện ích mô-đun tạo ra có thể xem tất cả các kho lưu trữ hiển thị với mô-đun lưu trữ tiện ích, cùng với tất cả các kho lưu trữ khác được tạo bởi cùng một tiện ích mô-đun (sử dụng tên được chỉ định trong lệnh gọi quy tắc kho lưu trữ làm
tên biểu kiến).
- Điều này có thể dẫn đến xung đột. Nếu kho lưu trữ mô-đun có thể thấy một kho lưu trữ có
tên rõ ràng
foo
và tiện ích tạo ra một kho lưu trữ có tên được chỉ địnhfoo
, thì đối với tất cả các kho lưu trữ do tiện ích đó tạofoo
đều tham chiếu đến tên trước.
- Điều này có thể dẫn đến xung đột. Nếu kho lưu trữ mô-đun có thể thấy một kho lưu trữ có
tên rõ ràng
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 sao cho 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 trong một tệp riêng
Khi các tiện ích nằm trong một tệp khác, tiện ích này sẽ cho phép một tiện ích tải các kho lưu trữ do một tiện ích khác tạo. Ngay cả khi không sử dụng chức năng này, bạn vẫn nên đặt các tệp đó vào các tệp riêng biệt phòng khi cần đến sau này. Nguyên nhân là do mã nhận dạng tiện ích dựa trên tệp đó. Vì vậy, việc di chuyển tiện ích vào một tệp khác sau này sẽ làm thay đổi API công khai và người dùng của bạn sẽ không tương thích ngược.
Chỉ định hệ điều hành và cấu trúc
Nếu tiện ích của bạn phụ thuộc vào hệ điều hành hoặc loại cấu trúc của hệ điều hành đó, hãy đảm bảo chỉ định điều này trong phần định nghĩa tiện ích bằng cách sử dụng các thuộc tính boolean os_dependent
và arch_dependent
. Việc này giúp đảm bảo Bazel nhận thấy cần phải đánh giá lại nếu có thay đổi đối với một trong hai cách nêu trên.
Chỉ mô-đun gốc mới ảnh hưởng trực tiếp đến tên kho lưu trữ
Hãy nhớ rằng khi một tiện ích tạo kho lưu trữ, kho lưu trữ đó sẽ được tạo trong không gian tên của tiện ích. Điều này có nghĩa là xảy ra xung đột nếu các mô-đun khác nhau sử dụng cùng một phần mở rộng và cuối cùng tạo ra một kho lưu trữ có cùng tên. Thuộc tính này thường biểu thị dưới dạng tag_class
của tiện ích mô-đun có đối số name
được chuyển dưới dạng giá trị name
của quy tắc kho lưu trữ.
Ví dụ: giả sử mô-đun gốc A
sẽ phụ thuộc vào mô-đun B
. Cả hai mô-đun đều phụ thuộc vào mô-đun mylang
. Nếu cả A
và B
gọi mylang.toolchain(name="foo")
, cả hai đều sẽ cố gắng tạo một kho lưu trữ có tên foo
trong mô-đun mylang
và sẽ xảy ra lỗi.
Để tránh điều này, hãy xoá khả năng đặt trực tiếp tên kho lưu trữ hoặc chỉ cho phép mô-đun gốc làm như vậy. Bạn có thể cho phép mô-đun gốc khả năng này vì không có gì phụ thuộc vào nó nên không phải lo lắng về việc một mô-đun khác tạo tên mâu thuẫn.