Bazel có thể phụ thuộc vào các mục tiêu của những dự án khác. Các phần phụ thuộc từ những 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 các nguồn của 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 trong 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ì mục tiêu đó 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ừ những phần khác của hệ thống tệp hoặc được tải xuống từ Internet. Tệp này sử dụng cùng cú pháp với các tệp BUILD
, nhưng cho phép một bộ 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 số quy tắc kho lưu trữ tích hợp sẵn và một tập hợp 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 dự án không phải Bazel
- 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ừ 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 dự án đó từ hệ thống tệp cục bộ, tham chiếu một kho lưu trữ git hoặc tải dự án đó xuống (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 trong 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ừ những 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 bạn sử dụng make
để tạo, 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.
Tuỳ thuộc vào các gói bên ngoài
Cấu phần phần mềm và kho lưu trữ Maven
Sử dụng bộ quy tắc rules_jvm_external
để tải các 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 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 nhóm mục tiêu cụ thể, hãy sử dụng bazel fetch
.
Để tìm nạp vô điều kiện tất cả các phần phụ thuộc bên ngoài, 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 theo từng không gian làm việc.
Phần phụ thuộc che khuất
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 của mình. Đây là yêu cầu 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. Nhưng đối với những trường hợp không đúng, bạn có thể tạo các phần phụ thuộc ảo. 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ì khiến các trình chạy kiểm thử này không thể cùng tồn tại 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ể dùng cơ chế này để ghép các viên kim cương. 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ì những 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, hãy sử dụng cờ --override_repository
. 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
.
Sau đây là một số trường hợp sử dụng:
- Gỡ lỗi. Ví dụ: bạn có thể ghi đè một kho lưu trữ
http_archive
vào 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 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 để thay vào đó trỏ đến các thư mục cục bộ.
Sử dụng proxy
Bazel sẽ lấy đị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 IPv4/IPv6 có hai ngăn xếp, 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 trường hợp 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
--host_jvm_args=-Djava.net.preferIPv6Addresses=true
lựa chọn khởi động, ví dụ: bằng cách thêm dòng sau vào tệp.bazelrc
:startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true
Nế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 quy trình kiểm thử tích hợp cần điều đó), hãy sử dụng cờ công cụ
--jvmopt=-Djava.net.preferIPv6Addresses=true
, chẳng hạn như bằng cách có dòng sau trong tệp.bazelrc
:build --jvmopt=-Djava.net.preferIPv6Addresses
Nế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=true
vào biến môi trườngCOURSIER_OPTS
để cung cấp các lựa 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ó 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 dự án thứ ba (C
) trong tệp WORKSPACE
, thì bạn sẽ 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 có 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. Bazel cũng xem xét những thay đổi đối với các tệp được tham chiếu trong định nghĩa (chẳng hạn như các tệp bản vá hoặc BUILD
).
Để buộc tải lại, hãy 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 output base. Trong trường hợp kho lưu trữ cục bộ, một symlink 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)/external
Xin 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 máy bay, prefetching các kho lưu trữ cần thiết bằng bazel fetch
hoặc bazel sync
là đủ; hơn nữa, việc sử dụng lựa chọn --nofetch
có thể tắt quá trình tìm nạp các kho lưu trữ khác trong quá trình tạo.
Đối với các bản dựng thực sự ngoại tuyến, 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ợ lựa 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 trong các thư mục do lựa chọn đó chỉ định 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 hàm 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.
Thao tác này được thực hiện 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ó lựa chọn nào để 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 khi không có mạng hay không đòi hỏi phải chặn mạng bên ngoài, như bazel đã làm trong kiểm thử khởi động của mình.
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 về:
- Phát hiện chế độ cài đặt hệ thống và ghi 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 dùng repository_ctx.download()
rồi viết một tệp BUILD để tạo 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
git(1)
của hệ thống, 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_archive
hỗ trợ một danh sáchurls
dưới dạng bản sao vàgit_repository
chỉ hỗ trợ mộtremote
duy nhất.http_archive
hoạt động với bộ nhớ đệm kho lưu trữ, nhưng không hoạt động vớigit_repository
. Hãy xem #5116 để biết thêm thông tin.
Không sử dụng bind()
. Hãy xem phần "Cân nhắc việc xoá bind" để biết nội dung thảo luận dài về các vấn đề và giải pháp thay thế.