舊版巨集

舊版巨集是從可建立目標的 BUILD 檔案呼叫的非結構化函式。載入階段結束後,舊版巨集就會消失,Bazel 只會看到具體的一組例項規則。

為何不應使用舊版巨集 (應改用符號巨集)

盡可能使用符號巨集

符號巨集

  • 從遠處防止動作
  • 透過精細的瀏覽權限隱藏實作詳細資料
  • 使用類型屬性,這表示自動標籤和選取轉換。
  • 更易閱讀
  • 即將推出惰性評估

用量

宏的常見用途是用於重複使用規則。

舉例來說,BUILD 檔案中的 genrule 會使用 //:generator 產生檔案,並在指令中硬式編碼 some_arg 引數:

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

您也可以使用巨集將規則連結在一起。以下範例顯示了鏈結的 genrule,其中一個 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() 陳述式的規則) 可從 native 模組中例項化:

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 引數。