Target A bergantung pada target B jika B diperlukan oleh A pada waktu build atau eksekusi. Hubungan bergantung pada menginduksi
Directed Acyclic Graph
(DAG) atas target, dan 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 bergantung padanya melalui jalur dengan panjang apa pun melalui grafik.
Faktanya, dalam konteks build, ada dua grafik dependensi, grafik dependensi aktual dan grafik dependensi yang dideklarasikan. Sering kali, kedua grafik tersebut sangat mirip sehingga perbedaan ini tidak perlu dibuat, tetapi berguna untuk diskusi di bawah.
Dependensi aktual dan yang dideklarasikan
Target X sebenarnya bergantung pada target Y jika Y harus ada, dibuat, dan terbaru agar X dapat dibuat dengan benar. Dibuat dapat berarti dibuat, diproses, dikompilasi, ditautkan, diarsipkan, dikompresi, dieksekusi, atau jenis tugas lain yang biasanya terjadi selama build.
Target X memiliki dependensi yang dideklarasikan pada target Y jika ada edge dependensi dari X ke 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 overapproximation dari A.
Penulis file BUILD harus secara eksplisit mendeklarasikan semua dependensi langsung aktual untuk setiap aturan ke sistem build, dan tidak lebih.
Kegagalan untuk mengamati prinsip ini menyebabkan perilaku yang tidak ditentukan: build mungkin gagal, tetapi lebih buruk lagi, build mungkin bergantung pada beberapa operasi sebelumnya, atau pada dependensi yang dideklarasikan transitif yang dimiliki target. Bazel memeriksa dependensi yang tidak ada dan melaporkan error, tetapi pemeriksaan ini tidak mungkin selesai dalam semua kasus.
Anda tidak perlu (dan tidak boleh) mencoba mencantumkan semua hal yang diimpor secara tidak langsung, meskipun diperlukan oleh A pada waktu eksekusi.
Selama build target X, alat build memeriksa penutupan transitif dependensi X secara keseluruhan untuk memastikan bahwa setiap perubahan pada target tersebut tercermin dalam hasil akhir, membangun kembali perantara sesuai kebutuhan.
Sifat dependensi yang transitif menyebabkan kesalahan umum. Terkadang, kode dalam satu file dapat menggunakan kode yang disediakan oleh dependensi tidak langsung — edge transitif, tetapi tidak langsung dalam grafik dependensi yang dideklarasikan. Dependensi tidak langsung tidak muncul dalam file BUILD. Karena aturan tidak bergantung langsung pada penyedia, tidak ada cara untuk melacak perubahan, seperti yang ditunjukkan dalam linimasa contoh berikut:
1. Dependensi yang dideklarasikan cocok dengan dependensi aktual
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 meng-overapproximate dependensi aktual. Semuanya berjalan lancar.
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 meng-overapproximate dependensi aktual.
Hal ini mungkin akan berhasil dibuat, karena penutupan transitif kedua grafik tersebut sama,
tetapi menyembunyikan masalah: a memiliki dependensi aktual, tetapi tidak dideklarasikan pada c.
3. Perbedaan antara grafik dependensi yang dideklarasikan dan aktual
Bahaya terungkap saat seseorang memfaktorkan ulang b sehingga tidak lagi bergantung pada
c, yang secara tidak sengaja merusak a tanpa
kesalahan mereka 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 underapproximation dari dependensi aktual, bahkan saat ditutup secara transitif; build kemungkinan akan gagal.
Masalah ini dapat dihindari dengan memastikan bahwa dependensi aktual dari
a ke 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 generik: 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. Hal ini dijelaskan dalam
Ensiklopedia Build.
Dependensi srcs
File yang digunakan langsung oleh aturan atau aturan yang menghasilkan file sumber.
Dependensi deps
Aturan yang mengarah ke modul yang dikompilasi secara terpisah yang menyediakan file header, simbol, library, data, dll.
Dependensi data
Target build mungkin memerlukan beberapa file data agar dapat berjalan dengan benar. File data ini bukan kode sumber: file ini tidak memengaruhi cara target dibuat. Misalnya, pengujian unit dapat membandingkan output fungsi dengan konten file. Saat membuat 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. Jadi, jika biner/library/pengujian memerlukan beberapa file untuk dijalankan, tentukan file tersebut (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 melihat file BUILD kami, Anda mungkin melihat bahwa beberapa label data merujuk ke direktori. Label ini diakhiri dengan /. atau / seperti contoh ini,
yang tidak boleh Anda gunakan:
Tidak direkomendasikan —
data = ["//data/regression:unittest/."]
Tidak direkomendasikan —
data = ["testdata/."]
Tidak direkomendasikan —
data = ["testdata/"]
Hal ini tampak praktis, terutama untuk pengujian karena memungkinkan pengujian menggunakan semua file data di direktori.
Namun, sebaiknya jangan lakukan hal ini. Untuk memastikan build ulang inkremental yang benar (dan eksekusi ulang pengujian) setelah perubahan, sistem build harus mengetahui kumpulan lengkap file yang menjadi input untuk build (atau pengujian). Saat Anda menentukan direktori, sistem build hanya melakukan build ulang saat direktori itu sendiri berubah (karena penambahan atau penghapusan file), tetapi tidak akan dapat mendeteksi pengeditan pada setiap file karena perubahan tersebut tidak memengaruhi direktori penutup.
Daripada menentukan direktori sebagai input ke sistem build, Anda harus
menghitung kumpulan file yang ada di dalamnya, baik secara eksplisit maupun menggunakan
glob() fungsi. (Gunakan ** untuk memaksa glob() menjadi rekursif.)
Direkomendasikan —
data = glob(["testdata/**"])
Sayangnya, ada beberapa skenario saat label direktori harus digunakan.
Misalnya, jika direktori testdata berisi file yang namanya tidak
sesuai dengan sintaksis label,
penghitungan file eksplisit, atau penggunaan fungsi
glob() akan menghasilkan error label yang tidak valid. Anda harus menggunakan label direktori dalam hal ini, tetapi berhati-hatilah terhadap risiko terkait build 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/..
Setiap aturan eksternal, seperti pengujian, yang perlu menggunakan beberapa file harus secara eksplisit mendeklarasikan ketergantungannya pada semua file tersebut. Anda dapat menggunakan filegroup() untuk mengelompokkan file bersama dalam file BUILD:
filegroup(
name = 'my_data',
srcs = glob(['my_unittest_data/*'])
)
Kemudian, Anda dapat mereferensikan label my_data sebagai dependensi data dalam pengujian.
| File BUILD | Visibilitas |