本頁面說明使用切面的基本概念和優點,並 提供簡單的進階範例
切面允許擴增建構依附元件圖表,提供額外資訊 和動作切面適用的幾個常見情況:
- 整合 Bazel 的 IDE 可利用切面來收集 專案。
- 程式碼生成工具可以利用不同面向,在
舉例來說,
BUILD
檔案可以指定階層 protobuf 程式庫 定義和語言專屬規則可以用切面 針對特定語言產生 protobuf 支援程式碼的動作。
切面基本概念
BUILD
檔案可以說明專案的原始碼:目標來源
檔案是專案的一部分,則應從哪些構件 (目標) 建構構件
以及這些檔案之間的依附元件等等
並使用這項資訊來建構,也就是找出一組動作
以便產生構件 (例如執行編譯器或連結器)
就會執行這些動作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 會呼叫實作函式
每個資料列對應的 rule (本例中為「java_library」)
指定目標規則導入函式會產生動作
建構構件 (例如 .jar
檔案),以及傳遞位置等資訊
這些構件的名稱和這些構件的名稱
供應商。
與規則類似,它們具有的實作函式, 產生動作和傳回提供者不過,他們的力量是來自於 以及建構依附元件圖的方式方面有實作方式 以及套用的所有屬性的清單假設 A. 會沿著名為「deps」的屬性傳播這個切面可套用至 一個目標 X,產生切面應用程式節點 A(X)。在應用程式申請期間 A 的部分會以遞迴方式套用至 X 在「deps」中參照的所有目標 屬性 (A 傳播清單所列的所有屬性)。
因此,將切面 A 套用至目標 X 的單一動作,就會產生「陰影圖」/ 目標的原始依附元件圖,如下圖所示:
圖 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 []
切面實作函式類似於規則的實作 函式。而會傳回提供者 actions,並使用兩個引數:
實作函式可透過以下方式存取目標規則的屬性:
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
定義切面的一組屬性。公開切面屬性
是 string
類型,稱為參數。參數必須包含 values
屬性。本範例有一個名為 extension
的參數
可以包含「*
」、「h
」或「cc
」的。
切面的參數值取自使用相同的字串屬性
要求切面的規則名稱 (請參閱 file_count_rule
的定義)。
由於沒有參數,因此無法透過指令列使用含有參數的切面
語法定義參數
切面也可具有 label
類型的私人屬性或
label_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
定義為具有該 ID 的提供者
欄位 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
以遞迴方式存取。