Dependensi

Laporkan masalah Lihat sumber

A target bergantung pada target B jika B diperlukan oleh A pada waktu build atau eksekusi. Hubungan bergantung pada menginduksi Directed Acyclic Graph (DAG) pada target, dan disebut sebagai grafik dependensi.

Dependensi direct target adalah target lain yang dapat dijangkau oleh jalur panjang 1 dalam grafik dependensi. Dependensi transitif target adalah target yang bergantung melalui jalur dengan panjang berapa pun melalui grafik.

Bahkan, dalam konteks build, ada dua grafik dependensi, grafik dependensi yang sebenarnya 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 dideklarasikan

X Target sebenarnya bergantung pada target Y jika Y harus ada, dibuat, dan diupdate agar X dapat dibuat dengan benar. Dibuat dapat dihasilkan, diproses, dikompilasi, ditautkan, diarsipkan, dikompresi, dieksekusi, atau jenis tugas lainnya yang rutin terjadi selama proses build.

X target memiliki dependensi yang dideklarasikan pada target Y jika ada batas dependensi dari X hingga Y dalam paket X.

Untuk build yang benar, grafik dependensi yang sebenarnya A harus merupakan 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 perkiraan A yang berlebihan.

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 setelah dependensi yang dideklarasikan transitif menjadi target yang dimiliki. Bazel memeriksa dependensi yang tidak ada dan melaporkan error, tetapi pemeriksaan ini tidak dapat diselesaikan dalam semua kasus.

Anda tidak perlu (dan tidak boleh) mencoba mencantumkan semuanya secara tidak langsung, meskipun diperlukan oleh A pada waktu eksekusi.

Selama build target X, alat build memeriksa seluruh cakupan transitif dependensi X untuk memastikan bahwa perubahan apa pun dalam target tersebut tercermin dalam hasil akhir, mem-build ulang perantara sesuai kebutuhan.

Sifat transitif dari dependensi menyebabkan kesalahan umum. Terkadang, kode dalam satu file dapat menggunakan kode yang disediakan oleh dependensi tidak langsung — tepi transitif tetapi tidak langsung dalam grafik dependensi yang dideklarasikan. Dependensi tidak langsung tidak muncul di file BUILD. Karena aturan tersebut tidak secara langsung bergantung pada penyedia, tidak ada cara untuk melacak perubahan, seperti yang ditunjukkan pada contoh linimasa berikut:

1. Dependensi yang dideklarasikan cocok dengan dependensi yang sebenarnya

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();
}
      
Grafik dependensi dideklarasikan dengan panah yang menghubungkan a, b, dan c
Grafik dependensi yang dideklarasikan
Grafik dependensi sebenarnya yang cocok dengan grafik dependensi
                  yang dideklarasikan dengan panah yang menghubungkan a, b, dan c
Grafik dependensi Aktual

Dependensi yang dideklarasikan lebih dari perkiraan dependensi yang sebenarnya. Tidak ada masalah.

2. Menambahkan dependensi yang tidak dideklarasikan

Bahaya laten diperkenalkan saat seseorang menambahkan kode ke a yang membuat dependensi sebenarnya langsung di c, tetapi lupa mendeklarasikannya di file build a/BUILD.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
Grafik dependensi dideklarasikan dengan panah yang menghubungkan a, b, dan c
Grafik dependensi yang dideklarasikan
Grafik dependensi sebenarnya dengan panah yang menghubungkan a, b, dan c. Sebuah panah kini juga menghubungkan A ke C. Ini tidak cocok dengan
                  grafik dependensi yang dideklarasikan
Grafik dependensi Aktual

Dependensi yang dideklarasikan tidak lagi mendekati dependensi yang sebenarnya. Hal ini mungkin dapat terjadi, karena penutupan transitif kedua grafik tersebut sama, tetapi menyamarkan masalah: a memiliki dependensi aktual tetapi tidak dideklarasikan pada c.

3. Perbedaan antara grafik dependensi yang dideklarasikan dan sebenarnya

Bahaya ditampilkan saat seseorang memfaktorkan ulang b sehingga tidak lagi bergantung pada c, yang secara tidak sengaja merusak a tanpa errornya sendiri.

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
Grafik dependensi dideklarasikan dengan panah yang menghubungkan a dan b.
                  b tidak lagi terhubung ke c, yang memutuskan koneksi a ke c
Grafik dependensi yang dideklarasikan
Grafik dependensi sebenarnya yang menunjukkan koneksi ke b dan c,
                  tetapi b tidak lagi terhubung ke c
Grafik dependensi Aktual

Grafik dependensi yang dideklarasikan sekarang menjadi perkiraan yang kurang dari dependensi yang sebenarnya, meskipun saat transitif ditutup; build kemungkinan akan gagal.

Masalah tersebut dapat dihindari dengan memastikan dependensi yang sebenarnya dari a ke c yang diperkenalkan pada 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-hal tersebut dijelaskan di bawah ini. 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. Detailnya dijelaskan dalam Encyclopedia Build.

srcs dependensi

File yang digunakan langsung oleh aturan atau 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 berjalan dengan benar. File data ini bukan kode sumber: file tersebut tidak memengaruhi cara mem-build target. Misalnya, pengujian unit dapat membandingkan output fungsi dengan konten 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 dalam direktori terpisah, tempat hanya file yang tercantum sebagai data yang tersedia. Jadi, jika biner/library/pengujian memerlukan beberapa file untuk dijalankan, tentukan (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 melihat file BUILD, Anda mungkin melihat bahwa beberapa label data merujuk ke direktori. Label ini diakhiri dengan /. atau / seperti dalam contoh berikut, yang tidak boleh Anda gunakan:

Tidak direkomendasikandata = ["//data/regression:unittest/."]

Tidak direkomendasikandata = ["testdata/."]

Tidak direkomendasikandata = ["testdata/"]

Cara ini tampaknya praktis, terutama untuk pengujian karena memungkinkan pengujian untuk menggunakan semua file data dalam direktori.

Namun, usahakan untuk tidak melakukannya. Untuk memastikan build ulang inkremental yang benar (dan eksekusi ulang pengujian) setelah perubahan, sistem build harus mengetahui serangkaian file lengkap yang merupakan input untuk build (atau pengujian). Saat Anda menentukan direktori, sistem build hanya akan melakukan build ulang 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 mencakupnya. Daripada menentukan direktori sebagai input ke sistem build, Anda harus menghitung kumpulan file yang ada di dalamnya, baik secara eksplisit maupun menggunakan fungsi glob(). (Gunakan ** untuk memaksa glob() menjadi rekursif.)

Direkomendasikandata = 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 yang eksplisit, atau penggunaan fungsi glob() 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 harus menggunakan beberapa file harus secara eksplisit mendeklarasikan dependensinya pada semua file. 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.

BANGUN file Visibilitas