Tổng quan về các phần phụ thuộc bên ngoài

Bazel hỗ trợ phần phụ thuộc bên ngoài, tệp nguồn (cả văn bản và tệp nhị phân) được dùng trong bản dựng không phải từ không gian làm việc của bạn. Ví dụ: chúng có thể là một bộ quy tắc được lưu trữ trong kho lưu trữ GitHub, một cấu phần phần mềm Maven hoặc một thư mục trên máy cục bộ bên ngoài không gian làm việc hiện tại.

Kể từ Bazel 6.0, có 2 cách để quản lý phần phụ thuộc bên ngoài bằng Bazel: hệ thống WORKSPACE truyền thống, tập trung vào kho lưu trữ và hệ thống MODULE.bazel mới hơn, tập trung vào mô-đun (có tên mã là Bzlmod, và được bật bằng cờ --enable_bzlmod). Bạn có thể sử dụng cả hai hệ thống này cùng nhau, nhưng Bzlmod sẽ thay thế hệ thống WORKSPACE trong các bản phát hành Bazel trong tương lai. Hãy xem hướng dẫn di chuyển Bzlmod để biết cách di chuyển.

Tài liệu này giải thích các khái niệm xung quanh việc quản lý phần phụ thuộc bên ngoài trong Bazel, trước khi đi sâu hơn một chút về 2 hệ thống theo thứ tự.

Khái niệm

Kho lưu trữ

Một thư mục có tệp WORKSPACE hoặc WORKSPACE.bazel, chứa tệp nguồn sẽ được dùng trong bản dựng Bazel. Thường được rút gọn thành kho lưu trữ.

Kho lưu trữ chính

Kho lưu trữ mà lệnh Bazel hiện tại đang chạy.

Không gian làm việc

Môi trường được chia sẻ bởi tất cả các lệnh Bazel chạy trong cùng một kho lưu trữ chính.

Xin lưu ý rằng trong lịch sử, các khái niệm về "kho lưu trữ" và "không gian làm việc" đã bị trộn lẫn; thuật ngữ "không gian làm việc" thường được dùng để chỉ kho lưu trữ chính và đôi khi thậm chí được dùng làm từ đồng nghĩa của "kho lưu trữ".

Tên kho lưu trữ chuẩn

Tên chuẩn mà một kho lưu trữ có thể được định địa chỉ. Trong ngữ cảnh của một không gian làm việc, mỗi kho lưu trữ có một tên chuẩn duy nhất. Bạn có thể định địa chỉ mục tiêu bên trong một kho lưu trữ có tên chuẩn là canonical_name bằng nhãn @@canonical_name//pac/kage:target (lưu ý dấu @ kép).

Kho lưu trữ chính luôn có chuỗi trống làm tên chuẩn.

Tên kho lưu trữ hiển thị

Tên mà một kho lưu trữ có thể được định địa chỉ trong ngữ cảnh của một kho lưu trữ khác. Bạn có thể coi đây là "biệt hiệu" của kho lưu trữ: Kho lưu trữ có tên chuẩn michael có thể có tên hiển thị là mike trong ngữ cảnh của kho lưu trữ alice, nhưng có thể có tên hiển thị là mickey trong ngữ cảnh của kho lưu trữ bob. Trong trường hợp này, bạn có thể định địa chỉ mục tiêu bên trong michael bằng nhãn @mike//pac/kage:target trong ngữ cảnh của alice (lưu ý dấu @ đơn).

Ngược lại, bạn có thể hiểu đây là ánh xạ kho lưu trữ: mỗi kho lưu trữ duy trì một ánh xạ từ "tên kho lưu trữ hiển thị" đến "tên kho lưu trữ chuẩn".

Quy tắc kho lưu trữ

Một lược đồ cho các định nghĩa kho lưu trữ cho biết Bazel cách hiện thực hoá một kho lưu trữ. Ví dụ: có thể là "tải một tệp lưu trữ zip xuống từ một URL nhất định và giải nén tệp đó" hoặc "tìm nạp một cấu phần phần mềm Maven nhất định và cung cấp cấu phần phần mềm đó dưới dạng mục tiêu java_import" hoặc đơn giản là "liên kết tượng trưng một thư mục cục bộ". Mọi kho lưu trữ đều được xác định bằng cách gọi một quy tắc kho lưu trữ với số lượng đối số thích hợp.

Hãy xem Quy tắc kho lưu trữ để biết thêm thông tin về cách viết quy tắc kho lưu trữ của riêng bạn.

Cho đến nay, các quy tắc kho lưu trữ phổ biến nhất là http_archive (tải một tệp lưu trữ xuống từ một URL và giải nén tệp đó) và local_repository (liên kết tượng trưng một thư mục cục bộ đã là kho lưu trữ Bazel).

Tìm nạp kho lưu trữ

Hành động cung cấp một kho lưu trữ trên ổ đĩa cục bộ bằng cách chạy quy tắc kho lưu trữ được liên kết. Các kho lưu trữ được xác định trong một không gian làm việc không có sẵn trên ổ đĩa cục bộ trước khi được tìm nạp.

Thông thường, Bazel chỉ tìm nạp một kho lưu trữ khi cần một nội dung nào đó từ kho lưu trữ đó và kho lưu trữ đó chưa được tìm nạp. Nếu kho lưu trữ đã được tìm nạp trước đó, Bazel chỉ tìm nạp lại kho lưu trữ đó nếu định nghĩa của kho lưu trữ đó đã thay đổi.

Bố cục thư mục

Sau khi được tìm nạp, bạn có thể tìm thấy kho lưu trữ trong thư mục con external trong cơ sở đầu ra, theo tên chuẩn của kho lưu trữ đó.

Bạn có thể chạy lệnh sau để xem nội dung của kho lưu trữ có tên chuẩn là canonical_name:

ls $(bazel info output_base)/external/ canonical_name 

Quản lý phần phụ thuộc bên ngoài bằng Bzlmod

Bzlmod, hệ thống con phần phụ thuộc bên ngoài mới, không trực tiếp hoạt động với các định nghĩa kho lưu trữ. Thay vào đó, hệ thống này xây dựng một biểu đồ phần phụ thuộc từ các mô-đun, chạy tiện ích trên biểu đồ và xác định các kho lưu trữ tương ứng.

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à mô-đun đó phụ thuộc vào. Một mô-đun phải có tệp MODULE.bazel ở gốc kho lưu trữ, bên cạnh tệp WORKSPACE. 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 phần phụ thuộc, cùng với các thông tin khác. Sau đây là một 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")

Một mô-đun chỉ được liệt kê các phần phụ thuộc trực tiếp mà Bzlmod tìm kiếm trong một sổ đăng ký Bazel – theo mặc định là Sổ đăng ký Trung tâm Bazel. Sổ đăng ký cung cấp các tệp MODULE.bazel của phần phụ thuộc, cho phép Bazel khám phá toàn bộ biểu đồ phần phụ thuộc bắc cầu trước khi thực hiện phân giải phiên bản.

Sau khi phân giải phiên bản (trong đó một phiên bản được chọn cho mỗi mô-đun), Bazel sẽ tham khảo lại sổ đăng ký để tìm hiểu cách xác định một kho lưu trữ cho mỗi mô-đun (trong hầu hết các trường hợp, sử dụng http_archive).

Các mô-đun cũng có thể chỉ định các phần dữ liệu tuỳ chỉnh được gọi là thẻ, được tiện ích mô-đun sử dụng sau khi phân giải mô-đun để xác định các kho lưu trữ bổ sung. Các tiện ích này có các chức năng tương tự như quy tắc kho lưu trữ, cho phép chúng thực hiện các hành động như thao tác I/O đối với tệp và gửi yêu cầu mạng. Trong số những việc khác, chúng cho phép Bazel tương tác với các hệ thống quản lý gói khác, đồng thời tuân thủ biểu đồ phần phụ thuộc được xây dựng từ các mô-đun Bazel.

Xác định kho lưu trữ bằng WORKSPACE

Trong lịch sử, bạn có thể quản lý phần phụ thuộc bên ngoài bằng cách xác định các kho lưu trữ trong tệp WORKSPACE (hoặc WORKSPACE.bazel). Tệp này có cú pháp tương tự như tệp BUILD, sử dụng quy tắc kho lưu trữ thay vì quy tắc bản dựng.

Đoạn mã sau đây là một ví dụ về cách sử dụng quy tắc kho lưu trữ http_archive trong tệp WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "foo",
    urls = ["https://example.com/foo.zip"],
    sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)

Đoạn mã này xác định một kho lưu trữ có tên chuẩn là foo. Trong hệ thống WORKSPACE, theo mặc định, tên chuẩn của một kho lưu trữ cũng là tên hiển thị của kho lưu trữ đó đối với tất cả các kho lưu trữ khác.

Thiếu sót của hệ thống WORKSPACE

Trong những năm kể từ khi hệ thống WORKSPACE được giới thiệu, người dùng đã báo cáo nhiều điểm khó khăn, bao gồm:

  • Bazel không đánh giá các tệp WORKSPACE của bất kỳ phần phụ thuộc nào, vì vậy, tất cả các phần phụ thuộc bắc cầu phải được xác định trong tệp WORKSPACE của kho lưu trữ chính, ngoài các phần phụ thuộc trực tiếp.
  • Để giải quyết vấn đề này, các dự án đã áp dụng mẫu "deps.bzl", trong đó họ xác định một macro lần lượt xác định nhiều kho lưu trữ và yêu cầu người dùng gọi macro này trong tệp WORKSPACE.
    • Điều này có vấn đề riêng: macro không thể load các tệp .bzl khác, vì vậy, các dự án này phải xác định các phần phụ thuộc bắc cầu trong macro "deps" này hoặc giải quyết vấn đề này bằng cách yêu cầu người dùng gọi nhiều macro "deps" theo lớp.
    • Bazel đánh giá tệp WORKSPACE theo tuần tự. Ngoài ra, các phần phụ thuộc được chỉ định bằng http_archive với URL, không có thông tin phiên bản. Điều này có nghĩa là không có cách đáng tin cậy nào để thực hiện phân giải phiên bản trong trường hợp phần phụ thuộc hình kim cương (A phụ thuộc vào BC; BC đều phụ thuộc vào các phiên bản khác nhau của D).

Do những thiếu sót của WORKSPACE, Bzlmod sẽ thay thế hệ thống WORKSPACE cũ trong các bản phát hành Bazel trong tương lai. Vui lòng đọc hướng dẫn di chuyển Bzlmod để biết cách di chuyển sang Bzlmod.