Aturan
alias
alias(name, actual, compatible_with, deprecation, features, restricted_to, tags, target_compatible_with, testonly, visibility)
Aturan alias
membuat nama lain yang dapat digunakan aturan tersebut.
Aliasing hanya berfungsi untuk target "reguler". Secara khusus, package_group
dan test_suite
tidak dapat diberi alias.
Aturan alias memiliki deklarasi visibilitasnya sendiri. Dalam aspek lain, kode ini berperilaku seperti aturan yang direferensikannya (misalnya testonly pada alias akan diabaikan; nilai pengujian saja dari aturan yang direferensikan akan digunakan) dengan beberapa pengecualian kecil:
-
Pengujian tidak dijalankan jika aliasnya disebutkan di command line. Untuk menentukan alias yang menjalankan pengujian yang direferensikan, gunakan aturan
test_suite
dengan satu target dalam atributtests
-nya. -
Saat menentukan grup lingkungan, alias untuk aturan
environment
tidak didukung. Opsi ini juga tidak didukung dalam opsi command line--target_environment
.
Contoh
filegroup( name = "data", srcs = ["data.txt"], ) alias( name = "other", actual = ":data", )
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. |
actual
|
|
config_setting
config_setting(name, constraint_values, define_values, deprecation, distribs, features, flag_values, licenses, tags, testonly, values, visibility)
Mencocokkan status konfigurasi yang diharapkan (dinyatakan sebagai flag build atau batasan platform) untuk tujuan memicu atribut yang dapat dikonfigurasi. Lihat bagian select untuk mengetahui cara menggunakan aturan ini dan Atribut yang dapat dikonfigurasi untuk mengetahui ringkasan fitur umum.
Contoh
Parameter berikut cocok dengan build apa pun yang menetapkan --compilation_mode=opt
atau -c opt
(baik secara eksplisit pada command line maupun secara implisit dari file .bazelrc):
config_setting( name = "simple", values = {"compilation_mode": "opt"} )
Berikut ini adalah build yang cocok dengan build apa pun yang menargetkan ARM dan menerapkan FOO=bar
yang ditetapkan secara kustom (misalnya, bazel build --cpu=arm --define FOO=bar ...
):
config_setting( name = "two_conditions", values = { "cpu": "arm", "define": "FOO=bar" } )
Parameter berikut cocok dengan build apa pun yang menetapkan
flag yang ditentukan pengguna
--//custom_flags:foo=1
(baik secara eksplisit pada command line maupun secara implisit dari file
.bazelrc):
config_setting( name = "my_custom_flag_is_set", flag_values = { "//custom_flags:foo": "1" }, )
Berikut ini cocok dengan build apa pun yang menargetkan platform dengan arsitektur x86_64 dan glibc
versi 2.25, dengan asumsi adanya constraint_value
dengan label
//example:glibc_2_25
. Perlu diperhatikan bahwa platform masih cocok jika menetapkan
nilai batasan tambahan di luar keduanya.
config_setting( name = "64bit_glibc_2_25", constraint_values = [ "@platforms//cpu:x86_64", "//example:glibc_2_25", ] )Dalam semua kasus ini, konfigurasi masih dapat berubah di dalam build, misalnya jika target perlu di-build untuk platform yang berbeda dengan dependensinya. Artinya, meskipun
config_setting
tidak cocok dengan flag command line level atas, konfigurasi tersebut mungkin masih cocok
dengan beberapa target build.
Catatan
- Lihat memilih untuk mengetahui apa yang terjadi jika beberapa
config_setting
cocok dengan status konfigurasi saat ini. - Untuk flag yang mendukung formulir singkat (misalnya
--compilation_mode
vs.-c
), definisivalues
harus menggunakan bentuk lengkap. Metode ini otomatis cocok dengan pemanggilan menggunakan salah satu formulir. -
Jika flag mengambil beberapa nilai (seperti
--copt=-Da --copt=-Db
atau flag Starlark yang berjenis daftar),values = { "flag": "a" }
cocok jika"a"
ada di mana saja dalam daftar sebenarnya.values = { "myflag": "a,b" }
berfungsi dengan cara yang sama: ini cocok dengan--myflag=a --myflag=b
,--myflag=a --myflag=b --myflag=c
,--myflag=a,b
, dan--myflag=c,b,a
. Semantik yang tepat bervariasi antar-tanda. Misalnya,--copt
tidak mendukung banyak nilai dalam instance yang sama:--copt=a,b
menghasilkan["a,b"]
, sedangkan--copt=a --copt=b
menghasilkan["a", "b"]
(sehinggavalues = { "copt": "a,b" }
cocok dengan nilai pertama, tetapi tidak dengan yang kedua). Namun,--ios_multi_cpus
(untuk aturan Apple) memang:-ios_multi_cpus=a,b
danios_multi_cpus=a --ios_multi_cpus=b
menghasilkan["a", "b"]
. Periksa definisi flag dan uji kondisi Anda dengan cermat untuk memverifikasi ekspektasi yang tepat. - Jika Anda perlu menentukan kondisi yang tidak dimodelkan oleh flag build bawaan, gunakan
flag yang ditentukan Starlark. Anda juga dapat menggunakan
--define
, tetapi cara ini menawarkan dukungan yang lebih lemah dan tidak direkomendasikan. Lihat di sini untuk diskusi selengkapnya. - Hindari mengulangi definisi
config_setting
yang identik dalam paket yang berbeda. Sebagai gantinya, referensikanconfig_setting
umum yang ditentukan dalam paket kanonis. values
,define_values
, danconstraint_values
dapat digunakan dalam kombinasi apa pun dalamconfig_setting
yang sama, tetapi setidaknya satu harus ditetapkan untukconfig_setting
tertentu.
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. |
constraint_values
|
constraint_values yang harus ditentukan platform target
agar cocok dengan config_setting ini. (Platform eksekusi tidak
dipertimbangkan di sini.) Nilai batasan tambahan apa pun yang telah diabaikan oleh platform. Lihat
Atribut Build yang Dapat Dikonfigurasi untuk mengetahui detailnya.
Jika dua |
define_values
|
values tetapi khusus untuk flag --define .
Artinya: config_setting( name = "a_and_b", values = { "define": "a=1", "define": "b=2", }) tidak berfungsi karena tombol yang sama ( config_setting( name = "a_and_b", define_values = { "a": "1", "b": "2", }) cocok dengan
|
flag_values
|
values , tetapi
untuk
flag build yang ditentukan pengguna.
Ini adalah atribut berbeda karena tanda yang ditentukan pengguna direferensikan sebagai label, sedangkan tanda bawaan direferensikan sebagai string arbitrer. |
values
|
Aturan ini mewarisi konfigurasi target dikonfigurasi yang mereferensikannya dalam pernyataan Demi kemudahan, nilai konfigurasi ditetapkan sebagai flag build (tanpa Jika flag tidak secara eksplisit ditetapkan pada command line, nilai defaultnya akan digunakan.
Jika sebuah tombol muncul beberapa kali dalam kamus, hanya instance terakhir yang akan digunakan.
Jika kunci mereferensikan tanda yang dapat ditetapkan beberapa kali pada command line (misalnya,
|
grup file
filegroup(name, srcs, data, compatible_with, deprecation, distribs, features, licenses, output_group, restricted_to, tags, target_compatible_with, testonly, visibility)
Gunakan filegroup
untuk memberikan nama yang mudah bagi kumpulan target.
Aturan ini kemudian dapat dirujuk dari aturan lain.
Sebaiknya gunakan filegroup
, bukan merujuk direktori secara langsung.
Hal ini tidak baik karena sistem build tidak memiliki pengetahuan penuh tentang semua file di bawah direktori tersebut, sehingga mungkin tidak akan mem-build ulang ketika file ini berubah. Ketika dikombinasikan dengan glob, filegroup
dapat memastikan bahwa semua file diketahui secara eksplisit oleh sistem build.
Contoh
Untuk membuat filegroup
yang terdiri dari dua file sumber, lakukan
filegroup( name = "mygroup", srcs = [ "a_file.txt", "some/subdirectory/another_file.txt", ], )
Atau, gunakan glob
untuk merendahkan direktori testdata:
filegroup( name = "exported_testdata", srcs = glob([ "testdata/*.dat", "testdata/logs/**/*.log", ]), )
Untuk memanfaatkan definisi ini, referensikan filegroup
dengan label dari aturan apa pun:
cc_library( name = "my_library", srcs = ["foo.cc"], data = [ "//my_package:exported_testdata", "//my_package:mygroup", ], )
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. |
srcs
|
Hal ini umum untuk menggunakan hasil ekspresi glob untuk
nilai atribut |
data
|
Target yang disebutkan dalam atribut |
output_group
|
"Grup output" adalah kategori artefak output target, yang ditentukan dalam implementasi aturan tersebut. |
kueri
genquery(name, deps, data, compatible_with, deprecation, distribs, exec_compatible_with, exec_properties, expression, features, licenses, opts, restricted_to, scope, strict, tags, target_compatible_with, testonly, visibility)
genquery()
menjalankan kueri yang ditentukan dalam bahasa kueri Blaze dan membuang hasilnya ke dalam file.
Agar build tetap konsisten, kueri hanya diizinkan mengunjungi
penutupan transitif target yang ditentukan dalam atribut
scope
. Kueri yang melanggar aturan ini akan gagal selama eksekusi jika strict
tidak ditentukan atau benar (jika strict
bernilai salah, target di luar cakupan akan dilewati dengan memberikan peringatan). Cara termudah untuk memastikan hal ini tidak terjadi adalah dengan menyebutkan label yang sama dalam cakupan seperti dalam ekspresi kueri.
Satu-satunya perbedaan antara kueri yang diizinkan di sini dan di command line adalah kueri yang berisi spesifikasi target karakter pengganti (misalnya //pkg:*
atau //pkg:all
) tidak diizinkan di sini.
Alasannya ada dua: pertama, karena genquery
harus menentukan cakupan untuk mencegah target di luar penutupan transitif kueri untuk memengaruhi output-nya; dan, kedua, karena file BUILD
tidak mendukung dependensi karakter pengganti (misalnya deps=["//a/..."]
tidak diizinkan).
Output genquery diurutkan menggunakan --order_output=full
untuk menerapkan output deterministik.
Nama file output adalah nama aturan.
Contoh
Contoh ini menulis daftar label dalam penutupan transitif target yang ditentukan ke sebuah file.
genquery( name = "kiwi-deps", expression = "deps(//kiwi:kiwi_lib)", scope = ["//kiwi:kiwi_lib"], )
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. |
expression
|
:b dalam atribut ini dalam file a/BUILD akan merujuk ke
target //:b .
|
opts
|
bazel query . Beberapa opsi kueri tidak diizinkan
di sini: --keep_going , --query_file , --universe_scope ,
--order_results , dan --order_output . Opsi yang tidak ditentukan di sini
akan memiliki nilai defaultnya seperti pada command line bazel query .
|
scope
|
|
strict
|
|
Genrule
genrule(name, srcs, outs, cmd, cmd_bash, cmd_bat, cmd_ps, compatible_with, deprecation, distribs, exec_compatible_with, exec_properties, exec_tools, executable, features, licenses, local, message, output_licenses, output_to_bindir, restricted_to, tags, target_compatible_with, testonly, toolchains, tools, visibility)
genrule
menghasilkan satu atau beberapa file menggunakan perintah Bash yang ditentukan pengguna.
Genrules adalah aturan build umum yang bisa Anda gunakan jika tidak ada aturan khusus untuk tugas tersebut.
Misalnya, Anda dapat menjalankan satu baris Bash. Namun, jika Anda perlu mengompilasi file C++, tetap ikuti aturan cc_*
yang ada, karena semua pekerjaan berat sudah dilakukan untuk Anda.
Jangan gunakan genrule untuk menjalankan pengujian. Ada dispensasi khusus untuk pengujian dan hasil pengujian, termasuk kebijakan caching dan variabel lingkungan. Pengujian umumnya perlu dijalankan setelah build selesai dan pada arsitektur target, sedangkan genrules dijalankan selama build dan pada arsitektur host (keduanya mungkin berbeda). Jika Anda memerlukan aturan pengujian tujuan umum, gunakan sh_test
.
Pertimbangan silang
Lihat panduan pengguna untuk mengetahui info selengkapnya tentang kompilasi silang.
Meskipun genrules berjalan selama build, output-nya sering kali digunakan setelah build, untuk deployment atau pengujian. Pertimbangkan contoh kompilasi kode C untuk mikrokontroler: compiler menerima file sumber C dan menghasilkan kode yang berjalan pada mikrokontroler. Kode yang dihasilkan jelas tidak dapat dijalankan pada CPU yang digunakan untuk mem-build-nya, tetapi compiler C (jika dikompilasi dari sumber) sendiri harus melakukannya.
Sistem build menggunakan konfigurasi host untuk mendeskripsikan mesin tempat build dijalankan dan konfigurasi target untuk mendeskripsikan mesin tempat output build seharusnya dijalankan. API ini menyediakan opsi untuk mengonfigurasi setiap direktori dan memisahkan file terkait ke dalam direktori terpisah untuk menghindari konflik.
Untuk genrules, sistem build memastikan bahwa dependensi dibuat dengan tepat:
srcs
dibuat (jika perlu) untuk konfigurasi target,
tools
dibuat untuk konfigurasi host, dan output dianggap
untuk konfigurasi target. Class ini juga menyediakan
variabel "Make" yang dapat diteruskan oleh perintah genrule ke alat yang sesuai.
genrule telah ditentukan untuk tidak menetapkan atribut deps
: aturan bawaan lainnya menggunakan
informasi meta dependen bahasa yang diteruskan di antara aturan untuk menentukan cara
menangani aturan dependen secara otomatis, tetapi level otomatisasi ini tidak dimungkinkan untuk genrules. Genrules hanya berfungsi pada level file dan runfile.
Kasus Khusus
Kompilasi host-host: dalam beberapa kasus, sistem build perlu menjalankan genrules sehingga output juga dapat dijalankan selama proses build. Misalnya, jika genrule mem-build beberapa compiler kustom yang kemudian digunakan oleh genrule lain, compiler pertama harus menghasilkan outputnya untuk konfigurasi host, karena di situlah compiler akan berjalan dalam genrule lainnya. Dalam hal ini,
sistem build melakukan hal yang benar secara otomatis: sistem akan membuat srcs
dan
outs
dari genrule pertama untuk konfigurasi host, bukan konfigurasi
target. Lihat panduan pengguna untuk info selengkapnya.
Alat JDK & C++: untuk menggunakan alat dari JDK atau rangkaian compiler C++, sistem build menyediakan sekumpulan variabel untuk digunakan. Lihat variabel"Buat" untuk mengetahui detailnya.
Lingkungan Genrule
Perintah genrule dijalankan oleh shell Bash yang dikonfigurasi agar gagal saat perintah atau pipeline gagal, menggunakan set -e -o pipefail
.
Alat build akan menjalankan perintah Bash di lingkungan proses yang bersih dan
hanya menentukan variabel inti seperti PATH
, PWD
,
TMPDIR
, dan beberapa variabel lainnya.
Untuk memastikan bahwa build dapat direproduksi, sebagian besar variabel yang ditentukan dalam lingkungan shell
pengguna tidak diteruskan ke perintah genrule. Namun, Bazel (tetapi tidak
Blaze) akan meneruskan nilai variabel lingkungan PATH
pengguna.
Setiap perubahan pada nilai PATH
akan menyebabkan Bazel mengeksekusi kembali perintah
pada build berikutnya.
Perintah genrule tidak boleh mengakses jaringan kecuali untuk menghubungkan proses yang merupakan turunan dari perintah itu sendiri, meskipun hal ini saat ini tidak diberlakukan.
Sistem build akan otomatis menghapus file output yang ada, tetapi membuat direktori induk yang diperlukan sebelum menjalankan genrule. Alat ini juga menghapus {i>output<i} file jika terjadi kegagalan.
Saran Umum
- Pastikan bahwa alat yang dijalankan oleh genrule bersifat deterministik dan hermetis. Objek tidak boleh menulis stempel waktu ke output-nya, dan harus menggunakan pengurutan yang stabil untuk kumpulan dan peta, serta hanya menulis jalur file relatif ke output, tanpa jalur absolut. Tidak mengikuti aturan ini akan menyebabkan perilaku build yang tidak terduga (Bazel tidak mem-build ulang genrule yang Anda kira akan digunakan) dan menurunkan performa cache.
- Gunakan
$(location)
secara ekstensif untuk output, alat, dan sumber. Karena pemisahan file output untuk konfigurasi yang berbeda, genrules tidak dapat mengandalkan jalur hard code dan/atau absolut. - Tulis makro Starlark yang umum jika genrules yang sama atau sangat mirip digunakan di beberapa tempat. Jika genrule yang rumit, pertimbangkan untuk menerapkannya dalam skrip atau sebagai aturan Starlark. Hal ini meningkatkan keterbacaan serta kemudahan pengujian.
- Pastikan bahwa kode keluar menunjukkan keberhasilan atau kegagalan genrule dengan benar.
- Jangan menulis pesan informasi ke stdout atau stderr. Meskipun berguna untuk proses debug, hal ini dapat dengan mudah menjadi derau; genrule yang berhasil harus senyap. Di sisi lain, genrule yang gagal akan memunculkan pesan error yang baik.
$$
evaluates to a$
, a literal dollar-sign, so in order to invoke a shell command containing dollar-signs such asls $(dirname $x)
, one must escape it thus:ls $$(dirname $$x)
.- Hindari membuat symlink dan direktori. Bazel tidak menyalin struktur direktori/symlink yang dibuat oleh genrules dan pemeriksaan dependensinya terhadap direktori tidak berjalan lancar.
- Saat mereferensikan genrule di aturan lain, Anda dapat menggunakan label genrule atau label setiap file output. Terkadang pendekatan yang satu lebih mudah dibaca, terkadang
pendekatan yang lain: mereferensikan output berdasarkan nama dalam
srcs
aturan yang memakai akan menghindari pengambilan output lain dari genrule secara tidak sengaja, tetapi dapat merepotkan jika genrule menghasilkan banyak output.
Contoh
Contoh ini menghasilkan foo.h
. Tidak ada sumber, karena perintah tidak mengambil input apa pun. "Biner" yang dijalankan oleh perintah adalah skrip perl dalam paket yang sama dengan genrule.
genrule( name = "foo", srcs = [], outs = ["foo.h"], cmd = "./$(location create_foo.pl) > \"$@\"", tools = ["create_foo.pl"], )
Contoh berikut menunjukkan cara menggunakan filegroup
dan output genrule
lain. Perhatikan bahwa penggunaan $(SRCS)
,
bukan perintah $(location)
eksplisit, juga akan berfungsi; contoh ini menggunakan perintah berikut untuk
demonstrasi.
genrule( name = "concat_all_files", srcs = [ "//some:files", # a filegroup with multiple files in it ==> $(locations) "//other:gen", # a genrule with a single output ==> $(location) ], outs = ["concatenated.txt"], cmd = "cat $(locations //some:files) $(location //other:gen) > $@", )
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. Anda dapat merujuk pada aturan ini dengan namanya di bagian srcs atau deps aturan BUILD lainnya. Jika aturan ini menghasilkan file sumber, Anda harus menggunakan
atribut srcs .
|
srcs
|
Atribut ini tidak cocok untuk mencantumkan alat yang dieksekusi oleh
Sistem build memastikan prasyarat ini dibangun sebelum menjalankan perintah
genrule; prasyarat tersebut dibuat menggunakan konfigurasi yang sama dengan permintaan build asli. Nama-nama file prasyarat ini tersedia untuk perintah sebagai daftar yang dipisahkan spasi di |
outs
|
File output tidak boleh melewati batas paket. Nama file output ditafsirkan sebagai relatif terhadap paket.
Jika flag
Perintah genrule diharapkan untuk membuat setiap file output di lokasi yang telah ditentukan.
Lokasi ini tersedia dalam |
cmd
|
$(location)
dan "Buat".
cmd_bash , cmd_ps , dan cmd_bat ,
jika tidak ada yang berlaku.
Jika panjang command line melebihi batas platform (64K di Linux/macOS, 8K di Windows),
genrule akan menulis perintah ke skrip dan menjalankan skrip tersebut untuk mengatasinya. Hal ini
berlaku untuk semua atribut cmd ( |
cmd_bash
|
Atribut ini memiliki prioritas lebih tinggi daripada |
cmd_bat
|
Atribut ini memiliki prioritas lebih tinggi daripada
|
cmd_ps
|
Atribut ini memiliki prioritas lebih tinggi daripada
Agar Powershell lebih mudah digunakan dan tidak terlalu rentan error, kami menjalankan perintah berikut untuk menyiapkan lingkungan sebelum menjalankan perintah Powershell dalam genrule.
|
exec_tools
|
tools , tetapi dependensi ini akan dikonfigurasi untuk platform eksekusi aturan, bukan konfigurasi host.
Ini berarti dependensi dalam exec_tools tidak tunduk pada batasan yang sama seperti dependensi dalam tools . Secara khusus, node tidak perlu menggunakan konfigurasi host untuk dependensi transitifnya sendiri. Lihat
tools untuk detail selengkapnya.
Tim Blaze memigrasikan semua penggunaan |
executable
|
Jika flag ini disetel ke Benar (True), output-nya adalah file yang dapat dieksekusi dan dapat dijalankan menggunakan
perintah Mendeklarasikan dependensi data untuk file yang dapat dieksekusi yang dihasilkan tidak didukung. |
local
|
Jika ditetapkan ke True, opsi ini akan memaksa
Hal ini sama dengan memberikan 'lokal' sebagai tag ( |
message
|
Pesan progres yang akan dicetak saat langkah build ini dijalankan. Secara default,
pesannya adalah "Menghasilkan output" (atau sesuatu yang sama hampa), tetapi Anda dapat memberikan
yang lebih spesifik. Gunakan atribut ini sebagai ganti |
output_licenses
|
common attributes
|
output_to_bindir
|
Jika disetel ke Benar (True), opsi ini akan menyebabkan file output ditulis ke direktori |
tools
|
Sistem build memastikan prasyarat ini dibangun sebelum menjalankan perintah genrule; prasyarat dibuat menggunakan konfigurasi host, karena alat ini dijalankan sebagai bagian dari build. Jalur
Setiap |
test_suite
test_suite(name, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, target_compatible_with, testonly, tests, visibility)
test_suite
menentukan serangkaian pengujian yang dianggap "berguna" bagi manusia. Hal ini
memungkinkan project untuk menentukan kumpulan pengujian, seperti "pengujian yang harus Anda jalankan sebelum checkin", "pengujian
stress project kami", atau "semua pengujian kecil". Perintah blaze test
mengikuti organisasi
semacam ini: Untuk pemanggilan seperti blaze test //some/test:suite
, Blaze pertama-tama
menghitung semua target pengujian yang disertakan secara transitif oleh target //some/test:suite
(kita
menyebut ini "test_suite development"), lalu Blaze mem-build dan menguji target tersebut.
Contoh
Paket pengujian untuk menjalankan semua pengujian kecil dalam paket saat ini.
test_suite( name = "small_tests", tags = ["small"], )
Paket pengujian yang menjalankan serangkaian pengujian tertentu:
test_suite( name = "smoke_tests", tests = [ "system_unittest", "public_api_unittest", ], )
Test suite untuk menjalankan semua pengujian pada paket saat ini yang tidak stabil.
test_suite( name = "non_flaky_test", tags = ["-flaky"], )
Argumen
Atribut | |
---|---|
name |
Nama unik untuk target ini. |
tags
|
Tag yang diawali dengan karakter "-" dianggap sebagai tag negatif. Karakter "-" yang mendahului tidak dianggap sebagai bagian dari tag, sehingga tag rangkaian "-small" cocok dengan ukuran "kecil" pengujian. Semua tag lainnya dianggap sebagai tag positif. Secara opsional, untuk menjadikan tag positif lebih eksplisit, tag juga dapat diawali dengan karakter "+", yang tidak akan dievaluasi sebagai bagian dari teks tag. Hal ini hanya membuat perbedaan positif dan negatif lebih mudah dibaca. Hanya aturan pengujian yang cocok dengan semua tag positif dan tidak ada tag negatif yang akan disertakan dalam rangkaian pengujian. Perlu diperhatikan bahwa ini tidak berarti bahwa error yang memeriksa dependensi pada pengujian yang difilter akan dilewati; dependensi pada pengujian yang dilewati tetap harus sah (misalnya, tidak diblokir oleh batasan visibilitas).
Kata kunci tag
Perhatikan bahwa
Jika memerlukan |
tests
|
Semua
Jika atribut |