Trang này trình bày kiến thức cơ bản về cách sử dụng macro, đồng thời trình bày các trường hợp sử dụng, gỡ lỗi và quy ước thông thường.
Macro là một hàm được gọi từ tệp BUILD
có thể tạo bản sao cho các quy tắc.
Macro chủ yếu được dùng để đóng gói và sử dụng lại mã của các quy tắc hiện có cũng như các macro khác. Khi kết thúc giai đoạn tải, các macro 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 đã tạo thực thể.
Cách sử dụng
Trường hợp sử dụng điển hình cho macro là khi bạn muốn sử dụng lại quy tắc.
Ví dụ: genrule trong tệp BUILD
tạo một tệp bằng cách sử dụng //:generator
với đố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 có các đối số khác nhau, bạn nên trích xuất mã này vào một hàm macro. Hãy gọi macro file_generator
. Macro này có tham số name
và arg
. Thay thế quy tắc gen bằng quy tắc 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ừ tệp .bzl
nằm trong gói //path
. Bằng cách đưa các định nghĩa hàm macro vào một tệp .bzl
riêng biệt, bạn sẽ đảm bảo các tệp BUILD
gọn gàng và mang tính khai báo. Bạn có thể tải tệp .bzl
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 quy tắc gen 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ể sử dụng macro để liên kết các quy tắc với nhau. Ví dụ này cho thấy các quy tắc tạo theo chuỗi, trong đó một quy tắc tạo sinh sử dụng đầu ra của một quy tắc tạo trước đó làm dữ liệu đầ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 giá trị mức độ hiển thị cho quy tắc tạo sinh 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 để không bị các mục tiêu khác phụ thuộc vào không gian làm việc.
Macro mở rộng
Khi bạn muốn tìm hiểu xem macro có chức năng gì, hãy sử dụng lệnh query
với --output=build
để xem biểu mẫu 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 > $@",
)
Tạo thực thể quy tắc gốc
Các quy tắc gốc (các quy tắc không cần câu lệnh load()
) có thể được tạo thực thể 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 (chẳng hạn như tệp BUILD
nào đang gọi macro), hãy sử dụng hàm native.package_name(). Xin lưu ý rằng native
chỉ có thể được sử dụng trong các tệp .bzl
chứ không phải trong các tệp WORKSPACE
hoặc BUILD
.
Độ phân giải nhãn trong macro
Vì macro đượ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 macro sẽ được diễn giải tương ứng với tệp BUILD
mà macro được sử dụng thay vì so với tệp .bzl
mà macro được xác định. Thường thì hành vi này không được 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ì các macro này thuộc bộ quy tắc Starlark đã phát hành.
Để có hành vi tương tự 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 workspace, 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 thấy giao diện của tệpBUILD
sau khi đánh giá. Tất cả các macro, glob, vòng lặp đều được mở rộng. Giới hạn đã biết: biểu thứcselect
hiện không hiển thị trong kết quả.Bạn có thể lọc kết quả dựa trên
generator_function
(hàm 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)'
Để tìm hiểu chính xác vị trí tạo quy tắc
foo
trong tệpBUILD
, bạn có thể thử thủ thuật sau. Chèn dòng này vào gần đầu tệpBUILD
:cc_library(name = "foo")
. Chạy Bazel. Bạn sẽ nhận được một ngoại lệ khi tạo quy tắcfoo
(do xung đột tên), ngoại lệ này sẽ hiển thị dấu vết ngăn xếp đầy đủ.Bạn cũng có thể sử dụng print để gỡ lỗi. Nó hiển thị thông báo dưới dạng một 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 các lệnh gọi đó có điều kiện theo tham sốdebugging
mặc định làFalse
trước khi gửi mã đến kho.
Lỗi
Nếu bạn muốn báo cáo lỗi, hãy sử dụng hàm fail (không thành công).
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
.
Không thể phát hiện lỗi.
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
Hội nghị
Tất cả hàm công khai (hàm không bắt đầu bằng dấu gạch dưới) tạo thực thể quy tắc phải có đối số
name
. Đối số này không được là đối số tuỳ chọn (không được cung cấp giá trị mặc định).Các hàm công khai phải dùng một chuỗi tài liệu theo các quy ước của Python.
Trong tệp
BUILD
, đối sốname
của macro phải là một đối số từ khoá (không phải là đối số vị trí).Thuộc tính
name
của các quy tắc do macro tạo ra phải bao gồm đối số tên làm tiền tố. Ví dụ:macro(name = "foo")
có thể tạofoo
cc_library
và quy tắc genfoo_gen
.Trong hầu hết 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ẽ được xử lý giống như thể bạn chưa truyền bất kỳ đối số nào. Do đó, bạn không cần thay thế lớp này bằng0
,False
hoặc[]
cho mục đích này. Thay vào đó, macro phải tuân theo các quy tắc mà macro tạo ra, vì các giá trị mặc định của macro 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 tham số không bao giờ được đặt (hoặc được đặt thànhNone
) khi truy cập thông qua ngôn ngữ truy vấn hoặc nội bộ hệ thống xây dựng.Macro phải có đối số
visibility
không bắt buộc.