Macro cũ là các hàm không có cấu trúc được gọi từ tệp BUILD có thể tạo mục tiêu. Vào cuối giai đoạn
tải, macro cũ không còn tồn tại
nữa và Bazel chỉ thấy tập hợp cụ thể 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 Ký hiệu)
Bạn nên sử dụng macro ký hiệu nếu có thể.
Macro ký hiệu
- 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 đã nhập, tức là tự động gắn nhãn và chọn lượt chuyển đổi.
- Dễ đọc hơn
- Sẽ sớm có tính năng đánh giá trì hoãn
Cách sử dụng
Trường hợp sử dụng macro điển hình là khi bạn muốn sử dụng lại một 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 bằng các đối số khác, 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 macro 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 ở trạng thái sạch và 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 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ể sử dụng macro để xâu chuỗi các quy tắc lại với nhau. Ví dụ này cho thấy các genrule được xâu chuỗi, trong đó một genrule sử dụng đầu ra của một 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ỉ gán giá trị 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 các quy tắc này.
.Mở rộng macro
Khi bạn muốn điều tra xem một macro làm gì, 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ể khởi tạo các quy tắc gốc (các 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 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 BUILD.
Phân giải nhãn trong macro
Vì 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 macro cũ được diễn giải tương ứng với tệp
BUILD mà macro được sử dụng chứ không phải tương ứng với t4/} tệp
mà macro được xác định..bzl 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 bộ 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,
)
Khi cờ --incompatible_resolve_select_keys_eagerly được bật, tất cả các khoá
là chuỗi nhãn sẽ tự động được phân giải thành các đối tượng Label
tương ứng với gói của tệp chứa lệnh gọi select. Nếu bạn không chọn cờ này, hãy gói chuỗi nhãn bằng
native.package_relative_label().
Gỡ lỗi
bazel query --output=build //my/path:allsẽ cho bạn biết tệpBUILDtrông như thế nào sau khi đánh giá. Tất cả macro cũ, glob, vòng lặp đều được mở rộng. Hạn chế đã biết: biểu thứcselectkhông xuất hiện trong đầu ra.Bạn có thể lọc đầu ra dựa trên
generator_function(hàm nào đã tạo 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
footrong 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 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ể sử dụng lệnh in để gỡ lỗi. Lệnh này hiển thị thông báo dưới dạng dòng nhật ký
DEBUGtrong giai đoạn tải. Ngoại trừ một số trường hợp hiếm gặp, hãy xoá các lệnh gọiprinthoặc đặt các lệnh gọi này theo điều kiện trong tham sốdebuggingcó giá trị mặc định làFalsetrước khi gửi mã đến kho lưu trữ.
Lỗi
Nếu bạn muốn đưa ra lỗi, hãy sử dụng fail
hàm. 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ọ. Bạn 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")
# ...
Hội nghị
Tất cả các hàm công khai (các hàm không bắt đầu bằng dấu gạch dưới) khởi tạo quy tắc phải có đối số
name. Đối số này không được là đối số không bắt buộc (không cung cấp giá trị mặc định).Các hàm công khai phải sử dụng chuỗi tài liệu tuân theo các quy ước của Python.
Trong các tệp
BUILD, đối sốnamecủa macro phải là đối số từ khoá (không phải đối số vị trí).Thuộc tính
namecủa các quy tắc do macro tạo phải bao gồm đối số tên làm tiền tố. Ví dụ:macro(name = "foo")có thể tạo acc_libraryfoovà a 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. Bạn có thể truyền trực tiếpNoneđến các quy tắc gốc. Các quy tắc này sẽ xử lýNonegiống như khi bạn không truyền đối số nào. Do đó, bạn không cần thay thế `None` bằng0,Falsehoặ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, vì các giá trị mặc định của các quy tắc này 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ẽ trông 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 xây dựng.Macro phải có đối số
visibilitykhông bắt buộc.