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

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

Bazel hỗ trợ các 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 sử dụng trong bản dựng không thuộc không gian làm việc của bạn. Ví dụ: chúng có thể là một tập hợp quy tắc được lưu trữ trong kho lưu trữ GitHub, 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 của bạn.

Kể từ phiên bản Bazel 6.0, có hai cách để quản lý các 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 tập trung vào mô-đun (tên mã là Bzlmod và được bật bằng cờ --enable_bzlmod). Hai hệ thống này có thể được sử dụng cùng nhau, nhưng Bzlmod sẽ thay thế hệ thống WORKSPACE trong các bản phát hành Bazel sau này, hãy xem hướng dẫn Bz0lmod} về cách di chuyển Bz0l.

Tài liệu này giải thích các khái niệm liên quan đến 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ề hai hệ thống này theo thứ tự.

Khái niệm

Kho lưu trữ

Cây thư mục có một tệp đánh dấu ranh giới ở gốc, chứa các tệp nguồn có thể dùng trong bản dựng Bazel. Thường được rút ngắn thành chỉ kho lưu trữ.

Tệp đánh dấu ranh giới repo có thể là MODULE.bazel (báo hiệu rằng repo này đại diện cho một mô-đun Bazel), REPO.bazel (xem bên dưới) hoặc trong ngữ cảnh cũ, WORKSPACE hoặc WORKSPACE.bazel. Mọi tệp đánh dấu ranh giới kho lưu trữ sẽ biểu thị ranh giới của một kho lưu trữ; nhiều tệp như vậy có thể cùng tồn tại trong một thư mục.

Kho lưu trữ chính

Kho lưu trữ nơi chạy lệnh Bazel hiện tại.

Thư mục gốc của kho lưu trữ chính còn được gọi là gốc không gian làm việc.

Workspace

Môi trường dùng chung của tất cả các lệnh Bazel đều chạy trong cùng một kho lưu trữ chính. Kho lưu trữ này bao gồm kho lưu trữ chính và tập hợp tất cả các kho lưu trữ bên ngoài đã xác định.

Xin lưu ý rằng trước đây, các khái niệm về "kho lưu trữ" và "không gian làm việc" không giống nhau, nê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 còn được dùng làm từ đồng nghĩa với "kho lưu trữ".

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

Tên chuẩn hoá mà kho lưu trữ có thể xác định được. 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. Mục tiêu bên trong kho lưu trữ có tên chính tắc là canonical_name có thể được xác định bằng nhãn @@canonical_name//pac/kage:target (lưu ý rằng @ 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ữ biểu kiến

Có thể xác định tên của một kho lưu trữ trong ngữ cảnh của một kho lưu trữ khác. Đây có thể được coi là "biệt hiệu" của kho lưu trữ: Kho lưu trữ có tên chính tắc michael có thể có tên rõ ràng là mike trong ngữ cảnh của kho lưu trữ alice, nhưng có thể có tên rõ ràng là mickey khi đặt trong ngữ cảnh của kho lưu trữ bob. Trong trường hợp này, mục tiêu bên trong michael có thể được nhãn @mike//pac/kage:target xử lý trong bối cảnh alice (lưu ý rằng @ đơn lẻ).

Ngược lại, đây có thể được hiểu là bản đồ kho lưu trữ: mỗi kho lưu trữ duy trì một mục ánh xạ từ "tên kho lưu trữ gốc" đến "tên kho lưu trữ chính tắc".

Quy tắc kho lưu trữ

Một giản đồ cho các định nghĩa về kho lưu trữ, cho Bazel biết cách cụ thể hoá kho lưu trữ. Ví dụ: bạn có thể làm như sau: "tải một tệp lưu trữ zip từ một URL nhất định xuống rồi trích xuất nó" hoặc "tìm nạp một cấu phần phần mềm Maven nhất định và cung cấp dưới dạng một 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ữ được xác định bằng cách gọi quy tắc kho lưu trữ có số lượng đối số thích hợp.

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.

Các quy tắc kho lưu trữ phổ biến nhất cho đến nay là http_archive (tải tệp lưu trữ xuống từ URL và trích xuất tệp lưu trữ đó), và local_repository liên kết tượng trưng cho 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 tạo một kho lưu trữ có sẵn 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 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 nội dung nào đó từ kho lưu trữ và kho lưu trữ này chưa được tìm nạp. Nếu kho lưu trữ này đã được tìm nạp trước đó, Bazel chỉ tìm nạp lại kho lưu trữ đó nếu định nghĩa đã thay đổi.

Bạn có thể dùng lệnh fetch để bắt đầu quá trình tìm nạp trước cho một kho lưu trữ, mục tiêu hoặc tất cả các kho lưu trữ cần thiết để thực hiện bất kỳ bản dựng nào. Tính năng này cho phép các bản dựng ngoại tuyến bằng cách sử dụng tuỳ chọn --nofetch.

Tuỳ chọn --fetch dùng để quản lý quyền truy cập mạng. Giá trị mặc định của thuộc tính này là true. Tuy nhiên, khi bạn đặt thành false (--nofetch), lệnh này sẽ sử dụng mọi phiên bản được lưu vào bộ nhớ đệm của phần phụ thuộc. Nếu không có phiên bản nào tồn tại, lệnh này sẽ dẫn đến không thành công.

Xem các tuỳ chọn tìm nạp để biết thêm thông tin về cách kiểm soát hoạt động tìm nạp.

Bố cục thư mục

Sau khi tìm nạp, bạn có thể tìm thấy kho lưu trữ này trong thư mục con externalcơ sở đầu ra, dưới tên chính tắc 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 chính tắc là canonical_name:

ls $(bazel info output_base)/external/ canonical_name 

Tệp REPO.bazel

Tệp REPO.bazel dùng để đánh dấu ranh giới trên cùng của cây thư mục cấu thành kho lưu trữ. Tệp này không cần chứa bất cứ thứ gì để đóng vai trò là tệp ranh giới của kho lưu trữ; tuy nhiên, bạn cũng có thể dùng tệp này để chỉ định một số thuộc tính phổ biến cho tất cả các mục tiêu bản dựng bên trong kho lưu trữ đó.

Cú pháp của tệp REPO.bazel tương tự như tệp BUILD, ngoại trừ việc không có câu lệnh load nào được hỗ trợ và chỉ có một hàm duy nhất là repo(). repo() lấy các đối số tương tự như hàm package() trong tệp BUILD; trong khi package() chỉ định các thuộc tính phổ biến cho tất cả mục tiêu bản dựng bên trong gói, repo() chỉ định các thuộc tính tương tự cho tất cả mục tiêu bản dựng bên trong kho lưu trữ.

Ví dụ: bạn có thể chỉ định một giấy phép chung cho tất cả các mục tiêu trong kho lưu trữ của mình bằng cách có tệp REPO.bazel sau:

repo(
    default_package_metadata = ["//:my_license"],
)

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

Bzlmod, hệ thống phụ 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 đó, nó tạo một biểu đồ phần phụ thuộc từ các mô-đun, chạy phần mở rộng ở đầu biểu đồ và xác định kho lưu trữ cho phù hợp.

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. Một mô-đun phải có một 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 và nhiều 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 tra cứu trong 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 các 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 tiến hành 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 tham khảo lại sổ đăng ký để tìm hiểu cách xác định kho lưu trữ cho từng 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ẻ. tiện ích mô-đun sẽ sử dụng các phần dữ liệu này sau quá trình 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 thực hiện các thao tác như thao tác I/O đối với tệp và gửi yêu cầu mạng. Ngoài ra, chúng cho phép Bazel tương tác với các hệ thống quản lý gói khác trong khi vẫn tôn trọng biểu đồ phần phụ thuộc được tạo từ các mô-đun Bazel.

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

Trước đây, bạn có thể quản lý các 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 các 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ề việc 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ã xác định một kho lưu trữ có tên chuẩn là foo. Theo mặc định, trong hệ thống WORKSPACE, tên chính tắc của một kho lưu trữ cũng là tên biểu kiến của tất cả các kho lưu trữ khác.

Xem danh sách đầy đủ các hàm có trong tệp WORKSPACE.

Nhược điểm của hệ thống WORKSPACE

Trong những năm kể từ khi hệ thống WORKSPACE ra mắt, người dùng đã báo cáo nhiều vấ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, ngoài các phần phụ thuộc trực tiếp, bạn phải xác định mọi phần phụ thuộc bắc cầu trong tệp WORKSPACE của kho lưu trữ chính.
  • Để giải quyết vấn đề này, các dự án đã sử dụng mẫu "deps.bzl", trong đó xác định macro để 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ũng 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 phần phụ thuộc bắc cầu trong macro "phần phụ thuộc" 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 lớp macro "phần phụ thuộc".
    • 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 cùng với các URL mà không có thông tin phiên bản nào. Điều này có nghĩa là không có cách đáng tin cậy nào để phân giải phiên bản trong trường hợp phần phụ thuộc kim cương (A phụ thuộc vào BC; BC cả hai 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 sau này của Bazel. Vui lòng đọc hướng dẫn di chuyển Bzlmod để biết cách di chuyển sang Bzlmod.