Phụ thuộc

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

A mục tiêu phụ thuộc vào B mục tiêu nếu cần B khi A tạo trong thời gian xây dựng hoặc thực thi. Mối quan hệ phụ thuộc vào tạo ra một Biểu đồ tuần hoàn hướng dẫn (DAG) thay vì mục tiêu. Đây được gọi là biểu đồ phần phụ thuộc.

Các phần phụ thuộc trực tiếp của mục tiêu là các phần phụ thuộc khác có thể truy cập được bằng đường dẫn độ dài 1 trong biểu đồ phần phụ thuộc. Các phần phụ thuộc bắc cầu của mục tiêu là các mục tiêu mà phần phụ thuộc thông qua đường dẫn có độ dài bất kỳ thông qua biểu đồ.

Trên thực tế, trong bối cảnh của bản dựng, có 2 biểu đồ phần phụ thuộc, biểu đồ phần phụ thuộc thực tế và biểu đồ phần phụ thuộc đã khai báo. Trong hầu hết các trường hợp, hai biểu đồ này tương tự nhau đến mức không cần đưa ra sự khác biệt, nhưng điều này rất hữu ích cho việc thảo luận dưới đây.

Các phần phụ thuộc thực tế và đã khai báo

X mục tiêu thực sự phụ thuộc vào mục tiêu Y nếu Y phải có, được tạo và cập nhật để X được tạo đúng cách. Được tạo có thể có nghĩa là bất kỳ tác vụ nào đượ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 thường xảy ra trong quá trình tạo.

X mục tiêu có một phần phụ thuộc đã khai báo trên Y mục tiêu nếu có một cạnh phụ thuộc từ X đến Y trong gói X.

Đối với các bản dựng chính xác, biểu đồ của các phần phụ thuộc thực tế A phải là đồ thị phụ của biểu đồ các phần phụ thuộc đã khai báo D. Nghĩa là, mỗi cặp nút 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 D. Có thể nói D là cách đánh giá quá mức A.

Người ghi tệp BUILD phải khai báo rõ ràng tất cả phần phụ thuộc trực tiếp thực tế cho mỗi quy tắc cho hệ thống xây dựng và không cần khai báo thêm nữa.

Việc không tuân thủ nguyên tắc này gây ra 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, bản dựng có thể phụ thuộc vào một số hoạt động trước đó, hoặc khi các phần phụ thuộc bắc cầu được khai báo mà mục tiêu xảy ra. Bazel kiểm tra các phần phụ thuộc và báo cáo lỗi bị thiếu, nhưng không thể quá trình kiểm tra này hoàn tất trong mọi trường hợp.

Bạn không cần (và không nên) cố liệt kê mọi thứ được gián tiếp nhập, ngay cả khi A cần cho quá trình này khi thực thi.

Trong quá trình tạo mục tiêu X, công cụ bản dựng sẽ kiểm tra toàn bộ quá trình đóng phần phụ thuộc bắc cầu của X để đảm bảo rằng mọi thay đổi trong các mục tiêu đó sẽ được phản ánh trong kết quả cuối cùng, tạo lại các trung gian khi cần.

Tính chất bắc cầu của các phần phụ thuộc dẫn đến 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 – một cạnh bắc cầu nhưng không phải là cạnh trực tiếp trong biểu đồ phần phụ thuộc đã khai báo. Các phần phụ thuộc gián tiếp 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ư minh hoạ trong tiến trình mẫu 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 sử dụng mã trong gói b. Mã trong gói b sử dụng mã trong gói c, do đó a phụ thuộc bắc cầu 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();
}
      
Biểu đồ phần phụ thuộc đã khai báo với các mũi tên kết nối a, b và c
Biểu đồ phần phụ thuộc đã khai báo
Biểu đồ phần phụ thuộc thực tế khớp với biểu đồ phần phụ thuộc đã khai báo với các mũi tên kết nối a, b và c
Thực tế biểu đồ phần phụ thuộc

Các phần phụ thuộc đã khai báo chiếm dụng nhiều hơn các phần phụ thuộc thực tế. Tất cả đều ổn.

2. Thêm phần phụ thuộc chưa được khai báo

Mối nguy hiểm tiềm ẩn được đưa ra khi ai đó thêm mã vào a để tạo phần phụ thuộc thực tế trực tiếp trên c, nhưng quên khai báo phần phụ thuộc đó trong tệp bản dựng a/BUILD.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
Biểu đồ phần phụ thuộc đã khai báo với các mũi tên kết nối a, b và c
Biểu đồ phần phụ thuộc đã khai báo
Biểu đồ phần phụ thuộc thực tế với các mũi tên kết nối a, b và c. Mũi tên
                  hiện kết nối cả A với C. Biểu đồ này không khớp với biểu đồ phần phụ thuộc đã khai báo
Thực tế biểu đồ phần phụ thuộc

Các phần phụ thuộc đã khai báo không còn chồng chéo với các phần phụ thuộc thực tế. Điều này có thể xây dựng chấp nhận được, vì việc đóng của hai biểu đồ bằng nhau, nhưng che giấu vấn đề: a có phần phụ thuộc thực tế nhưng không đượ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 được phát hiện khi có người tái cấu trúc b để không còn phụ thuộc vào c, vô tình làm hỏng a mà không gây ra sự cố riêng.

  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 với các mũi tên kết nối với a và b.
                  b không còn kết nối với c nữa, điều này làm hỏng kết nối của a với c
Biểu đồ phần phụ thuộc đã khai báo
Biểu đồ phần phụ thuộc thực tế cho thấy kết nối với b và c, nhưng b không kết nối với c nữa
Thực tế biểu đồ phần phụ thuộc

Biểu đồ phần phụ thuộc đã khai báo hiện không được ước tính đúng cách cho các phần phụ thuộc thực tế, ngay cả khi đã đóng tạm thời; quy trình tạo có thể không thành công.

Bạn có thể khắc phục vấn đề này bằng cách đảm bảo rằng phần phụ thuộc thực tế từ a đến c hiển thị ở Bước 2 đã được khai báo đúng 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 phần phụ thuộc chung khác nhau: srcs, depsdata. Phần dưới đây sẽ được giải thích. Để biết thêm thông tin, hãy xem phần Các thuộc tính phổ biến đối với tất cả cá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 cụ thể của quy tắc, ví dụ: compiler hoặc resources. Những thông tin này được nêu chi tiết trong Bách khoa toàn thư về bản dựng.

Phần phụ thuộc srcs

Các tệp được sử dụng trực tiếp bởi quy tắc hoặc quy tắc xuất ra tệp nguồn.

Phần phụ thuộc deps

Quy tắc trỏ đến các mô-đun được biên dịch riêng biệt cung cấp các tệp tiêu đề, biểu tượng, thư viện, dữ liệu, v.v.

Phần phụ thuộc data

Một 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ụ: kiểm thử đơn vị có thể so sánh kết quả của hàm với nội dung của tệp. Khi xây dựng kiểm thử đơn vị, bạn không cần tệp, nhưng bạn cần tệp đó khi chạy kiểm thử. Điều tương tự 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 kiểm thử trong một thư mục riêng biệt nơi chỉ có các tệp được liệt kê là data. Do đó, nếu tệp nhị phân/thư viện/kiểm thử cần chạy một số tệp, hãy chỉ định chúng (hoặc quy tắc xây dựng có 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",
        ...
    ],
)

Các tệp này có sẵn bằng đường dẫn tương đối path/to/data/file. Trong kiểm thử, bạn có thể tham khảo các tệp này bằng cách tham gia đường dẫn của thư mục nguồn kiểm thử và đường dẫn tương đối không gian làm việc, ví dụ: ${TEST_SRCDIR}/workspace/path/to/data/file.

Sử dụng nhãn để tham chiếu thư mục

Khi xem các tệp BUILD của chúng tôi, bạn có thể nhận thấy một số nhãn data tham chiếu đến các thư mục. Bạn không nên sử dụng các nhãn này kết thúc bằng /. hoặc / như các ví dụ sau:

Không nêndata = ["//data/regression:unittest/."]

Không nêndata = ["testdata/."]

Không nêndata = ["testdata/"]

Điều này có vẻ thuận tiện, đặc biệt là đối với thử nghiệm vì nó cho phép thử nghiệm sử dụng tất cả tệp dữ liệu trong thư mục.

Nhưng hãy cố gắng không làm điều này. Để đảm bảo tạo lại bản dựng gia tăng chính xác (và thực thi lại các thử nghiệm) sau khi thay đổi, hệ thống xây dựng phải biết tập hợp hoàn chỉnh các tệp là dữ liệu đầu vào cho bản dựng (hoặc thử nghiệm). Khi bạn chỉ định một thư mục, hệ thống xây dựng chỉ tạo lại bản dựng khi chính thư mục đó thay đổi (do việc thêm hoặc xoá tệp), nhưng sẽ không thể phát hiện các nội dung 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 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 trong đó một cách rõ ràng hoặc sử dụng hàm glob(). (Sử dụng ** để buộc glob() phải đệ quy.)

Nên dùngdata = glob(["testdata/**"])

Rất tiếc, có một số trường hợp 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 tuân thủ cú pháp nhãn, thì việc liệt kê tệp rõ ràng hoặc sử dụng hàm glob() sẽ tạo ra lỗi 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 với rủi ro liên quan đến các bản dựng lại không chính xác được mô tả ở trên.

Nếu phải sử dụng nhãn thư mục, hãy lưu ý rằng bạn không thể tham chiếu đến gói mẹ với đường dẫn ../ tương đối; thay vào đó, hãy sử dụng một đường dẫn tuyệt đối như //data/regression:unittest/..

Bất kỳ quy tắc bên ngoài nào, 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 tệp đó vào tất cả các tệp đó. Bạn có thể sử dụng filegroup() để nhóm các tệp 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 làm phần phụ thuộc dữ liệu trong kiểm thử.

XÂY DỰNG tệp Chế độ hiển thị