Bazel có thể phụ thuộc vào các mục tiêu từ các dự án khác. Các phần phụ thuộc từ các dự án khác này được gọi là phần phụ thuộc bên ngoài.
Tệp WORKSPACE (hoặc tệp WORKSPACE.bazel) trong
thư mục không gian làm việc
cho Bazel biết cách lấy nguồn của các dự án khác. Các dự án khác này có thể chứa một hoặc nhiều tệp BUILD có mục tiêu riêng. Các tệp BUILD trong dự án chính có thể phụ thuộc vào các mục tiêu bên ngoài này bằng cách sử dụng tên của chúng từ tệp WORKSPACE.
Ví dụ: giả sử có 2 dự án trên một hệ thống:
/
home/
user/
project1/
WORKSPACE
BUILD
srcs/
...
project2/
WORKSPACE
BUILD
my-libs/
Nếu project1 muốn phụ thuộc vào một mục tiêu, :foo, được xác định trong
/home/user/project2/BUILD, thì dự án này có thể chỉ định rằng một kho lưu trữ có tên
project2 có thể được tìm thấy tại /home/user/project2. Sau đó, các mục tiêu trong /home/user/project1/BUILD có thể phụ thuộc vào @project2//:foo.
Tệp WORKSPACE cho phép người dùng phụ thuộc vào các mục tiêu từ các phần khác của hệ thống tệp hoặc tải xuống từ Internet. Tệp này sử dụng cùng một cú pháp như tệp BUILD, nhưng cho phép một tập hợp quy tắc khác được gọi là quy tắc kho lưu trữ (đôi khi còn được gọi là quy tắc không gian làm việc). Bazel đi kèm với một vài
quy tắc kho lưu trữ tích hợp sẵn và một tập hợp các
quy tắc kho lưu trữ Starlark được nhúng.
Người dùng cũng có thể viết các quy tắc kho lưu trữ tuỳ chỉnh
để có được hành vi phức tạp hơn.
Các loại phần phụ thuộc bên ngoài được hỗ trợ
Bạn có thể sử dụng một số loại phần phụ thuộc bên ngoài cơ bản:
- Các phần phụ thuộc vào các dự án Bazel khác
- Các phần phụ thuộc vào các dự án không phải Bazel
- Các phần phụ thuộc vào các gói bên ngoài
Phụ thuộc vào các dự án Bazel khác
Nếu muốn sử dụng các mục tiêu từ một dự án Bazel thứ hai, bạn có thể
sử dụng
local_repository,
git_repository
hoặc http_archive
để liên kết tượng trưng từ hệ thống tệp cục bộ, tham chiếu đến một kho lưu trữ git hoặc tải xuống
nó (tương ứng).
Ví dụ: giả sử bạn đang làm việc trên một dự án, my-project/, và bạn muốn phụ thuộc vào các mục tiêu từ dự án của đồng nghiệp, coworkers-project/. Cả hai dự án đều sử dụng Bazel, vì vậy, bạn có thể thêm dự án của đồng nghiệp làm phần phụ thuộc bên ngoài, sau đó sử dụng mọi mục tiêu mà đồng nghiệp đã xác định từ các tệp BUILD của riêng bạn. Bạn sẽ thêm nội dung sau vào my_project/WORKSPACE:
local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
)
Nếu đồng nghiệp của bạn có một mục tiêu //foo:bar, thì dự án của bạn có thể tham chiếu đến mục tiêu đó dưới dạng
@coworkers_project//foo:bar. Tên dự án bên ngoài phải là
tên không gian làm việc hợp lệ.
Phụ thuộc vào các dự án không phải Bazel
Các quy tắc có tiền tố new_, chẳng hạn như
new_local_repository,
cho phép bạn tạo các mục tiêu từ các dự án không sử dụng Bazel.
Ví dụ: giả sử bạn đang làm việc trên một dự án, my-project/, và bạn muốn phụ thuộc vào dự án của đồng nghiệp, coworkers-project/. Dự án của đồng nghiệp sử dụng make để xây dựng, nhưng bạn muốn phụ thuộc vào một trong các tệp .so mà dự án đó tạo. Để thực hiện việc này, hãy thêm nội dung sau vào my_project/WORKSPACE:
new_local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
build_file = "coworker.BUILD",
)
build_file chỉ định một tệp BUILD để phủ lên dự án hiện có, ví dụ:
cc_library(
name = "some-lib",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
Sau đó, bạn có thể phụ thuộc vào @coworkers_project//:some-lib từ các tệp BUILD của dự án.
Phụ thuộc vào các gói bên ngoài
Kho lưu trữ và cấu phần phần mềm Maven
Sử dụng bộ quy tắc rules_jvm_external
để tải cấu phần phần mềm xuống từ kho lưu trữ Maven và cung cấp các cấu phần phần mềm đó dưới dạng phần phụ thuộc Java.
Tìm nạp các phần phụ thuộc
Theo mặc định, các phần phụ thuộc bên ngoài sẽ được tìm nạp khi cần trong quá trình bazel build. Nếu
bạn muốn tìm nạp trước các phần phụ thuộc cần thiết cho một tập hợp mục tiêu cụ thể, hãy sử dụng
bazel fetch.
Để tìm nạp tất cả các phần phụ thuộc bên ngoài một cách vô điều kiện, hãy sử dụng
bazel sync.
Vì các kho lưu trữ đã tìm nạp được lưu trữ trong cơ sở đầu ra, nên quá trình tìm nạp
diễn ra trên mỗi không gian làm việc.
Phần phụ thuộc che phủ
Bất cứ khi nào có thể, bạn nên có một chính sách phiên bản duy nhất trong dự án. Điều này là bắt buộc đối với các phần phụ thuộc mà bạn biên dịch và kết thúc trong tệp nhị phân cuối cùng. Tuy nhiên, trong trường hợp điều này không đúng, bạn có thể che phủ các phần phụ thuộc. Hãy xem xét trường hợp sau:
myproject/WORKSPACE
workspace(name = "myproject")
local_repository(
name = "A",
path = "../A",
)
local_repository(
name = "B",
path = "../B",
)
A/WORKSPACE
workspace(name = "A")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner",
urls = ["https://github.com/testrunner/v1.zip"],
sha256 = "...",
)
B/WORKSPACE
workspace(name = "B")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner",
urls = ["https://github.com/testrunner/v2.zip"],
sha256 = "..."
)
Cả hai phần phụ thuộc A và B đều phụ thuộc vào testrunner, nhưng chúng phụ thuộc vào các phiên bản khác nhau của testrunner. Không có lý do gì để các trình kiểm thử này không thể cùng tồn tại một cách hoà bình trong myproject, tuy nhiên, chúng sẽ xung đột với nhau vì có cùng tên. Để khai báo cả hai phần phụ thuộc, hãy cập nhật myproject/WORKSPACE:
workspace(name = "myproject")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner-v1",
urls = ["https://github.com/testrunner/v1.zip"],
sha256 = "..."
)
http_archive(
name = "testrunner-v2",
urls = ["https://github.com/testrunner/v2.zip"],
sha256 = "..."
)
local_repository(
name = "A",
path = "../A",
repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
name = "B",
path = "../B",
repo_mapping = {"@testrunner" : "@testrunner-v2"}
)
Bạn cũng có thể sử dụng cơ chế này để kết hợp các hình thoi. Ví dụ: nếu A và B có cùng một phần phụ thuộc nhưng gọi phần phụ thuộc đó bằng các tên khác nhau, thì các phần phụ thuộc đó có thể được kết hợp trong myproject/WORKSPACE.
Ghi đè kho lưu trữ từ dòng lệnh
Để ghi đè một kho lưu trữ đã khai báo bằng một kho lưu trữ cục bộ từ dòng lệnh,
sử dụng
--override_repository
cờ. Việc sử dụng cờ này sẽ thay đổi nội dung của các kho lưu trữ bên ngoài mà không thay đổi mã nguồn của bạn.
Ví dụ: để ghi đè @foo thành thư mục cục bộ /path/to/local/foo,
hãy truyền cờ --override_repository=foo=/path/to/local/foo.
Một số trường hợp sử dụng bao gồm:
- Gỡ lỗi sự cố. Ví dụ: bạn có thể ghi đè một kho lưu trữ
http_archivethành một thư mục cục bộ để dễ dàng thực hiện các thay đổi hơn. - Bán hàng. Nếu bạn đang ở trong một môi trường mà bạn không thể thực hiện các lệnh gọi mạng, hãy ghi đè các quy tắc kho lưu trữ dựa trên mạng để trỏ đến các thư mục cục bộ.
Sử dụng proxy
Bazel sẽ chọn địa chỉ proxy từ các biến môi trường HTTPS_PROXY và HTTP_PROXY, đồng thời sử dụng các địa chỉ này để tải tệp HTTP/HTTPS xuống (nếu được chỉ định).
Hỗ trợ IPv6
Trên các máy chỉ có IPv6, Bazel sẽ 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: nếu IPv4 được bật, thì IPv4 sẽ được ưu tiên. 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 vào 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 java.net.preferIPv6Addresses=true thuộc tính hệ thống.
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=trueNếu bạn đang chạy các mục tiêu bản dựng Java cũng cần kết nối với Internet (đôi khi, các bài kiểm thử tích hợp cần điều đó), hãy sử dụng
--jvmopt=-Djava.net.preferIPv6Addresses=truecờ công cụ, ví dụ: bằng cách có dòng sau trong tệp.bazelrc:build --jvmopt=-Djava.net.preferIPv6AddressesNếu bạn đang sử dụng rules_jvm_external, chẳng hạn như để phân giải phiên bản phần phụ thuộc, hãy thêm
-Djava.net.preferIPv6Addresses=truevàoCOURSIER_OPTSbiến môi trường để cung cấp các tuỳ chọn JVM cho Coursier
Phần phụ thuộc bắc cầu
Bazel chỉ đọc các phần phụ thuộc được liệt kê trong tệp WORKSPACE. Nếu dự án của bạn
(A) phụ thuộc vào một dự án khác (B) liệt kê một phần phụ thuộc vào một dự án thứ ba
project (C) trong tệp WORKSPACE, thì bạn phải thêm cả B
và C vào tệp WORKSPACE của dự án. Yêu cầu này có thể làm tăng kích thước tệp
WORKSPACE nhưng hạn chế khả năng một thư viện
bao gồm C ở phiên bản 1.0 và một thư viện khác bao gồm C ở phiên bản 2.0.
Lưu vào bộ nhớ đệm các phần phụ thuộc bên ngoài
Theo mặc định, Bazel sẽ chỉ tải lại các phần phụ thuộc bên ngoài nếu định nghĩa của chúng thay đổi. Các thay đổi đối với tệp được tham chiếu trong định nghĩa (chẳng hạn như bản vá hoặc tệp BUILD) cũng được bazel tính đến.
Để buộc tải lại, hãy sử dụng bazel sync.
Bố cục
Tất cả các phần phụ thuộc bên ngoài đều được tải xuống một thư mục trong thư mục con
external trong cơ sở đầu ra. Trong trường hợp kho lưu trữ cục bộ, một liên kết tượng trưng sẽ được tạo ở đó thay vì tạo một thư mục mới.
Bạn có thể xem thư mục external bằng cách chạy:
ls $(bazel info output_base)/externalXin lưu ý rằng việc chạy bazel clean sẽ không thực sự xoá thư mục bên ngoài. Để xoá tất cả các cấu phần phần mềm bên ngoài, hãy sử dụng bazel clean --expunge.
Bản dựng ngoại tuyến
Đôi khi, bạn nên hoặc cần chạy bản dựng ở chế độ ngoại tuyến. Đối với
các trường hợp sử dụng đơn giản, chẳng hạn như đi du lịch bằng máy bay,
prefetching các kho lưu trữ cần thiết bằng bazel fetch hoặc bazel sync có thể là đủ; hơn nữa, việc
sử dụng tuỳ chọn --nofetch, việc tìm nạp các kho lưu trữ khác có thể bị tắt
trong quá trình xây dựng.
Đối với các bản dựng ngoại tuyến thực sự, trong đó việc cung cấp các tệp cần thiết sẽ do một thực thể khác với bazel thực hiện, bazel hỗ trợ tuỳ chọn --distdir. Bất cứ khi nào một quy tắc kho lưu trữ yêu cầu bazel tìm nạp một tệp thông qua
ctx.download hoặc
ctx.download_and_extract
và cung cấp tổng băm của tệp
cần thiết, bazel sẽ tìm kiếm trước tiên trong các thư mục được chỉ định bởi tuỳ chọn đó cho
một tệp khớp với tên cơ sở của URL đầu tiên được cung cấp và sử dụng bản sao cục bộ đó
nếu băm khớp.
Bản thân Bazel sử dụng kỹ thuật này để khởi động ngoại tuyến từ cấu phần phần mềm
phân phối.
Bazel thực hiện việc này bằng cách thu thập tất cả các phần phụ thuộc bên ngoài cần thiết
trong một
distdir_tar nội bộ.
Tuy nhiên, bazel cho phép thực thi các lệnh tuỳ ý trong các quy tắc kho lưu trữ mà không biết liệu các lệnh đó có gọi đến mạng hay không. Do đó, bazel không có tuỳ chọn để thực thi các bản dựng hoàn toàn ngoại tuyến. Vì vậy, việc kiểm thử xem bản dựng có hoạt động chính xác ở chế độ ngoại tuyến hay không đòi hỏi phải chặn mạng bên ngoài, như bazel thực hiện trong bài kiểm thử khởi động.
Các phương pháp hay nhất
Quy tắc kho lưu trữ
Một quy tắc kho lưu trữ thường phải chịu trách nhiệm:
- Phát hiện chế độ cài đặt hệ thống và ghi các chế độ cài đặt đó vào tệp.
- Tìm tài nguyên ở nơi khác trên hệ thống.
- Tải tài nguyên xuống từ URL.
- Tạo hoặc liên kết tượng trưng các tệp BUILD vào thư mục kho lưu trữ bên ngoài.
Tránh sử dụng repository_ctx.execute khi có thể. Ví dụ: khi sử dụng một thư viện C++ không phải Bazel có bản dựng bằng Make, bạn nên sử dụng repository_ctx.download() rồi viết một tệp BUILD để xây dựng thư viện đó, thay vì chạy ctx.execute(["make"]).
Ưu tiên http_archive hơn git_repository và
new_git_repository. Lý do là:
- Các quy tắc kho lưu trữ Git phụ thuộc vào hệ thống
git(1), trong khi trình tải xuống HTTP được tích hợp vào Bazel và không có phần phụ thuộc hệ thống. http_archivehỗ trợ danh sáchurlsdưới dạng bản sao vàgit_repositorychỉ hỗ trợ mộtremoteduy nhất.http_archivehoạt động với bộ nhớ đệm kho lưu trữ, nhưng không hoạt động vớigit_repository. Xem #5116 để biết thêm thông tin.
Không sử dụng bind(). Xem phần "Cân nhắc việc xoá
liên kết" để biết thông tin thảo luận dài
về các vấn đề và giải pháp thay thế.