Macro

Báo cáo vấn đề Xem nguồn Hằng đêm · 7.3 · 7.2 · 7.1 · 7 · 6,5

Trang này trình bày kiến thức cơ bản về cách sử dụng macro và các trường hợp sử dụng thông thường, gỡ lỗi và quy ước.

Macro là một hàm được gọi từ tệp BUILD có thể tạo thực thể 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ó và các macro khác. Đến cuối giai đoạn tải, macro không còn tồn tại nữa và Bazel chỉ thấy một tập hợp cụ thể các quy tắc đượ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ụ: quy tắc gen trong tệp BUILD sẽ tạo ra một tệp 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 chứa các đối số khác nhau, bạn có thể cần trích xuất mã này vào hàm macro. Hãy gọi macro này là file_generator. có tham số namearg. 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",
)

Ở đâ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 .bzl, bạn sẽ giữ cho các tệp BUILD của mình luôn gọn gàng và mang tính khai báo, .bzl có thể tải tệp 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 ghi định nghĩa của macro vào đóng gói và tham số hoá định nghĩa quy tắc tạo 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 chuỗi genrules, trong đó một quy tắc tạo sử dụng dữ liệu đầ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 các tác giả vĩ mô để ẩn kết quả của các quy tắc trung gian khỏi bị phụ thuộc vào theo các mục tiêu khác trong không gian làm việc.

Macro mở rộng

Khi bạn muốn tìm hiểu chức năng của macro, 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 (quy tắc không cần câu lệnh load()) có thể là đượ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 (ví dụ: tệp BUILD nào đang gọi phương thức macro), hãy sử 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 thể dùng trong WORKSPACE hoặc BUILD tệp.

Độ phân giải nhãn trong macro

Vì macro được đánh giá trong giai đoạn tải, chuỗi nhãn, chẳng hạn như "//foo:bar" xuất hiện trong macro sẽ được diễn giải so với tệp BUILD mà macro được sử dụng thay vì so với .bzl là tệp được xác định trong đó. Đây thường là hành vi không mong muốn cho các macro được sử dụng trong các kho lưu trữ khác, chẳng hạn như vì chúng đều thuộc bộ quy tắc Starlark đã xuất bản.

Để có hành vi tương tự như đối với 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 biết cách tệp BUILD chăm sóc khách hàng. Tất cả macro, khối cầu và vòng lặp đều được mở rộng. Người quen giới hạn: biểu thức select hiện không được hiển thị trong kết quả.

  • Bạn có thể lọc kết quả dựa trên generator_function (hàm nào đã tạo quy tắc) hoặc generator_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 chính xác quy tắc foo được tạo ở đâu trong tệp BUILD, bạn có thể thử thủ thuật sau. Chèn dòng này vào gần đầu BUILD tệp: cc_library(name = "foo"). Chạy Bazel. Bạn sẽ nhận được ngoại lệ khi quy tắc foo được tạo (do xung đột tên), quy tắc này sẽ hiển thị cho bạn toàn bộ dấu vết ngăn xếp.

  • Bạn cũng có thể sử dụng print để gỡ lỗi. Chiến dịch 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 các trường hợp, hãy xoá print lệnh gọi hoặc đặt chúng thành có điều kiện trong Tham số debugging mặc định là False trước khi gửi mã đến kho hàng.

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õ cho người dùng về lỗi và cách sửa 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 (các hàm không bắt đầu bằng dấu gạch dưới) mà tạo thực thể quy tắc phải có đối số name. Đối số này không được 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 dùng một chuỗi tài liệu theo Python quy ước.

  • Trong các tệp BUILD, đối số name của macro phải là một từ khoá đối số (không phải đối số vị trí).

  • Thuộc tính name của quy tắc do macro tạo phải bao gồm tên đối số dưới dạng một tiền tố. Ví dụ: macro(name = "foo") có thể tạo một cc_library foo và một foo_gen quy tắc tạo sinh.

  • 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 chuyển trực tiếp đến các quy tắc gốc, các quy tắc này xử lý giống như thể mà bạn chưa truyền vào bất kỳ đối số nào. Do đó, bạn không cần phải thay thế với 0, False hoặc [] cho mục đích này. Thay vào đó, macro phải trì hoãn vào các quy tắc mà hệ thống tạo, vì cài đặt mặc định của chúng có thể phức tạp hoặc có thể thay đổi bất cứ lúc nào. Ngoài ra, một thông số được đặt rõ ràng thành giá trị mặc định trông khác với một giao diện không bao giờ được đặt (hoặc được đặt thành None) khi được truy cập thông qua ngôn ngữ truy vấn hoặc thành phần bên trong hệ thống xây dựng.

  • Macro phải có đối số visibility không bắt buộc.