A
target bergantung pada B
target jika B
diperlukan oleh A
pada waktu build atau
eksekusi. Relasi bergantung pada menyebabkan
Directed Acyclic Graph
(DAG) melebihi target, dan ini disebut grafik dependensi.
Dependensi langsung target adalah target lain yang dapat dijangkau oleh jalur dengan panjang 1 dalam grafik dependensi. Dependensi transitif target adalah target yang menjadi dependensinya melalui jalur dengan panjang berapa pun melalui grafik.
Bahkan, dalam konteks build, ada dua grafik dependensi, grafik dependensi sebenarnya dan grafik dependensi yang dideklarasikan. Biasanya, kedua grafik tersebut sangat mirip sehingga perbedaan ini tidak perlu dibuat, tetapi berguna untuk diskusi di bawah ini.
Dependensi aktual dan yang dideklarasikan
X
target sebenarnya bergantung pada Y
target jika Y
harus ada,
di-build, dan terbaru agar X
dapat di-build dengan benar. Build dapat
berarti dihasilkan, diproses, dikompilasi, ditautkan, diarsipkan, dikompresi, dieksekusi, atau
jenis tugas lain yang secara rutin terjadi selama proses build.
X
target memiliki dependensi yang dideklarasikan pada Y
target jika ada tepi
dependensi dari X
hingga Y
dalam paket X
.
Untuk build yang benar, grafik dependensi aktual A harus berupa subgrafik dari grafik dependensi yang dideklarasikan D. Artinya, setiap pasangan
node yang terhubung langsung x --> y
di A juga harus terhubung langsung di
D. Dapat dikatakan bahwa D adalah pendekatan yang berlebihan dari A.
Penulis file BUILD
harus secara eksplisit mendeklarasikan semua dependensi langsung yang sebenarnya
untuk setiap aturan ke sistem build, dan tidak lebih.
Kegagalan dalam mematuhi prinsip ini akan menyebabkan perilaku yang tidak ditentukan: build mungkin gagal, tetapi lebih buruk lagi, build mungkin bergantung pada beberapa operasi sebelumnya, atau pada dependensi transitif yang dideklarasikan oleh target. Bazel memeriksa dependensi yang hilang dan melaporkan error, tetapi pemeriksaan ini tidak mungkin selesai dalam semua kasus.
Anda tidak perlu (dan tidak boleh) berupaya mencantumkan semua yang diimpor secara tidak langsung,
meskipun diperlukan oleh A
pada waktu eksekusi.
Selama build X
target, alat build akan memeriksa seluruh penutupan
transitif dependensi X
untuk memastikan bahwa setiap perubahan dalam target tersebut
tercermin dalam hasil akhir, mem-build ulang intermediate sesuai kebutuhan.
Sifat transitif dependensi menyebabkan kesalahan umum. Terkadang, kode dalam satu file dapat menggunakan kode yang diberikan oleh dependensi tidak langsung — sebuah edge transitif tetapi bukan langsung dalam grafik dependensi yang dideklarasikan. Dependensi
tidak langsung tidak muncul dalam file BUILD
. Karena aturan tidak
bergantung secara langsung pada penyedia, tidak ada cara untuk melacak perubahan, seperti ditunjukkan dalam
contoh linimasa berikut:
1. Dependensi yang dideklarasikan cocok dengan dependensi yang sebenarnya
Pada awalnya, semuanya berfungsi. Kode dalam paket a
menggunakan kode dalam paket b
.
Kode dalam paket b
menggunakan kode dalam paket c
, sehingga a
secara transitif
bergantung pada c
.
a/BUILD |
b/BUILD |
---|---|
rule( name = "a", srcs = "a.in", deps = "//b:b", ) |
rule( name = "b", srcs = "b.in", deps = "//c:c", ) |
a / a.in |
b / b.in |
import b; b.foo(); |
import c; function foo() { c.bar(); } |
Dependensi yang dideklarasikan melebihi dependensi yang sebenarnya. Semuanya baik-baik saja.
2. Menambahkan dependensi yang tidak dideklarasikan
Bahaya laten diperkenalkan saat seseorang menambahkan kode ke a
yang membuat dependensi aktual langsung pada c
, tetapi lupa mendeklarasikannya dalam file build a/BUILD
.
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
Dependensi yang dideklarasikan tidak lagi mendekati dependensi yang sebenarnya.
Tindakan ini tidak masalah, karena penutupan transitif kedua grafik tersebut sama,
tetapi menyamarkan masalah: a
memiliki dependensi yang sebenarnya, tetapi tidak dideklarasikan pada c
.
3. Perbedaan antara grafik dependensi yang dideklarasikan dan yang sebenarnya
Bahaya akan terungkap saat seseorang memfaktorkan ulang b
sehingga tidak lagi bergantung pada c
, yang secara tidak sengaja merusak a
melalui kesalahannya sendiri.
b/BUILD |
|
---|---|
rule( name = "b", srcs = "b.in", deps = "//d:d", ) |
|
b / b.in |
|
import d; function foo() { d.baz(); } |
|
Grafik dependensi yang dideklarasikan sekarang merupakan perkiraan yang kurang dari dependensi yang sebenarnya, bahkan jika ditutup secara transitif; build kemungkinan akan gagal.
Masalah ini dapat diatasi dengan memastikan bahwa dependensi sebenarnya dari
a
hingga c
yang diperkenalkan di Langkah 2 dideklarasikan dengan benar dalam file BUILD
.
Jenis-jenis dependensi
Sebagian besar aturan build memiliki tiga atribut untuk menentukan berbagai jenis
dependensi umum: srcs
, deps
, dan data
. Hal ini dijelaskan di bawah. Untuk mengetahui detail selengkapnya, lihat Atribut yang umum untuk semua aturan.
Banyak aturan juga memiliki atribut tambahan untuk jenis dependensi khusus aturan, misalnya, compiler
atau resources
. Informasi ini dijelaskan dalam
Build Encyclopedia.
srcs
dependensi
File yang digunakan langsung oleh aturan atau aturan yang menghasilkan file sumber.
deps
dependensi
Aturan yang mengarah ke modul yang dikompilasi secara terpisah yang menyediakan file header, simbol, library, data, dll.
data
dependensi
Target build mungkin memerlukan beberapa file data agar dapat berjalan dengan benar. File data ini bukan kode sumber: tidak memengaruhi cara target dibuat. Misalnya, pengujian unit mungkin membandingkan output fungsi dengan isi file. Saat mem-build pengujian unit, Anda tidak memerlukan file, tetapi Anda memerlukannya saat menjalankan pengujian. Hal yang sama berlaku untuk alat yang diluncurkan selama eksekusi.
Sistem build menjalankan pengujian di direktori terisolasi tempat hanya file yang tercantum sebagai
data
yang tersedia. Dengan demikian, jika biner/library/pengujian memerlukan beberapa file untuk dijalankan,
tentukan (atau aturan build yang berisi file tersebut) di data
. Contoh:
# I need a config file from a directory named env:
java_binary(
name = "setenv",
...
data = [":env/default_env.txt"],
)
# I need test data from another directory
sh_test(
name = "regtest",
srcs = ["regtest.sh"],
data = [
"//data:file1.txt",
"//data:file2.txt",
...
],
)
File ini tersedia menggunakan jalur relatif path/to/data/file
. Dalam pengujian,
Anda dapat merujuk ke file ini dengan menggabungkan jalur direktori sumber
pengujian dan jalur relatif ruang kerja, misalnya,
${TEST_SRCDIR}/workspace/path/to/data/file
.
Menggunakan label untuk mereferensikan direktori
Saat memeriksa file BUILD
, Anda mungkin melihat bahwa beberapa label data
mengacu ke direktori. Label ini diakhiri dengan /.
atau /
seperti contoh berikut, yang tidak boleh Anda gunakan:
Tidak direkomendasikan —
data = ["//data/regression:unittest/."]
Tidak direkomendasikan —
data = ["testdata/."]
Tidak direkomendasikan —
data = ["testdata/"]
Tampaknya nyaman, terutama untuk pengujian karena memungkinkan pengujian untuk menggunakan semua file data dalam direktori.
Tapi jangan lakukan ini. Untuk memastikan rebuild inkremental yang benar (dan
eksekusi ulang pengujian) setelah perubahan, sistem build harus mengetahui
set lengkap file yang merupakan input untuk build (atau pengujian). Saat Anda menentukan direktori, sistem build hanya akan melakukan build ulang ketika direktori tersebut berubah (karena penambahan atau penghapusan file), tetapi tidak akan dapat mendeteksi pengeditan pada setiap file karena perubahan tersebut tidak memengaruhi direktori yang melingkupinya.
Daripada menentukan direktori sebagai input untuk sistem build, sebaiknya Anda
mengenulasikan kumpulan file yang ada di dalamnya, baik secara eksplisit maupun menggunakan
fungsi glob()
. (Gunakan **
untuk memaksa
glob()
menjadi rekursif.)
Direkomendasikan —
data = glob(["testdata/**"])
Sayangnya, ada beberapa skenario di mana label direktori harus digunakan.
Misalnya, jika direktori testdata
berisi file yang namanya tidak sesuai dengan sintaksis label, enumerasi eksplisit file, atau penggunaan fungsi glob()
akan menghasilkan error label yang tidak valid. Anda harus menggunakan label direktori dalam kasus ini, tetapi waspadai
risiko terkait pembuatan ulang yang salah seperti yang dijelaskan di atas.
Jika Anda harus menggunakan label direktori, perlu diingat bahwa Anda tidak dapat merujuk ke
paket induk dengan jalur ../
relatif. Sebagai gantinya, gunakan jalur absolut seperti
//data/regression:unittest/.
.
Aturan eksternal apa pun, seperti pengujian, yang perlu menggunakan beberapa file harus secara eksplisit mendeklarasikan dependensinya pada semua file tersebut. Anda dapat menggunakan filegroup()
untuk
mengelompokkan file dalam file BUILD
:
filegroup(
name = 'my_data',
srcs = glob(['my_unittest_data/*'])
)
Anda kemudian dapat mereferensikan label my_data
sebagai dependensi data dalam pengujian Anda.
BANGUN file | Visibilitas |