Macro cũ là các hàm không có cấu trúc được gọi từ các tệp BUILD
có thể tạo mục tiêu. Vào cuối giai đoạn tải, các macro cũ sẽ không còn tồn tại nữa và Bazel chỉ thấy tập hợp cụ thể của các quy tắc được khởi tạo.
Lý do bạn không nên sử dụng macro cũ (và nên sử dụng macro Symbolic)
Nếu có thể, bạn nên sử dụng macro tượng trưng.
Macro tượng trưng
- Ngăn chặn hành động từ xa
- Cho phép ẩn chi tiết triển khai thông qua khả năng hiển thị chi tiết
- Lấy các thuộc tính được nhập, tức là tự động gắn nhãn và chọn chuyển đổi.
- Dễ đọc hơn
- Sắp có đánh giá trì hoãn
Cách sử dụng
Trường hợp sử dụng điển hình cho một macro là khi bạn muốn sử dụng lại một quy tắc.
Ví dụ: genrule trong tệp BUILD
sẽ tạo một tệp bằng cách sử dụng //:generator
với một đối số some_arg
được mã hoá cứng trong lệnh:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
Nếu muốn tạo thêm tệp bằng các đối số khác nhau, bạn có thể muốn trích xuất mã này sang một hàm macro. Để tạo một macro có tên là file_generator
, có các tham số name
và arg
, chúng ta có thể thay thế genrule bằng nội dung sau:
load("//path:generator.bzl", "file_generator")
file_generator(
name = "file",
arg = "some_arg",
)
file_generator(
name = "file-two",
arg = "some_arg_two",
)
file_generator(
name = "file-three",
arg = "some_arg_three",
)
Tại đây, bạn tải biểu tượng file_generator
từ một tệp .bzl
nằm trong gói //path
. Bằng cách đặt các định nghĩa hàm macro trong một tệp .bzl
riêng biệt, bạn sẽ giữ cho các tệp BUILD
của mình gọn gàng và khai báo. Tệp .bzl
có thể được tải từ bất kỳ gói nào trong không gian làm việc.
Cuối cùng, trong path/generator.bzl
, hãy viết định nghĩa của macro để đóng gói và tham số hoá định nghĩa genrule ban đầu:
def file_generator(name, arg, visibility=None):
native.genrule(
name = name,
outs = [name + ".txt"],
cmd = "$(location //:generator) %s > $@" % arg,
tools = ["//:generator"],
visibility = visibility,
)
Bạn cũng có thể dùng macro để liên kết các quy tắc với nhau. Ví dụ này cho thấy các genrule được liên kết, trong đó một genrule sử dụng đầu ra của genrule trước đó làm đầu vào:
def chained_genrules(name, visibility=None):
native.genrule(
name = name + "-one",
outs = [name + ".one"],
cmd = "$(location :tool-one) $@",
tools = [":tool-one"],
visibility = ["//visibility:private"],
)
native.genrule(
name = name + "-two",
srcs = [name + ".one"],
outs = [name + ".two"],
cmd = "$(location :tool-two) $< $@",
tools = [":tool-two"],
visibility = visibility,
)
Ví dụ này chỉ chỉ định một giá trị về khả năng hiển thị cho genrule thứ hai. Điều này cho phép tác giả macro ẩn đầu ra của các quy tắc trung gian để các mục tiêu khác trong không gian làm việc không phụ thuộc vào đầu ra đó.
Macro mở rộng
Khi bạn muốn tìm hiểu chức năng của một macro, hãy sử dụng lệnh query
với --output=build
để xem dạng mở rộng:
$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
name = "file",
tools = ["//:generator"],
outs = ["//test:file.txt"],
cmd = "$(location //:generator) some_arg > $@",
)
Khởi tạo các quy tắc gốc
Bạn có thể tạo thực thể cho các quy tắc gốc (những quy tắc không cần câu lệnh load()
) từ mô-đun gốc:
def my_macro(name, visibility=None):
native.cc_library(
name = name,
srcs = ["main.cc"],
visibility = visibility,
)
Nếu bạn cần biết tên gói (ví dụ: tệp BUILD
nào đang gọi macro), hãy dùng hàm native.package_name(). Xin lưu ý rằng bạn chỉ có thể dùng native
trong tệp .bzl
chứ không dùng được trong tệp BUILD
.
Độ phân giải nhãn trong macro
Vì các macro cũ được đánh giá trong giai đoạn tải, nên các chuỗi nhãn như "//foo:bar"
xuất hiện trong một macro cũ sẽ được diễn giải tương ứng với tệp BUILD
mà macro được dùng thay vì tương ứng với tệp .bzl
mà macro được xác định. Hành vi này thường không mong muốn đối với các macro được dùng trong các kho lưu trữ khác, chẳng hạn như vì chúng là một phần của tập hợp quy tắc Starlark đã xuất bản.
Để có cùng hành vi như đối với các quy tắc Starlark, hãy gói các chuỗi nhãn bằng hàm khởi tạo Label
:
# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
native.cc_library(
name = name,
deps = deps + select({
# Due to the use of Label, this label is resolved within @my_ruleset,
# regardless of its site of use.
Label("//config:needs_foo"): [
# Due to the use of Label, this label will resolve to the correct target
# even if the canonical name of @dep_of_my_ruleset should be different
# in the main repo, such as due to repo mappings.
Label("@dep_of_my_ruleset//tools:foo"),
],
"//conditions:default": [],
}),
**kwargs,
)
Gỡ lỗi
bazel query --output=build //my/path:all
sẽ cho bạn biết tệpBUILD
có dạng như thế nào sau khi đánh giá. Tất cả các macro, glob và vòng lặp cũ đều được mở rộng. Hạn chế đã biết: Các biểu thứcselect
không xuất hiện trong đầu ra.Bạn có thể lọc kết quả dựa trên
generator_function
(hàm nào đã tạo ra các quy tắc) hoặcgenerator_name
(thuộc tính tên của macro):bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'
Để biết chính xác quy tắc
foo
được tạo ở đâu trong tệpBUILD
, bạn có thể thử mẹo sau. Chèn dòng này vào gần đầu tệpBUILD
:cc_library(name = "foo")
. Chạy Bazel. Bạn sẽ gặp phải một ngoại lệ khi quy tắcfoo
được tạo (do xung đột tên), ngoại lệ này sẽ cho bạn thấy toàn bộ dấu vết ngăn xếp.Bạn cũng có thể dùng lệnh print để gỡ lỗi. Thao tác này sẽ hiển thị thông báo dưới dạng dòng nhật ký
DEBUG
trong giai đoạn tải. Ngoại trừ một số ít trường hợp, hãy xoá các lệnh gọiprint
hoặc đặt điều kiện cho các lệnh gọi đó trong một tham sốdebugging
mặc định làFalse
trước khi gửi mã đến kho lưu trữ.
Lỗi
Nếu bạn muốn gửi một lỗi, hãy sử dụng hàm fail. Giải thích rõ ràng cho người dùng về vấn đề đã xảy ra và cách khắc phục tệp BUILD
của họ. Không thể bắt lỗi.
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
Quy ước
Tất cả các hàm công khai (hàm không bắt đầu bằng dấu gạch dưới) khởi tạo các quy tắc đều phải có đối số
name
. Đối số này không được là đối số không bắt buộc (không đưa ra giá trị mặc định).Các hàm công khai phải sử dụng chuỗi tài liệu theo quy ước của Python.
Trong các tệp
BUILD
, đối sốname
của macro phải là đối số từ khoá (không phải đối số vị trí).Thuộc tính
name
của các quy tắc do một macro tạo ra phải có đối số tên làm tiền tố. Ví dụ:macro(name = "foo")
có thể tạocc_library
foo
và genrulefoo_gen
.Trong hầu hết các trường hợp, các tham số không bắt buộc phải có giá trị mặc định là
None
.None
có thể được truyền trực tiếp đến các quy tắc gốc, các quy tắc này sẽ coiNone
như thể bạn không truyền đối số nào. Do đó, bạn không cần thay thế bằng0
,False
hoặc[]
cho mục đích này. Thay vào đó, macro sẽ chuyển sang các quy tắc mà macro tạo ra, vì các giá trị mặc định của quy tắc có thể phức tạp hoặc có thể thay đổi theo thời gian. Ngoài ra, một tham số được đặt rõ ràng thành giá trị mặc định sẽ khác với một tham số chưa bao giờ được đặt (hoặc được đặt thànhNone
) khi được truy cập thông qua ngôn ngữ truy vấn hoặc nội bộ hệ thống bản dựng.Macro phải có đối số
visibility
không bắt buộc.