Nhãn là giá trị nhận dạng cho một mục tiêu. Một nhãn điển hình ở dạng chuẩn đầy đủ sẽ có dạng như sau:
@@myrepo//my/app/main:app_binary
Phần đầu tiên của nhãn là tên kho lưu trữ, @@myrepo
. Cú pháp @
kép cho biết đây là tên kho lưu trữ chuẩn, là tên duy nhất trong không gian làm việc. Các nhãn có tên kho lưu trữ chuẩn xác định rõ ràng một mục tiêu bất kể chúng xuất hiện trong ngữ cảnh nào.
Tên kho lưu trữ chính tắc thường là một chuỗi khó hiểu trông giống như @@rules_java++toolchains+local_jdk
. Điều thường thấy hơn nhiều là các nhãn có tên kho lưu trữ rõ ràng, trông như sau:
@myrepo//my/app/main:app_binary
Điểm khác biệt duy nhất là tên kho lưu trữ được thêm tiền tố bằng một @
thay vì hai.
Đây là một kho lưu trữ có tên hiển thị là myrepo
, có thể khác nhau tuỳ thuộc vào ngữ cảnh mà nhãn này xuất hiện.
Trong trường hợp thông thường, nhãn đề cập đến cùng một kho lưu trữ mà nhãn đó được dùng, bạn có thể bỏ qua phần tên kho lưu trữ. Vì vậy, bên trong @@myrepo
, nhãn đầu tiên thường được viết là
//my/app/main:app_binary
Phần thứ hai của nhãn là tên gói không đủ tiêu chuẩn my/app/main
, đường dẫn đến gói tương ứng với thư mục gốc của kho lưu trữ. Tên kho lưu trữ và tên gói không đủ điều kiện tạo thành tên gói đủ điều kiện @@myrepo//my/app/main
. Khi nhãn đề cập đến cùng một gói mà nhãn được dùng, bạn có thể bỏ qua tên gói (và dấu hai chấm nếu muốn). Vì vậy, bên trong @@myrepo//my/app/main
, nhãn này có thể được viết theo một trong hai cách sau:
app_binary
:app_binary
Theo quy ước, dấu hai chấm sẽ bị bỏ qua đối với các tệp, nhưng vẫn được giữ lại đối với các quy tắc. Tuy nhiên, điều này không có ý nghĩa gì khác.
Phần nhãn sau dấu hai chấm, app_binary
là tên mục tiêu không đủ tiêu chuẩn. Khi khớp với thành phần cuối cùng của đường dẫn gói, bạn có thể bỏ qua thành phần này và dấu hai chấm. Do đó, hai nhãn này tương đương nhau:
//my/app/lib
//my/app/lib:lib
Tên của một mục tiêu tệp trong thư mục con của gói là đường dẫn của tệp tương ứng với thư mục gốc của gói (thư mục chứa tệp BUILD
). Vì vậy, tệp này nằm trong thư mục con my/app/main/testdata
của kho lưu trữ:
//my/app/main:testdata/input.txt
Các chuỗi như //my/app
và @@some_repo//my/app
có hai ý nghĩa tuỳ thuộc vào ngữ cảnh mà chúng được dùng: khi Bazel mong đợi một nhãn, chúng có nghĩa là //my/app:app
và @@some_repo//my/app:app
. Nhưng khi Bazel mong đợi một gói (ví dụ: trong các thông số kỹ thuật package_group
), chúng sẽ tham chiếu đến gói chứa nhãn đó.
Một lỗi thường gặp trong các tệp BUILD
là sử dụng //my/app
để tham chiếu đến một gói hoặc đến tất cả các mục tiêu trong một gói – điều này không đúng. Hãy nhớ rằng, nó tương đương với //my/app:app
, vì vậy, nó đặt tên cho mục tiêu app
trong gói my/app
của kho lưu trữ hiện tại.
Tuy nhiên, bạn nên dùng //my/app
để tham chiếu đến một gói trong quy cách của package_group
hoặc trong các tệp .bzl
, vì nó cho biết rõ rằng tên gói là tuyệt đối và nằm trong thư mục cấp cao nhất của không gian làm việc.
Bạn không thể dùng nhãn tương đối để tham chiếu đến các mục tiêu trong những gói khác; trong trường hợp này, bạn phải luôn chỉ định giá trị nhận dạng kho lưu trữ và tên gói.
Ví dụ: nếu cây nguồn chứa cả gói my/app
và gói my/app/testdata
(mỗi gói trong số 2 gói này có tệp BUILD
riêng), thì gói sau sẽ chứa một tệp có tên testdepot.zip
. Sau đây là 2 cách (một cách sai, một cách đúng) để tham chiếu đến tệp này trong //my/app:BUILD
:
Sai – testdata
là một gói khác, vì vậy bạn không thể sử dụng đường dẫn tương đối
testdata/testdepot.zip
Chính xác – tham chiếu đến testdata
bằng đường dẫn đầy đủ
//my/app/testdata:testdepot.zip
Các nhãn bắt đầu bằng @@//
là thông tin tham chiếu đến kho lưu trữ chính. Các nhãn này vẫn hoạt động ngay cả khi ở kho lưu trữ bên ngoài.
Do đó, @@//a/b/c
khác với //a/b/c
khi được tham chiếu từ một kho lưu trữ bên ngoài.
Phương thức đầu tiên tham chiếu trở lại kho lưu trữ chính, trong khi phương thức thứ hai tìm kiếm //a/b/c
trong chính kho lưu trữ bên ngoài.
Điều này đặc biệt phù hợp khi viết các quy tắc trong kho lưu trữ chính tham chiếu đến các mục tiêu trong kho lưu trữ chính và sẽ được dùng từ các kho lưu trữ bên ngoài.
Để biết thông tin về các cách mà bạn có thể tham chiếu đến mục tiêu, hãy xem mẫu mục tiêu.
Quy cách từ vựng của nhãn
Cú pháp nhãn không khuyến khích việc sử dụng các siêu ký tự có ý nghĩa đặc biệt đối với trình bao. Điều này giúp tránh các vấn đề vô tình trích dẫn và giúp bạn dễ dàng tạo các công cụ và tập lệnh thao tác nhãn, chẳng hạn như Ngôn ngữ truy vấn Bazel.
Dưới đây là thông tin chi tiết chính xác về tên mục tiêu được phép.
Tên mục tiêu – package-name:target-name
target-name
là tên của mục tiêu trong gói. Tên của một quy tắc là giá trị của thuộc tính name
trong khai báo của quy tắc trong tệp BUILD
; tên của một tệp là đường dẫn tương đối của tệp đó so với thư mục chứa tệp BUILD
.
Tên mục tiêu phải được tạo hoàn toàn bằng các ký tự trong tập hợp a
–z
, A
–Z
, 0
–9
và các ký hiệu dấu câu !%-@^_"#$&'()*-+,;<=>?[]{|}~/.
.
Tên tệp phải là tên đường dẫn tương đối ở dạng chuẩn, tức là không được bắt đầu hoặc kết thúc bằng dấu gạch chéo (ví dụ: /foo
và foo/
đều bị cấm) cũng như không được chứa nhiều dấu gạch chéo liên tiếp làm dấu phân cách đường dẫn (ví dụ: foo//bar
). Tương tự, các tham chiếu cấp trên (..
) và tham chiếu thư mục hiện tại (./
) đều bị cấm.
Sai – Không dùng ..
để tham chiếu đến các tệp trong những gói khác
Chính xác – Sử dụng //package-name:filename
Mặc dù thường dùng /
trong tên của đích đến tệp, nhưng hãy tránh dùng /
trong tên của các quy tắc. Đặc biệt là khi sử dụng dạng viết tắt của nhãn, điều này có thể khiến người đọc nhầm lẫn. Nhãn //foo/bar/wiz
luôn là một tên viết tắt cho //foo/bar/wiz:wiz
, ngay cả khi không có gói foo/bar/wiz
nào như vậy; nhãn này không bao giờ đề cập đến //foo:bar/wiz
, ngay cả khi mục tiêu đó tồn tại.
Tuy nhiên, có một số trường hợp bạn nên dùng dấu gạch chéo hoặc đôi khi thậm chí cần phải dùng. Ví dụ: tên của một số quy tắc phải khớp với tệp nguồn chính của quy tắc đó, có thể nằm trong một thư mục con của gói.
Tên gói – //package-name:target-name
Tên của một gói là tên của thư mục chứa tệp BUILD
, tương ứng với thư mục cấp cao nhất của kho lưu trữ chứa gói đó.
Ví dụ: my/app
.
Ở cấp độ kỹ thuật, Bazel thực thi những điều sau:
- Các ký tự được phép trong tên gói là chữ cái viết thường
a
đếnz
, chữ cái viết hoaA
đếnZ
, chữ số0
đến9
, ký tự! \"#$%&'()*+,-.;<=>?@[]^_`{|}
(có một ký tự dấu cách ở đó!) và tất nhiên là dấu gạch chéo/
(vì đây là dấu phân cách thư mục). - Tên gói không được bắt đầu hoặc kết thúc bằng ký tự dấu gạch chéo
/
. - Tên gói không được chứa chuỗi con
//
. Điều này sẽ không có ý nghĩa gì – đường dẫn thư mục tương ứng sẽ là gì? - Tên gói không được chứa chuỗi con
/./
hoặc/../
hoặc/.../
, v.v. Quy định này được thực thi để tránh nhầm lẫn khi dịch giữa tên gói logic và tên thư mục thực, dựa trên ý nghĩa ngữ nghĩa của ký tự dấu chấm trong chuỗi đường dẫn.
Ở cấp độ thực tế:
- Đối với một ngôn ngữ có cấu trúc thư mục quan trọng đối với hệ thống mô-đun (ví dụ: Java), bạn cần chọn tên thư mục là các giá trị nhận dạng hợp lệ trong ngôn ngữ đó. Ví dụ: đừng bắt đầu bằng một chữ số đứng đầu và tránh các ký tự đặc biệt, đặc biệt là dấu gạch dưới và dấu gạch ngang.
- Mặc dù Bazel hỗ trợ các mục tiêu trong gói gốc của không gian làm việc (ví dụ:
//:foo
), nhưng tốt nhất là bạn nên để trống gói đó để tất cả các gói có ý nghĩa đều có tên mô tả.
Quy tắc
Một quy tắc chỉ định mối quan hệ giữa đầu vào và đầu ra, cũng như các bước để tạo đầu ra. Các quy tắc có thể thuộc một trong nhiều loại khác nhau (đôi khi được gọi là lớp quy tắc), tạo ra các tệp thực thi và thư viện đã biên dịch, các tệp thực thi kiểm thử và các đầu ra được hỗ trợ khác như mô tả trong Bách khoa toàn thư về bản dựng.
Các tệp BUILD
khai báo các mục tiêu bằng cách gọi các quy tắc.
Trong ví dụ bên dưới, chúng ta thấy khai báo my_app
mục tiêu bằng cách sử dụng quy tắc cc_binary
.
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
deps = [
"//absl/base",
"//absl/strings",
],
)
Mỗi lệnh gọi quy tắc đều có một thuộc tính name
(phải là một tên mục tiêu hợp lệ), khai báo một mục tiêu trong gói của tệp BUILD
.
Mỗi quy tắc có một tập hợp thuộc tính; các thuộc tính áp dụng cho một quy tắc nhất định, cũng như ý nghĩa và ngữ nghĩa của từng thuộc tính là một hàm của loại quy tắc; hãy xem Bách khoa toàn thư về bản dựng để biết danh sách các quy tắc và thuộc tính tương ứng. Mỗi thuộc tính đều có tên và loại. Một số loại phổ biến mà một thuộc tính có thể có là số nguyên, nhãn, danh sách nhãn, chuỗi, danh sách chuỗi, nhãn đầu ra, danh sách nhãn đầu ra. Bạn không cần chỉ định tất cả các thuộc tính trong mọi quy tắc. Do đó, các thuộc tính tạo thành một từ điển từ các khoá (tên) đến các giá trị được nhập (không bắt buộc).
Thuộc tính srcs
có trong nhiều quy tắc có loại "danh sách nhãn"; giá trị của thuộc tính này (nếu có) là danh sách nhãn, mỗi nhãn là tên của một mục tiêu là đầu vào cho quy tắc này.
Trong một số trường hợp, tên của loại quy tắc có phần tuỳ ý và thú vị hơn là tên của các tệp do quy tắc tạo ra, và điều này đúng với genrule. Để biết thêm thông tin, hãy xem phần Quy tắc chung: genrule.
Trong các trường hợp khác, tên có ý nghĩa quan trọng: đối với các quy tắc *_binary
và *_test
, chẳng hạn, tên quy tắc xác định tên của tệp thực thi do bản dựng tạo ra.
Biểu đồ có hướng không chu kỳ này trên các mục tiêu được gọi là biểu đồ mục tiêu hoặc biểu đồ phần phụ thuộc bản dựng và là miền mà công cụ Bazel Query hoạt động.
Mục tiêu | Tệp BUILD |