巨集

本頁面將介紹使用巨集的基本概念,包括常見用途、偵錯和慣例。

巨集是從 BUILD 檔案呼叫的函式,可用來建立規則例項。巨集主要用於封裝現有規則和其他巨集的程式碼,並重複使用這些程式碼。

巨集有兩種類型:本頁所述的符號巨集和舊版巨集。建議您盡可能使用符號巨集,以便清楚顯示程式碼。

符號巨集提供類型引數 (字串轉換為標籤,相對於巨集呼叫的位置),以及限制和指定建立目標的顯示設定。這些類別的設計可接受延遲評估 (將於日後的 Bazel 版本中新增)。Bazel 8 預設可使用符號巨集。如果本文件中提及 macros,則指符號巨集

用量

.bzl 檔案中定義巨集時,請使用兩個參數 (attrsimplementation) 呼叫 macro() 函式。

屬性

attrs 會接受屬性名稱至屬性類型的字典,代表巨集的引數。兩個常見的屬性 (名稱和顯示) 會隱含地新增至所有巨集,且不會包含在傳遞至 attrs 的字典中。

# macro/macro.bzl
my_macro = macro(
    attrs = {
        "deps": attr.label_list(mandatory = True, doc = "The dependencies passed to the inner cc_binary and cc_test targets"),
        "create_test": attr.bool(default = False, configurable = False, doc = "If true, creates a test target"),
    },
    implementation = _my_macro_impl,
)

屬性類型宣告可接受 參數 mandatorydefaultdoc。大多數屬性類型也接受 configurable 參數,該參數會決定屬性是否接受 select。如果屬性為 configurable,則會將非 select 值解析為無法設定的 select - "foo" 會變成 select({"//conditions:default": "foo"})。詳情請參閱「選取」一節。

實作

implementation 會接受含有巨集邏輯的函式。實作函式通常會透過呼叫一或多個規則來建立目標,且通常為私有 (名稱開頭為底線)。傳統上,它們會與巨集同名,但前置字串為 _,後置字串為 _impl

與規則實作函式不同,規則實作函式會採用單一引數 (ctx),其中包含屬性參照,而巨集實作函式會接受每個引數的參數。

# macro/macro.bzl
def _my_macro_impl(name, deps, create_test):
    cc_library(
        name = name + "_cc_lib",
        deps = deps,
    )

    if create_test:
        cc_test(
            name = name + "_test",
            srcs = ["my_test.cc"],
            deps = deps,
        )

聲明

巨集是在 BUILD 檔案中載入並呼叫其定義來宣告。


# pkg/BUILD

my_macro(
    name = "macro_instance",
    deps = ["src.cc"] + select(
        {
            "//config_setting:special": ["special_source.cc"],
            "//conditions:default": [],
        },
    ),
    create_tests = True,
)

這會建立 //pkg:macro_instance_cc_lib//pkg:macro_instance_test 目標。

詳細資料

建立目標的命名慣例

由符號巨集建立的任何目標或子巨集名稱,必須與巨集的 name 參數相符,或是前置 name,後接 _ (建議)、.-。舉例來說,my_macro(name = "foo") 可能只會建立名為 foo 的檔案或目標,或是前置字串為 foo_foo-foo. 的檔案或目標,例如 foo_bar

您可以宣告違反巨集命名慣例的目標或檔案,但無法建構,也無法用做依附元件。

與巨集例項位於相同套件中的非巨集檔案和目標,名稱應與潛在的巨集目標名稱衝突,但系統不會強制執行這項排他性規定。我們正在實作延遲評估功能,以改善符號巨集的效能,因為在違反命名架構的套件中,符號巨集會受到影響。

限制

符號巨集與舊版巨集相比,有額外的限制。

符號巨集

  • 必須使用 name 引數和 visibility 引數
  • 必須具備 implementation 函式
  • 可能不會傳回值
  • 不得變更其 args
  • 除非是特殊 finalizer 巨集,否則不得呼叫 native.existing_rules()
  • 可能無法呼叫 native.package()
  • 可能無法呼叫 glob()
  • 可能無法呼叫 native.environment_group()
  • 必須建立名稱符合命名架構的目標
  • 無法參照未宣告或未以引數傳入的輸入檔案 (詳情請參閱「可見度」)。

顯示設定

待辦事項:展開這個區段

指定可視度

根據預設,由符號巨集建立的目標,會對建立這些目標的套件顯示。它們也接受 visibility 屬性,可將該可見度擴展至巨集的呼叫端 (透過從巨集呼叫直接傳遞 visibility 屬性至建立的目標) 和其他套件 (透過在目標的可見度中明確指定)。

依附元件可見度

巨集必須能查看所參照的檔案和目標。他們可以透過下列任一方式進行:

  • 明確傳遞為 attr 值至巨集

# pkg/BUILD
my_macro(... deps = ["//other_package:my_tool"] )
  • attr 值的隱含預設值
# my_macro:macro.bzl
my_macro = macro(
  attrs = {"deps" : attr.label_list(default = ["//other_package:my_tool"])} )
  • 巨集定義已顯示
# other_package/BUILD
cc_binary(
    name = "my_tool",
    visibility = "//my_macro:\\__pkg__",
)

選取

如果屬性是 configurable,巨集實作函式一律會將屬性值顯示為 select 值。例如,請考慮下列巨集:

my_macro = macro(
    attrs = {"deps": attr.label_list()},  # configurable unless specified otherwise
    implementation = _my_macro_impl,
)

如果 my_macro 是使用 deps = ["//a"] 叫用,則會導致 _my_macro_impl 以其 deps 參數設為 select({"//conditions:default": ["//a"]}) 的狀態下叫用。

規則目標會反轉這項轉換作業,並將簡單的 select 儲存為無條件值;在本例中,如果 _my_macro_impl 宣告規則目標 my_rule(..., deps = deps),則該規則目標的 deps 會儲存為 ["//a"]

終結器

規則完成器是特殊的符號巨集,無論其在 BUILD 檔案中的字彙位置為何,都會在載入套件的最後階段評估,也就是在定義所有非完成器目標後。與一般符號式巨集不同,終結器可以呼叫 native.existing_rules(),其行為與舊版巨集略有不同:只會傳回一組非終結器規則目標。最終器可能會斷言該組合的狀態或定義新目標。

如要宣告終結器,請使用 finalizer = True 呼叫 macro()

def _my_finalizer_impl(name, visibility, tags_filter):
    for r in native.existing_rules().values():
        for tag in r.get("tags", []):
            if tag in tags_filter:
                my_test(
                    name = name + "_" + r["name"] + "_finalizer_test",
                    deps = [r["name"]],
                    data = r["srcs"],
                    ...
                )
                continue

my_finalizer = macro(
    attrs = {"tags_filter": attr.string_list(configurable = False)},
    implementation = _impl,
    finalizer = True,
)

懶惰

重要事項:我們正在實作延遲巨集展開和評估功能。這項功能尚未推出。

目前,系統會在載入 BUILD 檔案後立即評估所有巨集,這可能會對套件中目標的效能造成負面影響,因為這些套件也包含耗用大量資源的無關巨集。日後,只有在建構作業需要非完成器符號巨集時,系統才會評估這些巨集。前置字串命名結構定義可協助 Bazel 判斷要針對要求的目標展開哪個巨集。

遷移作業疑難排解

以下列舉一些常見的遷移問題和解決方法。

  • 舊版巨集呼叫 glob()

glob() 呼叫移至 BUILD 檔案 (或從 BUILD 檔案呼叫的舊版巨集),然後使用標籤清單屬性將 glob() 值傳遞至符號巨集:

# BUILD file
my_macro(
    ...,
    deps = glob(...),
)
  • 舊版巨集的參數不是有效的 starlark attr 類型。

盡可能將邏輯納入巢狀符號式巨集,但請將頂層巨集設為舊版巨集。

  • 舊版巨集會呼叫規則,建立違反命名結構定義的目標

沒關係,只要不要依賴「違規」目標即可。系統會悄悄略過命名檢查。