Mô-đun Bazel

Báo cáo sự cố Xem nguồn

Mô-đun Bazel là một dự án Bazel có thể có nhiều phiên bản, mỗi phiên bản phát hành siêu dữ liệu về các mô-đun khác mà nó phụ thuộc vào. Điều này tương đồng với các khái niệm quen thuộc trong các hệ thống quản lý phần phụ thuộc khác, chẳng hạn như một cấu phần phần mềm Maven, một gói npm, một mô-đun Go hoặc một thùng hàng hoá.

Một mô-đun phải có tệp MODULE.bazel ở gốc kho lưu trữ. Tệp này là tệp kê khai của mô-đun, khai báo tên, phiên bản, danh sách các phần phụ thuộc trực tiếp và các thông tin khác. Ví dụ cơ bản:

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

Xem danh sách đầy đủ các lệnh có trong tệp MODULE.bazel.

Để phân giải mô-đun, Bazel bắt đầu bằng cách đọc tệp MODULE.bazel của mô-đun gốc, sau đó liên tục yêu cầu tệp MODULE.bazel của bất kỳ phần phụ thuộc nào từ Sổ đăng ký Bazel cho đến khi phát hiện thấy toàn bộ biểu đồ phần phụ thuộc.

Theo mặc định, Bazel chọn một phiên bản của từng mô-đun sẽ sử dụng. Bazel đại diện cho từng mô-đun bằng một kho lưu trữ và tham khảo lại sổ đăng ký để tìm hiểu cách xác định từng kho lưu trữ.

Định dạng phiên bản

Bazel có hệ sinh thái đa dạng và các dự án sử dụng nhiều cơ chế tạo phiên bản. Cho đến nay, SemVer là phổ biến nhất, nhưng cũng có nhiều dự án nổi bật sử dụng các lược đồ khác nhau như Abseil, có phiên bản dựa trên ngày, ví dụ: 20210324.2).

Vì lý do này, Bzlmod sử dụng một phiên bản thông số kỹ thuật SemVer thoải mái hơn. Những điểm khác biệt bao gồm:

  • SemVer quy định rằng phần "phát hành" của phiên bản phải bao gồm 3 phân đoạn: MAJOR.MINOR.PATCH. Trong Bazel, yêu cầu này được nới lỏng để cho phép số lượng phân đoạn bất kỳ.
  • Trong SemVer, mỗi phân đoạn trong phần "bản phát hành" chỉ được chứa chữ số. Trong Bazel, giá trị này được nới lỏng để cho phép cả chữ cái và ngữ nghĩa so sánh khớp với "giá trị nhận dạng" trong phần "bản phát hành trước".
  • Ngoài ra, ngữ nghĩa của các phiên bản lớn, nhỏ và bản vá sẽ không được thực thi. Tuy nhiên, hãy xem mức độ tương thích để biết thông tin chi tiết về cách chúng tôi biểu thị khả năng tương thích ngược.

Mọi phiên bản SemVer hợp lệ đều là phiên bản mô-đun Bazel hợp lệ. Ngoài ra, hai phiên bản SemVer ab so sánh a < b khi và chỉ khi các phiên bản này được so sánh với các phiên bản mô-đun Bazel.

Chọn phiên bản

Hãy xem xét vấn đề về phần phụ thuộc của kim cương, một phần phụ thuộc trong không gian quản lý phần phụ thuộc đã tạo phiên bản. Giả sử bạn có biểu đồ phần phụ thuộc:

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

Bạn nên sử dụng phiên bản nào của D? Để giải quyết câu hỏi này, Bzlmod sử dụng thuật toán Lựa chọn phiên bản tối thiểu (MVS) được đưa vào hệ thống mô-đun Go. MVS giả định rằng tất cả các phiên bản mới của một mô-đun đều có khả năng tương thích ngược, vì vậy, hãy chọn phiên bản cao nhất được chỉ định bởi bất kỳ phần phụ thuộc nào (D 1.1 trong ví dụ của chúng tôi). Giá trị này được gọi là "tối thiểu" vì D 1.1 là phiên bản sớm nhất có thể đáp ứng các yêu cầu của chúng tôi, ngay cả khi D 1.2 hay mới hơn tồn tại, chúng tôi cũng sẽ không chọn các phiên bản đó. Việc sử dụng MVS sẽ tạo ra một quy trình lựa chọn phiên bản có độ chân thực caocó thể tái tạo.

Phiên bản Yanked

Tổ chức quản lý tên miền có thể khai báo một số phiên bản nhất định là bị giật nếu cần tránh (chẳng hạn như do lỗ hổng bảo mật). Bazel gửi lỗi khi chọn một phiên bản bị giật của một mô-đun. Để khắc phục lỗi này, hãy nâng cấp lên một phiên bản mới hơn, không bị giật hoặc sử dụng cờ --allow_yanked_versions để cho phép rõ ràng phiên bản bị kéo.

Mức độ tương thích

Trong phiên bản Go, giả định của MVS về khả năng tương thích ngược sẽ hoạt động vì nó coi các phiên bản không tương thích ngược của một mô-đun là một mô-đun riêng. Xét về SeVer, điều đó có nghĩa là A 1.xA 2.x được coi là các mô-đun riêng biệt và có thể cùng tồn tại trong biểu đồ phần phụ thuộc đã phân giải. Đổi lại, điều này có thể thực hiện được bằng cách mã hoá phiên bản lớn trong đường dẫn gói trong Go, nhờ đó sẽ không có bất kỳ xung đột thời gian biên dịch hoặc thời gian liên kết nào.

Tuy nhiên, Bazel không thể đảm bảo như vậy, vì vậy, công cụ này cần có số "phiên bản chính" để phát hiện các phiên bản không tương thích ngược. Số này được gọi là mức tương thích và được chỉ định bởi từng phiên bản mô-đun trong lệnh module(). Với thông tin này, Bazel có thể báo lỗi khi phát hiện thấy các phiên bản của cùng một mô-đun có nhiều mức độ tương thích tồn tại trong biểu đồ phần phụ thuộc đã phân giải.

Ghi đè

Chỉ định các chế độ ghi đè trong tệp MODULE.bazel để thay đổi hành vi của độ phân giải mô-đun Bazel. Chỉ các chế độ ghi đè của mô-đun gốc mới có hiệu lực. Nếu một mô-đun được dùng làm phần phụ thuộc, thì các chế độ ghi đè của mô-đun đó sẽ bị bỏ qua.

Mỗi chế độ ghi đè được chỉ định cho một tên mô-đun nhất định. Điều này ảnh hưởng đến tất cả các phiên bản của mô-đun đó trong biểu đồ phần phụ thuộc. Mặc dù chỉ có các chế độ ghi đè của mô-đun gốc mới có hiệu lực, nhưng các chế độ này có thể dành cho các phần phụ thuộc bắc cầu mà mô-đun gốc không phụ thuộc trực tiếp.

Ghi đè phiên bản đơn

single_version_override phục vụ nhiều mục đích:

  • Với thuộc tính version, bạn có thể ghim một phần phụ thuộc vào một phiên bản cụ thể, bất kể phiên bản của phần phụ thuộc đó được yêu cầu trong biểu đồ phần phụ thuộc.
  • Với thuộc tính registry, bạn có thể buộc phần phụ thuộc này đến từ một sổ đăng ký cụ thể, thay vì làm theo quy trình lựa chọn sổ đăng ký thông thường.
  • Với thuộc tính patch*, bạn có thể chỉ định một bộ bản vá để áp dụng cho mô-đun đã tải xuống.

Tất cả các thuộc tính này đều không bắt buộc và có thể kết hợp với nhau.

Ghi đè nhiều phiên bản

Bạn có thể chỉ định multiple_version_override để cho phép nhiều phiên bản của cùng một mô-đun cùng tồn tại trong biểu đồ phần phụ thuộc đã được phân giải.

Bạn có thể chỉ định danh sách rõ ràng các phiên bản được phép cho mô-đun. Tất cả các phiên bản này phải xuất hiện trong biểu đồ phần phụ thuộc trước khi giải quyết – phải tồn tại một số phần phụ thuộc bắc cầu tuỳ thuộc vào từng phiên bản được cho phép. Sau khi độ phân giải, chỉ còn các phiên bản được phép của mô-đun, trong khi Bazel nâng cấp các phiên bản khác của mô-đun lên phiên bản được phép cao hơn gần nhất ở cùng mức độ tương thích. Nếu không có phiên bản nào được cho phép cao hơn ở cùng mức độ tương thích, thì Bazel sẽ gửi lỗi.

Ví dụ: nếu các phiên bản 1.1, 1.3, 1.5, 1.72.0 tồn tại trong biểu đồ phần phụ thuộc trước khi phân giải và phiên bản chính là mức độ tương thích:

  • Việc ghi đè nhiều phiên bản cho phép 1.3, 1.72.0 dẫn đến việc 1.1 được nâng cấp lên 1.3, 1.5 được nâng cấp lên 1.7 và các phiên bản khác vẫn giữ nguyên.
  • Việc ghi đè nhiều phiên bản cho phép 1.52.0 sẽ dẫn đến lỗi, vì 1.7 không có phiên bản cao hơn nào có cùng mức độ tương thích để nâng cấp lên.
  • Việc ghi đè nhiều phiên bản cho phép 1.92.0 sẽ dẫn đến lỗi, vì 1.9 không có trong biểu đồ phần phụ thuộc trước khi giải quyết.

Ngoài ra, người dùng cũng có thể ghi đè sổ đăng ký bằng cách sử dụng thuộc tính registry, tương tự như cơ chế ghi đè một phiên bản.

Ghi đè không phải đăng ký

Thao tác ghi đè không phải cơ quan đăng ký sẽ xoá hoàn toàn mô-đun khỏi độ phân giải phiên bản. Bazel không yêu cầu các tệp MODULE.bazel này từ sổ đăng ký, mà thay vào đó yêu cầu từ chính kho lưu trữ đó.

Bazel hỗ trợ các cơ chế ghi đè sau đây không phải cơ chế đăng ký:

Xác định các kho lưu trữ không đại diện cho mô-đun Bazel

Với bazel_dep, bạn có thể xác định các kho lưu trữ đại diện cho các mô-đun Bazel khác. Đôi khi, bạn cần xác định một kho lưu trữ không đại diện cho mô-đun Bazel; ví dụ: một kho lưu trữ chứa tệp JSON thuần tuý được đọc dưới dạng dữ liệu.

Trong trường hợp này, bạn có thể sử dụng lệnh use_repo_rule để trực tiếp xác định kho lưu trữ bằng cách gọi quy tắc kho lưu trữ. Kho lưu trữ này sẽ chỉ hiển thị với mô-đun được xác định.

Trong trường hợp này, việc này được triển khai bằng cách sử dụng cùng một cơ chế như phần mở rộng của mô-đun, cho phép bạn xác định các kho lưu trữ linh hoạt hơn.

Tên kho lưu trữ và phần phụ thuộc nghiêm ngặt

Tên biểu thị của một kho lưu trữ sao lưu một mô-đun cho các phần phụ thuộc trực tiếp sẽ mặc định là tên mô-đun, trừ phi thuộc tính repo_name của lệnh bazel_dep có giá trị khác. Lưu ý điều này có nghĩa là một mô-đun chỉ có thể tìm thấy các phần phụ thuộc trực tiếp của mình. Điều này giúp ngăn chặn tình trạng gián đoạn ngoài ý muốn do những thay đổi trong các phần phụ thuộc bắc cầu.

Tên chính tắc của kho lưu trữ sao lưu mô-đun là module_name~version (ví dụ: bazel_skylib~1.0.3) hoặc module_name~ (ví dụ: bazel_features~), tuỳ thuộc vào việc có nhiều phiên bản mô-đun trong toàn bộ biểu đồ phần phụ thuộc hay không (xem multiple_version_override). Lưu ý rằng định dạng tên chuẩn không phải là API mà bạn nên dựa vào và có thể thay đổi bất cứ lúc nào. Thay vì mã hoá cứng tên chính tắc, hãy sử dụng cách được hỗ trợ để lấy trực tiếp tên chính tắc từ Bazel: * Trong tệp BUILD và .bzl, hãy sử dụng Label.repo_name trên thực thể Label được tạo từ chuỗi nhãn theo tên biểu kiến của kho lưu trữ, ví dụ: Label("@bazel_skylib").repo_name. * Khi tìm kiếm tệp chạy, hãy sử dụng $(rlocationpath ...) hoặc một trong các thư viện tệp chạy trong @bazel_tools//tools/{bash,cpp,java}/runfiles hoặc đối với bộ quy tắc rules_foo trong @rules_foo//foo/runfiles. * Khi tương tác với Bazel qua một công cụ bên ngoài như IDE hoặc máy chủ ngôn ngữ, hãy dùng lệnh bazel mod dump_repo_mapping để liên kết từ tên rõ ràng đến tên chính tắc của một nhóm kho lưu trữ nhất định.

Tiện ích mô-đun cũng có thể đưa các kho lưu trữ bổ sung vào phạm vi hiển thị của mô-đun.