Trang này giải đáp một số câu hỏi thường gặp về các phần phụ thuộc bên ngoài trong Bazel.
MODULE.bazel
Tôi nên đặt phiên bản cho mô-đun Bazel như thế nào?
Việc đặt version bằng chỉ thị module trong kho lưu trữ nguồn
MODULE.bazel có thể có một số nhược điểm và tác dụng phụ không mong muốn nếu không
được quản lý cẩn thận:
Trùng lặp: việc phát hành một phiên bản mới của mô-đun thường bao gồm cả việc tăng phiên bản trong
MODULE.bazelvà gắn thẻ bản phát hành, hai bước riêng biệt có thể bị lệch. Mặc dù quy trình tự động hoá có thể giảm thiểu rủi ro này, nhưng việc tránh hoàn toàn sẽ đơn giản và an toàn hơn.Không nhất quán: người dùng ghi đè một mô-đun bằng một cam kết cụ thể bằng cách sử dụng ghi đè không đăng ký sẽ thấy phiên bản không chính xác. Ví dụ: nếu
MODULE.bazeltrong kho lưu trữ nguồn đặtversion = "0.3.0"nhưng các cam kết bổ sung đã được thực hiện kể từ bản phát hành đó, thì người dùng ghi đè bằng một trong các cam kết đó vẫn sẽ thấy0.3.0. Trên thực tế, phiên bản này phải phản ánh rằng phiên bản đó đang ở trước bản phát hành, ví dụ:0.3.1-rc1.Vấn đề ghi đè không đăng ký: việc sử dụng các giá trị giữ chỗ có thể gây ra vấn đề khi người dùng ghi đè một mô-đun bằng ghi đè không đăng ký. Ví dụ:
0.0.0không sắp xếp dưới dạng phiên bản cao nhất, thường là hành vi dự kiến mà người dùng muốn khi thực hiện ghi đè không đăng ký.
Do đó, tốt nhất là bạn nên tránh đặt phiên bản trong kho lưu trữ nguồn MODULE.bazel. Thay vào đó, hãy đặt phiên bản trong MODULE.bazel được lưu trữ trong sổ đăng ký
(ví dụ: Sổ đăng ký trung tâm Bazel), đây là nguồn thực tế của
phiên bản mô-đun trong quá trình phân giải phần phụ thuộc bên ngoài của Bazel (xem Sổ đăng ký
Bazel).
Việc này thường được tự động hoá, ví dụ: kho lưu trữ quy tắc mẫu rules-template sử dụng Hành động GitHub bazel-contrib/publish-to-bcr publish.yaml để
phát hành bản phát hành lên BCR. Hành động này tạo một bản vá cho kho lưu trữ nguồn
MODULE.bazel với phiên bản phát hành. Bản vá này được lưu trữ trong sổ đăng ký và được áp dụng khi mô-đun được tìm nạp trong quá trình phân giải phần phụ thuộc bên ngoài của Bazel.
Bằng cách này, phiên bản trong các bản phát hành trong sổ đăng ký sẽ được đặt chính xác thành
phiên bản đã phát hành. Do đó, bazel_dep, single_version_override và
multiple_version_override sẽ hoạt động như dự kiến, đồng thời tránh được các vấn đề tiềm ẩn
khi thực hiện ghi đè không đăng ký vì phiên bản trong kho lưu trữ
nguồn sẽ là giá trị mặc định (''), giá trị này sẽ luôn được xử lý
chính xác (sau cùng thì đây là giá trị phiên bản mặc định) và sẽ hoạt động như
dự kiến khi sắp xếp (chuỗi trống được coi là phiên bản cao nhất).
Mức độ tương thích là gì?
Bạn nên ngừng sử dụng compatibility_level.
Việc tăng compatibility_level dẫn đến các xung đột phiên bản mà người dùng cuối khó giải quyết. Do đó, bắt đầu từ Bazel 8.6.0 và 9.1.0, cả
compatibility_level và max_compatibility_level đều không hoạt động.
Người duy trì mô-đun giới thiệu các thay đổi lớn gây lỗi phải đảm bảo rằng lỗi bản dựng cung cấp thông báo lỗi rõ ràng và các đường dẫn di chuyển có thể thực hiện.
Tài liệu cũ:
compatibility_level của mô-đun Bazel
phải được tăng trong cùng một cam kết giới thiệu thay đổi không tương thích ngược ("gây lỗi").
Tuy nhiên, Bazel có thể đưa ra lỗi nếu phát hiện thấy các phiên bản của cùng một mô-đun có mức độ tương thích khác nhau tồn tại trong biểu đồ phần phụ thuộc đã phân giải. Điều này có thể xảy ra khi ví dụ: hai mô-đun phụ thuộc vào các phiên bản của mô-đun thứ ba có mức độ tương thích khác nhau.
Do đó, việc tăng compatibility_level quá thường xuyên có thể gây gián đoạn rất nhiều và không được khuyến khích. Để tránh tình trạng này, bạn chỉ nên tăng compatibility_level khi thay đổi gây lỗi ảnh hưởng đến hầu hết các trường hợp sử dụng và không dễ di chuyển và/hoặc giải quyết.
Tại sao MODULE.bazel không hỗ trợ load?
Trong quá trình phân giải phần phụ thuộc, tệp MODULE.bazel của tất cả các phần phụ thuộc bên ngoài được tham chiếu sẽ được tìm nạp từ sổ đăng ký. Ở giai đoạn này, kho lưu trữ nguồn của các phần phụ thuộc chưa được tìm nạp; vì vậy, nếu tệp MODULE.bazel load một tệp khác, thì Bazel không có cách nào để thực sự tìm nạp tệp đó mà không cần tìm nạp toàn bộ kho lưu trữ nguồn. Lưu ý rằng tệp MODULE.bazel là đặc biệt vì được lưu trữ trực tiếp trên sổ đăng ký.
Có một số trường hợp sử dụng mà những người yêu cầu load trong MODULE.bazel thường quan tâm và có thể giải quyết mà không cần load:
- Đảm bảo rằng phiên bản được liệt kê trong MODULE.bazel nhất quán với siêu dữ liệu bản dựng được lưu trữ ở nơi khác, ví dụ: trong tệp .bzl: Bạn có thể thực hiện việc này bằng cách sử dụng phương thức
native.module_versiontrong tệp .bzl được tải từ tệp BUILD. - Chia tệp MODULE.bazel rất lớn thành các phần có thể quản lý,
đặc biệt là đối với monorepo: Mô-đun gốc có thể sử dụng chỉ thị
includeđể chia tệp MODULE.bazel thành nhiều phân đoạn. Vì cùng một lý do mà chúng tôi không cho phéploadtrong tệp MODULE.bazel, nên bạn không thể sử dụngincludetrong các mô-đun không phải gốc. - Người dùng hệ thống WORKSPACE cũ có thể nhớ việc khai báo một kho lưu trữ, sau đó ngay lập tức
loadtừ kho lưu trữ đó để thực hiện logic phức tạp. Tính năng này đã được thay thế bằng các tiện ích mô-đun.
Tôi có thể chỉ định phạm vi SemVer cho bazel_dep không?
Không. Một số trình quản lý gói khác như npm và Cargo hỗ trợ phạm vi phiên bản (ngầm hoặc rõ ràng) và điều này thường yêu cầu một trình giải quyết ràng buộc (khiến người dùng khó dự đoán kết quả đầu ra hơn) và khiến việc phân giải phiên bản không thể tái tạo mà không có tệp khoá.
Thay vào đó, Bazel sử dụng Lựa chọn phiên bản tối thiểu như Go, giúp dễ dàng dự đoán kết quả đầu ra và đảm bảo khả năng tái tạo. Đây là sự đánh đổi phù hợp với các mục tiêu thiết kế của Bazel.
Hơn nữa, các phiên bản mô-đun Bazel là một tập hợp con của SemVer, vì vậy, những gì có ý nghĩa trong môi trường SemVer nghiêm ngặt không phải lúc nào cũng chuyển sang các phiên bản mô-đun Bazel.
Tôi có thể tự động nhận phiên bản mới nhất cho bazel_dep không?
Một số người dùng đôi khi yêu cầu khả năng chỉ định bazel_dep(name = "foo",
version = "latest") để tự động nhận phiên bản mới nhất của một phần phụ thuộc. Điều này
tương tự như câu hỏi về phạm vi SemVer, và câu trả lời cũng
là không.
Giải pháp được đề xuất ở đây là để quy trình tự động hoá xử lý việc này. Ví dụ: Renovate hỗ trợ các mô-đun Bazel.
Đôi khi, người dùng đặt câu hỏi này thực sự đang tìm cách nhanh chóng lặp lại trong quá trình phát triển cục bộ. Bạn có thể thực hiện việc này bằng cách sử dụng a
local_path_override.
Tại sao lại có tất cả các use_repo này?
Việc sử dụng tiện ích mô-đun trong tệp MODULE.bazel đôi khi đi kèm với chỉ thị use_repo lớn. Ví dụ: cách sử dụng thông thường của tiện ích
go_deps từ gazelle có thể như sau:
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
"org_golang_x_net",
... # potentially dozens of lines...
)
Chỉ thị use_repo dài có thể có vẻ dư thừa, vì thông tin này có thể đã có trong tệp go.mod được tham chiếu.
Lý do Bazel cần chỉ thị use_repo này là vì nó chạy các tiện ích mô-đun một cách chậm trễ. Tức là, tiện ích mô-đun chỉ chạy nếu kết quả của tiện ích đó được quan sát. Vì "đầu ra" của tiện ích mô-đun là định nghĩa kho lưu trữ, nên điều này có nghĩa là chúng ta chỉ chạy tiện ích mô-đun nếu một kho lưu trữ mà tiện ích đó xác định được yêu cầu (ví dụ: nếu mục tiêu @org_golang_x_net//:foo được xây dựng, trong ví dụ ở trên). Tuy nhiên, chúng ta không biết tiện ích mô-đun sẽ xác định kho lưu trữ nào cho đến khi chạy tiện ích đó. Đây là nơi chỉ thị use_repo xuất hiện; người dùng có thể cho Bazel biết kho lưu trữ nào mà họ mong đợi tiện ích sẽ tạo và sau đó, Bazel sẽ chỉ chạy tiện ích khi các kho lưu trữ cụ thể này được sử dụng.
Để giúp duy trì chỉ thị use_repo này, tiện ích mô-đun có thể trả về
một extension_metadata
đối tượng từ hàm triển khai. Người dùng có thể chạy lệnh bazel mod tidy để cập nhật chỉ thị use_repo cho các tiện ích mô-đun này.
Di chuyển Bzlmod
MODULE.bazel hay WORKSPACE được đánh giá trước?
Khi cả --enable_bzlmod và --enable_workspace đều được đặt, bạn sẽ tự hỏi hệ thống nào được tham khảo trước. Câu trả lời ngắn gọn là MODULE.bazel (Bzlmod) được đánh giá trước.
Câu trả lời dài là "hệ thống nào đánh giá trước" không phải là câu hỏi đúng để
hỏi; thay vào đó, câu hỏi đúng để hỏi là: trong bối cảnh của kho lưu trữ có
tên chính tắc @@foo, tên kho lưu trữ rõ ràng @bar phân giải thành gì? Ngoài ra, ánh xạ kho lưu trữ của @@base là gì?
Nhãn có tên kho lưu trữ rõ ràng (một @ dẫn đầu) có thể tham chiếu đến nhiều thứ dựa trên bối cảnh mà chúng được phân giải. Khi bạn thấy nhãn @bar//:baz và tự hỏi nhãn đó thực sự trỏ đến đâu, trước tiên, bạn cần tìm hiểu kho lưu trữ bối cảnh là gì: ví dụ: nếu nhãn nằm trong tệp BUILD nằm trong kho lưu trữ @@foo, thì kho lưu trữ bối cảnh là @@foo.
Sau đó, tuỳ thuộc vào kho lưu trữ bối cảnh là gì, bạn có thể sử dụng bảng "khả năng hiển thị kho lưu trữ" trong hướng dẫn di chuyển để tìm hiểu kho lưu trữ nào mà tên rõ ràng thực sự phân giải thành.
- Nếu kho lưu trữ bối cảnh là kho lưu trữ chính (
@@):- Nếu
barlà tên kho lưu trữ rõ ràng do tệp MODULE.bazel của mô-đun gốc giới thiệu (thông qua bất kỳbazel_dep,use_repo,module,use_repo_rule), thì@barsẽ phân giải thành nội dung mà tệp MODULE.bazel đó khai báo. - Nếu không, nếu
barlà kho lưu trữ được xác định trong WORKSPACE (nghĩa là tên chính tắc của kho lưu trữ đó là@@bar), thì@barsẽ phân giải thành@@bar. - Nếu không,
@barsẽ phân giải thành một nội dung như@@[unknown repo 'bar' requested from @@]và điều này cuối cùng sẽ dẫn đến lỗi.
- Nếu
- Nếu kho lưu trữ bối cảnh là kho lưu trữ thế giới Bzlmod (tức là kho lưu trữ đó tương ứng với mô-đun Bazel không phải gốc hoặc được tạo bởi tiện ích mô-đun), thì kho lưu trữ đó sẽ chỉ thấy các kho lưu trữ thế giới Bzlmod khác và không thấy kho lưu trữ thế giới WORKSPACE.
- Đáng chú ý là điều này bao gồm mọi kho lưu trữ được giới thiệu trong tiện ích mô-đun giống như
non_module_depstrong mô-đun gốc hoặc các thực thểuse_repo_ruletrong mô-đun gốc.
- Đáng chú ý là điều này bao gồm mọi kho lưu trữ được giới thiệu trong tiện ích mô-đun giống như
- Nếu kho lưu trữ bối cảnh được xác định trong WORKSPACE:
- Trước tiên, hãy kiểm tra xem định nghĩa kho lưu trữ bối cảnh có thuộc tính
repo_mappingkỳ diệu hay không. Nếu có, hãy chuyển qua ánh xạ trước (vì vậy, đối với một kho lưu trữ được xác định bằngrepo_mapping = {"@bar": "@baz"}, chúng ta sẽ xem xét tại@bazbên dưới). - Nếu
barlà tên kho lưu trữ rõ ràng do tệp MODULE.bazel của mô-đun gốc giới thiệu, thì@barsẽ phân giải thành nội dung mà tệp MODULE.bazel đó khai báo. (Điều này giống như mục 1 trong trường hợp kho lưu trữ chính.) - Nếu không,
@barsẽ phân giải thành@@bar. Điều này rất có thể sẽ trỏ đến kho lưu trữbarđược xác định trong WORKSPACE; nếu không xác định được kho lưu trữ như vậy, Bazel sẽ gửi lỗi.
- Trước tiên, hãy kiểm tra xem định nghĩa kho lưu trữ bối cảnh có thuộc tính
Để có phiên bản ngắn gọn hơn:
- Kho lưu trữ thế giới Bzlmod (không bao gồm kho lưu trữ chính) sẽ chỉ thấy các kho lưu trữ thế giới Bzlmod.
- Kho lưu trữ thế giới WORKSPACE (bao gồm cả kho lưu trữ chính) sẽ thấy trước tiên nội dung mà mô-đun gốc trong thế giới Bzlmod xác định, sau đó quay lại xem các kho lưu trữ thế giới WORKSPACE.
Xin lưu ý rằng các nhãn trong dòng lệnh Bazel (bao gồm cờ Starlark, giá trị cờ có kiểu nhãn và mẫu mục tiêu bản dựng/kiểm thử) được coi là có kho lưu trữ chính làm kho lưu trữ bối cảnh.
Khác
Làm cách nào để chuẩn bị và chạy bản dựng ngoại tuyến?
Sử dụng lệnh bazel fetch để tìm nạp trước các kho lưu trữ. Bạn có thể sử dụng cờ --repo (như bazel fetch --repo @foo) để chỉ tìm nạp kho lưu trữ @foo (được phân giải trong bối cảnh của kho lưu trữ chính, xem câu hỏi ở trên) hoặc sử dụng mẫu mục tiêu (như bazel fetch @foo//:bar) để tìm nạp tất cả các phần phụ thuộc bắc cầu của @foo//:bar (tương đương với bazel build --nobuild @foo//:bar).
Để đảm bảo không có hoạt động tìm nạp nào xảy ra trong quá trình xây dựng, hãy sử dụng --nofetch. Chính xác hơn, điều này khiến mọi nỗ lực chạy quy tắc kho lưu trữ không phải cục bộ đều không thành công.
Nếu bạn muốn tìm nạp kho lưu trữ và sửa đổi kho lưu trữ đó để kiểm thử cục bộ, hãy cân nhắc sử dụng
lệnh bazel vendor.
Làm cách nào để sử dụng proxy HTTP?
Bazel tuân thủ các biến môi trường http_proxy và HTTPS_PROXY thường được các chương trình khác chấp nhận, chẳng hạn như curl.
Làm cách nào để Bazel ưu tiên IPv6 trong các thiết lập IPv4/IPv6 ngăn xếp kép?
Trên các máy chỉ có IPv6, Bazel có thể tải các phần phụ thuộc xuống mà không cần thay đổi. Tuy nhiên, trên các máy IPv4/IPv6 ngăn xếp kép, Bazel tuân theo cùng một quy ước như Java, ưu tiên IPv4 nếu được bật. Trong một số trường hợp, chẳng hạn như khi mạng IPv4
không thể phân giải/truy cập vào các địa chỉ bên ngoài, điều này có thể gây ra các ngoại lệ Network
unreachable và lỗi bản dựng. Trong những trường hợp này, bạn có thể ghi đè
hành vi của Bazel để ưu tiên IPv6 bằng cách sử dụng
java.net.preferIPv6Addresses=true thuộc tính
hệ thống.
Cụ thể:
Sử dụng tuỳ chọn khởi động
--host_jvm_args=-Djava.net.preferIPv6Addresses=truestartup option, ví dụ: bằng cách thêm dòng sau vào tệp.bazelrcfile:startup --host_jvm_args=-Djava.net.preferIPv6Addresses=trueKhi chạy các mục tiêu bản dựng Java cần kết nối với Internet (chẳng hạn như cho các bài kiểm thử tích hợp), hãy sử dụng
--jvmopt=-Djava.net.preferIPv6Addresses=truecờ công cụ. Ví dụ: thêm vào tệp.bazelrcfile:build --jvmopt=-Djava.net.preferIPv6AddressesNếu bạn đang sử dụng
rules_jvm_externalđể phân giải phiên bản phần phụ thuộc, hãy thêm-Djava.net.preferIPv6Addresses=truevàoCOURSIER_OPTSbiến môi trường để cung cấp các tuỳ chọn JVM cho Coursier.
Có thể chạy các quy tắc kho lưu trữ từ xa bằng tính năng thực thi từ xa không?
Không; hoặc ít nhất là chưa. Người dùng sử dụng các dịch vụ thực thi từ xa để tăng tốc bản dựng có thể nhận thấy rằng các quy tắc kho lưu trữ vẫn được chạy cục bộ. Ví dụ: http_archive sẽ được tải xuống trước tiên trên máy cục bộ (sử dụng mọi bộ nhớ đệm tải xuống cục bộ nếu có), được trích xuất, sau đó mỗi tệp nguồn sẽ được tải lên dịch vụ thực thi từ xa dưới dạng tệp đầu vào. Bạn sẽ tự hỏi tại sao dịch vụ thực thi từ xa không chỉ tải xuống và trích xuất kho lưu trữ đó, giúp tiết kiệm một chuyến đi khứ hồi vô ích.
Một phần lý do là các quy tắc kho lưu trữ (và tiện ích mô-đun) tương tự như "tập lệnh" do chính Bazel chạy. Trình thực thi từ xa thậm chí không nhất thiết phải cài đặt Bazel.
Một lý do khác là Bazel thường cần các tệp BUILD trong kho lưu trữ đã tải xuống và trích xuất để thực hiện quá trình tải và phân tích, quá trình này được thực hiện cục bộ.
Có những ý tưởng sơ bộ để giải quyết vấn đề này bằng cách hình dung lại các quy tắc kho lưu trữ dưới dạng quy tắc bản dựng, điều này sẽ cho phép chúng được chạy từ xa một cách tự nhiên, nhưng ngược lại, sẽ làm nảy sinh các mối lo ngại mới về kiến trúc (ví dụ: các lệnh query có thể cần chạy các hành động, làm phức tạp thiết kế của chúng).
Để biết thêm thông tin thảo luận trước đây về chủ đề này, hãy xem bài viết Cách hỗ trợ các kho lưu trữ cần Bazel để được tìm nạp.