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

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

Trang này trả lời 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 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ả 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 bản 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 thể thực sự tìm nạp tệp đó mà không tìm nạp toàn bộ bản lưu trữ nguồn. Xin lưu ý rằng bản thân 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 vài trường hợp sử dụng mà mọi người thường quan tâm khi yêu cầu load trong MODULE.bazel 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ể 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 một 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 monorepos: Mô-đun gốc có thể sử dụng lệnh include để chia tệp MODULE.bazel thành nhiều phân đoạn. Cũng vì lý do này mà chúng tôi không cho phép load trong các tệp MODULE.bazel, include không thể được sử dụng 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 một 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 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ợ các 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 quy tắc ràng buộc (khiến người dùng khó dự đoán kết quả hơn) và khiến độ 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ả 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 áp dụng cho các phiên bản mô-đun Bazel.

Tôi có thể tự động tải 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 có thể chỉ định bazel_dep(name = "foo", version = "latest") để tự động tải 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à 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 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 như 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 một lệnh 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...
)

Lệnh use_repo dài có vẻ thừa vì thông tin có thể đã có trong tệp go.mod được tham chiếu.

Lý do Bazel cần lệnh use_repo này là vì lệnh này chạy các tiện ích mô-đun một cách lười biếng. Tức là một phần mở rộng mô-đun chỉ chạy nếu kết quả của phần mở rộng đó đượ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 phần mở rộng mô-đun sẽ xác định kho lưu trữ nào cho đến khi chúng ta chạy phần mở rộng đó. Đây là nơi lệnh use_repo xuất hiện; người dùng có thể cho Bazel biết họ muốn phần mở rộng tạo ra kho lưu trữ nào, sau đó Bazel sẽ chỉ chạy phần mở rộng khi các kho lưu trữ cụ thể này được sử dụng.

Để giúp duy trì lệnh use_repo này, tiện ích mô-đun có thể trả về đối tượng extension_metadata từ hàm triển khai của mô-đun. Người dùng có thể chạy lệnh bazel mod tidy để cập nhật các lệnh use_repo cho các tiện ích mô-đun này.

Di chuyển bzlmod

Mô-đun nào được đánh giá trước, MODULE.bazel hay WORKSPACE?

Khi bạn đặt cả --enable_bzlmod--enable_workspace, bạn sẽ tự nhiên thắc mắc 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à "đâu là tên đánh giá trước" không phải là câu hỏi phù hợp; thay vào đó, câu hỏi phù hợp là: trong ngữ cảnh của kho lưu trữ có tên chuẩn @@foo, tên kho lưu trữ hiển thị @bar sẽ phân giải thành tên nào? Ngoài ra, bản đồ kho lưu trữ của @@base là gì?

Nhãn có tên kho lưu trữ rõ ràng (một @ ở đầu) có thể tham chiếu đến nhiều nội dung dựa trên ngữ cảnh mà nhãn được phân giải. Khi thấy một 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ữ ngữ 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ữ ngữ cảnh là @@foo.

Sau đó, tuỳ thuộc vào kho lưu trữ ngữ cảnh, bạn có thể sử dụng bảng"mức độ hiển thị của kho lưu trữ" trong hướng dẫn di chuyển để tìm ra kho lưu trữ mà tên hiển thị thực sự phân giải đến.

  • Nếu kho lưu trữ ngữ 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 đó xác nhận.
    2. Nếu không, nếu bar là một kho lưu trữ được xác định trong WORKSPACE (tức là tên chuẩn hoá 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à cuối cùng sẽ dẫn đến lỗi.
  • Nếu kho lưu trữ ngữ 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 được tạo bằng một tiện ích mô-đun), thì kho lưu trữ này sẽ chỉ thấy các kho lưu trữ Bzlmod-world khác và không có 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 non_module_deps trong mô-đun gốc hoặc các bản sao 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ữ ngữ cảnh có thuộc tính repo_mapping ma thuật hay không. Nếu có, hãy xem xét mối liên kết trước (vì vậy, đố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 đó xác nhận. (Đây là 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. Rất có thể, đường dẫn này sẽ trỏ đến một repo bar được xác định trong WORKSPACE; nếu không xác định được repo như vậy, Bazel sẽ gửi một lỗi.

Để có phiên bản ngắn gọn hơn:

  • 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.
  • Trước tiên, kho lưu trữ WORKSPACE-world (bao gồm cả kho lưu trữ chính) sẽ xem mô-đun gốc trong thế giới Bzlmod xác định gì, sau đó quay lại xem 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 nhập bằng 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à kho lưu trữ ngữ 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ải trướ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 ngữ 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ả 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. 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 không thành công.

Nếu bạn muốn tìm nạp kho lưu trữ sửa đổi các 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 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 IPv4/IPv6 có 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/đến 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 thuộc tính hệ thống java.net.preferIPv6Addresses=true. Cụ thể:

  • Sử dụng tuỳ chọn khởi động --host_jvm_args=-Djava.net.preferIPv6Addresses=true, ví dụ: 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ư để kiểm thử tích hợp), hãy sử dụng cờ công cụ --jvmopt=-Djava.net.preferIPv6Addresses=true. Ví dụ: đưa vào tệp .bazelrc:

    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 biến môi trường COURSIER_OPTS để cung cấp các tuỳ chọn JVM cho Coursier.

Có thể chạy quy tắc repo 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 các bản dựng có thể nhận thấy rằng các quy tắc kho lưu trữ vẫn chạy cục bộ. Ví dụ: trước tiên, http_archive sẽ được tải xuống máy cục bộ (sử dụng bất kỳ bộ nhớ đệm tải xuống cục bộ nào 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 có thể 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 lượt đi lạ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 việc tải và phân tích, các thao tác này được thực hiện trên máy.

Có một số ý 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 repo dưới dạng quy tắc bản 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 các mối 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 hành động, làm phức tạp thiết kế của chúng).

Để biết 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.