A
mục tiêu phụ thuộc vào B
mục tiêu nếu cần B
cho A
khi tạo hoặc
thời gian thực thi. Mối quan hệ phụ thuộc tạo ra
Đồ thị không chu trình có hướng
(DAG) trên mục tiêu và được gọi là biểu đồ phần phụ thuộc.
Phần phụ thuộc trực tiếp của mục tiêu là các mục tiêu khác có thể truy cập được bằng đường dẫn có độ dài 1 trong biểu đồ phần phụ thuộc. Các phần phụ thuộc ngoại tuyến của mục tiêu là các mục tiêu mà nó phụ thuộc vào thông qua đường dẫn có độ dài bất kỳ thông qua biểu đồ.
Trên thực tế, trong ngữ cảnh bản dựng, có 2 biểu đồ phần phụ thuộc là biểu đồ về các phần phụ thuộc thực tế và biểu đồ về các phần phụ thuộc đã khai báo. Hầu hết thời gian, hai đồ thị giống nhau đến mức không cần tạo ra sự khác biệt này, nhưng sẽ hữu ích cho thảo luận dưới đây.
Các phần phụ thuộc thực tế và phần phụ thuộc đã khai báo
X
mục tiêu thực sự phụ thuộc vào mục tiêu Y
nếu phải có Y
,
tạo và cập nhật mới nhất để X
được tạo một cách chính xác. Được tạo có thể
giá trị trung bình được tạo, xử lý, biên dịch, liên kết, lưu trữ, nén, thực thi hoặc
bất kỳ loại tác vụ nào khác thường xuyên diễn ra trong quá trình xây dựng.
X
mục tiêu có phần phụ thuộc đã khai báo trên mục tiêu Y
nếu có phần phụ thuộc
cạnh từ X
sang Y
trong gói X
.
Để có bản dựng chính xác, biểu đồ về các phần phụ thuộc thực tế A phải là đồ thị con của
biểu đồ các phần phụ thuộc đã khai báo D. Tức là mỗi cặp
các nút được kết nối trực tiếp x --> y
trong A cũng phải được kết nối trực tiếp trong
N. Có thể nói D là sự gần đúng của A.
Người ghi tệp BUILD
phải khai báo rõ ràng tất cả các giá trị trực tiếp thực tế
phần phụ thuộc cho mọi quy tắc vào hệ thống xây dựng và không còn phần phụ thuộc nào nữa.
Việc không tuân thủ nguyên tắc này sẽ dẫn đến hành vi không xác định: bản dựng có thể không thành công, nhưng tệ hơn nữa, bản dựng có thể phụ thuộc vào một số thao tác trước đó hoặc dựa trên bắc cầu các phần phụ thuộc đã khai báo mà mục tiêu có. Bazel kiểm tra xem có thiếu thông tin không phụ thuộc và báo cáo lỗi, nhưng không thể trong mọi trường hợp.
Bạn không cần (và không nên) cố gắng liệt kê mọi thứ được nhập gián tiếp,
ngay cả khi A
cần đến mã này tại thời điểm thực thi.
Trong quá trình tạo bản dựng của X
mục tiêu, công cụ bản dựng sẽ kiểm tra toàn bộ chuyển đổi bắc cầu
đóng các phần phụ thuộc của X
để đảm bảo rằng mọi thay đổi trong các mục tiêu đó
phản ánh trong kết quả cuối cùng, xây dựng lại các chất trung gian nếu cần.
Bản chất bắc cầu của các phần phụ thuộc là một lỗi phổ biến. Đôi khi,
mã trong một tệp có thể sử dụng mã do phần phụ thuộc gián tiếp cung cấp — phần
cạnh bắc cầu nhưng không trực tiếp trong biểu đồ phần phụ thuộc đã khai báo. Gián tiếp
phần phụ thuộc không xuất hiện trong tệp BUILD
. Vì quy tắc này không
phụ thuộc trực tiếp vào nhà cung cấp, nên không có cách nào để theo dõi các thay đổi, như được minh hoạ trong
tiến trình ví dụ sau đây:
1. Các phần phụ thuộc đã khai báo khớp với các phần phụ thuộc thực tế
Ban đầu, mọi thứ đều hoạt động. Mã trong gói a
dùng mã trong gói b
.
Mã trong gói b
sử dụng mã trong gói c
, do đó a
có tính chuyển đổi
phụ thuộc vào c
.
a/BUILD |
b/BUILD |
---|---|
rule( name = "a", srcs = "a.in", deps = "//b:b", ) |
rule( name = "b", srcs = "b.in", deps = "//c:c", ) |
a / a.in |
b / b.in |
import b; b.foo(); |
import c; function foo() { c.bar(); } |
Các phần phụ thuộc đã khai báo ước tính vượt quá các phần phụ thuộc thực tế. Mọi thứ đều ổn.
2. Thêm phần phụ thuộc chưa được khai báo
Một mối nguy hiểm tiềm ẩn được đưa ra khi người dùng thêm mã vào a
mà sẽ tạo ra
phần phụ thuộc thực tế trực tiếp trên c
, nhưng quên khai báo phần này trong tệp bản dựng
a/BUILD
.
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
Các phần phụ thuộc được khai báo không còn ước tính quá mức các phần phụ thuộc thực tế nữa.
Điều này có thể chấp nhận được vì đóng bắc cầu của hai đồ thị bằng nhau,
nhưng che giấu một vấn đề: a
có một phần phụ thuộc thực tế nhưng chưa được khai báo trên c
.
3. Sự khác biệt giữa biểu đồ phần phụ thuộc thực tế và đã khai báo
Mối nguy hiểm sẽ lộ ra khi ai đó tái cấu trúc b
để sản phẩm không còn phụ thuộc vào nữa
c
, vô tình phá vỡ a
qua không
lỗi của chính họ.
b/BUILD |
|
---|---|
rule( name = "b", srcs = "b.in", deps = "//d:d", ) |
|
b / b.in |
|
import d; function foo() { d.baz(); } |
|
Biểu đồ phần phụ thuộc đã khai báo hiện là số liệu ước tính không đầy đủ các phần phụ thuộc, ngay cả khi đóng theo cách bắc cầu; bản dựng có thể bị lỗi.
Chúng tôi có thể ngăn chặn vấn đề này bằng cách đảm bảo rằng sự phụ thuộc thực tế của
a
đến c
giới thiệu ở Bước 2 đã được khai báo đúng cách trong tệp BUILD
.
Các loại phần phụ thuộc
Hầu hết các quy tắc xây dựng đều có ba thuộc tính để chỉ định các loại
các phần phụ thuộc chung: srcs
, deps
và data
. Những thông tin này sẽ được giải thích ở bên dưới. Cho
chi tiết khác, xem
Thuộc tính chung cho tất cả quy tắc.
Nhiều quy tắc cũng có các thuộc tính bổ sung cho các loại
phần phụ thuộc, ví dụ: compiler
hoặc resources
. Thông tin chi tiết trong
Xây dựng Bách khoa toàn thư.
Phần phụ thuộc srcs
Các tệp do quy tắc hoặc các quy tắc xuất ra tệp nguồn sử dụng trực tiếp.
Phần phụ thuộc deps
Quy tắc trỏ đến các mô-đun được biên dịch riêng cung cấp tệp tiêu đề, biểu tượng, thư viện, dữ liệu, v.v.
Phần phụ thuộc data
Mục tiêu bản dựng có thể cần một số tệp dữ liệu để chạy đúng cách. Các tệp dữ liệu này không phải là mã nguồn: chúng không ảnh hưởng đến cách tạo mục tiêu. Ví dụ: một kiểm thử đơn vị có thể so sánh kết quả của một hàm với nội dung của tệp. Khi tạo bài kiểm thử đơn vị mà bạn không cần tệp, nhưng bạn cần tệp này khi chạy bài kiểm thử. Điều này cũng áp dụng cho các công cụ được khởi chạy trong quá trình thực thi.
Hệ thống xây dựng chạy các bài kiểm thử trong một thư mục riêng biệt chỉ trong đó các tệp được liệt kê là
Bạn có thể dùng data
. Do đó, nếu một tệp nhị phân/thư viện/kiểm thử cần một số tệp để chạy,
chỉ định các lớp đó (hoặc một quy tắc bản dựng chứa chúng) trong data
. Ví dụ:
# I need a config file from a directory named env:
java_binary(
name = "setenv",
...
data = [":env/default_env.txt"],
)
# I need test data from another directory
sh_test(
name = "regtest",
srcs = ["regtest.sh"],
data = [
"//data:file1.txt",
"//data:file2.txt",
...
],
)
Bạn có thể truy cập các tệp này bằng đường dẫn tương đối path/to/data/file
. Trong quá trình kiểm thử,
bạn có thể tham khảo các tệp này bằng cách kết hợp đường dẫn của nguồn kiểm thử
và đường dẫn tương đối với Workspace, ví dụ:
${TEST_SRCDIR}/workspace/path/to/data/file
.
Sử dụng nhãn để tham chiếu đến các thư mục
Khi xem qua các tệp BUILD
của chúng tôi, bạn có thể nhận thấy rằng một số nhãn data
tham chiếu đến thư mục. Các nhãn này kết thúc bằng /.
hoặc /
như các ví dụ sau,
mà bạn không nên dùng:
Không nên –
data = ["//data/regression:unittest/."]
Không nên –
data = ["testdata/."]
Không nên –
data = ["testdata/"]
Việc này có vẻ thuận tiện, đặc biệt là đối với các bài kiểm thử vì nó cho phép kiểm thử sử dụng tất cả tệp dữ liệu trong thư mục.
Tuy nhiên, hãy cố gắng không làm việc này. Để đảm bảo tạo lại chính xác (và
thực thi lại kiểm thử) sau khi thay đổi, hệ thống xây dựng phải biết được
tập hợp tệp hoàn chỉnh làm đầu vào cho bản dựng (hoặc kiểm thử). Khi bạn chỉ định
một thư mục, hệ thống xây dựng chỉ thực hiện việc tạo lại khi chính thư mục đó
các thay đổi (do tệp được thêm hoặc bị xoá), nhưng sẽ không thể phát hiện
các chỉnh sửa đối với các tệp riêng lẻ vì những thay đổi đó không ảnh hưởng đến thư mục đính kèm.
Thay vì chỉ định các thư mục làm dữ liệu đầu vào cho hệ thống xây dựng, bạn nên
liệt kê tập hợp các tệp chứa trong đó, theo cách rõ ràng hoặc sử dụng
Hàm glob()
. (Sử dụng **
để buộc
glob()
là đệ quy.)
Nên dùng –
data = glob(["testdata/**"])
Rất tiếc, có một số trường hợp bắt buộc phải sử dụng nhãn thư mục.
Ví dụ: nếu thư mục testdata
chứa các tệp có tên không trùng khớp
tuân theo cú pháp nhãn,
sau đó liệt kê rõ ràng các tệp hoặc sử dụng
Hàm glob()
tạo ra nhãn không hợp lệ
. Bạn phải sử dụng nhãn thư mục trong trường hợp này, nhưng hãy cẩn thận
rủi ro liên quan đến việc tạo lại không chính xác như mô tả ở trên.
Nếu bạn phải sử dụng nhãn thư mục, xin lưu ý rằng bạn không thể tham chiếu tới
gói mẹ có đường dẫn ../
tương đối; thay vào đó, hãy sử dụng đường dẫn tuyệt đối như
//data/regression:unittest/.
Mọi quy tắc bên ngoài (chẳng hạn như quy trình kiểm thử) cần sử dụng nhiều tệp đều phải
khai báo rõ ràng sự phụ thuộc của nó vào tất cả các công cụ đó. Bạn có thể sử dụng filegroup()
để
nhóm các tệp lại với nhau trong tệp BUILD
:
filegroup(
name = 'my_data',
srcs = glob(['my_unittest_data/*'])
)
Sau đó, bạn có thể tham chiếu nhãn my_data
dưới dạng phần phụ thuộc dữ liệu trong chương trình kiểm thử.
XÂY DỰNG tệp | Chế độ hiển thị |