Buat program dengan Bazel

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.
Laporkan masalah Lihat sumber

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

Panduan memulai

Untuk menjalankan Bazel, buka direktori workspace dasar Anda atau subdirektorinya dan ketik bazel. Lihat build jika 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: Kanoniskan tanda Bazel.
  • clean: Menghapus file output dan secara opsional menghentikan server.
  • cquery: Menjalankan kueri grafik dependensi pascaanalisis.
  • dump: Mengeluarkan 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: Membuat 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 JVM yang menghosting Bazel.
  • bazel helptarget-syntax: Menjelaskan sintaksis untuk menentukan target.
  • bazel help info-keys: Menampilkan daftar kunci yang digunakan oleh perintah info.

Alat bazel melakukan 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 mem-build aplikasi. Dengan Bazel, Anda dapat menjalankan build dari volume yang hanya dapat dibaca.

Untuk mem-build program dengan Bazel, ketik bazel build diikuti dengan target yang ingin Anda build.

bazel build //foo

Setelah mengeluarkan perintah untuk mem-build //foo, Anda akan melihat output yang serupa 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 termasuk dependensi yang dideklarasikan, file yang tercantum langsung dalam file BUILD target, dan dependensi transitif, file yang tercantum dalam file BUILD dari dependensi target Anda. Setelah mengidentifikasi semua dependensi, Bazel menganalisis kebenaran tersebut dan membuat tindakan build. Terakhir, Bazel menjalankan compiler dan alat lain pada build.

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

Di akhir build, Bazel mencetak target mana yang diminta, apakah berhasil atau tidak, dan jika ya, tempat file output dapat ditemukan. Skrip yang menjalankan build dapat mengurai output ini dengan andal; lihat --show_result untuk detail selengkapnya.

Jika Anda mengetik kembali perintah yang sama, 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 build null. Karena tidak ada yang berubah, tidak ada paket untuk dimuat ulang dan tidak ada langkah build untuk dieksekusi. Jika sesuatu berubah dalam 'foo' atau dependensinya, Bazel akan mengeksekusi ulang beberapa tindakan build, atau menyelesaikan build inkremental.

Membuat beberapa target

Bazel memungkinkan sejumlah cara untuk menentukan target yang akan dibuat. Secara kolektif, 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 kumpulan target, menggunakan karakter pengganti. Dalam kasus yang paling sederhana, setiap label yang valid juga merupakan pola target yang valid, yang mengidentifikasi kumpulan satu target secara tepat.

Semua pola target yang dimulai dengan // di-resolve 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 dalam semua paket di bawah direktori foo.
//foo/...:all Semua target aturan dalam semua paket di bawah direktori foo.
//foo/...:* Semua target (aturan dan file) di semua paket dalam direktori foo.
//foo/...:all-targets Semua target (aturan dan file) di semua paket dalam direktori foo.
//... Semua target dalam paket di ruang kerja. Jumlah ini tidak termasuk target dari repositori eksternal.
//:all Semua target dalam paket level teratas, jika ada file 'BUILD' di root ruang kerja.

Pola target yang tidak dimulai dengan // di-resolve 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
  • //foo:bar/wiz sebaliknya
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 di direktori utama ruang kerja.

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

foo/... adalah karakter pengganti atas paket, yang menunjukkan semua paket secara rekursif di bawah direktori foo (untuk semua root jalur paket). :all adalah karakter pengganti pada target, dan cocok dengan semua aturan dalam sebuah paket. Keduanya dapat digabungkan, seperti dalam foo/...:all, dan saat kedua karakter pengganti digunakan, ini 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 _deploy.jar file 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 target build seperti _deploy.jar tidak diinginkan.

Selain itu, Bazel mengizinkan garis miring yang digunakan, bukan titik dua, yang diperlukan oleh sintaksis label. Hal ini sering kali mudah dilakukan jika menggunakan perluasan nama file Bash. Misalnya, foo/bar/wiz setara dengan //foo/bar:wiz (jika ada paket foo/bar) atau //foo:bar/wiz (jika ada paket foo).

Banyak perintah Bazel menerima daftar pola target sebagai argumen, dan semuanya menerima operator negasi awalan -. Hal ini dapat digunakan untuk mengurangi kumpulan target dari kumpulan yang ditetapkan oleh argumen sebelumnya. Perlu diperhatikan bahwa ini berarti pesanan penting. Misalnya,

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

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

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

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

Penting untuk diperhatikan bahwa mengurangi target dengan cara ini tidak akan menjamin bahwa target tidak dibuat, karena mungkin karena dependensi target yang tidak dikurangkan. Misalnya, jika ada //foo:all-apis target yang antara lain bergantung pada //foo/bar:api, maka target yang kedua akan dibuat sebagai bagian dari pembuatan class pertama.

Target dengan tags = ["manual"] tidak disertakan dalam pola target karakter pengganti (..., :*, :all, dll.) saat ditentukan dalam perintah seperti bazel build dan bazel test; Anda harus menentukan target pengujian tersebut dengan pola target eksplisit pada command line jika Anda ingin Bazel membuat/mengujinya. Sebaliknya, bazel query tidak otomatis melakukan pemfilteran tersebut (yang akan mengalahkan tujuan bazel query).

Mengambil dependensi eksternal

Secara default, Bazel akan mendownload dan symlink dependensi eksternal selama build. Namun, hal ini tidak diinginkan, baik karena Anda ingin mengetahui kapan dependensi eksternal baru ditambahkan atau karena Anda ingin "melakukan pengambilan data" dependensi (misalnya, sebelum penerbangan yang akan berlangsung offline). Jika tidak ingin dependensi baru ditambahkan selama proses build, Anda dapat menentukan flag --fetch=false. Perhatikan bahwa tanda ini hanya berlaku untuk aturan repositori yang tidak mengarah ke direktori di sistem file lokal. Perubahan, misalnya, pada local_repository, new_local_repository serta aturan repositori Android SDK dan NDK akan selalu berlaku terlepas dari nilai --fetch .

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

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

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

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

fetch mengambil daftar target untuk mengambil dependensi. Misalnya, ini akan mengambil dependensi yang diperlukan untuk mem-build //foo:bar dan //bar:baz:

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

Untuk mengambil semua dependensi eksternal untuk ruang kerja, jalankan:

bazel fetch //...

Anda tidak perlu menjalankan pengambilan bazel sama sekali jika Anda memiliki semua alat yang digunakan (dari stoples library hingga JDK itu sendiri) di bawah root ruang kerja Anda. 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 untuk tidak mengambil 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 men-cache semua file yang didownload dalam cache repositori, yang secara default berada 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 bahwa file tersebut memiliki salinan file yang benar, yaitu, jika permintaan download memiliki jumlah SHA256 dari file yang ditentukan dan file dengan hash tersebut berada dalam cache. Jadi, menentukan hash untuk setiap file eksternal bukan hanya ide yang bagus dari perspektif keamanan, tetapi juga membantu menghindari download yang tidak diperlukan.

Setelah setiap cache ditemukan, waktu modifikasi file dalam cache akan diperbarui. Dengan cara ini, penggunaan terakhir file di 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.

Direktori file distribusi

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

Dengan 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. Ini hanya berfungsi jika hash file ditentukan dalam deklarasi WORKSPACE.

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

Menjalankan Bazel di lingkungan yang celah udara

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

Namun, dependensi implisit ini dapat menyebabkan masalah saat menjalankan Bazel di lingkungan yang memiliki celah udara, meskipun Anda telah membuat vendor semua dependensi WORKSPACE. Untuk mengatasi hal itu, Anda dapat menyiapkan direktori distribusi yang berisi dependensi tersebut pada mesin dengan akses jaringan, lalu mentransfernya ke lingkungan airgap dengan pendekatan offline.

Untuk menyiapkan direktori distribusi, gunakan flag --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 yang keliru, periksa terlebih dahulu 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 tertentu tersebut:

bazel build @additional_distfiles//:archives.tar

Ekspor tarball ini ke direktori yang dapat disalin ke lingkungan Airgap. Perhatikan tanda --strip-components, karena --distdir dapat cukup rumit dengan tingkat hierarki direktori:

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

Terakhir, saat Anda menggunakan Bazel di lingkungan Airgap, teruskan flag --distdir yang mengarah ke direktori tersebut. 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, kompilasi dan opsi penautan, serta opsi konfigurasi toolchain lainnya. Kami menyebut kumpulan lengkap data lingkungan sebagai konfigurasi.

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

Biasanya, ada banyak library yang merupakan prasyarat baik dari target build (//foo:bin) yang diminta maupun satu atau beberapa alat host, misalnya beberapa library dasar. Library tersebut harus di-build dua kali, satu kali untuk konfigurasi host, dan satu lagi untuk konfigurasi target. Bazel menangani memastikan bahwa kedua varian dibuat, dan bahwa file turunan disimpan terpisah untuk menghindari interferensi; biasanya target tersebut dapat di-build secara serentak, karena keduanya tidak saling bergantung. Jika Anda melihat pesan progres yang menunjukkan bahwa target tertentu sedang dibuat dua kali, kemungkinan besar ini adalah penjelasannya.

Bazel menggunakan salah satu dari dua cara untuk memilih konfigurasi host, berdasarkan opsi --distinct_host_configuration. Opsi boolean ini agak halus, dan setelan ini dapat meningkatkan (atau memperburuk) kecepatan build Anda.

--distinct_host_configuration=false

Jika opsi ini disetel ke salah, konfigurasi host dan permintaan akan sama: semua alat yang diperlukan selama proses build akan dibuat dengan cara yang sama persis seperti program target. Setelan ini berarti tidak ada library yang perlu di-build dua kali selama satu build.

Namun, hal ini berarti perubahan pada konfigurasi permintaan Anda juga memengaruhi konfigurasi host, yang menyebabkan semua alat dibuat ulang, lalu semua yang bergantung pada output alat akan dibuat ulang. Dengan demikian, misalnya, hanya mengubah opsi penaut antara build dapat menyebabkan semua alat ditautkan kembali, lalu semua tindakan yang menggunakannya akan dijalankan kembali, dan sebagainya, sehingga menghasilkan build ulang yang sangat besar.

--distinct_host_configuration=true (default)

Jika opsi ini benar, alih-alih menggunakan konfigurasi yang sama untuk host dan permintaan, konfigurasi host yang sepenuhnya berbeda akan digunakan. Konfigurasi host diperoleh 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 host.
  • Gunakan nilai --host_javabase untuk --javabase
  • Gunakan nilai --host_java_toolchain untuk --java_toolchain
  • Menggunakan build yang dioptimalkan untuk kode C++ (-c opt).
  • Tidak ada 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, berbeda dari yang digunakan oleh konfigurasi permintaan yang mungkin.
  • Menyembunyikan stempel biner dengan data build (lihat opsi --embed_*).
  • Nilai lainnya tetap default.

Ada banyak alasan mengapa lebih baik memilih konfigurasi host yang berbeda dari konfigurasi permintaan. Beberapa terlalu esoterik untuk disebutkan di sini, tetapi dua di antaranya layak untuk ditunjukkan.

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

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

Meskipun demikian, untuk build tertentu, opsi ini mungkin halangan. Secara khusus, build yang jarang mengubah konfigurasi (terutama build Java tertentu), dan build yang memiliki jumlah kode yang harus dibuat di konfigurasi host maupun target yang besar, mungkin tidak bermanfaat.

Membuat ulang build inkremental

Salah satu tujuan utama project Bazel adalah memastikan build ulang inkremental yang benar. Alat build sebelumnya, terutama yang didasarkan pada Make, membuat beberapa asumsi yang tidak terdengar dalam implementasi build inkremental.

Pertama, stempel waktu file tersebut akan meningkat secara monoton. Meskipun demikian, biasanya, sangat sulit untuk mengabaikan asumsi ini. Sinkronisasi ke revisi file sebelumnya menyebabkan waktu modifikasi file tersebut berkurang; Sistem berbasis make tidak akan dibuat ulang.

Secara umum, meskipun 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 kembali compiler tersebut, dan Anda harus menghapus output tidak valid dari build sebelumnya secara manual menggunakan make clean.

Selain itu, Make tidak dapat diandalkan untuk menghentikan penghentian salah satu subprosesnya setelah subproses tersebut mulai menulis ke file outputnya. Meskipun eksekusi Make saat ini akan gagal, pemanggilan Make berikutnya akan secara buta mengasumsikan bahwa file output yang terpotong valid (karena lebih baru daripada inputnya), dan tidak akan dibuat ulang. Demikian pula, jika proses Make dibunuh, situasi serupa dapat terjadi.

Bazel menghindari asumsi tersebut, mis. Bazel mempertahankan database dari semua pekerjaan yang telah dilakukan sebelumnya, dan hanya akan menghapus langkah build jika menemukan bahwa kumpulan file input (dan stempel waktunya) ke langkah build tersebut, dan perintah kompilasi untuk langkah build tersebut, sama persis dengan yang ada dalam database, dan, 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.

Manfaatnya bagi pengguna build inkremental yang benar adalah: lebih sedikit waktu yang dihabiskan karena kebingungan. (Selain itu, lebih sedikit waktu yang diperlukan untuk menunggu rebuild yang disebabkan oleh penggunaan make clean, baik yang diperlukan maupun preemtif.)

Konsistensi build dan build inkremental

Secara formal, kita mendefinisikan status build sebagai konsisten ketika semua file output yang diharapkan ada, dan isinya 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 hingga Anda menjalankan alat build berikutnya hingga selesai. Kami mendeskripsikan situasi ini sebagai inkonsistensi yang tidak stabil, karena ini hanya sementara, dan konsistensi dipulihkan dengan menjalankan alat build.

Ada jenis inkonsistensi lain yang pernis: konsistensi yang tidak stabil. Jika build mencapai status tidak konsisten yang stabil, pemanggilan alat build yang berhasil secara berulang tidak akan memulihkan konsistensi: build telah mengalami "stuck", dan output tetap salah. Status stabil yang tidak konsisten adalah alasan utama pengguna Make (dan alat build lainnya) mengetik make clean. Menemukan bahwa alat build gagal dengan cara ini (dan kemudian memulihkannya dari alat tersebut) bisa 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: buat setiap build menjadi build yang bersih. Pendekatan ini jelas terlalu memakan waktu agar menjadi praktis (kecuali mungkin untuk engineer rilis), dan oleh karena itu, berguna untuk build build, alat build harus dapat melakukan build inkremental tanpa mengorbankan konsistensi.

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

Seperti halnya semua jaminan, ada beberapa cetakan kecil: ada beberapa cara umum untuk masuk ke 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 alat build yang normal atau "wajar".

Jika Anda pernah mendeteksi status tidak konsisten dengan Bazel, harap laporkan bug.

Eksekusi dengan sandbox

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

Bazel akan mencetak peringatan jika sistem Anda tidak mendukung sandbox untuk memperingatkan Anda tentang fakta bahwa build tidak dijamin akan menjadi hermetic 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. Hal 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 berupa kegagalan yang menghasilkan pesan yang mirip dengan namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory. Dalam hal ini, coba nonaktifkan sandbox untuk genrules dengan --strategy=Genrule=standalone dan untuk aturan lain 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 dalam rilis berikutnya.

Fase build

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

Fase pemuatan

Pertama adalah memuat saat 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 untuk memuat file BUILD dari sistem file. Pada build berikutnya, terutama jika tidak ada file BUILD yang berubah, pemuatan akan terjadi dengan sangat cepat.

Error yang dilaporkan selama fase ini meliputi: paket tidak ditemukan, target tidak ditemukan, error gramatikal dan gramatikal dalam file BUILD, serta error evaluasi.

Fase analisis

Fase kedua, analisis, melibatkan analisis semantik dan validasi setiap aturan build, pembuatan grafik dependensi build, dan penentuan pekerjaan yang akan dilakukan pada setiap langkah build secara tepat.

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

Error yang dilaporkan pada tahap ini mencakup: dependensi yang tidak pantas, input yang tidak valid pada 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, hanya membaca file BUILD untuk menentukan pekerjaan yang akan diselesaikan. Hal ini sesuai desain, dan menjadikan Bazel sebagai dasar yang baik untuk alat analisis, seperti perintah query Bazel, yang diimplementasikan di atas fase pemuatan.

Fase eksekusi

Fase ketiga dan terakhir build adalah eksekusi. Fase ini memastikan bahwa output dari setiap langkah dalam build konsisten dengan inputnya, menjalankan ulang alat kompilasi/penautan/dll. jika diperlukan. Langkah ini adalah tempat build menghabiskan sebagian besar waktunya, mulai dari beberapa detik hingga lebih dari satu jam untuk build besar. Error yang dilaporkan selama fase ini meliputi: file sumber yang hilang, error pada alat yang dieksekusi oleh beberapa tindakan build, atau kegagalan alat untuk menghasilkan kumpulan output yang diharapkan.