Halaman ini menjelaskan dasar-dasar dan manfaat penggunaan aspek dan menyajikan analisis contoh.
Aspek memungkinkan penambahan grafik dependensi build dengan informasi dan tindakan tambahan. Beberapa skenario umum ketika aspek dapat berguna:
- IDE yang mengintegrasikan Bazel dapat menggunakan aspek untuk mengumpulkan informasi tentang project.
- Alat pembuatan kode dapat memanfaatkan aspek untuk dieksekusi pada inputnya dengan cara
tidak bergantung pada target. Sebagai contoh, file
BUILD
dapat menentukan hierarki dari library protobuf definisi, dan aturan spesifik per 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: sumber apa
adalah bagian dari project, artefak (target) apa yang harus dibuat
file-file itu, apa dependensi di antara
file-file itu, dll. Bazel menggunakan
informasi ini untuk menjalankan build, yaitu, ia mencari tahu rangkaian tindakan
yang diperlukan untuk menghasilkan artefak (seperti menjalankan compiler atau penaut) dan
menjalankan tindakan-tindakan tersebut. Bazel mencapai hal ini dengan
membangun ketergantungan
grafik antar-target dan mengunjungi grafik ini untuk mengumpulkan tindakan tersebut.
Perhatikan 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 dari
aturan yang sesuai (dalam hal ini "java_library") untuk setiap
pada contoh di atas. Fungsi penerapan 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 mirip dengan aturan karena memiliki fungsi implementasi yang menghasilkan tindakan dan menampilkan penyedia. Namun, kekuatannya berasal dari cara grafik dependensi dibuat untuknya. Aspek memiliki implementasi dan daftar semua atribut yang dipromosikan. Pertimbangkan aspek A yang disebarkan bersama atribut bernama "deps". Aspek ini dapat diterapkan pada target X, yang menghasilkan node aplikasi aspek A(X). Selama penerapannya, aspek A diterapkan secara rekursif ke semua target yang dirujuk X dalam atribut "deps" (semua atribut dalam daftar penyebaran A).
Jadi, satu tindakan penerapan aspek A ke target X akan menghasilkan "grafik bayangan" pengguna grafik dependensi target asli yang ditampilkan di gambar berikut:
Gambar 2. Buat grafik dengan aspek.
Satu-satunya tepi yang dibayangi adalah tepi di sepanjang atribut dalam
set propagasi, sehingga tepi runtime_deps
tidak dibayangi dalam
contoh ini. Fungsi penerapan aspek kemudian dipanggil di semua node dalam
grafik bayangan yang mirip dengan cara penerapan aturan dipanggil di node
grafik asli.
Contoh sederhana
Contoh ini menunjukkan cara mencetak file sumber secara rekursif untuk
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 bagi contoh tersebut menjadi beberapa bagian dan pelajari 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
_print_aspect_impl
.
attr_aspects
adalah daftar atribut aturan yang diperluas oleh aspek.
Dalam hal ini, aspek akan diterapkan di sepanjang atribut deps
dari
aturan yang diterapkan.
Argumen umum lainnya untuk attr_aspects
adalah ['*']
yang akan menyebarkan
aspek untuk semua atribut sebuah aturan.
Implementasi Aspect
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 implementasi aturan fungsi-fungsi lainnya. Properti tersebut menampilkan penyedia, dapat menghasilkan actions, dan mengambil dua argumen:
target
: target tempat aspek diterapkan.ctx
: Objekctx
yang dapat digunakan untuk mengakses atribut serta menghasilkan output dan tindakan.
Fungsi penerapan dapat mengakses atribut aturan target melalui
ctx.rule.attr
Alat ini dapat memeriksa
penyedia yang
disediakan oleh target tempatnya diterapkan (melalui argumen target
).
Aspek diperlukan untuk menampilkan daftar penyedia. Dalam contoh ini, aspek tidak menyediakan apa pun, sehingga mengembalikan daftar kosong.
Memanggil aspek menggunakan command line
Cara termudah untuk menerapkan aspek adalah dari command line menggunakan argumen --aspects
. Dengan asumsi aspek di atas 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
menggunakan 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 di target, yang berpotensi memfilternya berdasarkan ekstensi. Contoh ini menunjukkan cara menggunakan penyedia untuk menampilkan nilai, cara menggunakan parameter untuk meneruskan argumen ke 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 suatu aspek. Atribut aspek publik
menentukan parameter dan hanya dapat berupa jenis bool
, int
, atau string
.
Untuk aspek yang diterapkan aturan, parameter int
dan string
harus memiliki
values
yang ditentukan untuk pod tersebut. Contoh ini memiliki parameter yang disebut extension
yang diizinkan memiliki '*
', 'h
', atau 'cc
' sebagai nilai.
Untuk aspek yang diterapkan oleh aturan, nilai parameter diambil dari aturan yang meminta
aspek, menggunakan atribut aturan yang memiliki nama dan jenis yang sama.
(lihat definisi file_count_rule
).
Untuk aspek command line, nilai parameter dapat diteruskan menggunakan
--aspects_parameters
penanda. Batasan values
dari parameter int
dan string
mungkin
dihilangkan.
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 aspek:
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
Implementasi Aspect
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 penerapan aspek menampilkan struct penyedia yang dapat diakses oleh dependensinya.
Dalam contoh ini, FileCountInfo
didefinisikan sebagai penyedia yang memiliki satu
bidang count
. Praktik terbaiknya adalah secara eksplisit menentukan {i>field<i}
menggunakan atribut fields
.
Kumpulan penyedia untuk aplikasi aspek A(X) adalah gabungan penyedia
yang berasal dari penerapan aturan untuk target X dan dari
penerapan aspek A. Penyedia yang disebarkan oleh penerapan aturan
dibuat dan dibekukan sebelum aspek diterapkan dan tidak dapat diubah dari
aspek. Akan terjadi error jika target dan aspek yang diterapkan masing-masing
penyedia dengan jenis yang sama, dengan pengecualian
OutputGroupInfo
(yang digabungkan, asalkan
dan aspek menentukan kelompok output yang berbeda) dan
InstrumentedFilesInfo
(yang diambil dari aspek). Ini berarti bahwa implementasi
aspek dapat
jangan pernah menampilkan DefaultInfo
.
Parameter dan atribut pribadi diteruskan dalam atribut
ctx
. Contoh ini mereferensikan parameter extension
dan menentukan
file apa yang dihitung.
Untuk penyedia yang ditampilkan, nilai atribut yang digunakan untuk menyebarkan
aspek (dari daftar attr_aspects
) diganti dengan
hasil penerapan aspek ke penyedia 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 'deps' target asli tempat
aspek telah diterapkan.
Pada contoh, aspek mengakses penyedia FileCountInfo
dari elemen
dependensi target untuk mengakumulasi jumlah total 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 beri nilai default (*
). Perhatikan bahwa dengan
memiliki nilai {i>default<i} yang
bukan salah satu dari 'cc
', 'h
', atau '*
' adalah sebuah kesalahan
karena
batasan 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 dalam aspek
melalui aturan. Karena parameter extension
memiliki nilai default dalam
penerapan aturan, extension
akan dianggap sebagai parameter opsional.
Saat target file_count
dibuat, aspek kita akan dievaluasi untuk
itu sendiri, dan semua target yang dapat diakses secara rekursif melalui deps
.