舊版巨集是從 BUILD
檔案呼叫的非結構化函式,可建立目標。在載入階段結束時,舊版巨集就不會再存在,而 Bazel 只會看到具體的一組例項化規則。
為什麼不應使用舊版巨集 (而應改用符號巨集)
盡可能使用符號巨集。
符號巨集
- 防止遠端操作
- 透過精細的顯示設定,隱藏實作詳細資料
- 取得輸入的屬性,進而自動標記及選取轉換。
- 更易於閱讀
- 即將推出延遲求值
用量
巨集的典型用途是重複使用規則。
舉例來說,BUILD
檔案中的 genrule 會使用 //:generator
產生檔案,且指令中會硬式編碼 some_arg
引數:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
如要使用不同引數產生更多檔案,建議將這段程式碼擷取至巨集函式。如要建立名為 file_generator
的巨集,其中包含 name
和 arg
參數,請將 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
呼叫,或在預設為False
的debugging
參數下,將呼叫設為有條件,再將程式碼提交至儲存庫。
錯誤
如要擲回錯誤,請使用 fail 函式。向使用者清楚說明發生錯誤的原因,以及如何修正檔案。BUILD
無法擷取錯誤。
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
慣例
所有會例項化規則的公開函式 (開頭不是底線的函式) 都必須有
name
引數。這個引數不應為選用引數 (請勿提供預設值)。公用函式應使用遵循 Python 慣例的說明字串。
在
BUILD
檔案中,巨集的name
引數必須是關鍵字引數 (而非位置引數)。巨集產生的規則
name
屬性應包含名稱引數做為前置字元。舉例來說,macro(name = "foo")
可以產生cc_library
foo
和 genrulefoo_gen
。在大多數情況下,選用參數的預設值應為
None
。None
可以直接傳遞至原生規則,系統會將其視為您未傳遞任何引數。因此,您不必為此目的將其替換為0
、False
或[]
。巨集應改為延遲至其建立的規則,因為這些規則的預設值可能很複雜,或會隨時間變更。此外,透過查詢語言或建構系統內部存取時,明確設為預設值的參數與從未設定 (或設為None
) 的參數看起來不同。巨集應有選用的
visibility
引數。