Tutorial Bazel: Mem-build Project Java

Laporkan masalah Lihat sumber Per Malam · 7,2 · 7,1 · 7,0 · 6,5 · 6,4

Tutorial ini membahas dasar-dasar membangun aplikasi Java dengan Bazel. Anda akan menyiapkan ruang kerja Anda dan membuat project Java sederhana yang mengilustrasikan konsep utama Bazel, seperti target dan file BUILD.

Perkiraan waktu penyelesaian: 30 menit.

Yang akan Anda pelajari

Dalam tutorial ini, Anda akan mempelajari cara:

  • Membuat target
  • Memvisualisasikan dependensi project
  • Bagi project menjadi beberapa target dan paket
  • Mengontrol visibilitas target di berbagai paket
  • Mereferensikan target melalui label
  • Men-deploy target

Sebelum memulai

Instal Bazel

Untuk mempersiapkan tutorial, Instal Bazel terlebih dahulu jika Anda belum menginstalnya.

Menginstal JDK

  1. Instal Java JDK (versi yang lebih disukai adalah 11, namun versi antara 8 dan 15 didukung).

  2. Setel variabel lingkungan JAVA_HOME agar mengarah ke JDK.

    • Di Linux/macOS:

      export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
      
    • Di Windows:

      1. Buka Control Panel.
      2. Buka "Sistem dan Keamanan" > "Sistem" > "Setelan Sistem Lanjutan" > "Lanjutan" tab > "Variabel Lingkungan..." .
      3. Di bagian "Variabel pengguna" (yang ada di atas), klik "Baru...".
      4. Di bagian "Nama variabel" , masukkan JAVA_HOME.
      5. Klik "Jelajahi Direktori...".
      6. Buka direktori JDK (misalnya C:\Program Files\Java\jdk1.8.0_152).
      7. Klik "OK" di semua jendela dialog.

Mendapatkan contoh project

Ambil project contoh dari repositori GitHub Bazel:

git clone https://github.com/bazelbuild/examples

Project contoh untuk tutorial ini ada dalam examples/java-tutorial dan disusun sebagai berikut:

java-tutorial
├── BUILD
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── cmdline
│                   │   ├── BUILD
│                   │   └── Runner.java
│                   ├── Greeting.java
│                   └── ProjectRunner.java
└── WORKSPACE

Membangun dengan Bazel

Menyiapkan ruang kerja

Sebelum dapat membuat project, Anda perlu menyiapkan ruang kerjanya. Ruang kerja adalah direktori yang menyimpan file sumber proyek Anda dan {i>output<i} build Bazel. Ini juga berisi file yang dikenali Bazel sebagai khusus:

  • File WORKSPACE, yang mengidentifikasi direktori dan kontennya sebagai {i>workspace <i}dan berada di akar dari struktur direktori proyek,

  • Satu atau beberapa file BUILD, yang memberi tahu Bazel cara membuat bagian yang berbeda dari menyelesaikan proyek tersebut. (Direktori dalam ruang kerja yang berisi file BUILD adalah paket. Anda akan belajar tentang paket nanti dalam tutorial ini.)

Untuk menetapkan direktori sebagai ruang kerja Bazel, buat file kosong bernama WORKSPACE di direktori tersebut.

Saat Bazel membuat project, semua input dan dependensi harus sama Workspace. File yang berada di ruang kerja lain terpisah dari satu ruang kerja satu lagi kecuali ditautkan, yang berada di luar cakupan tutorial ini.

Memahami file BUILD

File BUILD berisi beberapa jenis petunjuk yang berbeda untuk Bazel. Jenis yang paling penting adalah aturan build, yang memberi tahu Bazel cara membuat {i>output<i} yang diinginkan, seperti {i>executable<i} atau {i>library<i}. Setiap instance aturan build di file BUILD disebut target dan mengarah ke set tertentu dari file sumber dan dependensi. Target juga dapat menunjuk ke target.

Lihat file java-tutorial/BUILD:

java_binary(
    name = "ProjectRunner",
    srcs = glob(["src/main/java/com/example/*.java"]),
)

Dalam contoh kita, target ProjectRunner membuat instance bawaan Bazel Aturan java_binary. Aturan itu memberitahu Bazel untuk membuat file .jar dan skrip shell wrapper (keduanya dinamai sesuai target).

Atribut pada target secara eksplisit menyatakan dependensi dan opsinya. Meskipun atribut name bersifat wajib, banyak yang opsional. Misalnya, di kolom ProjectRunner target aturan, name adalah nama target, srcs menentukan file sumber yang digunakan Bazel untuk membangun target, dan main_class menentukan class yang berisi metode utama. (Anda mungkin telah memperhatikan bahwa contoh menggunakan glob untuk meneruskan kumpulan file sumber ke Bazel alih-alih mencantumkannya satu per satu.)

Membangun project

Untuk membuat project contoh, buka direktori java-tutorial dan jalankan:

bazel build //:ProjectRunner

Di label target, bagian // adalah lokasi file BUILD relatif dengan {i>root <i}ruang kerja (dalam hal ini, {i>root<i} itu sendiri), dan ProjectRunner adalah nama target dalam file BUILD. (Anda akan pelajari 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, kamu baru saja membangun target Bazel pertamamu! Tempat bazel build menghasilkan output di direktori bazel-bin di root ruang kerja. Jelajahi untuk mendapatkan ide tentang struktur {i>output<i} Bazel.

Sekarang uji biner yang baru Anda bangun:

bazel-bin/ProjectRunner

Meninjau grafik dependensi

Bazel memerlukan dependensi build yang dideklarasikan secara eksplisit dalam file BUILD. Bazel menggunakan pernyataan tersebut untuk membuat grafik dependensi proyek, yang memungkinkan build inkremental yang akurat.

Untuk memvisualisasikan dependensi project contoh, Anda bisa membuat teks grafik dependensi dengan menjalankan perintah ini di root workspace:

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 implisit dan host), lalu memformat output sebagai grafik.

Kemudian, tempel teks ke GraphViz.

Seperti yang dapat Anda lihat, proyek ini memiliki satu target yang membangun dua file sumber dengan tidak ada dependensi tambahan:

Grafik dependensi &#39;ProjectRunner&#39; target

Setelah menyiapkan ruang kerja Anda, bangun project Anda, dan periksa dependensi, maka Anda dapat menambahkan beberapa kerumitan.

Sempurnakan build Bazel Anda

Meskipun satu target sudah cukup untuk project kecil, sebaiknya Anda membagi dari proyek yang lebih besar menjadi beberapa target dan paket untuk mempercepat proses build (yaitu, hanya membangun ulang apa yang berubah) dan untuk mempercepat build Anda dengan membangun beberapa bagian proyek sekaligus.

Menentukan beberapa target build

Anda dapat membagi build contoh project 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 terlebih dahulu membangun library greeter, lalu Biner ProjectRunner. Atribut deps dalam java_binary memberi tahu Bazel bahwa library greeter diperlukan untuk membangun biner ProjectRunner.

Untuk membuat versi baru project 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 bangun:

bazel-bin/ProjectRunner

Jika Anda sekarang mengubah ProjectRunner.java dan mem-build ulang project, hanya Bazel 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:

Grafik dependensi &#39;ProjectRunner&#39; target Setelah menambahkan dependensi

Sekarang Anda telah membangun proyek dengan dua target. Target ProjectRunner di-build dua file sumber dan bergantung pada satu target lain (:greeter), yang membuat satu file sumber tambahan.

Menggunakan beberapa paket

Sekarang, mari kita bagi project menjadi beberapa paket. Jika Anda melihat src/main/java/com/example/cmdline, Anda dapat melihat bahwa file tersebut juga berisi file BUILD, plus beberapa file sumber. Oleh karena itu, bagi Bazel, ruang kerja sekarang berisi dua paket, //src/main/java/com/example/cmdline dan // (sejak 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 // (oleh karena itu label target //:greeter) - Bazel mengetahui hal ini melalui atribut deps. Lihatlah grafik dependensi:

Grafik dependensi &#39;runner&#39; target

Namun, agar build berhasil, Anda harus secara eksplisit memberikan target runner dalam visibilitas //src/main/java/com/example/cmdline/BUILD terhadap target dalam //BUILD menggunakan atribut visibility. Hal ini karena secara default target hanya terlihat oleh target lain dalam file BUILD yang sama. (Bazel menggunakan target visibilitas untuk mencegah masalah seperti library yang berisi detail implementasi yang 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 membuat 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 bangun:

./bazel-bin/src/main/java/com/example/cmdline/runner

Sekarang Anda telah mengubah project untuk di-build sebagai dua paket, masing-masing berisi satu paket menargetkan, dan memahami dependensi di antara mereka.

Menggunakan label untuk mereferensikan target

Dalam file BUILD dan command line, Bazel menggunakan label target untuk mereferensikan target - misalnya, //:ProjectRunner atau //src/main/java/com/example/cmdline:runner. Sintaksnya adalah sebagai berikut:

//path/to/package:target-name

Jika targetnya adalah target aturan, path/to/package adalah jalur ke yang berisi file BUILD, dan target-name adalah nama yang Anda berikan targetnya di file BUILD (atribut name). Jika targetnya adalah file target, maka 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 BUILD yang sama , Anda bahkan dapat melewati ID root ruang kerja // dan cukup menggunakan :target-name.

Misalnya, untuk target di file java-tutorial/BUILD, Anda tidak perlu melakukannya menentukan jalur paket, karena root ruang kerja itu sendiri adalah paket (//), dan dua label target Anda hanya //: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.

Kemas target Java untuk deployment

Sekarang, mari kita kemas target Java untuk deployment dengan membangun biner dengan semua dependensi runtime-nya. Hal ini memungkinkan Anda menjalankan biner di luar lingkungan pengembangan Anda.

Seperti yang Anda ingat, aturan build java_binary menghasilkan .jar dan skrip shell wrapper. Lihat isi dari 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 bukan dependensinya, Greeting.class. Skrip runner yang dihasilkan Bazel menambahkan greeter.jar ke classpath, jadi jika dibiarkan seperti ini, ia akan berjalan secara lokal, tetapi tidak akan berjalan secara mandiri di komputer lain. Untungnya, aturan java_binary memungkinkan Anda untuk membangun biner mandiri yang dapat di-deploy. Untuk membangunnya, 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 membuat runner_deploy.jar, yang dapat dijalankan secara mandiri lingkungan pengembangan Anda karena berisi runtime yang diperlukan dependensi. 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:

Selamat membangun!