巨集

回報問題 查看來源

本頁面提供巨集使用的基本知識,包括一般用途、偵錯和慣例。

巨集是從 BUILD 檔案呼叫的函式,可對規則執行個體化。巨集主要用於封裝,以及重複使用現有規則和其他巨集。完成載入階段後,巨集就不會再存在,而 Bazel 只會看到一組具體化的例項化規則。

使用方式

巨集的常見用途是重複使用規則時。

舉例來說,BUILD 檔案中的 Genrule 會產生檔案,並在指令中使用含有 some_arg 引數以硬式編碼方式加入的 //:generator 引數:

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

如果要以不同引數產生更多檔案,建議您將這個程式碼擷取至巨集函式。讓我們呼叫 file_generator 巨集,其中包含 namearg 參數。將 genrule 替換成以下內容:

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",
)

在這裡,您會從位於 //path 套件的 .bzl 檔案載入 file_generator 符號。將巨集函式定義放入獨立的 .bzl 檔案後,即可確保 BUILD 檔案保持簡潔且宣告式,以便從工作區的任何套件載入 .bzl 檔案。

最後,在 path/generator.bzl 中編寫巨集定義,以封裝原始 genrule 定義:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

您也可以使用巨集將規則鏈結在一起。以下範例顯示了鏈結的 Genrules,其中 Genrule 使用先前 Genrule 的輸出內容做為輸入:

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,
  )

此範例只會將瀏覽權限值指派給第二個 Genrule。這可讓巨集作者隱藏中繼規則的輸出內容,使其不依賴工作區中的其他目標。

展開巨集

如要調查巨集的用途,請使用 query 指令搭配 --output=build 查看展開的表單:

$ 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 > $@",
)

建立原生規則例項

原生規則 (不需要 load() 陳述式的規則) 可以透過原生模組執行個體化:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

如果您需要知道套件名稱 (例如呼叫巨集的 BUILD 檔案),請使用 native.package_name() 函式。請注意,native 只能用於 .bzl 檔案,無法在 BUILD 檔案中使用。

巨集中的標籤解析度

由於巨集是在載入階段中評估,因此系統會將巨集中顯示的 "//foo:bar" 等標籤字串解譯為使用該巨集的 BUILD 檔案,而不是相對於定義的 .bzl 檔案。這種行為通常不建議用於其他存放區中的巨集,例如這些是已發布的 Starlark 規則集的一部分。

如要取得與 Starlark 規則相同的行為,請使用 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,
  )

偵錯

  • bazel query --output=build //my/path:all 會顯示 BUILD 檔案在評估後的外觀。所有巨集、glob 和迴圈已知限制:select 運算式目前不會顯示在輸出內容中。

  • 您可以根據 generator_function (產生規則的函式) 或 generator_name (巨集的名稱屬性) 篩選輸出內容:bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • 如要找出在 BUILD 檔案中產生 foo 規則的確切位置,請嘗試下列技巧。在 BUILD 檔案頂端附近插入此行:cc_library(name = "foo")。執行 Bazel。建立 foo 規則時 (因名稱衝突導致),系統會顯示完整的堆疊追蹤。

  • 您也可以使用 print 進行偵錯。並在載入階段將訊息顯示為 DEBUG 記錄行。除非在極少數情況下,否則請移除 print 呼叫,或在 debugging 參數 (預設為 False) 下方將其設為條件式,然後再將程式碼提交給庫。

錯誤

如果想要擲回錯誤,請使用「fail」函式。向使用者清楚說明發生錯誤的內容,以及 BUILD 檔案的修正方式。您無法擷取錯誤。

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

慣例

  • 執行個體化規則的所有公用函式 (開頭不是底線的函式) 都必須包含 name 引數。這個引數不應為選用 (沒有提供預設值)。

  • 公開函式應按照 Python 慣例使用 docstring

  • BUILD 檔案中,巨集的 name 引數必須是關鍵字引數 (而非位置引數)。

  • 巨集所產生規則的 name 屬性應包含名稱引數做為前置字串。舉例來說,macro(name = "foo") 可以產生 cc_library foo 和 Genrule foo_gen

  • 在大多數情況下,選用參數應使用預設值 NoneNone 可以直接傳遞至原生規則,這與您是否未在任何引數中傳遞。因此您不需要將它替換為 0False[]。相反地,這個巨集應改為其建立的規則,因為預設規則可能很複雜,或可能會隨時間變更。此外,明確設為預設值的參數會與透過查詢語言或建構系統內部進行存取時從未設定 (或設為 None) 的參數不同。

  • 巨集應包含選用的 visibility 引數。