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

Báo cáo vấn đề Xem nguồn Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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 thiết lập version bằng chỉ thị module trong kho 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:

  • Sao ché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ù tự động hoá có thể giảm thiểu rủi ro này, nhưng cách đơn giản và an toàn nhất là tránh hoàn toà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 lệnh ghi đè không đăng ký sẽ thấy một 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 này phải phản ánh rằng phiên bản đó đi 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 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. Đây thường là hành vi mà người dùng mong muốn khi thực hiện một chế độ 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 tham số này trong MODULE.bazel được lưu trữ trong sổ đăng ký (ví dụ: Bazel Central Registry), đâ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).

Thao tác này thường được tự động hoá, ví dụ: kho quy tắc mẫu rules-template sử dụng bazel-contrib/publish-to-bcr publish.yaml GitHub Action để xuất bản bản phát hành lên BCR. Thao tác này tạo một bản vá cho kho lưu trữ nguồn MODULE.bazel bằng 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 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 (rốt cuộc đâ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ột mô-đun Bazel phải được tăng lên trong cùng một cam kết giới thiệu một thay đổi không tương thích ngược ("phá vỡ").

Tuy nhiên, Bazel có thể gửi lỗi nếu phát hiện thấy các phiên bản của cùng một mô-đuncá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 chẳng hạn như hai mô-đun phụ thuộc vào các phiên bản của một mô-đun thứ ba có cá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 ra nhiều gián đoạn và không nên làm. Để tránh trường hợp này, bạn chỉ nên tăng compatibility_level khi thay đổi có tính chất đột phá ảnh hưởng đến hầu hết các trường hợp sử dụng và không dễ dàng 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ừ các sổ đăng ký. Ở giai đoạn này, các kho lưu trữ nguồn của các phần phụ thuộc chưa được tìm nạp; do đó, nếu tệp MODULE.bazel load là 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à tệp đặ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, chẳng hạn như trong tệp .bzl: Bạn có thể đạt được điều 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 nguyê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ì lý do tương tự, chúng tôi không cho phép load trong các tệp MODULE.bazel, include không thể dùng trong các mô-đun không phải là mô-đun 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 load từ kho lưu trữ đó để thực hiện logic phức tạp. Khả năng này đã được thay thế bằng tiện ích mô-đun.

Tôi có thể chỉ định một dải SemVer cho bazel_dep không?

Không. Một số trình quản lý gói khác như npmCargo hỗ trợ các dải phiên bản (một cách ngầm định hoặc rõ ràng), và điều này thường đòi hỏi một trình giải quyết ràng buộc (khiến người dùng khó dự đoán được đầu ra) 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 Minimal Version Selection (Lựa chọn phiên bản tối thiểu) như Go. Điều này giúp bạn dễ dàng dự đoán kết quả và đảm bảo khả năng tái tạo. Đây là một 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ì hợp lý trong môi trường SemVer nghiêm ngặt không phải lúc nào cũng được 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 của bazel_dep không?

Đôi khi, một số người dùng yêu cầu có thể chỉ định bazel_dep(name = "foo", version = "latest") để tự động nhận phiên bản mới nhất của một dep. Điều này tương tự như câu hỏi về các dải SemVer và câu trả lời cũng là không.

Giải pháp được đề xuất ở đây là sử dụng tính năng tự động hoá để xử lý vấn đề 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 lặp lại nhanh chóng 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 local_path_override.

Tại sao lại có nhiều use_repo đến vậy?

Việc sử dụng tiện ích mô-đun trong các 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ể trông 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...
)

Lệnh 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à 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ì "đầu ra" của một tiện ích mô-đun là các định nghĩa về kho lưu trữ, nên điều này có nghĩa là chúng ta 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 @org_golang_x_net//:foo mục tiêu được tạo, trong ví dụ ở trên). Tuy nhiên, chúng ta không biết tiện ích mô-đun sẽ xác định repo nào cho đến sau 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 những kho lưu trữ mà họ muốn tiện ích tạo ra, sau đó Bazel sẽ chỉ chạy tiện ích khi những kho lưu trữ cụ thể này được sử dụng.

Để giúp duy trì chỉ thị use_repo này, một tiện ích mô-đun có thể trả về một đối tượng extension_metadata từ hàm triển khai của tiện ích. 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

Tệp nào được đánh giá trước, MODULE.bazel hay WORKSPACE?

Khi cả --enable_bzlmod--enable_workspace đều được đặt, bạn sẽ thắc mắc không biết hệ thống nào được tham khảo trước. Câu trả lời ngắn gọn là MODULE.bazel (Bzlmod) sẽ được đánh giá trước.

Câu trả lời dài là "cái nào được đánh giá trước" không phải là câu hỏi phù hợp để hỏi; thay vào đó, câu hỏi phù hợp để hỏi là: trong bối cảnh của kho lưu trữ có tên chuẩn @@foo, tên kho lưu trữ rõ ràng @bar sẽ phân giải thành gì? Hoặc, mối liên kết kho lưu trữ của @@base là gì?

Các nhãn có tên kho lưu trữ rõ ràng (một @ duy nhất ở đầu) có thể đề cập đến nhiều thứ dựa trên ngữ cảnh mà chúng được phân giải. Khi thấy nhãn @bar//:baz và thắc mắc về nội dung mà nhãn đó thực sự trỏ đến, trước tiên, bạn cần tìm hiểu xem kho lưu trữ ngữ cảnh là gì: ví dụ: nếu nhãn nằm trong một tệp BUILD nằm trong kho lưu trữ @@foo, thì kho lưu trữ ngữ 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 tên rõ ràng thực sự phân giải thành kho lưu trữ nào.

  • Nếu kho lưu trữ theo 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 nào), thì @bar sẽ phân giải thành nội dung mà tệp MODULE.bazel đó khai báo.
    2. Nếu bar là một 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 giá trị như @@[unknown repo 'bar' requested from @@] và điều này 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ữ này tương ứng với một mô-đun Bazel không phải gốc hoặc do một tiện ích mô-đun tạo ra), thì kho lưu trữ này 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 một tiện ích mô-đun tương tự 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ữ ngữ 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 quy trình lập bản đồ trước (ví dụ: đối với một 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 đó khai báo. (Trường hợp này giống với trường hợp 1 trong 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 một repo bar được xác định trong WORKSPACE; nếu không xác định repo như vậy, Bazel sẽ báo lỗi.

Để có 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ẽ xem mô-đun gốc trong thế giới Bzlmod xác định những gì, 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 xây 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 (chẳng hạn như bazel fetch --repo @foo) để chỉ tìm nạp @foo của kho lưu trữ (được phân giải trong bối cảnh của kho lưu trữ chính, hãy xem câu hỏi ở trên), hoặc sử dụng một mẫu mục tiêu (chẳng hạn 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 tạo bản dựng, hãy sử dụng --nofetch. Cụ thể hơn, điều này khiến mọi nỗ lực chạy một quy tắc kho lưu trữ không phải cục bộ đều thất bại.

Nếu bạn muốn tìm nạp các 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 để 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 chế độ 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 có ngăn xếp kép IPv4/IPv6, 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 trường hợp 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 thuộc tính hệ thống java.net.preferIPv6Addresses=true. Cụ thể:

  • Sử dụng lựa chọn khởi động --host_jvm_args=-Djava.net.preferIPv6Addresses=true, chẳng hạn như bằng cách thêm dòng sau vào tệp .bazelrc:

    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 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:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Nếu bạn đang 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 biến môi trường COURSIER_OPTS để cung cấp các lựa chọn JVM cho Coursier.

Có thể chạy các quy tắc về 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 quá trình tạo 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 thiết bị cục bộ trước (nếu có, hãy sử dụng mọi bộ nhớ đệm tải xuống cục bộ), sau đó được trích xuất và 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. Việc đặt câu 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ữ đó, tiết kiệm một chuyến khứ hồi vô ích là điều tự nhiên.

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 kho lưu trữ đã tải xuống và giải nén để 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 tái tạo các quy tắc kho lưu trữ dưới dạng quy tắc xây dựng, điều này sẽ cho phép các quy tắc này chạy từ xa một cách tự nhiên, nhưng ngược lại, sẽ làm nảy sinh những lo ngại mới về cấu trúc (ví dụ: các lệnh query có thể cần chạy các thao tác, làm phức tạp thiết kế của chúng).

Để xem thêm nội dung thảo luận trước đây về chủ đề này, hãy xem phần Cách hỗ trợ các kho lưu trữ cần Bazel để được tìm nạp.