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 tệp lưu trữ nguồn
MODULE.bazel có thể gây ra 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 liên quan đến 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ể không đồng bộ. Mặc dù việc 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 tính nă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 tệp 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 phải phản ánh rằng phiên bản đó trước bản phát hành, ví dụ:0.3.1-rc1.Vấn đề về tính năng ghi đè không đăng ký: việc sử dụng các giá trị phần giữ chỗ có thể gây ra vấn đề khi người dùng ghi đè một mô-đun bằng tính năng ghi đè không đăng ký. Ví dụ:
0.0.0không sắp xếp theo phiên bản cao nhất, thường là hành vi mà người dùng mong muốn khi thực hiện thao tác ghi đè không đăng ký.
Do đó, tốt nhất là bạn nên tránh đặt phiên bản trong tệp 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ông tin thực tế cho
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).
Quá trình này thường được tự động hoá, ví dụ: kho 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 tệp 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ư mong đợi, đồng thời tránh được các vấn đề tiềm ẩn
khi thực hiện thao tác ghi đè không đăng ký vì phiên bản trong tệp 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ư
mong đợi khi sắp xếp (chuỗi trống được coi là phiên bản cao nhất).
Khi nào tôi nên tăng cấp độ tương thích?
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 ("phá vỡ").
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ó các cấp độ tương thích khác nhau 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ó các cấp độ 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 lớn 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 phá vỡ ả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, các tệp 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 tìm nạp toàn bộ tệp lưu trữ nguồn. Lưu ý rằng tệp MODULE.bazel là tệp đặc biệt vì tệp này đượ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 các kho lưu trữ đơn: 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ợ các phạm vi phiên bản (ngầm ẩn hoặc rõ ràng). Đ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 nếu không có tệp khoá.
Thay vào đó, Bazel sử dụng Tính năng chọn phiên bản tối thiểu như Go, giúp người dùng 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?
Đôi khi, một số người dùng 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 phần phụ thuộc. Điều này
tương tự như câu hỏi về các phạm vi SemVer, và câu trả lời cũng
là không.
Giải pháp được đề xuất ở đây là để quá 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 tiện ích
go_deps từ gazelle thường sẽ 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ể 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ì Bazel chạy các tiện ích mô-đun một cách trì hoãn. Tức là, một tiện ích mô-đun chỉ chạy nếu kết quả của tiện ích đó được quan sát. Vì "kết quả đầu ra" của tiện ích mô-đun là các định nghĩa kho lưu trữ, nên điều này có nghĩa là chúng tôi chỉ chạy một 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 tạo, trong ví dụ ở trên). Tuy nhiên, chúng tôi 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ữ 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 các chỉ thị use_repo cho các tiện ích mô-đun này.
Di chuyển Bzlmod
Hệ thống nào được đánh giá trước, MODULE.bazel hay WORKSPACE?
Khi bạn đặt cả --enable_bzlmod và --enable_workspace, 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 phù hợp để
hỏi; đúng hơn, câu hỏi phù hợp để 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 tiền tố @) 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 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, 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 đó yêu cầu. - 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à 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ữ Bzlmod-world (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ữ Bzlmod-world khác và không thấy kho lưu trữ WORKSPACE-world.
- Đá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_mappingđặc biệt hay không. Nếu có, hãy xem qua ánh xạ trước (vì vậy, đối với a kho lưu trữ được xác định bằngrepo_mapping = {"@bar": "@baz"}, chúng ta sẽ xem 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 đó yêu cầu. (Đ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 kho lưu trữ như vậy, Bazel sẽ đưa ra 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
Phiên bản ngắn gọn hơn:
- Các kho lưu trữ Bzlmod-world (ngoại trừ kho lưu trữ chính) sẽ chỉ thấy các kho lưu trữ Bzlmod-world.
- Các kho lưu trữ WORKSPACE-world (bao gồm cả kho lưu trữ chính) trước tiên sẽ thấy 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ữ WORKSPACE-world.
Xin lưu ý rằng các nhãn trong dòng lệnh Bazel (bao gồm cả 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ó quá trình tìm nạp nào xảy ra trong quá trình tạo bản 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 cục bộ đều thất bại.
Nếu bạn muốn tìm nạp kho lưu trữ và sửa đổi chúng để 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 theo 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 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 máy cục bộ trước (sử dụng mọi bộ nhớ đệm tải xuống cục bộ nếu có), được trích xuất và 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 tệp lưu trữ đó, giúp tiết kiệm một chuyến 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 các tệp 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 đó 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.