Tutorial ini memperkenalkan dasar-dasar Bazel dengan menunjukkan cara membangun project Go (Golang). Anda akan mempelajari cara menyiapkan ruang kerja, membuat program kecil, mengimpor library, dan menjalankan pengujiannya. Selama proses ini, Anda akan mempelajari konsep utama Bazel, seperti target dan file BUILD
.
Perkiraan waktu penyelesaian: 30 menit
Sebelum memulai
Menginstal Bazel
Sebelum memulai, instal bazel terlebih dahulu jika Anda belum melakukannya.
Anda dapat memeriksa apakah Bazel telah diinstal dengan menjalankan bazel version
di direktori mana pun.
Instal Go (opsional)
Anda tidak perlu menginstal Go untuk membuat project Go dengan Bazel. Set aturan Bazel Go otomatis mendownload dan menggunakan toolchain Go, bukan menggunakan toolchain yang diinstal di mesin Anda. Tindakan ini memastikan semua developer dalam project membangun dengan versi Go yang sama.
Namun, Anda mungkin masih ingin menginstal toolchain Go untuk menjalankan perintah seperti go
get
dan go mod tidy
.
Anda dapat memeriksa apakah Go telah diinstal dengan menjalankan go version
di direktori mana pun.
Mendapatkan project contoh
Contoh Bazel disimpan di repositori Git, jadi Anda harus menginstal Git jika belum melakukannya. Untuk mendownload repositori contoh, jalankan perintah ini:
git clone https://github.com/bazelbuild/examples
Project contoh untuk tutorial ini ada di direktori examples/go-tutorial
.
Lihat isinya:
go-tutorial/
└── stage1
└── stage2
└── stage3
Ada tiga subdirektori (stage1
, stage2
, dan stage3
), masing-masing untuk
bagian tutorial ini yang berbeda. Setiap tahap dibangun berdasarkan tahap sebelumnya.
Membangun dengan Bazel
Mulai di direktori stage1
, tempat kita akan menemukan program. Kita dapat
membangunnya dengan bazel build
, lalu menjalankannya:
$ cd go-tutorial/stage1/
$ bazel build //:hello
INFO: Analyzed target //:hello (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //:hello up-to-date:
bazel-bin/hello_/hello
INFO: Elapsed time: 0.473s, Critical Path: 0.25s
INFO: 3 processes: 1 internal, 2 darwin-sandbox.
INFO: Build completed successfully, 3 total actions
$ bazel-bin/hello_/hello
Hello, Bazel! 💚
Kita juga dapat membangun dan menjalankan program dengan satu perintah bazel run
:
$ bazel run //:hello
bazel run //:hello
INFO: Analyzed target //:hello (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //:hello up-to-date:
bazel-bin/hello_/hello
INFO: Elapsed time: 0.128s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
INFO: Running command line: bazel-bin/hello_/hello
Hello, Bazel! 💚
Memahami struktur project
Lihat project yang baru saja kita buat.
hello.go
berisi kode sumber Go untuk program.
package main
import "fmt"
func main() {
fmt.Println("Hello, Bazel! 💚")
}
BUILD
berisi beberapa petunjuk untuk Bazel, yang memberi tahu apa yang ingin kita bangun.
Biasanya, Anda akan menulis file seperti ini di setiap direktori. Untuk project ini, kita
memiliki satu target go_binary
yang membangun program kita dari hello.go
.
load("@rules_go//go:def.bzl", "go_binary")
go_binary(
name = "hello",
srcs = ["hello.go"],
)
MODULE.bazel
melacak dependensi project Anda. File ini juga menandai direktori root project Anda, sehingga Anda hanya akan menulis satu file MODULE.bazel
per project. File ini memiliki tujuan yang sama dengan file go.mod
Go. Anda sebenarnya tidak memerlukan file
go.mod
dalam project Bazel, tetapi file tersebut mungkin masih berguna agar
Anda dapat terus menggunakan go get
dan go mod tidy
untuk pengelolaan dependensi. Set aturan Bazel Go dapat mengimpor dependensi dari go.mod
, tetapi kita akan membahasnya dalam tutorial lain.
File MODULE.bazel
kita berisi satu dependensi pada
rules_go, set aturan Go. Kita memerlukan dependensi ini karena Bazel tidak memiliki dukungan bawaan untuk Go.
bazel_dep(
name = "rules_go",
version = "0.50.1",
)
Terakhir, MODULE.bazel.lock
adalah file yang dibuat oleh Bazel yang berisi hash
dan metadata lainnya tentang dependensi kita. File ini mencakup dependensi implisit yang ditambahkan oleh Bazel itu sendiri, sehingga cukup panjang, dan kita tidak akan menampilkannya di sini. Sama seperti
go.sum
, Anda harus melakukan commit file MODULE.bazel.lock
ke kontrol sumber untuk
memastikan semua orang di project Anda mendapatkan versi yang sama untuk setiap dependensi. Anda tidak perlu mengedit MODULE.bazel.lock
secara manual.
Memahami file BUILD
Sebagian besar interaksi Anda dengan Bazel akan dilakukan melalui file BUILD
(atau
file BUILD.bazel
), jadi penting untuk memahami fungsinya.
File BUILD
ditulis dalam bahasa skrip yang disebut
Starlark, yaitu subset terbatas dari Python.
File BUILD
berisi daftar
target. Target adalah sesuatu yang dapat dibangun Bazel, seperti biner, library, atau pengujian.
Target memanggil fungsi aturan dengan daftar
atribut untuk mendeskripsikan apa yang harus dibangun. Contoh kita memiliki dua atribut: name
mengidentifikasi target di command line, dan srcs
adalah daftar jalur file sumber (dipisahkan dengan garis miring, relatif terhadap direktori yang berisi file BUILD
).
Aturan memberi tahu Bazel cara membangun target. Dalam contoh kami, kami menggunakan aturan
go_binary
. Setiap aturan menentukan tindakan
(perintah) yang menghasilkan serangkaian file output. Misalnya, go_binary
menentukan
tindakan kompilasi dan penautan Go yang menghasilkan file output yang dapat dieksekusi.
Bazel memiliki aturan bawaan untuk beberapa bahasa seperti Java dan C++. Anda dapat menemukan dokumentasinya di Build Encyclopedia. Anda dapat menemukan kumpulan aturan untuk banyak bahasa dan alat lainnya di Bazel Central Registry (BCR).
Menambahkan library
Lanjutkan ke direktori stage2
, tempat kita akan membuat program baru yang
mencetak ramalan Anda. Program ini menggunakan paket Go terpisah sebagai library yang
memilih pesan dari daftar pesan yang telah ditentukan.
go-tutorial/stage2
├── BUILD
├── MODULE.bazel
├── MODULE.bazel.lock
├── fortune
│ ├── BUILD
│ └── fortune.go
└── print_fortune.go
fortune.go
adalah file sumber untuk library. Library fortune
adalah
paket Go terpisah, sehingga file sumbernya berada di direktori terpisah. Bazel tidak mengharuskan Anda menyimpan paket Go di direktori terpisah, tetapi hal ini merupakan konvensi yang kuat dalam ekosistem Go, dan mengikutinya akan membantu Anda tetap kompatibel dengan alat Go lainnya.
package fortune
import "math/rand"
var fortunes = []string{
"Your build will complete quickly.",
"Your dependencies will be free of bugs.",
"Your tests will pass.",
}
func Get() string {
return fortunes[rand.Intn(len(fortunes))]
}
Direktori fortune
memiliki file BUILD
sendiri yang memberi tahu Bazel cara membangun
paket ini. Kita menggunakan go_library
di sini, bukan go_binary
.
Kita juga perlu menyetel atribut importpath
ke string yang dapat digunakan untuk mengimpor library ke file sumber Go lainnya. Nama ini harus berupa
jalur repositori (atau jalur modul) yang digabungkan dengan direktori dalam
repositori.
Terakhir, kita perlu menyetel atribut visibility
ke ["//visibility:public"]
.
visibility
dapat ditetapkan pada target
apa pun. Menentukan paket Bazel mana yang mungkin bergantung pada target ini. Dalam kasus ini, kita ingin agar target apa pun dapat bergantung pada library ini, jadi kita menggunakan nilai khusus //visibility:public
.
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "fortune",
srcs = ["fortune.go"],
importpath = "github.com/bazelbuild/examples/go-tutorial/stage2/fortune",
visibility = ["//visibility:public"],
)
Anda dapat mem-build library ini dengan:
$ bazel build //fortune
Selanjutnya, lihat cara print_fortune.go
menggunakan paket ini.
package main
import (
"fmt"
"github.com/bazelbuild/examples/go-tutorial/stage2/fortune"
)
func main() {
fmt.Println(fortune.Get())
}
print_fortune.go
mengimpor paket menggunakan string yang sama yang dideklarasikan dalam atribut
importpath
library fortune
.
Kita juga perlu mendeklarasikan dependensi ini ke Bazel. Berikut adalah file BUILD
di direktori
stage2
.
load("@rules_go//go:def.bzl", "go_binary")
go_binary(
name = "print_fortune",
srcs = ["print_fortune.go"],
deps = ["//fortune"],
)
Anda dapat menjalankannya dengan perintah di bawah.
bazel run //:print_fortune
Target print_fortune
memiliki atribut deps
, daftar target lain yang
bergantung padanya. File ini berisi "//fortune"
, string label yang merujuk ke target
di direktori fortune
bernama fortune
.
Bazel mengharuskan semua target mendeklarasikan dependensinya secara eksplisit dengan
atribut seperti deps
. Hal ini mungkin tampak rumit karena dependensi juga
ditentukan dalam file sumber, tetapi kejelasan Bazel memberikan keunggulan. Bazel
membangun grafik tindakan
yang berisi semua perintah, input, dan output sebelum menjalankan perintah apa pun,
tanpa membaca file sumber apa pun. Bazel kemudian dapat meng-cache hasil tindakan atau mengirim
tindakan untuk eksekusi jarak jauh tanpa logika
spesifik per bahasa bawaan.
Memahami label
Label adalah string yang digunakan Bazel
untuk mengidentifikasi target atau file. Label digunakan dalam argumen command line dan dalam
atribut file BUILD
seperti deps
. Kita sudah melihat beberapa di antaranya, seperti //fortune
,
//:print-fortune
, dan @rules_go//go:def.bzl
.
Label memiliki tiga bagian: nama repositori, nama paket, dan nama target (atau file).
Nama repositori ditulis di antara @
dan //
serta digunakan untuk merujuk ke
target dari modul Bazel yang berbeda (karena alasan historis, modul dan
repositori terkadang digunakan secara sinonim). Dalam label,
@rules_go//go:def.bzl
, nama repositori adalah rules_go
. Nama repositori dapat dihilangkan saat merujuk ke target di repositori yang sama.
Nama paket ditulis di antara //
dan :
serta digunakan untuk merujuk ke
target dari paket Bazel yang berbeda. Di label @rules_go//go:def.bzl
,
nama paketnya adalah go
. Paket Bazel adalah sekumpulan file dan
target yang ditentukan oleh file BUILD
atau BUILD.bazel
di direktori tingkat teratasnya.
Nama paketnya adalah jalur yang dipisahkan dengan garis miring dari direktori root modul
(yang berisi MODULE.bazel
) ke direktori yang berisi file BUILD
. Paket dapat menyertakan subdirektori, tetapi hanya jika subdirektori tersebut tidak berisi file BUILD
yang menentukan paketnya sendiri.
Sebagian besar project Go memiliki satu file BUILD
per direktori dan satu paket Go per
BUILD
file. Nama paket dalam label dapat dihilangkan saat merujuk ke target dalam direktori yang sama.
Nama target ditulis setelah :
dan merujuk ke target dalam paket.
Nama target dapat dihilangkan jika sama dengan komponen terakhir dari
nama paket (jadi //a/b/c:c
sama dengan //a/b/c
; //fortune:fortune
sama dengan
//fortune
).
Di command line, Anda dapat menggunakan ...
sebagai pengganti untuk merujuk ke semua target dalam paket. Hal ini berguna untuk membangun atau menguji semua target dalam repositori.
# Build everything
$ bazel build //...
Menguji project Anda
Selanjutnya, buka direktori stage3
, tempat kita akan menambahkan pengujian.
go-tutorial/stage3
├── BUILD
├── MODULE.bazel
├── MODULE.bazel.lock
├── fortune
│ ├── BUILD
│ ├── fortune.go
│ └── fortune_test.go
└── print-fortune.go
fortune/fortune_test.go
adalah file sumber pengujian baru kita.
package fortune
import (
"slices"
"testing"
)
// TestGet checks that Get returns one of the strings from fortunes.
func TestGet(t *testing.T) {
msg := Get()
if i := slices.Index(fortunes, msg); i < 0 {
t.Errorf("Get returned %q, not one the expected messages", msg)
}
}
File ini menggunakan variabel fortunes
yang tidak diekspor, sehingga perlu dikompilasi ke dalam paket Go yang sama dengan fortune.go
. Lihat file BUILD
untuk melihat cara kerjanya:
load("@rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "fortune",
srcs = ["fortune.go"],
importpath = "github.com/bazelbuild/examples/go-tutorial/stage3/fortune",
visibility = ["//visibility:public"],
)
go_test(
name = "fortune_test",
srcs = ["fortune_test.go"],
embed = [":fortune"],
)
Kita memiliki target fortune_test
baru yang menggunakan aturan go_test
untuk mengompilasi dan menautkan executable pengujian. go_test
perlu mengompilasi fortune.go
dan
fortune_test.go
bersama-sama dengan perintah yang sama, jadi kita menggunakan atribut embed
di sini untuk menggabungkan atribut target fortune
ke dalam
fortune_test
. embed
paling umum digunakan dengan go_test
dan go_binary
,
tetapi juga berfungsi dengan go_library
, yang terkadang berguna untuk kode yang dibuat.
Anda mungkin bertanya-tanya apakah atribut embed
terkait dengan paket
embed
Go, yang digunakan untuk mengakses file data yang
disalin ke dalam file yang dapat dieksekusi. Ini adalah tabrakan nama yang tidak menguntungkan: atribut embed
rules_go diperkenalkan sebelum paket embed
Go. Sebagai gantinya, rules_go
menggunakan embedsrcs
untuk mencantumkan file yang dapat dimuat dengan paket embed
.
Coba jalankan pengujian kami dengan bazel test
:
$ bazel test //fortune:fortune_test
INFO: Analyzed target //fortune:fortune_test (0 packages loaded, 0 targets configured).
INFO: Found 1 test target...
Target //fortune:fortune_test up-to-date:
bazel-bin/fortune/fortune_test_/fortune_test
INFO: Elapsed time: 0.168s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
//fortune:fortune_test PASSED in 0.3s
Executed 0 out of 1 test: 1 test passes.
There were tests whose specified size is too big. Use the --test_verbose_timeout_warnings command line option to see which ones these are.
Anda dapat menggunakan karakter pengganti ...
untuk menjalankan semua pengujian. Bazel juga akan membangun target yang bukan pengujian, sehingga dapat mendeteksi error kompilasi bahkan dalam paket yang tidak memiliki pengujian.
$ bazel test //...
Kesimpulan dan bacaan lebih lanjut
Dalam tutorial ini, kita telah membuat dan menguji project Go kecil dengan Bazel, dan kita telah mempelajari beberapa konsep inti Bazel selama proses tersebut.
- Untuk mulai membuat aplikasi lain dengan Bazel, lihat tutorial untuk C++, Java, Android, dan iOS.
- Anda juga dapat memeriksa daftar aturan yang direkomendasikan untuk bahasa lainnya.
- Untuk mengetahui informasi selengkapnya tentang Go, lihat modul rules_go, terutama dokumentasi aturan Go inti.
- Untuk mempelajari lebih lanjut cara bekerja dengan modul Bazel di luar project Anda, lihat dependensi eksternal. Khususnya, untuk mengetahui informasi tentang cara menggunakan modul dan toolchain Go melalui sistem modul Bazel, lihat Go dengan bzlmod.