A
target bergantung pada target B
jika B
diperlukan oleh A
pada waktu build atau
eksekusi. Hubungan bergantung pada menyebabkan
Directed Acyclic Graph
(DAG) di atas target, dan disebut grafik dependensi.
Dependensi langsung target adalah target lain yang dapat dijangkau oleh jalur dengan panjang 1 di grafik dependensi. Dependensi transitif target adalah target yang menjadi dependensinya melalui jalur apa pun melalui grafik.
Faktanya, dalam konteks build, ada dua grafik dependensi, yaitu grafik dependensi sebenarnya dan grafik dependensi yang dideklarasikan. Sebagian besar waktu, kedua grafik sangat mirip sehingga perbedaan ini tidak perlu dibuat, tetapi hal ini berguna untuk diskusi di bawah.
Dependensi sebenarnya dan yang dideklarasikan
Target X
sebenarnya bergantung pada target Y
jika Y
harus ada,
di-build, dan terbaru agar X
dapat di-build dengan benar. Di-build dapat
berarti dibuat, diproses, dikompilasi, ditautkan, diarsipkan, dikompresi, dieksekusi, atau
jenis tugas lainnya yang rutin terjadi selama build.
X
target memiliki dependensi yang dideklarasikan pada Y
target jika ada edge
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 x --> y
yang terhubung langsung 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 dari itu.
Kegagalan untuk mematuhi prinsip ini menyebabkan perilaku yang tidak ditentukan: build mungkin gagal, tetapi yang lebih buruk, build mungkin bergantung pada beberapa operasi sebelumnya, atau pada dependensi yang dideklarasikan secara transitif yang kebetulan dimiliki target. Bazel memeriksa keberadaan dependensi yang hilang dan melaporkan error, tetapi tidak mungkin pemeriksaan ini selesai dalam semua kasus.
Anda tidak perlu (dan tidak boleh) mencoba mencantumkan semua yang diimpor secara tidak langsung,
meskipun diperlukan oleh A
pada waktu eksekusi.
Selama build target X
, alat build akan memeriksa seluruh penutupan
transitif dependensi X
untuk memastikan bahwa setiap perubahan pada target tersebut
tercermin dalam hasil akhir, dengan mem-build ulang dependensi perantara sesuai kebutuhan.
Sifat transitif dependensi menyebabkan kesalahan umum. Terkadang,
kode dalam satu file dapat menggunakan kode yang disediakan oleh dependensi tidak langsung —
sisi 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
contoh linimasa berikut:
1. Dependensi yang dideklarasikan cocok dengan dependensi sebenarnya
Pada awalnya, semuanya akan 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 terlalu mendekati dependensi yang sebenarnya. Semuanya baik-baik saja.
2. Menambahkan dependensi yang tidak dideklarasikan
Bahaya laten muncul 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 terlalu mendekati dependensi yang sebenarnya.
Hal ini mungkin dapat di-build dengan baik, karena penutupan transitif dari kedua grafik tersebut sama,
tetapi menyembunyikan masalah: a
memiliki dependensi yang sebenarnya tetapi tidak dideklarasikan pada c
.
3. Divergensi antara grafik dependensi yang dideklarasikan dan aktual
Bahaya ini terungkap saat seseorang memfaktorkan ulang b
sehingga tidak lagi bergantung pada
c
, yang secara tidak sengaja merusak a
tanpa
kesalahan dari dirinya 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 kini merupakan perkiraan yang kurang dari dependensi aktual, meskipun ditutup secara transitif; build kemungkinan akan gagal.
Masalah ini dapat dihindari dengan memastikan 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 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
. Hal ini dijelaskan dalam
Ensiklopedia Build.
Dependensi srcs
File yang digunakan secara 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 mem-build pengujian unit, Anda tidak memerlukan file tersebut, tetapi Anda memerlukannya saat menjalankan pengujian. Hal yang sama berlaku untuk alat yang diluncurkan selama eksekusi.
Sistem build menjalankan pengujian di direktori terisolasi yang hanya menyediakan file yang tercantum sebagai
data
. Jadi, jika biner/library/pengujian memerlukan beberapa file untuk dijalankan,
tentukan file tersebut (atau aturan build yang memuatnya) 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
merujuk 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/"]
Cara ini tampak nyaman, terutama untuk pengujian karena memungkinkan pengujian menggunakan semua file data dalam direktori.
Namun, cobalah untuk tidak melakukannya. Untuk memastikan pembuatan ulang inkremental yang benar (dan
eksekusi ulang pengujian) setelah perubahan, sistem build harus mengetahui
kumpulan lengkap file yang merupakan input untuk build (atau pengujian). Saat Anda menentukan
direktori, sistem build akan melakukan build ulang hanya jika direktori itu sendiri
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 ke sistem build, Anda harus
menguraikan 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 yang mengharuskan penggunaan label direktori.
Misalnya, jika direktori testdata
berisi file yang namanya tidak
sesuai dengan sintaksis label,
enumerasi file eksplisit, atau penggunaan
fungsi glob()
akan menghasilkan error label
yang tidak valid. Dalam hal ini, Anda harus menggunakan label direktori, tetapi waspadai 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 dependensinya pada semua file tersebut. Anda dapat menggunakan filegroup()
untuk
mengelompokkan file 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 |