Tutorial ini membahas dasar-dasar pembuatan aplikasi Java dengan
Bazel. Anda akan menyiapkan ruang kerja dan mem-build project Java sederhana yang
mengilustrasikan konsep Bazel utama, seperti target dan file BUILD
.
Estimasi waktu penyelesaian: 30 menit.
Yang akan Anda pelajari
Dalam tutorial ini, Anda akan mempelajari cara:
- Membuat target
- Memvisualisasikan dependensi project
- Memisahkan project menjadi beberapa target dan paket
- Mengontrol visibilitas target di seluruh paket
- Mereferensikan target melalui label
- Men-deploy target
Sebelum memulai
Menginstal Bazel
Untuk mempersiapkan tutorial, Instal Bazel terlebih dahulu jika Anda belum menginstalnya.
Menginstal JDK
Instal Java JDK (versi yang lebih disukai adalah 11, tetapi versi antara 8 dan 15 didukung).
Tetapkan variabel lingkungan JAVA_HOME agar mengarah ke JDK.
Di Linux/macOS:
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
Di Windows:
- Buka Control Panel.
- Buka "System and Security" > "System" > "Advanced System Settings" > tab "Advanced" > "Environment Variables..." .
- Di bagian daftar "Variabel pengguna" (yang ada di bagian atas), klik "Baru...".
- Di kolom "Nama variabel", masukkan
JAVA_HOME
. - Klik "Jelajahi Direktori...".
- Buka direktori JDK (misalnya
C:\Program Files\Java\jdk1.8.0_152
). - Klik "Oke" di semua jendela dialog.
Mendapatkan project contoh
Ambil project contoh dari repositori GitHub Bazel:
git clone https://github.com/bazelbuild/examples
Project contoh untuk tutorial ini berada di direktori examples/java-tutorial
dan disusun sebagai berikut:
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── WORKSPACE
Mem-build dengan Bazel
Menyiapkan ruang kerja
Sebelum dapat mem-build project, Anda harus menyiapkan ruang kerjanya. Ruang kerja adalah direktori yang menyimpan file sumber project dan output build Bazel. File ini juga berisi file yang dikenali Bazel sebagai file khusus:
File
WORKSPACE
, yang mengidentifikasi direktori dan kontennya sebagai ruang kerja Bazel dan berada di root struktur direktori project,Satu atau beberapa file
BUILD
, yang memberi tahu Bazel cara mem-build berbagai bagian project. (Direktori dalam ruang kerja yang berisi fileBUILD
adalah paket. Anda akan mempelajari paket nanti dalam tutorial ini.)
Untuk menetapkan direktori sebagai ruang kerja Bazel, buat file kosong bernama
WORKSPACE
di direktori tersebut.
Saat Bazel mem-build project, semua input dan dependensi harus berada di ruang kerja yang sama. File yang berada di ruang kerja yang berbeda tidak saling terkait, kecuali jika ditautkan, yang berada di luar cakupan tutorial ini.
Memahami file BUILD
File BUILD
berisi beberapa jenis petunjuk untuk Bazel.
Jenis yang paling penting adalah aturan build, yang memberi tahu Bazel cara mem-build
output yang diinginkan, seperti library atau biner yang dapat dieksekusi. Setiap instance
aturan build dalam file BUILD
disebut target dan mengarah ke
kumpulan file sumber dan dependensi tertentu. Target juga dapat mengarah ke target
lain.
Lihat file java-tutorial/BUILD
:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
Dalam contoh kita, target ProjectRunner
membuat instance
aturan java_binary
bawaan Bazel. Aturan ini memberi tahu Bazel untuk
mem-build file .jar
dan skrip shell wrapper (keduanya diberi nama sesuai target).
Atribut dalam target secara eksplisit menyatakan dependensi dan opsi.
Meskipun atribut name
bersifat wajib, banyak atribut yang bersifat opsional. Misalnya, dalam
target aturan ProjectRunner
, name
adalah nama target, srcs
menentukan
file sumber yang digunakan Bazel untuk mem-build target, dan main_class
menentukan
class yang berisi metode utama. (Anda mungkin telah memperhatikan bahwa contoh kami
menggunakan glob untuk meneruskan kumpulan file sumber ke Bazel,
bukan mencantumkannya satu per satu.)
Mem-build project
Untuk mem-build project contoh, buka direktori java-tutorial
dan jalankan:
bazel build //:ProjectRunner
Pada label target, bagian //
adalah lokasi file BUILD
yang relatif terhadap root ruang kerja (dalam hal ini, root itu sendiri),
dan ProjectRunner
adalah nama target dalam file BUILD
. (Anda akan mempelajari label target secara lebih mendetail di akhir tutorial ini.)
Bazel menghasilkan output yang mirip dengan berikut ini:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
Selamat, Anda baru saja mem-build target Bazel pertama Anda. Bazel menempatkan output
build di direktori bazel-bin
di root ruang kerja. Jelajahi
kontennya untuk mendapatkan ide tentang struktur output Bazel.
Sekarang, uji biner yang baru Anda build:
bazel-bin/ProjectRunner
Meninjau grafik dependensi
Bazel mewajibkan dependensi build untuk dideklarasikan secara eksplisit dalam file BUILD. Bazel menggunakan pernyataan tersebut untuk membuat grafik dependensi project, yang memungkinkan build inkremental yang akurat.
Untuk memvisualisasikan dependensi project contoh, Anda dapat membuat representasi teks grafik dependensi dengan menjalankan perintah ini di root ruang kerja:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
Perintah di atas memberi tahu Bazel untuk mencari semua dependensi untuk target
//:ProjectRunner
(tidak termasuk dependensi host dan implisit) dan memformat
output sebagai grafik.
Kemudian, tempelkan teks ke GraphViz.
Seperti yang dapat Anda lihat, project memiliki satu target yang mem-build dua file sumber tanpa dependensi tambahan:
Setelah menyiapkan ruang kerja, mem-build project, dan memeriksa dependensinya, Anda dapat menambahkan beberapa kompleksitas.
Meningkatkan kualitas build Bazel
Meskipun satu target sudah cukup untuk project kecil, Anda dapat membagi project yang lebih besar menjadi beberapa target dan paket untuk memungkinkan build inkremental yang cepat (yaitu, hanya mem-build ulang yang berubah) dan mempercepat build dengan mem-build beberapa bagian project sekaligus.
Menentukan beberapa target build
Anda dapat membagi build project contoh menjadi dua target. Ganti konten
file java-tutorial/BUILD
dengan kode berikut:
java_binary(
name = "ProjectRunner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
Dengan konfigurasi ini, Bazel akan mem-build library greeter
terlebih dahulu, lalu
biner ProjectRunner
. Atribut deps
di java_binary
memberi tahu Bazel bahwa
library greeter
diperlukan untuk mem-build biner ProjectRunner
.
Untuk mem-build project versi baru ini, jalankan perintah berikut:
bazel build //:ProjectRunner
Bazel menghasilkan output yang mirip dengan berikut ini:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s
Sekarang, uji biner yang baru Anda build:
bazel-bin/ProjectRunner
Jika sekarang Anda mengubah ProjectRunner.java
dan mem-build ulang project, Bazel hanya
akan mengompilasi ulang file tersebut.
Dengan melihat grafik dependensi, Anda dapat melihat bahwa ProjectRunner
bergantung pada
input yang sama seperti sebelumnya, tetapi struktur build-nya berbeda:
Sekarang Anda telah mem-build project dengan dua target. Target ProjectRunner
mem-build
dua file sumber dan bergantung pada satu target lain (:greeter
), yang mem-build
satu file sumber tambahan.
Menggunakan beberapa paket
Sekarang, mari kita bagi project menjadi beberapa paket. Jika melihat
direktori src/main/java/com/example/cmdline
, Anda dapat melihat bahwa direktori tersebut juga berisi
file BUILD
, ditambah beberapa file sumber. Oleh karena itu, bagi Bazel, ruang kerja kini
berisi dua paket, //src/main/java/com/example/cmdline
dan //
(karena
ada file BUILD
di root ruang kerja).
Lihat file src/main/java/com/example/cmdline/BUILD
:
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"],
)
Target runner
bergantung pada target greeter
dalam paket //
(sehingga
label target //:greeter
) - Bazel mengetahuinya melalui atribut deps
.
Lihat grafik dependensi:
Namun, agar build berhasil, Anda harus secara eksplisit memberikan visibilitas target runner
dalam //src/main/java/com/example/cmdline/BUILD
ke target dalam
//BUILD
menggunakan atribut visibility
. Hal ini karena secara default, target
hanya terlihat oleh target lain dalam file BUILD
yang sama. (Bazel menggunakan visibilitas
target untuk mencegah masalah seperti library yang berisi detail implementasi
bocor ke API publik.)
Untuk melakukannya, tambahkan atribut visibility
ke target greeter
di
java-tutorial/BUILD
seperti yang ditunjukkan di bawah ini:
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
Sekarang Anda dapat mem-build paket baru dengan menjalankan perintah berikut di root ruang kerja:
bazel build //src/main/java/com/example/cmdline:runner
Bazel menghasilkan output yang mirip dengan berikut ini:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.576s, Critical Path: 0.81s
Sekarang, uji biner yang baru Anda build:
./bazel-bin/src/main/java/com/example/cmdline/runner
Sekarang Anda telah memodifikasi project untuk di-build sebagai dua paket, yang masing-masing berisi satu target, dan memahami dependensi di antara keduanya.
Menggunakan label untuk mereferensikan target
Dalam file BUILD
dan di command line, Bazel menggunakan label target untuk mereferensikan
target - misalnya, //:ProjectRunner
atau
//src/main/java/com/example/cmdline:runner
. Sintaksisnya adalah sebagai berikut:
//path/to/package:target-name
Jika target adalah target aturan, path/to/package
adalah jalur ke
direktori yang berisi file BUILD
, dan target-name
adalah nama yang Anda berikan untuk
target dalam file BUILD
(atribut name
). Jika target adalah target
file, path/to/package
adalah jalur ke root paket, dan
target-name
adalah nama file target, termasuk jalur lengkapnya.
Saat mereferensikan target di root repositori, jalur paket kosong,
cukup gunakan //:target-name
. Saat mereferensikan target dalam file BUILD
yang sama, Anda bahkan dapat melewati ID root ruang kerja //
dan hanya menggunakan
:target-name
.
Misalnya, untuk target dalam file java-tutorial/BUILD
, Anda tidak perlu
menentukan jalur paket, karena root ruang kerja itu sendiri adalah paket (//
), dan
dua label target Anda hanyalah //:ProjectRunner
dan //:greeter
.
Namun, untuk target dalam file //src/main/java/com/example/cmdline/BUILD
, Anda
harus menentukan jalur paket lengkap //src/main/java/com/example/cmdline
dan label target Anda adalah //src/main/java/com/example/cmdline:runner
.
Memaketkan target Java untuk deployment
Sekarang, mari kita paketkan target Java untuk deployment dengan mem-build biner dengan semua dependensi runtime-nya. Hal ini memungkinkan Anda menjalankan biner di luar lingkungan pengembangan.
Seperti yang Anda ingat, aturan build java_binary
menghasilkan .jar
dan skrip shell wrapper. Lihat isi
runner.jar
menggunakan perintah ini:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar
Isinya adalah:
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
Seperti yang dapat Anda lihat, runner.jar
berisi Runner.class
, tetapi tidak berisi dependensinya,
Greeting.class
. Skrip runner
yang dihasilkan Bazel menambahkan greeter.jar
ke classpath, jadi jika Anda membiarkannya seperti ini, skrip akan berjalan secara lokal, tetapi
tidak akan berjalan secara mandiri di komputer lain. Untungnya, aturan java_binary
memungkinkan Anda mem-build biner mandiri yang dapat di-deploy. Untuk mem-build-nya, tambahkan
_deploy.jar
ke nama target:
bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
Bazel menghasilkan output yang mirip dengan berikut ini:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s
Anda baru saja mem-build runner_deploy.jar
, yang dapat dijalankan secara mandiri di luar
lingkungan pengembangan karena berisi dependensi runtime
yang diperlukan. Lihat konten JAR mandiri ini menggunakan
perintah yang sama seperti sebelumnya:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
Kontennya mencakup semua class yang diperlukan untuk dijalankan:
META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class
Bacaan lebih lanjut
Untuk mengetahui detail selengkapnya, lihat:
rules_jvm_external untuk aturan guna mengelola dependensi Maven transitif.
Dependensi Eksternal untuk mempelajari lebih lanjut cara menggunakan repositori lokal dan jarak jauh.
Aturan lainnya untuk mempelajari Bazel lebih lanjut.
Tutorial build C++ untuk mulai mem-build project C++ dengan Bazel.
Tutorial aplikasi Android dan tutorial aplikasi iOS) untuk memulai mem-build aplikasi seluler untuk Android dan iOS dengan Bazel.
Selamat membangun!