Câu hỏi thường gặp

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 liên quan đến cả việc tăng phiên bản trong MODULE.bazel và 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 chế độ ghi đè không đăng ký sẽ thấy phiên bản không chính xác. Ví dụ: nếu MODULE.bazel trong kho lưu trữ nguồn đặt version = "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ấy 0.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ề chế độ 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 chế độ ghi đè không đăng ký. Ví dụ: 0.0.0 khô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 chế độ 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ô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 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 và do đó, bazel_dep, single_version_overridemultiple_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 chế độ 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ư mong đợi 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 về 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_levelmax_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à đườ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ô-đunmứ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 và không được khuyến khích. Để tránh tình trạng này, compatibility_level chỉ nên được tăng 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 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ì 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_version trong 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ép load trong tệp MODULE.bazel, nên không thể sử dụng include trong 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 kho lưu trữ, sau đó load ngay từ 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ư npmCargo hỗ trợ phạm vi phiên bản (ngầm ẩn 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 nếu 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à 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 thỉnh thoả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ề 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, những 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 mở rộng từ gazelle thường 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ể 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 trì hoãn. Tức là, tiện ích mô-đun chỉ chạy nếu kết quả của nó được quan sát. Vì "kết quả đầu ra" của tiện ích mô-đun là định nghĩa kho lưu trữ, điều này có nghĩa là chúng tôi chỉ chạy tiện ích mô-đun nếu 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 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--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 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 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, 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 (@@):
    1. Nếu bar là 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ì @bar sẽ phân giải thành nội dung mà tệp MODULE.bazel đó yêu cầu.
    2. Nếu không, nếu bar là 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ì @bar sẽ phân giải thành @@bar.
    3. Nếu không, @bar sẽ 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 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_deps trong mô-đun gốc hoặc các thực thể use_repo_rule trong mô-đun gốc.
  • Nếu kho lưu trữ bối cảnh được xác định trong WORKSPACE:
    1. 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 kho lưu trữ được xác định bằng repo_mapping = {"@bar": "@baz"}, chúng ta sẽ xem xét @baz bên dưới).
    2. Nếu bar là 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ì @bar sẽ phân giải thành nội dung mà tệp MODULE.bazel đó yêu cầu. (Điều này giống với mục 1 trong trường hợp kho lưu trữ chính.)
    3. Nếu không, @bar sẽ 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 kho lưu trữ như vậy không được xác định, Bazel sẽ đưa ra lỗi.

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 kiểm thử/bản dựng) đượ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ữ 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 để cách ly bản dựng của tôi khỏi Internet?

Dưới đây là một số trang web trên Internet có thể truy cập công khai mà bản dựng Bazel điển hình dựa vào và những việc bạn có thể làm để cách ly bản thân khỏi các sự cố tiềm ẩn, đặc biệt là người dùng Bazel doanh nghiệp.

  • releases.bazel.build: Bazelisk tải các tệp nhị phân bản phát hành Bazel xuống từ trang web này. Bạn có thể định cấu hình Bazelisk để tải xuống từ bản sao nội bộ của công ty.
  • bcr.bazel.build: Bazel tham khảo BCR để biết siêu dữ liệu mô-đun trong quá trình phân giải mô-đun. Bạn có thể sao chép chính BCR và đặt cờ --registry để loại bỏ phần phụ thuộc của bạn vào cơ sở hạ tầng phân phát BCR (xem tuyên bố từ chối trách nhiệm để biết thêm thông tin). Ngoài ra, bạn có thể đảm bảo tệp MODULE.bazel.lock của mình được cập nhật và thiết lập máy CI hoặc máy nhà phát triển bằng bộ nhớ đệm tải xuống được điền sẵn (--repository_cache). Nếu được thiết lập đúng cách, bộ nhớ đệm tải xuống sẽ chứa tất cả các tệp sổ đăng ký cần thiết cho bản dựng và tệp khoá sẽ chứa tổng kiểm tra của các tệp đó; sau đó, Bazel sẽ sử dụng kết quả được lưu vào bộ nhớ đệm và tránh hoàn toàn việc truy cập Internet trong quá trình phân giải mô-đun.
  • mirror.bazel.buildgithub.com: Nhiều mô-đun có kho lưu trữ nguồn được lưu trữ trên hai trang web này. Hãy cân nhắc thiết lập bản sao nội bộ của công ty cho kho lưu trữ nguồn và sử dụng --downloader_config hoặc --module_mirrors để trỏ Bazel vào các kho lưu trữ đó. Ngoài ra, bộ nhớ đệm tải xuống được điền sẵn như đã đề cập trong dấu đầu dòng trước đó cũng sẽ giúp Bazel hoàn toàn tránh truy cập Internet để tìm kho lưu trữ nguồn.

Làm cách nào để sử dụng proxy HTTP?

Bazel tuân theo các biến môi trường http_proxyHTTPS_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=true startup option, ví dụ: bằng cách thêm dòng sau vào tệp .bazelrc file:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • Khi 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=true cờ công cụ. Ví dụ: thêm vào tệp .bazelrc file:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Nế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=true vào COURSIER_OPTS biế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, 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 về kiến trúc mới (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.