Halaman ini membahas cara mem-build program dengan Bazel, sintaksis perintah build, dan sintaksis pola target.
Panduan memulai
Untuk menjalankan Bazel, buka direktori ruang kerja 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
: Mem-build target yang ditentukan.canonicalize-flags
: Mengonversi flag Bazel menjadi kanonis.clean
: Menghapus file output dan secara opsional menghentikan server.cquery
: Menjalankan kueri grafik dependensi pasca-analisis.dump
: Men-dump 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
: Mem-build dan menjalankan target pengujian yang ditentukan.version
: Mencetak informasi versi untuk Bazel.
Mendapatkan bantuan
bazel help command
: Mencetak bantuan dan opsi untukcommand
.bazel help
startup_options
: Opsi untuk JVM yang menghosting Bazel.bazel help
target-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
.
Mem-build 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 Anda. Bazel memungkinkan Anda melakukan 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 mem-build //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. Hal 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
kebenarannya 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 penaut) saat dimulai, dan jumlah yang diselesaikan dari total jumlah tindakan build. Saat build dimulai, jumlah total tindakan sering kali meningkat saat Bazel menemukan seluruh grafik tindakan, tetapi jumlahnya akan stabil dalam beberapa detik.
Di akhir build, Bazel akan mencetak target yang diminta, baik berhasil
di-build maupun tidak, dan jika berhasil, tempat 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 jauh 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 yang perlu dimuat ulang dan tidak ada langkah build untuk dieksekusi. Jika ada yang berubah di 'foo' atau dependensinya, Bazel akan mengeksekusi ulang beberapa tindakan build, atau menyelesaikan build inkremental.
Mem-build beberapa target
Bazel memungkinkan sejumlah cara untuk menentukan target yang akan di-build. Secara kolektif,
hal ini dikenal sebagai pola target. Sintaksis ini digunakan dalam perintah seperti
build
, test
, atau query
.
Sementara label digunakan untuk menentukan setiap
target, 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 tepat 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 dalam paket tingkat atas, 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:
|
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 berulang, kecuali yang mengarah ke bawah basis output, seperti symlink yang dibuat di direktori root ruang kerja.
Selain itu, Bazel tidak mengikuti symlink saat mengevaluasi pola target
rekursif di direktori apa pun yang berisi file dengan nama berikut:
DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN
foo/...
adalah karakter pengganti di 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. Kedua karakter ini dapat digabungkan, seperti dalam foo/...:all
, dan jika 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 di-build oleh aturan apa pun,
seperti file _deploy.jar
yang terkait dengan aturan java_binary
.
Hal ini menyiratkan bahwa :*
menunjukkan superset dari :all
; meskipun berpotensi
membingungkan, sintaksis ini memungkinkan karakter pengganti :all
yang sudah dikenal untuk digunakan untuk
build standar, dengan target build seperti _deploy.jar
tidak diinginkan.
Selain itu, Bazel memungkinkan garis miring digunakan, bukan titik dua yang diperlukan oleh
sintaksis label; hal 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 //foo:bar/wiz
(jika ada paket foo
).
Banyak perintah Bazel menerima daftar pola target sebagai argumen, dan semuanya
menghormati operator negasi awalan -
. Ini dapat digunakan untuk mengurangi kumpulan
target dari kumpulan yang ditentukan oleh argumen sebelumnya. Perhatikan bahwa hal ini berarti
urutan sangat 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 -
ditafsirkan sebagai opsi tambahan.)
Namun, perlu diperhatikan bahwa mengurangi target dengan cara ini tidak akan
memastikan bahwa target tidak di-build, karena target tersebut mungkin merupakan dependensi target
yang tidak dikurangi. Misalnya, jika ada target //foo:all-apis
yang di antaranya bergantung pada //foo/bar:api
, //foo/bar:api
akan di-build sebagai
bagian dari mem-build //foo:all-apis
.
Target dengan tags = ["manual"]
tidak disertakan dalam pola target karakter pengganti
(...
, :*
, :all
, dll.) saat ditentukan dalam perintah seperti
bazel build
dan bazel test
(tetapi disertakan dalam
pola target karakter pengganti negatif, yaitu akan dikurangkan). Anda harus
menentukan target pengujian tersebut dengan pola target eksplisit di command line jika
ingin Bazel mem-build/mengujinya. Sebaliknya, bazel query
tidak melakukan
pemfilteran tersebut secara otomatis (yang akan mengacaukan tujuan
bazel query
).
Mengambil dependensi eksternal
Secara default, Bazel akan mendownload dan membuat symlink dependensi eksternal selama
build. Namun, hal ini dapat tidak diinginkan, baik karena Anda ingin mengetahui
saat dependensi eksternal baru ditambahkan atau karena Anda ingin
"mengambil data terlebih dahulu" dependensi (misalnya, sebelum penerbangan saat 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 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
tidak mengizinkan pengambilan selama build, Anda harus menjalankan bazel fetch
:
- Sebelum Anda mem-build untuk pertama kalinya.
- Setelah Anda menambahkan dependensi eksternal baru.
Setelah dijalankan, Anda tidak perlu menjalankannya lagi hingga file MODULE.bazel berubah.
fetch
menggunakan daftar target untuk mengambil dependensi. Misalnya, hal 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 //...
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,
berada di ~/.cache/bazel/_bazel_$USER/cache/repos/v1/
. Lokasi
dapat diubah dengan opsi --repository_cache
. Cache
dibagikan di antara semua ruang kerja dan versi bazel yang diinstal.
Entri diambil dari cache jika
Bazel tahu pasti bahwa file tersebut memiliki salinan file yang benar, yaitu jika
permintaan download memiliki jumlah SHA256 dari file yang ditentukan dan file dengan
hash tersebut ada di cache. Jadi, menentukan hash untuk setiap file eksternal
tidak hanya merupakan ide yang baik dari perspektif keamanan; hal ini juga membantu menghindari
download yang tidak perlu.
Setelah setiap hit cache, 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: Sebaiknya gunakan cache repositori untuk mencapai build offline.
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 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. Hal ini hanya berfungsi jika
hash file ditentukan dalam deklarasi aturan repo.
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 tetap efisien, meskipun jumlah file dalam direktori tersebut bertambah besar.
Menjalankan Bazel di lingkungan yang terisolasi
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 oleh semua orang. Misalnya, alat Android di-unbundle dan diambil hanya saat mem-build project Android.
Namun, dependensi implisit ini dapat menyebabkan masalah saat menjalankan Bazel di lingkungan airgapped, meskipun Anda telah menyediakan semua dependensi eksternal. Untuk mengatasinya, Anda dapat menyiapkan cache repositori (dengan Bazel 7 atau yang lebih baru) atau direktori distribusi (dengan Bazel sebelum 7) yang berisi dependensi ini di komputer dengan akses jaringan, lalu mentransfernya ke lingkungan airgapped dengan pendekatan offline.
Cache repositori (dengan Bazel 7 atau yang lebih baru)
Untuk menyiapkan cache repositori, gunakan
tanda
--repository_cache
. Anda harus melakukannya satu kali untuk setiap versi biner Bazel baru, karena
dependensi implisit dapat berbeda untuk setiap rilis.
Untuk mengambil dependensi tersebut di luar lingkungan airgapped, buat terlebih dahulu workspace kosong:
mkdir empty_workspace && cd empty_workspace
touch MODULE.bazel
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 airgapped, 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 guna 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 harus melakukannya satu kali untuk setiap versi biner Bazel baru, karena
dependensi implisit dapat berbeda untuk setiap rilis.
Untuk mem-build dependensi ini di luar lingkungan airgapped, pertama-tama periksa hierarki sumber Bazel pada 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
yang dipisahkan secara fisik. Perhatikan flag --strip-components
, karena --distdir
dapat
cukup rewel dengan tingkat tingkatan direktori:
tar xvf bazel-bin/external/additional_distfiles/archives.tar \
-C "$NEW_DIRECTORY" --strip-components=3
Terakhir, saat Anda menggunakan Bazel di lingkungan airgapped, teruskan flag --distdir
yang mengarah 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, dan 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 //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 dijalankan
64-bit, tetapi sistem build juga harus mem-build berbagai alat yang digunakan selama
build itu sendiri—misalnya alat yang di-build dari sumber, lalu kemudian
digunakan dalam, misalnya, genrule—dan alat ini harus di-build untuk dijalankan 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 dibuat dua kali, sekali untuk konfigurasi
exec, dan sekali untuk konfigurasi target. Bazel menangani
pastikan kedua varian di-build, dan file turunan disimpan
terpisah untuk menghindari interferensi; 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 di-build dua kali, kemungkinan besar ini 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 mencaridefault_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
). - Tidak menghasilkan 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 kemungkinan konfigurasi permintaan apa pun.
- Menyembunyikan stempel biner dengan data build (lihat opsi
--embed_*
). - Semua nilai lainnya tetap pada setelan defaultnya.
Ada banyak alasan mengapa mungkin lebih baik untuk memilih konfigurasi exec yang berbeda dari konfigurasi permintaan. Yang paling penting:
Pertama, dengan menggunakan biner yang dihapus dan dioptimalkan, Anda mengurangi waktu yang dihabiskan untuk menautkan dan menjalankan alat, ruang disk yang ditempati 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 penaut), seperti yang dijelaskan sebelumnya.
Memperbaiki build ulang inkremental
Salah satu sasaran utama project Bazel adalah memastikan pembuatan ulang inkremental yang benar. Alat build sebelumnya, terutama yang berbasis Make, membuat beberapa asumsi yang tidak masuk akal dalam penerapan build inkremental.
Pertama, stempel waktu file meningkat secara monoton. Meskipun ini adalah kasus umum, asumsi ini sangat mudah dilanggar; menyinkronkan ke revisi file sebelumnya menyebabkan waktu perubahan file tersebut berkurang; sistem berbasis Make tidak akan mem-build ulang.
Secara lebih umum, meskipun Make 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 andal terhadap penghentian salah satu subprosesnya yang tidak berhasil 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 mengelola database semua pekerjaan yang sebelumnya dilakukan, 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, bahwa kumpulan file output (dan stempel waktunya) untuk entri database sama persis dengan stempel waktu file di disk. Setiap perubahan pada file input atau file output, atau pada perintah itu sendiri, akan menyebabkan eksekusi ulang langkah build.
Manfaat bagi pengguna dari build inkremental yang benar adalah: lebih sedikit waktu yang terbuang karena
kebingungan. (Selain itu, lebih sedikit waktu yang dihabiskan untuk menunggu build ulang yang disebabkan oleh penggunaan make
clean
, baik yang diperlukan maupun yang bersifat preventif.)
Membangun konsistensi dan build inkremental
Secara formal, kita mendefinisikan 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 hingga Anda menjalankan alat build berikutnya 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 berbahaya: inkonsistensi
stabil. Jika build mencapai status tidak konsisten yang stabil, pemanggilan
alat build yang berhasil berulang kali tidak akan memulihkan konsistensi: build
telah "macet", dan output tetap salah. Status yang tidak konsisten dan stabil
adalah alasan utama pengguna Make (dan alat build lainnya) mengetik make clean
.
Menemukan bahwa alat build telah gagal dengan cara ini (lalu memulihkannya) dapat memakan waktu dan sangat menjengkelkan.
Secara konseptual, cara termudah untuk mencapai build yang konsisten adalah dengan menghapus semua output build sebelumnya dan memulai lagi: buat setiap build menjadi build 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 sulit dilakukan, dan seperti yang dijelaskan di atas, banyak alat build lainnya tidak dapat menghindari status yang tidak konsisten dan 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 build, Bazel tidak memberikan jaminan tentang konsistensi hasil build saat ini. Namun, cara ini menjamin bahwa hasil build berikutnya akan memulihkan konsistensi.)
Seperti semua jaminan, ada beberapa detail kecil: ada beberapa cara yang diketahui 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 stabil yang tidak konsisten yang timbul dari penggunaan normal atau "wajar" atas alat build.
Jika Anda pernah mendeteksi status yang stabil dan tidak konsisten dengan Bazel, harap laporkan bug.
Eksekusi dengan sandbox
Bazel menggunakan sandbox untuk menjamin bahwa tindakan berjalan secara hermetis dan
dengan benar. Bazel menjalankan spawns (berbicara longgar: tindakan) di sandbox yang
hanya berisi sekumpulan 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, 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
tanda --ignore_unsupported_sandboxing
ke Bazel.
Di 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
.
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 sehingga kami dapat
menyelidiki dan memberikan perbaikan dalam rilis berikutnya.
Fase build
Di Bazel, build terjadi dalam tiga fase yang berbeda; sebagai pengguna, memahami perbedaan di antara ketiga fase tersebut akan memberikan insight tentang opsi yang mengontrol build (lihat di bawah).
Fase pemuatan
Yang pertama adalah pemuatan, yang selama prosesnya 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 karena banyak file BUILD yang dimuat dari sistem file. Dalam build berikutnya, terutama jika tidak ada file BUILD yang berubah, pemuatan terjadi dengan sangat cepat.
Error yang dilaporkan selama fase ini mencakup: paket tidak ditemukan, target tidak ditemukan, error leksik 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 dalam cache dari satu build ke build berikutnya dan hanya melakukan analisis ulang yang diperlukan, yang dapat membuat build inkremental sangat cepat jika paket belum berubah sejak build sebelumnya.
Error yang dilaporkan pada tahap ini mencakup: dependensi yang tidak sesuai, 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, hanya membaca file BUILD untuk menentukan pekerjaan yang harus dilakukan. Hal ini memang disengaja, dan membuat Bazel menjadi fondasi yang baik untuk alat analisis, seperti perintah kueri Bazel, yang diterapkan 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. 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 mencakup: file sumber yang hilang, error dalam alat yang dijalankan oleh beberapa tindakan build, atau kegagalan alat untuk menghasilkan kumpulan output yang diharapkan.