Membuat program dengan Bazel

7/0.9/2.5. Lihat sumber Nightly {/3/}

Halaman ini membahas cara membuat program dengan Bazel, sintaksis perintah build, dan sintaksis pola target.

Panduan memulai

Untuk menjalankan Bazel, buka direktori workspace dasar atau subdirektorinya, lalu ketik bazel. Lihat build jika Anda perlu membuat ruang kerja baru.

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

Perintah yang tersedia

  • analyze-profile: Menganalisis data profil build.
  • aquery: Menjalankan kueri pada grafik tindakan pasca-analisis.
  • build: Membuat target yang ditentukan.
  • canonicalize-flags: Melakukan kanonikalisasi flag Bazel.
  • clean: Menghapus file output dan secara opsional menghentikan server.
  • cquery: Menjalankan kueri grafik dependensi pasca-analisis.
  • dump: Membuang status internal proses server Bazel.
  • help: Mencetak bantuan untuk perintah, atau indeks.
  • info: Menampilkan info runtime tentang server bazel.
  • fetch: Mengambil semua dependensi eksternal target.
  • mobile-install: Menginstal aplikasi di perangkat seluler.
  • query: Menjalankan kueri grafik dependensi.
  • run: Menjalankan target yang ditentukan.
  • shutdown: Menghentikan server Bazel.
  • test: Membangun dan menjalankan target pengujian yang ditentukan.
  • version: Mencetak informasi versi untuk Bazel.

Mendapatkan bantuan

  • bazel help command: Mencetak bantuan dan opsi untuk command.
  • bazel helpstartup_options: Opsi untuk Bazel yang menghosting JVM.
  • bazel helptarget-syntax: Menjelaskan sintaksis untuk menentukan target.
  • bazel help info-keys: Menampilkan daftar kunci yang digunakan oleh perintah info.

Alat bazel menjalankan banyak fungsi, yang disebut perintah. Yang paling sering digunakan adalah bazel build dan bazel test. Anda dapat menjelajahi pesan bantuan online menggunakan bazel help.

Membuat satu target

Sebelum dapat memulai build, Anda memerlukan ruang kerja. Ruang kerja adalah hierarki direktori yang berisi semua file sumber yang diperlukan untuk membangun aplikasi Anda. Bazel memungkinkan Anda membuat build dari volume yang sepenuhnya hanya baca.

Untuk membangun program dengan Bazel, ketik bazel build diikuti dengan target yang ingin Anda bangun.

bazel build //foo

Setelah mengeluarkan perintah untuk membangun //foo, Anda akan melihat output yang mirip dengan ini:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

Pertama, Bazel memuat semua paket dalam grafik dependensi target Anda. Ini mencakup dependensi yang dideklarasikan, file yang tercantum langsung dalam file BUILD target, dan dependensi transitif, file yang tercantum dalam file BUILD dependensi target Anda. Setelah mengidentifikasi semua dependensi, Bazel menganalisis dependensi tersebut untuk memastikan ketepatan dan membuat tindakan build. Terakhir, Bazel mengeksekusi compiler dan alat build lainnya.

Selama fase eksekusi build, Bazel akan mencetak pesan progres. Pesan progres mencakup langkah build saat ini (seperti, compiler atau linker) saat dimulai, dan jumlah yang selesai dibandingkan jumlah total tindakan build. Saat build dimulai, jumlah total tindakan sering kali meningkat saat Bazel menemukan seluruh grafik tindakan, tetapi jumlahnya stabil dalam beberapa detik.

Di akhir build, Bazel akan mencetak target mana yang diminta, apakah target tersebut berhasil dibuat, dan jika ya, di mana file output dapat ditemukan. Skrip yang menjalankan build dapat mengurai output ini dengan andal. Lihat --show_result untuk detail selengkapnya.

Jika Anda mengetik perintah yang sama lagi, build akan selesai lebih cepat.

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

Ini adalah null build. Karena tidak ada yang berubah, tidak ada paket yang perlu dimuat ulang dan tidak ada langkah build untuk dieksekusi. Jika ada perubahan di 'foo' atau dependensinya, Bazel akan mengeksekusi ulang beberapa tindakan build, atau menyelesaikan build inkremental.

Membuat beberapa target

Bazel memungkinkan berbagai cara untuk menentukan target yang akan dibuat. Jika digabungkan, ini dikenal sebagai pola target. Sintaksis ini digunakan dalam perintah seperti build, test, atau query.

Sementara label digunakan untuk menentukan target individual, seperti untuk mendeklarasikan dependensi dalam file BUILD, pola target Bazel menentukan beberapa target. Pola target adalah generalisasi sintaksis label untuk set target, menggunakan karakter pengganti. Dalam kasus yang paling sederhana, setiap label yang valid juga merupakan pola target yang valid, yang mengidentifikasi kumpulan yang berisi hanya satu target.

Semua pola target yang dimulai dengan // telah diselesaikan secara relatif terhadap ruang kerja saat ini.

//foo/bar:wiz Hanya satu target //foo/bar:wiz.
//foo/bar Setara dengan //foo/bar:bar.
//foo/bar:all Semua target aturan dalam paket foo/bar.
//foo/... Semua target aturan di semua paket di bawah direktori foo.
//foo/...:all Semua target aturan di semua paket di bawah direktori foo.
//foo/...:* Semua target (aturan dan file) di semua paket di bawah direktori foo.
//foo/...:all-targets Semua target (aturan dan file) di semua paket di bawah direktori foo.
//... Semua target aturan dalam paket di repositori utama. Tidak menyertakan target dari repositori eksternal.
//:all Semua target aturan di paket tingkat teratas, jika ada file `BUILD` di root ruang kerja.

Pola target yang tidak dimulai dengan // akan di-resolve secara relatif terhadap direktori kerja saat ini. Contoh ini mengasumsikan direktori kerja foo:

:foo Setara dengan //foo:foo.
bar:wiz Setara dengan //foo/bar:wiz.
bar/wiz Setara dengan:
  • //foo/bar/wiz:wiz jika foo/bar/wiz adalah paket
  • //foo/bar:wiz jika foo/bar adalah paket
  • Jika tidak, //foo:bar/wiz
bar:all Setara dengan //foo/bar:all.
:all Setara dengan //foo:all.
...:all Setara dengan //foo/...:all.
... Setara dengan //foo/...:all.
bar/...:all Setara dengan //foo/bar/...:all.

Secara default, symlink direktori diikuti untuk pola target rekursif, kecuali yang mengarah ke bawah basis output, seperti symlink praktis yang dibuat dalam direktori utama ruang kerja.

Selain itu, Bazel tidak mengikuti symlink saat mengevaluasi pola target rekursif di direktori mana pun yang berisi file dengan nama berikut: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/... adalah karakter pengganti pada paket, yang menunjukkan semua paket secara rekursif di bawah direktori foo (untuk semua root jalur paket). :all adalah karakter pengganti di atas target, yang cocok dengan semua aturan dalam sebuah paket. Keduanya dapat digabungkan, seperti dalam foo/...:all, dan jika kedua karakter pengganti digunakan, keduanya dapat disingkat menjadi foo/....

Selain itu, :* (atau :all-targets) adalah karakter pengganti yang cocok dengan setiap target dalam paket yang cocok, termasuk file yang biasanya tidak dibuat oleh aturan apa pun, seperti file _deploy.jar yang terkait dengan aturan java_binary.

Ini menyiratkan bahwa :* menunjukkan superset dari :all; meskipun berpotensi membingungkan, sintaksis ini memungkinkan karakter pengganti :all yang sudah dikenal digunakan untuk build standar, saat membangun target seperti _deploy.jar tidak diinginkan.

Selain itu, Bazel memungkinkan penggunaan garis miring, bukan tanda titik dua yang diperlukan oleh sintaksis label; cara ini sering kali praktis saat menggunakan perluasan nama file Bash. Misalnya, foo/bar/wiz setara dengan //foo/bar:wiz (jika ada paket foo/bar) atau dengan //foo:bar/wiz (jika ada paket foo).

Banyak perintah Bazel menerima daftar pola target sebagai argumen, dan semuanya mengikuti operator negasi awalan -. Ini dapat digunakan untuk mengurangi kumpulan target dari kumpulan yang ditentukan oleh argumen sebelumnya. Perhatikan bahwa ini berarti urutan itu penting. Misalnya,

bazel build foo/... bar/...

berarti "bangun semua target di bawah foo dan semua target di bawah bar", sedangkan

bazel build -- foo/... -foo/bar/...

berarti "build semua target di bawah foo kecuali yang berada di bawah foo/bar". (Argumen -- diperlukan untuk mencegah argumen berikutnya yang dimulai dengan - diinterpretasikan sebagai opsi tambahan.)

Penting untuk diketahui bahwa pengurangan target dengan cara ini tidak akan menjamin bahwa target tidak dibangun, karena mungkin merupakan dependensi target yang tidak dikurangi. Misalnya, jika ada target //foo:all-apis yang antara lain bergantung pada //foo/bar:api, target yang terakhir akan dibangun sebagai bagian dari pembuatan target tersebut.

Target dengan tags = ["manual"] tidak disertakan dalam pola target karakter pengganti (..., :*, :all, dll.) jika ditentukan dalam perintah seperti bazel build dan bazel test (tetapi keduanya termasuk dalam pola target karakter pengganti negatif, yaitu target tersebut akan dikurangi). Anda harus menentukan target pengujian dengan pola target yang eksplisit pada command line jika ingin Bazel mem-build/mengujinya. Sebaliknya, bazel query tidak melakukan pemfilteran semacam itu secara otomatis (yang akan menggagalkan tujuan bazel query).

Mengambil dependensi eksternal

Secara default, Bazel akan mendownload dan melakukan symlink dependensi eksternal selama build. Namun, hal ini mungkin tidak diinginkan, baik karena Anda ingin mengetahui kapan dependensi eksternal baru ditambahkan atau karena Anda ingin "mengambil data" dependensi (misalnya, sebelum penerbangan tempat Anda akan offline). Jika ingin mencegah dependensi baru ditambahkan selama build, Anda dapat menentukan flag --fetch=false. Perlu diperhatikan bahwa flag ini hanya berlaku untuk aturan repositori yang tidak mengarah ke direktori dalam sistem file lokal. Perubahan, misalnya, pada local_repository, new_local_repository, serta aturan repositori NDK dan Android SDK akan selalu berlaku, apa pun nilai --fetch .

Jika Anda melarang pengambilan selama build dan Bazel menemukan dependensi eksternal baru, build akan gagal.

Anda dapat mengambil dependensi secara manual dengan menjalankan bazel fetch. Jika tidak mengizinkan pengambilan selama build, Anda harus menjalankan bazel fetch:

  • Sebelum Anda membangun untuk pertama kalinya.
  • Setelah Anda menambahkan dependensi eksternal baru.

Setelah dijalankan, Anda tidak perlu menjalankannya lagi sampai file WORKSPACE berubah.

fetch mengambil daftar target yang dependensinya akan diambil. Misalnya, perintah ini akan mengambil dependensi yang diperlukan untuk membangun //foo:bar dan //bar:baz:

bazel fetch //foo:bar //bar:baz

Untuk mengambil semua dependensi eksternal untuk ruang kerja, jalankan:

bazel fetch //...

Dengan Bazel 7 atau yang lebih baru, jika Bzlmod diaktifkan, Anda juga dapat mengambil semua dependensi eksternal dengan menjalankan

bazel fetch

Anda tidak perlu menjalankan pengambilan bazel sama sekali jika memiliki semua alat yang digunakan (dari jar library hingga JDK itu sendiri) di root ruang kerja. Namun, jika Anda menggunakan apa pun di luar direktori ruang kerja, Bazel akan otomatis menjalankan bazel fetch sebelum menjalankan bazel build.

Cache repositori

Bazel mencoba menghindari pengambilan file yang sama beberapa kali, meskipun file yang sama diperlukan di ruang kerja yang berbeda, atau jika definisi repositori eksternal berubah, tetapi masih memerlukan file yang sama untuk didownload. Untuk melakukannya, bazel meng-cache semua file yang didownload di cache repositori yang, secara default, terletak di ~/.cache/bazel/_bazel_$USER/cache/repos/v1/. Lokasi dapat diubah dengan opsi --repository_cache. Cache digunakan bersama oleh semua ruang kerja dan versi bazel yang terinstal. Entri diambil dari cache jika Bazel tahu pasti bahwa file tersebut memiliki salinan file yang benar, yaitu jika permintaan download memiliki jumlah SHA256 file yang ditentukan dan file dengan hash tersebut ada di cache. Jadi, menentukan hash untuk setiap file eksternal bukan hanya ide yang baik dari segi keamanan, tetapi juga membantu menghindari download yang tidak perlu.

Setelah setiap cache ditemukan, waktu modifikasi file dalam cache akan diperbarui. Dengan cara ini, penggunaan terakhir file dalam direktori cache dapat ditentukan dengan mudah, misalnya untuk membersihkan cache secara manual. Cache tidak pernah dibersihkan secara otomatis, karena mungkin berisi salinan file yang tidak lagi tersedia di upstream.

[Tidak digunakan lagi] Direktori file distribusi

Tidak digunakan lagi: Penggunaan cache repositori lebih disarankan untuk mencapai build offline.

Direktori distribusi adalah mekanisme Bazel lain untuk menghindari download yang tidak perlu. Bazel mencari direktori distribusi sebelum cache repositori. Perbedaan utamanya adalah direktori distribusi memerlukan persiapan manual.

Dengan menggunakan opsi --distdir=/path/to-directory, Anda dapat menentukan direktori hanya baca tambahan untuk mencari file, bukan mengambilnya. File diambil dari direktori tersebut jika nama file sama dengan nama dasar URL, dan selain itu, hash file sama dengan yang ditentukan dalam permintaan download. Proses ini hanya berfungsi jika hash file ditentukan dalam deklarasi WORKSPACE.

Meskipun kondisi pada nama file tidak diperlukan untuk ketepatan, jumlah ini akan mengurangi jumlah file kandidat menjadi satu per direktori yang ditentukan. Dengan cara ini, menentukan direktori file distribusi akan tetap efisien, meskipun jumlah file dalam direktori tersebut menjadi banyak.

Menjalankan Bazel di lingkungan dengan air gap

Agar ukuran biner Bazel tetap kecil, dependensi implisit Bazel diambil melalui jaringan saat dijalankan untuk pertama kalinya. Dependensi implisit ini berisi toolchain dan aturan yang mungkin tidak diperlukan untuk semua orang. Misalnya, alat Android tidak dipaketkan dan hanya diambil saat membuat project Android.

Namun, dependensi implisit ini dapat menyebabkan masalah saat menjalankan Bazel di lingkungan dengan air gap, meskipun Anda telah mem-vendor semua dependensi eksternal. Untuk mengatasinya, Anda dapat menyiapkan cache repositori (dengan Bazel 7 atau yang lebih baru) atau direktori distribusi (dengan Bazel sebelum versi 7) yang berisi dependensi tersebut pada komputer yang memiliki akses jaringan, lalu mentransfernya ke lingkungan airgapp dengan pendekatan offline.

Cache repositori (dengan Bazel 7 atau yang lebih baru)

Untuk menyiapkan cache repositori, gunakan tanda --repository_cache. Anda perlu melakukan ini sekali untuk setiap versi biner Bazel baru, karena dependensi implisit dapat berbeda untuk setiap rilis.

Untuk mengambil dependensi tersebut di luar lingkungan airgap Anda, buat ruang kerja kosong terlebih dahulu:

mkdir empty_workspace && cd empty_workspace
touch MODULE.bazel
touch WORKSPACE

Untuk mengambil dependensi Bzlmod bawaan, jalankan

bazel fetch --repository_cache="path/to/repository/cache"

Jika Anda masih mengandalkan file WORKSPACE lama, untuk mengambil dependensi WORKSPACE bawaan, jalankan

bazel sync --repository_cache="path/to/repository/cache"

Terakhir, saat Anda menggunakan Bazel di lingkungan dengan airgapp, teruskan flag --repository_cache yang sama. Untuk memudahkan, Anda dapat menambahkannya sebagai entri .bazelrc:

common --repository_cache="path/to/repository/cache"

Selain itu, Anda mungkin juga perlu meng-clone BCR secara lokal dan menggunakan tanda --registry untuk mengarahkan salinan lokal Anda untuk mencegah Bazel mengakses BCR melalui internet. Tambahkan baris berikut ke .bazelrc Anda:

common --registry="path/to/local/bcr/registry"
Direktori distribusi (dengan Bazel sebelum versi 7)

Untuk menyiapkan direktori distribusi, gunakan tanda --distdir. Anda perlu melakukan ini sekali untuk setiap versi biner Bazel baru, karena dependensi implisit dapat berbeda untuk setiap rilis.

Untuk mem-build dependensi ini di luar lingkungan airgapp, pertama-tama periksa hierarki sumber Bazel di versi yang tepat:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

Kemudian, build tarball yang berisi dependensi runtime implisit untuk versi Bazel spesifik tersebut:

bazel build @additional_distfiles//:archives.tar

Ekspor tarball ini ke direktori yang dapat disalin ke lingkungan dengan airgapp Anda. Perhatikan flag --strip-components, karena --distdir bisa sangat rumit dengan level bertingkat direktori:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

Terakhir, saat Anda menggunakan Bazel di lingkungan dengan airgapp, teruskan flag --distdir yang menunjuk ke direktori. Untuk memudahkan, Anda dapat menambahkannya sebagai entri .bazelrc:

build --distdir=path/to/directory

Konfigurasi build dan kompilasi silang

Semua input yang menentukan perilaku dan hasil build tertentu dapat dibagi menjadi dua kategori yang berbeda. Jenis pertama adalah informasi intrinsik yang disimpan dalam file BUILD project Anda: aturan build, nilai atributnya, dan kumpulan lengkap dependensi transitifnya. Jenis kedua adalah data eksternal atau lingkungan, yang disediakan oleh pengguna atau oleh alat build: pilihan arsitektur target, opsi kompilasi dan penautan, serta opsi konfigurasi toolchain lainnya. Kami menyebut kumpulan lengkap data lingkungan sebagai konfigurasi.

Dalam build tertentu, mungkin ada lebih dari satu konfigurasi. Pertimbangkan kompilasi silang, tempat Anda mem-build file //foo:bin yang dapat dieksekusi untuk arsitektur 64-bit, tetapi workstation Anda adalah mesin 32-bit. Jelas, build akan memerlukan pembuatan //foo:bin menggunakan toolchain yang mampu membuat file yang dapat dieksekusi 64-bit, tetapi sistem build juga harus membangun berbagai alat yang digunakan selama build itu sendiri—misalnya alat yang dibangun dari sumber, kemudian digunakan di, misalnya, genrule—dan alat ini harus dibangun untuk berjalan di workstation Anda. Dengan demikian, kita dapat mengidentifikasi dua konfigurasi: konfigurasi exec, yang digunakan untuk membuat alat yang berjalan selama build, dan konfigurasi target (atau konfigurasi permintaan, tetapi kami mengatakan "konfigurasi target" lebih sering meskipun kata tersebut sudah memiliki banyak arti), yang digunakan untuk membangun biner yang pada akhirnya Anda minta.

Biasanya, ada banyak library yang merupakan prasyarat dari target build yang diminta (//foo:bin) dan satu atau beberapa alat exec, misalnya beberapa library dasar. Library tersebut harus dibangun dua kali, sekali untuk konfigurasi exec, dan sekali untuk konfigurasi target. Bazel menangani bahwa kedua varian telah dibuat, dan file turunan disimpan terpisah untuk menghindari gangguan; biasanya target tersebut dapat di-build secara serentak, karena tidak bergantung satu sama lain. Jika Anda melihat pesan progres yang menunjukkan bahwa target tertentu sedang dibangun dua kali, kemungkinan besar itu adalah penjelasannya.

Konfigurasi exec berasal dari konfigurasi target sebagai berikut:

  • Gunakan versi Crosstool (--crosstool_top) yang sama seperti yang ditentukan dalam konfigurasi permintaan, kecuali jika --host_crosstool_top ditentukan.
  • Gunakan nilai --host_cpu untuk --cpu (default: k8).
  • Gunakan nilai yang sama dari opsi ini seperti yang ditentukan dalam konfigurasi permintaan: --compiler, --use_ijars, dan jika --host_crosstool_top digunakan, nilai --host_cpu akan digunakan untuk mencari default_toolchain di Crosstool (mengabaikan --compiler) untuk konfigurasi exec.
  • Gunakan nilai --host_javabase untuk --javabase
  • Gunakan nilai --host_java_toolchain untuk --java_toolchain
  • Gunakan build yang dioptimalkan untuk kode C++ (-c opt).
  • Jangan buat informasi proses debug (--copt=-g0).
  • Menghapus informasi debug dari file yang dapat dieksekusi dan library bersama (--strip=always).
  • Tempatkan semua file turunan di lokasi khusus, yang berbeda dengan yang digunakan oleh konfigurasi permintaan yang memungkinkan.
  • Menyembunyikan stempel biner dengan data build (lihat opsi --embed_*).
  • Semua nilai lainnya tetap dalam setelan default.

Ada banyak alasan mengapa mungkin lebih baik untuk memilih konfigurasi exec yang berbeda dari konfigurasi permintaan. Yang terpenting:

Pertama, dengan menggunakan biner yang dihilangkan dan dioptimalkan, Anda mengurangi waktu yang dihabiskan untuk menautkan dan mengeksekusi alat, kapasitas disk yang digunakan oleh alat, dan waktu I/O jaringan dalam build terdistribusi.

Kedua, dengan memisahkan konfigurasi exec dan permintaan di semua build, Anda akan menghindari build ulang yang sangat mahal yang akan dihasilkan dari perubahan kecil pada konfigurasi permintaan (seperti mengubah opsi linker), seperti yang dijelaskan sebelumnya.

Memperbaiki build ulang inkremental

Salah satu tujuan utama project Bazel adalah memastikan build ulang inkremental yang tepat. Alat build sebelumnya, terutama yang berbasis Make, membuat beberapa asumsi tidak wajar dalam implementasi build inkremental.

Pertama, stempel waktu file tersebut bertambah secara monoton. Meskipun ini adalah kasus standar, sangat mudah untuk menghindari asumsi ini; menyinkronkan ke revisi file sebelumnya menyebabkan waktu modifikasi file tersebut berkurang; Sistem berbasis Make tidak akan dibangun ulang.

Secara lebih umum, saat mendeteksi perubahan pada file, Make tidak mendeteksi perubahan pada perintah. Jika Anda mengubah opsi yang diteruskan ke compiler pada langkah build tertentu, Make tidak akan menjalankan ulang compiler, dan Anda harus menghapus output yang tidak valid dari build sebelumnya secara manual menggunakan make clean.

Selain itu, Make tidak kuat terhadap penghentian salah satu subprosesnya yang gagal setelah subproses tersebut mulai menulis ke file outputnya. Meskipun eksekusi Make saat ini akan gagal, pemanggilan Make berikutnya akan secara membabi buta akan mengasumsikan bahwa file output yang terpotong valid (karena lebih baru dari inputnya), dan tidak akan dibuat ulang. Demikian pula, jika proses {i>Make<i} terhenti, situasi serupa dapat terjadi.

Bazel menghindari asumsi ini, dan lain-lain. Bazel menyimpan database dari semua pekerjaan yang telah dilakukan sebelumnya, dan hanya akan menghilangkan langkah build jika menemukan bahwa set file input (dan stempel waktunya) ke langkah build tersebut, dan perintah kompilasi untuk langkah build tersebut, sama persis dengan yang ada di database, dan, bahwa kumpulan file output (dan stempel waktunya) untuk entri database sama persis dengan stempel waktu file pada disk. Setiap perubahan pada file input atau file output, atau pada perintah itu sendiri, akan menyebabkan eksekusi ulang langkah build.

Manfaat bagi pengguna build inkremental yang benar adalah: lebih sedikit waktu yang terbuang karena kebingungan. (Selain itu, waktu yang dihabiskan untuk menunggu build ulang yang disebabkan oleh penggunaan make clean, baik diperlukan maupun pencegahan, juga menjadi lebih sedikit.)

Membangun konsistensi dan build inkremental

Secara formal, kita menentukan status build sebagai konsisten jika semua file output yang diharapkan ada, dan kontennya sudah benar, seperti yang ditentukan oleh langkah-langkah atau aturan yang diperlukan untuk membuatnya. Saat Anda mengedit file sumber, status build dikatakan tidak konsisten, dan tetap tidak konsisten sampai Anda menjalankan alat build lagi hingga berhasil diselesaikan. Kami mendeskripsikan situasi ini sebagai inkonsistensi yang tidak stabil, karena hanya bersifat sementara, dan konsistensi dipulihkan dengan menjalankan alat build.

Ada jenis inkonsistensi lain yang merusak: inkonsistensi stabil. Jika build mencapai status tidak konsisten yang stabil, pemanggilan alat build yang berhasil secara berulang tidak akan memulihkan konsistensi: build terhambat", dan output-nya tetap salah. Status tidak konsisten stabil adalah alasan utama pengguna Make (dan alat build lainnya) mengetik make clean. Mengetahui bahwa alat build telah gagal dengan cara ini (dan kemudian memulihkannya) dapat memakan waktu dan sangat menjengkelkan.

Secara konseptual, cara paling sederhana untuk mencapai build yang konsisten adalah dengan membuang semua output build sebelumnya dan memulai lagi: menjadikan setiap build menjadi build yang bersih. Pendekatan ini jelas terlalu memakan waktu untuk dilakukan secara praktis (kecuali mungkin bagi engineer rilis). Oleh karena itu, agar berguna, alat build harus dapat menjalankan build inkremental tanpa mengorbankan konsistensi.

Analisis dependensi inkremental yang benar itu sulit, dan seperti yang dijelaskan di atas, banyak alat build lainnya berfungsi buruk untuk menghindari status tidak konsisten yang stabil selama build inkremental. Sebaliknya, Bazel menawarkan jaminan berikut: setelah pemanggilan alat build berhasil tanpa melakukan pengeditan, build akan berada dalam status konsisten. (Jika Anda mengedit file sumber selama proses build, Bazel tidak menjamin konsistensi hasil build saat ini. Namun, cara ini menjamin bahwa hasil build berikutnya akan memulihkan konsistensi.)

Seperti semua jaminan, ada beberapa syaratnya: ada beberapa cara umum untuk mendapatkan status tidak konsisten yang stabil dengan Bazel. Kami tidak akan menjamin untuk menyelidiki masalah yang timbul dari upaya yang disengaja untuk menemukan bug dalam analisis dependensi inkremental, tetapi kami akan menyelidiki dan melakukan yang terbaik untuk memperbaiki semua status tidak konsisten yang stabil yang timbul dari penggunaan normal atau "wajar" atas alat build.

Jika Anda mendeteksi keadaan tidak konsisten yang stabil dengan Bazel, laporkan bug.

Eksekusi dengan sandbox

Bazel menggunakan sandbox untuk menjamin bahwa tindakan berjalan secara hermetis dan benar. Bazel menjalankan spawns (berbicara longgar: tindakan) di sandbox yang hanya berisi sekumpulan file minimal yang diperlukan alat untuk melakukan tugasnya. Saat ini, sandboxing berfungsi di Linux 3.12 atau yang lebih baru dengan opsi CONFIG_USER_NS diaktifkan, dan juga di macOS 10.11 atau yang lebih baru.

Bazel akan mencetak peringatan jika sistem Anda tidak mendukung sandbox untuk memberi tahu Anda tentang fakta bahwa build tidak dijamin bersifat hermetis dan dapat memengaruhi sistem host dengan cara yang tidak diketahui. Untuk menonaktifkan peringatan ini, Anda dapat meneruskan flag --ignore_unsupported_sandboxing ke Bazel.

Pada beberapa platform, seperti node cluster Google Kubernetes Engine atau Debian, namespace pengguna dinonaktifkan secara default karena masalah keamanan. Ini dapat diperiksa dengan melihat file /proc/sys/kernel/unprivileged_userns_clone: jika ada dan berisi 0, namespace pengguna dapat diaktifkan dengan sudo sysctl kernel.unprivileged_userns_clone=1.

Dalam beberapa kasus, sandbox Bazel gagal menjalankan aturan karena penyiapan sistem. Gejala umumnya adalah kegagalan yang menghasilkan pesan yang mirip dengan namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory. Jika demikian, coba nonaktifkan sandbox untuk genrules dengan --strategy=Genrule=standalone dan untuk aturan lainnya dengan --spawn_strategy=standalone. Selain itu, laporkan bug di issue tracker kami dan sebutkan distribusi Linux yang Anda gunakan agar kami dapat menyelidiki dan memberikan perbaikan pada rilis berikutnya.

Fase build

Di Bazel, build terjadi dalam tiga fase yang berbeda; sebagai pengguna, memahami perbedaan di antara keduanya akan memberikan insight tentang opsi yang mengontrol build (lihat di bawah).

Fase pemuatan

Yang pertama adalah memuat di mana semua file BUILD yang diperlukan untuk target awal, dan penutupan transitif dependensinya, dimuat, diurai, dievaluasi, dan di-cache.

Untuk build pertama setelah server Bazel dimulai, fase pemuatan biasanya memerlukan waktu beberapa detik saat file BUILD dimuat dari sistem file. Pada build berikutnya, terutama jika tidak ada file BUILD yang berubah, pemuatan terjadi dengan sangat cepat.

Error yang dilaporkan selama fase ini meliputi: paket tidak ditemukan, target tidak ditemukan, kesalahan leksis dan tata bahasa dalam file BUILD, dan error evaluasi.

Fase analisis

Fase kedua, analisis, melibatkan analisis semantik dan validasi setiap aturan build, konstruksi grafik dependensi build, dan menentukan secara persis tugas apa yang akan dilakukan dalam setiap langkah build.

Seperti pemuatan, analisis juga memerlukan waktu beberapa detik saat dihitung secara keseluruhan. Namun, Bazel menyimpan grafik dependensi dari satu build ke build berikutnya dalam cache dan hanya menganalisis ulang apa yang diperlukan, yang dapat membuat build inkremental menjadi sangat cepat jika paket tidak berubah sejak build sebelumnya.

Error yang dilaporkan pada tahap ini mencakup: dependensi yang tidak pantas, input yang tidak valid ke aturan, dan semua pesan error khusus aturan.

Fase pemuatan dan analisis berlangsung cepat karena Bazel menghindari I/O file yang tidak perlu pada tahap ini, dan hanya membaca file BUILD untuk menentukan pekerjaan yang akan dilakukan. Hal ini sesuai dengan desain dan menjadikan Bazel sebagai fondasi yang baik untuk alat analisis, seperti perintah query dari Bazel, yang diimplementasikan di atas fase pemuatan.

Fase eksekusi

Fase ketiga dan terakhir dari build adalah eksekusi. Fase ini memastikan bahwa output dari setiap langkah dalam build konsisten dengan inputnya, sehingga menjalankan ulang alat kompilasi/penautan/dll. sesuai kebutuhan. Pada langkah ini, build menghabiskan sebagian besar waktunya, mulai dari beberapa detik hingga lebih dari satu jam untuk build besar. Error yang dilaporkan selama fase ini mencakup: tidak ada file sumber, error dalam alat yang dijalankan oleh beberapa tindakan build, atau kegagalan alat untuk menghasilkan rangkaian output yang diharapkan.