Halaman ini menjelaskan dasar-dasar dan manfaat penggunaan berbagai aspek, serta memberikan contoh sederhana dan lanjutan.
Aspek memungkinkan penambahan grafik dependensi build dengan informasi dan tindakan tambahan. Beberapa skenario umum terkait beberapa aspek yang dapat berguna:
- IDE yang mengintegrasikan Bazel dapat menggunakan aspek untuk mengumpulkan informasi tentang project.
- Alat pembuatan kode dapat memanfaatkan aspek untuk dijalankan pada inputnya dengan cara
target-agnostic. Contohnya, file
BUILD
dapat menentukan hierarki definisi library protobuf, dan aturan khusus bahasa dapat menggunakan aspek untuk melampirkan tindakan yang menghasilkan kode dukungan protobuf untuk bahasa tertentu.
Dasar-dasar aspek
File BUILD
memberikan deskripsi kode sumber project: file sumber
apa yang merupakan bagian dari project, artefak (target) apa yang harus dibuat dari
file tersebut, dependensi antara file tersebut, dll. Bazel menggunakan
informasi ini untuk menjalankan build, yaitu, mengetahui kumpulan tindakan
yang diperlukan untuk menghasilkan artefak (seperti menjalankan compiler atau linker) dan
mengeksekusi tindakan tersebut. Bazel melakukannya dengan membuat grafik
dependensi antara target dan membuka grafik ini untuk mengumpulkan tindakan tersebut.
Pertimbangkan file BUILD
berikut:
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'], ...)
File BUILD
ini menentukan grafik dependensi yang ditampilkan dalam gambar berikut:
Gambar 1. Grafik dependensi file BUILD
.
Bazel menganalisis grafik dependensi ini dengan memanggil fungsi implementasi
aturan yang sesuai (dalam hal ini "java_library") untuk setiap
target dalam contoh di atas. Fungsi implementasi aturan menghasilkan tindakan yang
mem-build artefak, seperti file .jar
, dan meneruskan informasi seperti lokasi
dan nama artefak tersebut, ke dependensi terbalik target tersebut di
penyedia.
Aspek serupa dengan aturan karena memiliki fungsi implementasi yang menghasilkan tindakan dan menampilkan penyedia. Namun, keunggulannya berasal dari cara grafik dependensi dibuat untuk mereka. Aspek memiliki implementasi dan daftar semua atribut yang disebarkan. Pertimbangkan aspek A yang menyebarkan di sepanjang atribut yang bernama "dependensi". Aspek ini dapat diterapkan ke target X, yang menghasilkan node aplikasi aspek A(X). Selama penerapannya, aspek A diterapkan secara rekursif ke semua target yang dirujuk X dalam atribut "dependensi" (semua atribut dalam daftar propagasi A).
Oleh karena itu, satu tindakan penerapan aspek A ke target X akan menghasilkan "grafik bayangan" dari grafik dependensi asli target yang ditampilkan dalam gambar berikut:
Gambar 2. Membuat grafik dengan berbagai aspek.
Satu-satunya tepi yang dibayangi adalah tepi di sepanjang atribut dalam
kumpulan propagasi, sehingga tepi runtime_deps
tidak dibayangi dalam contoh
ini. Fungsi implementasi aspek kemudian dipanggil pada semua node dalam
grafik bayangan yang mirip dengan cara implementasi aturan dipanggil pada node
grafik asli.
Contoh sederhana
Contoh ini menunjukkan cara mencetak file sumber secara berulang untuk
aturan dan semua dependensinya yang memiliki atribut deps
. Contoh ini menunjukkan
implementasi aspek, definisi aspek, dan cara memanggil aspek
dari command line 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'],
)
Mari kita urai contoh menjadi bagian-bagiannya dan periksa masing-masing satu per satu.
Definisi aspek
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
Definisi aspek mirip dengan definisi aturan, dan ditentukan menggunakan
fungsi aspect
.
Sama seperti aturan, aspek memiliki fungsi implementasi yang dalam hal ini adalah
_print_aspect_impl
.
attr_aspects
adalah daftar atribut aturan yang menjadi sarana penyebaran aspek.
Dalam hal ini, aspek akan diterapkan di sepanjang atribut deps
dari aturan tempat aspek tersebut diterapkan.
Argumen umum lainnya untuk attr_aspects
adalah ['*']
yang akan menyebarkan
aspek ke semua atribut aturan.
Penerapan aspek
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 []
Fungsi penerapan aspek mirip dengan fungsi penerapan aturan. Keduanya menampilkan penyedia, dapat menghasilkan tindakan, dan mengambil dua argumen:
target
: target tempat aspek diterapkan.ctx
: Objekctx
yang dapat digunakan untuk mengakses atribut serta menghasilkan output dan tindakan.
Fungsi implementasi dapat mengakses atribut aturan target melalui
ctx.rule.attr
. Fitur ini dapat memeriksa penyedia yang disediakan oleh target tempatnya diterapkan (melalui argumen target
).
Aspek diperlukan untuk menampilkan daftar penyedia. Dalam contoh ini, aspek tidak memberikan apa pun sehingga menampilkan daftar kosong.
Memanggil aspek menggunakan command line
Cara termudah untuk menerapkan aspek adalah dari command line menggunakan
argumen
--aspects
. Dengan asumsi bahwa aspek di atas telah ditentukan dalam file bernama print.bzl
,
ini:
bazel build //MyExample:example --aspects print.bzl%print_aspect
akan menerapkan print_aspect
ke example
target dan semua
aturan target yang dapat diakses secara rekursif melalui atribut deps
.
Flag --aspects
mengambil satu argumen, yang merupakan spesifikasi aspek
dalam format <extension file label>%<aspect top-level name>
.
Contoh lanjutan
Contoh berikut menunjukkan penggunaan aspek dari aturan target yang menghitung file pada target, dan berpotensi memfilternya berdasarkan ekstensi. Contoh ini menunjukkan cara menggunakan penyedia untuk menampilkan nilai, cara menggunakan parameter untuk meneruskan argumen ke dalam implementasi aspek, dan cara memanggil aspek dari aturan.
File 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 = '*'),
},
)
File 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',
)
Definisi aspek
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
Contoh ini menunjukkan cara aspek menyebar melalui atribut deps
.
attrs
menentukan kumpulan atribut untuk aspek. Atribut aspek publik
berjenis string
dan disebut parameter. Parameter harus memiliki atribut values
yang ditentukan. Contoh ini memiliki parameter bernama extension
yang diizinkan untuk memiliki nilai '*
', 'h
', atau 'cc
'.
Nilai parameter untuk aspek diambil dari atribut string dengan nama yang sama dari aturan yang meminta aspek (lihat definisi file_count_rule
). Aspek dengan parameter tidak dapat digunakan melalui command line karena tidak ada sintaksis untuk menentukan parameter.
Aspek juga diizinkan memiliki atribut pribadi jenis label
atau
label_list
. Atribut label pribadi dapat digunakan untuk menentukan dependensi pada
alat atau library yang diperlukan untuk tindakan yang dihasilkan oleh aspek. Tidak ada
atribut pribadi yang ditentukan dalam contoh ini, tetapi cuplikan kode berikut
menunjukkan cara meneruskan alat ke suatu aspek:
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
Penerapan aspek
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)]
Sama seperti fungsi penerapan aturan, fungsi implementasi aspek menampilkan struct penyedia yang dapat diakses oleh dependensinya.
Dalam contoh ini, FileCountInfo
ditentukan sebagai penyedia yang memiliki satu
kolom count
. Praktik terbaiknya adalah menentukan kolom
penyedia secara eksplisit menggunakan atribut fields
.
Kumpulan penyedia untuk aplikasi aspek A(X) adalah gabungan penyedia
yang berasal dari penerapan aturan untuk target X dan dari
implementasi aspek A. Penyedia yang disebarkan oleh implementasi aturan
akan dibuat dan dibekukan sebelum aspek diterapkan dan tidak dapat diubah dari
aspek. Akan terjadi error jika target dan aspek yang diterapkan ke keduanya
menyediakan penyedia dengan jenis yang sama, kecuali
OutputGroupInfo
(yang digabungkan, asalkan
aturan dan aspeknya menentukan grup output yang berbeda) dan
InstrumentedFilesInfo
(yang diambil dari aspek tersebut). Ini berarti implementasi aspek mungkin
tidak pernah menampilkan DefaultInfo
.
Parameter dan atribut pribadi diteruskan dalam atribut
ctx
. Contoh ini mereferensikan parameter extension
dan menentukan
file yang akan dihitung.
Untuk penyedia yang kembali, nilai atribut yang bersamanya
Aspek disebarkan (dari daftar attr_aspects
) akan diganti dengan
hasil penerapan aspek tersebut. Misalnya, jika target
X memiliki Y dan Z dalam dependensinya, ctx.rule.attr.deps
untuk A(X) akan menjadi [A(Y), A(Z)].
Dalam contoh ini, ctx.rule.attr.deps
adalah objek Target yang merupakan
hasil penerapan aspek ke 'dependensi' target asli yang
diterapkan aspek ini.
Dalam contoh ini, aspek mengakses penyedia FileCountInfo
dari
dependensi target untuk mengakumulasi total jumlah transitif file.
Memanggil aspek dari aturan
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 = '*'),
},
)
Implementasi aturan menunjukkan cara mengakses FileCountInfo
melalui ctx.attr.deps
.
Definisi aturan menunjukkan cara menentukan parameter (extension
)
dan memberinya nilai default (*
). Perlu diketahui bahwa memiliki nilai default yang
bukan salah satu dari 'cc
', 'h
', atau '*
' akan menimbulkan error karena
pembatasan yang ditempatkan pada parameter dalam definisi aspek.
Memanggil aspek melalui aturan target
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
Contoh ini menunjukkan cara meneruskan parameter extension
ke aspek
melalui aturan. Karena parameter extension
memiliki nilai default dalam
implementasi aturan, extension
akan dianggap sebagai parameter opsional.
Saat target file_count
di-build, aspek akan dievaluasi untuk
dirinya sendiri, dan semua target yang dapat diakses secara rekursif melalui deps
.