長寬比

回報問題 查看原始碼 夜間 7.4 ,直接在 Google Cloud 控制台實際操作。 7.3 · 7.2 · 7.1 · 7.0 · 6.5

本頁面說明使用面向的基本概念和優點,並提供簡單和進階範例。

切面允許擴增建構依附元件圖表,提供額外資訊 和動作以下是一些可使用面向的常見情境:

  • 整合 Bazel 的 IDE 可使用面向收集專案相關資訊。
  • 程式碼產生工具可利用各個層面,以不區分目標的方式執行輸入內容。舉例來說,BUILD 檔案可以指定階層 protobuf 程式庫 定義和語言專屬規則可以用切面 針對特定語言產生 protobuf 支援程式碼的動作。

顯示比例基本概念

BUILD 檔案會提供專案原始碼的說明:專案包含哪些來源檔案、應從這些檔案建構哪些構件 (目標)、這些檔案之間的相依性為何等等。Bazel 會使用這些資訊執行建構作業,也就是找出產生構件的必要動作集合 (例如執行編譯器或連結器),並執行這些動作。Bazel 會在目標之間建構相依關係圖,並查看這張圖表來收集這些動作。

請參考以下 BUILD 檔案:

java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)

這個 BUILD 檔案定義了依附元件圖表,如下圖所示:

建構圖表

圖 1. BUILD 檔案依附元件圖。

Bazel 會針對上述範例中的每個目標,為對應的規則 (在本例中為「java_library」) 呼叫實作函式,藉此分析這個相依關係圖。規則實作函式會產生動作,這些動作會建構 .jar 檔案等構件,並將位置和這些構件的名稱等資訊傳遞至 供應器中這些目標的反向依附元件。

與規則類似,它們具有的實作函式, 產生動作和傳回提供者不過,他們的力量是來自於 以及建構依附元件圖的方式一個面向會具有實作項目,以及一份列出其沿途會傳播的所有屬性清單。請考慮沿著名為「deps」的屬性傳播的 A 層面。這個面向可套用至目標 X,產生面向應用節點 A(X)。在應用程式申請期間 A 的部分會以遞迴方式套用至 X 在「deps」中參照的所有目標 屬性 (A 傳播清單所列的所有屬性)。

因此,將 A 層面套用至目標 X 的單一動作,會產生目標原始依附關係圖的「陰影圖表」,如下圖所示:

使用 Aspect 建構圖表

圖 2. 使用面向建立圖表。

唯一遮蔽的邊緣是 屬性上屬性沿途的邊緣 因此,runtime_deps 邊緣不會在此處覆蓋 範例。接著,系統會在陰影圖表的所有節點上叫用面向實作函式,這與在原始圖表的節點上叫用規則實作方式相似。

簡單範例

此範例說明如何遞迴式列印規則的來源檔案,以及所有具有 deps 屬性的依附元件。這份文件會說明面向性的實作方式、面向性定義,以及如何從 Bazel 指令列叫用面向性。

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

讓我們將範例分成各個部分,然後逐一檢查。

切面定義

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

面向定義與規則定義類似,且使用 aspect 函式定義。

就像規則一樣,切面具有實作函式, _print_aspect_impl

attr_aspects 是會隨著切面傳播的規則屬性清單。 在此情況下,該面向會隨著deps 套用任何規則

attr_aspects 的另一個常見引數是 ['*'],可將面向傳播至規則的所有屬性。

實作方面

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

切面實作函式類似於規則的實作 函式。這些方法會傳回提供者、可產生動作,並採用兩個引數:

  • target:要套用特徵的目標
  • ctx:可用來存取屬性的 ctx 物件 並產生輸出內容和動作

實作函式可透過以下方式存取目標規則的屬性: ctx.rule.attr。這項工具能檢查 由套用該目標的目標提供 (透過 target 引數)。

您必須有切面才能傳回提供者清單。在這個範例中 此函式並未提供任何內容,因此會傳回空白清單。

使用指令列叫用切面

套用切面最簡單的方法是,在指令列中使用 --aspects 引數。假設上述顯示比例是在名為 print.bzl 的檔案中定義:

bazel build //MyExample:example --aspects print.bzl%print_aspect

會將 print_aspect 套用至目標 example,且所有 可透過 deps 屬性以遞迴方式存取的目標規則。

--aspects 旗標會採用一個引數,也就是以 <extension file label>%<aspect top-level name> 格式指定的顯示比例。

進階範例

下例示範如何從目標規則使用切面 ,可能依副檔名篩選檔案。 說明如何使用供應器傳回值,以及如何使用參數傳送 將引數新增至切面實作,以及如何從規則叫用切面。

file_count.bzl 檔案:

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

BUILD.bazel 檔案:

load('//:file_count.bzl', 'file_count_rule')

cc_library(
    name = 'lib',
    srcs = [
        'lib.h',
        'lib.cc',
    ],
)

cc_binary(
    name = 'app',
    srcs = [
        'app.h',
        'app.cc',
        'main.cc',
    ],
    deps = ['lib'],
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

切面定義

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

這個範例說明如何透過 deps 屬性傳播相片。

attrs 會定義某個面向的一組屬性。公開切面屬性 定義參數,且只能是 boolintstring 類型。 針對規則套用的層面,intstring 參數必須指定 values。這個範例包含名為 extension 的參數,可使用 '*'、'h' 或 'cc' 做為值。

在規則傳播方面,參數值會從要求存取的規則取得 切面,方法是使用規則屬性,並設定相同的名稱和類型。 (請參閱 file_count_rule 的定義)。

針對指令列方面,您可以使用 --aspects_parameters 旗標傳遞參數值。intstring 參數的 values 限制可能會省略。

元件也允許使用 labellabel_list 類型的私人屬性。私有標籤屬性可用來指定以下項目的依附元件: 例如針對切面產生的動作需要用到的工具或程式庫這個範例中並未定義私人屬性,但下列程式碼片段示範如何將工具傳入至某個面向:

...
    attrs = {
        '_protoc' : attr.label(
            default = Label('//tools:protoc'),
            executable = True,
            cfg = "exec"
        )
    }
...

切面實作

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

如同規則實作函式,切面實作函式 會傳回可供其依附元件存取的提供者結構。

在本範例中,FileCountInfo 定義為具有一個欄位 count 的供應器。建議最佳做法是 使用 fields 屬性設定供應器。

若要為某個面向應用程式 A (X) 提供一組供應器,您必須將針對目標 X 的規則實作和面向 A 的實作,兩者供應器的集合合併起來。規則導入作業中傳播的供應商 在套用切面前,系統會建立並凍結 如果目標和每個切面都套用成功,就會發生錯誤 提供相同類型的提供者,但不包含 OutputGroupInfo (合併後, 指定不同的輸出群組) 和 InstrumentedFilesInfo (從長寬比擷取)。這表示切面的實作可能永遠不會傳回 DefaultInfo

參數和私有屬性會傳入 ctx。這個範例參照 extension 參數,並判定 決定要列入計算的檔案

若是傳回提供者,這些提供者的屬性值 所傳播的切面 (來自 attr_aspects 清單) 會替換成 其特徵的套用結果例如,如果指定 X 的依附元件包含 Y 和 Z,A(X) 的 ctx.rule.attr.deps 會是 [A(Y)、A(Z)]。 在這個範例中,ctx.rule.attr.deps 是目標物件 將切面套用至「deps」的結果原始目標 也已套用切面。

在這個範例中,此面向項目會從目標的依附元件存取 FileCountInfo 供應器,累積檔案的傳遞總數。

從規則叫用切面

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

規則執行方式示範瞭如何存取 FileCountInfo 透過 ctx.attr.deps

規則定義說明如何定義參數 (extension) 並為其提供預設值 (*)。請注意,如果預設值不是 'cc'、'h' 或 '*' 中的任一值,就會發生錯誤,因為在面向的定義中,參數受到限制。

透過目標規則叫用切面

load('//:file_count.bzl', 'file_count_rule')

cc_binary(
    name = 'app',
...
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

這裡示範如何透過規則將 extension 參數傳遞至面向。由於 extension 參數的 執行規則時,extension 就會視為選用參數。

建立 file_count 目標後,將會評估我們的切面 所有目標,並透過 deps 以遞迴方式存取。

參考資料