Aturan
alias
Lihat sumber aturanalias(name, actual, compatible_with, deprecation, features, restricted_to, tags, target_compatible_with, testonly, visibility)
Aturan alias
membuat nama lain yang dapat disebut aturan.
Alias hanya berfungsi untuk target "biasa". Secara khusus, package_group
dan test_suite
tidak dapat dialiaskan.
Aturan alias memiliki deklarasi visibilitasnya sendiri. Dalam semua hal, perilaku ini berperilaku seperti aturan yang direferensikan (misalnya, hanya pengujian pada alias yang diabaikan; status "hanya pengujian" dari aturan yang direferensikan digunakan) dengan beberapa pengecualian kecil:
-
Pengujian tidak akan dijalankan jika aliasnya disebutkan pada command line. Untuk menentukan alias yang menjalankan pengujian yang direferensikan, gunakan aturan
test_suite
dengan satu target dalam atributtests
. -
Saat menentukan grup lingkungan, alias ke aturan
environment
tidak didukung. Keduanya 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
|
|
konfigurasi_setelan
Lihat sumber aturanconfig_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 memicu pemicu atribut yang dapat dikonfigurasi. Baca bagian memilih cara menggunakan aturan ini dan Atribut yang dapat dikonfigurasi untuk ringkasan fitur umum.
Contoh
Berikut ini adalah build yang menetapkan --compilation_mode=opt
atau -c opt
(baik secara eksplisit pada command line atau secara implisit dari file .bazelrc):
config_setting( name = "simple", values = {"compilation_mode": "opt"} )
Berikut adalah semua build yang menargetkan ARM dan menerapkan FOO=bar
yang ditentukan secara khusus (misalnya, bazel build --cpu=arm --define FOO=bar ...
):
config_setting( name = "two_conditions", values = { "cpu": "arm", "define": "FOO=bar" } )
Berikut ini adalah build yang menetapkan flag yang ditentukan pengguna
--//custom_flags:foo=1
(baik secara eksplisit di command line atau secara implisit dari file .bazelrc):
config_setting( name = "my_custom_flag_is_set", flag_values = { "//custom_flags:foo": "1" }, )
Berikut ini adalah build yang menargetkan platform dengan arsitektur x86_64 dan glibc versi 2.25, dengan asumsi adanya constraint_value
dengan label //example:glibc_2_25
. Perhatikan bahwa platform tetap cocok jika menentukan 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 dapat berubah dalam build, misalnya jika target perlu dibuat untuk platform yang berbeda dari dependensinya. Artinya, meskipun
config_setting
tidak cocok dengan flag command line level teratas, flag ini mungkin masih cocok dengan beberapa target build.
Catatan
- Lihat pilih untuk yang akan 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 formulir lengkap. Ini akan otomatis mencocokkan pemanggilan menggunakan salah satu bentuk tersebut. -
Jika flag mengambil beberapa nilai (seperti
--copt=-Da --copt=-Db
atau flag Starlark berjenis daftar),values = { "flag": "a" }
akan cocok jika"a"
ada di mana saja dalam daftar yang 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 flag. Misalnya,--copt
tidak mendukung banyak nilai dalam instance yang sama:--copt=a,b
menghasilkan["a,b"]
, sementara--copt=a --copt=b
menghasilkan["a", "b"]
(sehinggavalues = { "copt": "a,b" }
cocok dengan yang pertama, tetapi tidak 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 lebih lanjut. - 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 oleh platform target agar cocok dengan config_setting ini. (Platform eksekusi tidak
dipertimbangkan di sini.) Semua nilai batasan tambahan yang dimiliki platform akan diabaikan. 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 kunci yang sama ( config_setting( name = "a_and_b", define_values = { "a": "1", "b": "2", }) cocok dengan
|
flag_values
|
values , tetapi
untuk
flag build buatan pengguna.
Ini adalah atribut yang berbeda karena flag yang ditentukan pengguna direferensikan sebagai label, sementara flag bawaan direferensikan sebagai string arbitrer. |
values
|
Aturan ini mewarisi konfigurasi target yang dikonfigurasi dan merujuknya dalam pernyataan Demi kenyamanan, nilai konfigurasi ditentukan sebagai flag build (tanpa Jika flag tidak ditetapkan secara eksplisit pada command line, nilai defaultnya akan digunakan.
Jika kunci muncul beberapa kali dalam kamus, hanya instance terakhir yang digunakan.
Jika kunci mereferensikan flag yang dapat ditetapkan beberapa kali pada command line (misalnya
|
grup file
Lihat sumber aturanfilegroup(name, srcs, data, compatible_with, deprecation, distribs, features, licenses, output_group, restricted_to, tags, target_compatible_with, testonly, visibility)
Gunakan filegroup
untuk memberi nama yang mudah untuk koleksi target.
Kemudian, aturan tersebut dapat dirujuk dari aturan lain.
Sebaiknya gunakan filegroup
, bukan mereferensikan direktori secara langsung.
Yang kedua tidak lagi bagus karena sistem build tidak memiliki pengetahuan penuh tentang semua file
di bawah direktori, sehingga sistem tidak dapat mem-build ulang saat file ini berubah. Jika dikombinasikan dengan glob, filegroup
dapat memastikan bahwa semua file secara eksplisit diketahui 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 mengubah direktori data pengujian:
filegroup( name = "exported_testdata", srcs = glob([ "testdata/*.dat", "testdata/logs/**/*.log", ]), )
Untuk memanfaatkan definisi ini, referensikan filegroup
dengan label dari aturan mana 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
|
Sangatlah umum untuk menggunakan hasil ekspresi glob untuk nilai atribut |
data
|
Target yang disebutkan dalam atribut |
output_group
|
"Grup output" adalah kategori artefak output dari target, yang ditentukan dalam penerapan aturan tersebut. |
kueri
Lihat sumber aturangenquery(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 untuk mengunjungi penutupan transitif dari target yang ditentukan dalam atribut scope
. Kueri yang melanggar aturan ini akan gagal selama eksekusi jika strict
tidak ditentukan atau bernilai benar (jika strict
bernilai salah, target di luar cakupan akan dilewati dengan 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 pada command line adalah bahwa kueri yang berisi spesifikasi target karakter pengganti (misalnya //pkg:*
atau //pkg:all
) tidak diizinkan di sini.
Alasannya adalah dua kali lipat: pertama, karena genquery
harus
menentukan cakupan untuk mencegah target di luar penutupan transitif
kueri untuk memengaruhi outputnya; 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 untuk 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 di 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 default seperti pada command line bazel query .
|
scope
|
|
strict
|
|
Genrule
Lihat sumber aturangenrule(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 dapat Anda gunakan jika tidak ada aturan khusus untuk tugas tersebut.
Misalnya, Anda dapat menjalankan one-liner Bash. Namun, jika Anda perlu mengompilasi file C++, tetap gunakan aturan cc_*
yang ada, karena semua tugas berat telah dilakukan untuk Anda.
Perlu diperhatikan bahwa genrule memerlukan shell untuk menafsirkan argumen perintah. Anda juga dapat mereferensikan program arbitrer yang tersedia di PATH dengan mudah, tetapi tindakan ini akan membuat perintah non-hermetic dan mungkin tidak dapat direproduksi. Jika Anda hanya perlu menjalankan satu alat, sebaiknya gunakan run_binary.
Jangan gunakan genrule untuk menjalankan pengujian. Ada dispensasi khusus untuk pengujian dan hasil pengujian, termasuk kebijakan cache dan variabel lingkungan. Pengujian umumnya harus dijalankan
setelah build selesai dan pada arsitektur target, sedangkan genrules dieksekusi selama
build dan arsitektur exec (keduanya mungkin berbeda). Jika Anda memerlukan aturan pengujian tujuan umum, gunakan sh_test
.
Pertimbangan Lintas Kompilasi
Lihat panduan pengguna untuk mengetahui info selengkapnya tentang kompilasi silang.
Meskipun aturan berjalan selama proses build, output sering kali digunakan setelah proses build, untuk deployment atau pengujian. Pertimbangkan contoh untuk mengompilasi kode C untuk pengontrol mikro: compiler menerima file sumber C dan menghasilkan kode yang berjalan pada pengontrol mikro. Kode yang dihasilkan jelas tidak dapat dijalankan di CPU yang digunakan untuk membuatnya, tetapi compiler C (jika dikompilasi dari sumber) harus melakukannya.
Sistem build menggunakan konfigurasi exec untuk mendeskripsikan mesin tempat build berjalan dan konfigurasi target untuk mendeskripsikan mesin tempat output build seharusnya berjalan. Bagian ini menyediakan opsi untuk mengonfigurasi setiap file dan memisahkan file yang sesuai ke dalam direktori terpisah untuk menghindari konflik.
Untuk genrules, sistem build memastikan bahwa dependensi dibuat dengan benar:
srcs
dibuat (jika diperlukan) untuk konfigurasi target,
tools
dibuat untuk konfigurasi exec, dan output dianggap
untuk konfigurasi target. Atribut ini juga menyediakan
variabel "Make" yang dapat diteruskan oleh perintah genrule ke alat yang sesuai.
Hal ini memang sengaja dibuat agar genrule tidak menetapkan atribut deps
: aturan bawaan lainnya menggunakan informasi meta yang bergantung pada bahasa yang diteruskan antar-aturan untuk secara otomatis menentukan cara menangani aturan dependen, tetapi tingkat otomatisasi ini tidak mungkin dilakukan untuk genrules. Genrules berfungsi
sepenuhnya pada tingkat file dan runfiles.
Kasus Khusus
Kompilasi Exec-exec: dalam beberapa kasus, sistem build perlu menjalankan genrules sehingga output juga dapat dijalankan selama build. Jika misalnya, Gengen membuat beberapa compiler kustom yang kemudian digunakan oleh genrule lain, yang pertama harus menghasilkan outputnya untuk konfigurasi exec, karena di situlah compiler akan berjalan di genrule lainnya. Dalam hal ini, sistem build melakukan hal yang benar secara otomatis: sistem mem-build srcs
dan outs
dari genrule pertama untuk konfigurasi exec, bukan konfigurasi target. Lihat panduan pengguna untuk info
selengkapnya.
JDK & C++ Tooling: untuk menggunakan alat dari JDK atau suite compiler C++, sistem build akan menyediakan kumpulan variabel yang akan digunakan. Lihat Variabel"Make" untuk detailnya.
Lingkungan Genrule
Perintah genrule dieksekusi oleh shell Bash yang dikonfigurasi agar gagal saat perintah
atau pipeline gagal, menggunakan set -e -o pipefail
.
Alat build mengeksekusi perintah Bash dalam lingkungan proses yang bersih yang
hanya menentukan variabel inti seperti PATH
, PWD
,
TMPDIR
, dan beberapa lainnya.
Untuk memastikan bahwa build dapat direproduksi, sebagian besar variabel yang ditentukan di lingkungan shell pengguna tidak diteruskan ke perintah genrule. Namun, Bazel (tetapi bukan Blaze) meneruskan nilai variabel lingkungan PATH
pengguna.
Setiap perubahan pada nilai PATH
akan menyebabkan Bazel menjalankan kembali perintah
pada build berikutnya.
Perintah genrule tidak boleh mengakses jaringan kecuali untuk menghubungkan proses yang merupakan turunan dari perintah itu sendiri, meskipun saat ini tidak diterapkan.
Sistem build otomatis menghapus file output yang ada, tetapi membuat direktori induk yang diperlukan sebelum menjalankan genrule. Tindakan ini juga akan menghapus file output jika terjadi kegagalan.
Saran Umum
- Pastikan alat yang dijalankan oleh genrule bersifat deterministik dan hermetik. Pengujian tidak boleh menulis stempel waktu ke outputnya, dan harus menggunakan pengurutan yang stabil untuk kumpulan dan peta, serta hanya menulis jalur file relatif ke output, bukan jalur absolut. Tidak mengikuti aturan ini akan menyebabkan perilaku build yang tidak terduga (Bazel tidak membuat kembali genrule yang Anda kira akan melakukannya) 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. - Menulis makro Starlark umum jika genaturan yang sama atau sangat mirip digunakan di beberapa tempat. Jika genrule bersifat kompleks, pertimbangkan untuk menerapkannya dalam skrip atau sebagai aturan Starlark. Hal ini meningkatkan keterbacaan serta kemudahan untuk diuji.
- Pastikan bahwa kode keluar menunjukkan dengan benar keberhasilan atau kegagalan genrule.
- Jangan menulis pesan informasi untuk stdout atau stderr. Meskipun berguna untuk proses debug, hal ini dapat dengan mudah menjadi derau; genrule yang berhasil akan diam. Di sisi lain, genrule yang gagal akan menghasilkan 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 akan menyalin struktur direktori/symlink yang dibuat oleh genrules dan pemeriksaan dependensi direktorinya tidak akan baik.
- Saat mereferensikan genrule dalam aturan lain, Anda dapat menggunakan label genrule atau label file output individual. Terkadang satu pendekatan lebih mudah dibaca, terkadang
yang lain: mereferensikan output berdasarkan nama dalam
srcs
aturan yang pemakaiannya akan menghindari pengambilan output lain dari genrule secara tidak sengaja, tetapi bisa merepotkan jika genrule menghasilkan banyak output.
Contoh
Contoh ini menghasilkan foo.h
. Tidak ada sumber, karena perintah tersebut tidak menggunakan
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
lainnya. Perlu diperhatikan bahwa penggunaan $(SRCS)
, bukan perintah $(location)
eksplisit, juga akan berfungsi; contoh ini menggunakan perintah terakhir demi 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 aturan ini berdasarkan nama di bagian srcs atau deps pada aturan BUILD lainnya. Jika aturan menghasilkan file sumber, Anda harus menggunakan atribut
srcs .
|
srcs
|
Atribut ini tidak cocok untuk mencantumkan alat yang dijalankan oleh
Sistem build memastikan bahwa prasyarat ini di-build sebelum menjalankan perintah
genrule; persyaratan ini di-build menggunakan konfigurasi yang sama dengan permintaan build asli. Nama
file prasyarat ini tersedia untuk perintah sebagai
daftar yang dipisahkan spasi di |
outs
|
File output tidak boleh melintasi batas paket. Nama file output diinterpretasikan sebagai relatif terhadap paket.
Jika tanda
Perintah genrule diharapkan membuat setiap file output di lokasi yang telah ditetapkan.
Lokasi tersedia dalam |
cmd
|
$(location)
dan variabel"Buat".
cmd_bash , cmd_ps , dan cmd_bat ,
jika tidak ada yang berlaku.
Jika panjang command line melebihi batas platform (64K pada Linux/macOS, 8K pada Windows),
genrule akan menulis perintah ke skrip dan mengeksekusi skrip tersebut untuk mengatasinya. Ini berlaku untuk semua atribut cmd ( |
cmd_bash
|
Atribut ini memiliki prioritas yang lebih tinggi daripada |
cmd_bat
|
Atribut ini memiliki prioritas yang lebih tinggi daripada
|
cmd_ps
|
Atribut ini memiliki prioritas yang lebih tinggi daripada
Agar Powershell lebih mudah digunakan dan tidak terlalu rentan mengalami error, kami menjalankan perintah berikut untuk menyiapkan lingkungan sebelum menjalankan perintah Powershell di genrule.
|
exec_tools
|
tools .
Sebelumnya, |
executable
|
Jika tanda ini ditetapkan ke Benar (True), output akan berupa file yang dapat dieksekusi dan dapat dijalankan menggunakan
perintah Mendeklarasikan dependensi data untuk file yang dapat dieksekusi yang dihasilkan tidak didukung. |
local
|
Jika disetel ke Benar (True), opsi ini akan memaksa
Hal ini setara dengan memberikan 'lokal' sebagai tag ( |
message
|
Pesan progres yang akan dicetak saat langkah build ini dieksekusi. Secara default, pesannya adalah "Menghasilkan output" (atau sesuatu yang kurang lebih sama), tetapi Anda dapat memberikan output yang lebih spesifik. Gunakan atribut ini, bukan |
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 di-build sebelum menjalankan perintah genrule;
di-build menggunakan konfigurasi exec, karena alat ini dieksekusi sebagai bagian dari build. Jalur
Setiap |
suite_pengujian
Lihat sumber aturantest_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 set pengujian, seperti "pengujian yang harus Anda jalankan sebelum check in", "uji stres project kami", atau "semua pengujian kecil". Perintah blaze test
mematuhi jenis organisasi ini: Untuk pemanggilan seperti blaze test //some/test:suite
, Blaze akan terlebih dahulu menggabungkan semua target pengujian yang disertakan secara transitif oleh target //some/test:suite
(kami menyebutnya "ekspansi test_suite"), lalu Blaze membuat dan menguji target tersebut.
Contoh
Suite pengujian untuk menjalankan semua pengujian kecil dalam paket saat ini.
test_suite( name = "small_tests", tags = ["small"], )
Rangkaian pengujian yang menjalankan serangkaian pengujian yang ditentukan:
test_suite( name = "smoke_tests", tests = [ "system_unittest", "public_api_unittest", ], )
Suite pengujian untuk menjalankan semua pengujian dalam 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 "-" sebelumnya tidak dianggap sebagai bagian dari tag, sehingga tag suite "-small" cocok dengan ukuran "kecil" pengujian. Semua tag lainnya dianggap sebagai tag positif. Secara opsional, untuk membuat tag positif lebih eksplisit, tag juga dapat dimulai dengan karakter "+", yang tidak akan dievaluasi sebagai bagian dari teks tag. Kode 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. Perhatikan bahwa ini tidak berarti bahwa pemeriksaan error untuk dependensi pada pengujian yang difilter akan dilewati; dependensi pada pengujian yang dilewati masih harus bersifat legal (misalnya, tidak diblokir oleh batasan visibilitas).
Kata kunci tag
Perhatikan bahwa
Jika memerlukan |
tests
|
Semua
Jika atribut |