Dependensi

Laporkan masalah Lihat sumber Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Target A bergantung pada target B jika B diperlukan oleh A pada waktu build atau eksekusi. Relasi bergantung pada memunculkan Directed Acyclic Graph (DAG) pada 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 menjadi dependensinya melalui jalur dengan panjang apa pun dalam grafik.

Sebenarnya, 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 berguna untuk pembahasan di bawah.

Dependensi aktual dan yang dideklarasikan

Target X sebenarnya bergantung pada target Y jika Y harus ada, dibuat, dan diupdate agar X dapat dibuat dengan benar. Dibangun 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 tepi dependensi dari X ke Y dalam paket X.

Untuk build yang benar, grafik dependensi sebenarnya 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 overaproksimasi dari A.

Penulis file BUILD harus secara eksplisit mendeklarasikan semua dependensi langsung yang sebenarnya untuk setiap aturan ke sistem build, dan tidak lebih.

Kegagalan untuk mematuhi prinsip ini menyebabkan perilaku yang tidak terdefinisi: build mungkin gagal, tetapi yang lebih buruk, build mungkin bergantung pada beberapa operasi sebelumnya, atau pada dependensi transitif yang kebetulan dimiliki target. 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 semua yang diimpor secara tidak langsung, meskipun diperlukan oleh A pada waktu eksekusi.

Selama build target X, alat build memeriksa seluruh penutupan transitif dependensi X untuk memastikan bahwa setiap perubahan pada target tersebut tercermin dalam hasil akhir, membangun ulang perantara sesuai kebutuhan.

Sifat transitif dependensi menyebabkan kesalahan umum. Terkadang, kode dalam satu file dapat menggunakan kode yang disediakan oleh dependensi tidak langsung — tepi transitif, tetapi bukan tepi 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 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 yang 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 sebenarnya

Dependensi yang dideklarasikan memperkirakan dependensi 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();
      
 
Grafik dependensi yang dideklarasikan dengan panah yang menghubungkan a, b, dan c
Grafik dependensi yang dideklarasikan
Grafik dependensi sebenarnya dengan panah yang menghubungkan a, b, dan c. Panah
                  kini menghubungkan A ke C juga. Hal ini tidak sesuai dengan
                  grafik dependensi yang dideklarasikan
Grafik dependensi sebenarnya

Dependensi yang dideklarasikan tidak lagi memperkirakan secara berlebihan dependensi sebenarnya. Build ini mungkin berhasil, karena penutupan transitif dari kedua grafik sama, tetapi menyembunyikan masalah: a memiliki dependensi aktual, tetapi tidak dideklarasikan pada c.

3. Perbedaan antara grafik dependensi yang dideklarasikan dan yang sebenarnya

Bahaya ini 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 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 sebenarnya

Grafik dependensi yang dideklarasikan sekarang merupakan perkiraan yang lebih rendah dari dependensi sebenarnya, bahkan saat ditutup secara transitif; build kemungkinan akan gagal.

Masalah ini dapat dihindari dengan memastikan bahwa dependensi sebenarnya dari a ke c yang diperkenalkan di Langkah 2 dinyatakan dengan benar dalam file BUILD.

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 detail selengkapnya, lihat Atribut umum untuk semua aturan.

Banyak aturan juga memiliki atribut tambahan untuk jenis dependensi khusus aturan, misalnya, compiler atau resources. Hal ini dijelaskan secara mendetail dalam Build Encyclopedia.

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. Oleh karena itu, 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 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 berikut, yang tidak boleh Anda gunakan:

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

Tidak direkomendasikandata = ["testdata/."]

Tidak direkomendasikandata = ["testdata/"]

Hal ini tampak praktis, terutama untuk pengujian karena memungkinkan pengujian menggunakan semua file data dalam direktori.

Namun, usahakan untuk tidak melakukannya. Untuk memastikan pembangunan ulang inkremental yang benar (dan eksekusi ulang pengujian) setelah perubahan, sistem build harus mengetahui kumpulan lengkap file yang menjadi input ke build (atau pengujian). Saat Anda menentukan direktori, sistem build hanya melakukan pembangunan 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 yang melampirkan. Daripada menentukan direktori sebagai input ke sistem build, Anda harus mencantumkan 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 label direktori digunakan. Misalnya, jika direktori testdata berisi file yang namanya tidak sesuai dengan sintaksis label, maka enumerasi file secara eksplisit, atau penggunaan fungsi glob() akan menghasilkan error label yang tidak valid. Dalam hal ini, Anda harus menggunakan label direktori, tetapi berhati-hatilah terhadap risiko terkait pembangunan ulang yang salah yang dijelaskan di atas.

Jika Anda harus menggunakan label direktori, ingatlah 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 menyatakan ketergantungannya 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