Lipat-lipat perintah yang sama diketik setiap hari: go run . saat mencoba, go test -v ./... sebelum commit, go build -o bin/aplikasi . saat rilis, plus gofmt dan go vet di sela-selanya. Setiap anggota tim mengingat urutannya masing-masing, dan yang baru bergabung harus menanya satu per satu. Makefile menyelesaikannya dengan satu pintu berbasis target: make test, make build, make lint — nama pendek yang sama untuk semua orang, didefinisikan sekali di dalam repositori. Pada chapter ini kita akan membaca anatomi Makefile untuk proyek Go, menjalankan seluruh target-nya sungguhan, lalu melihat alternatif task runner modern seperti Taskfile dan just.
Anatomi Makefile #
Make membaca berkas Makefile yang berisi aturan berbentuk target: kebutuhan diikuti recipe — baris perintah shell yang diawali satu karakter tab. Tiga elemen lain yang penting untuk proyek Go:
| Elemen | Peran |
|---|---|
NAMA := nilai | Variabel; recipe memakainya sebagai $(NAMA) |
.PHONY: target | Menandai target bukan berkas nyata sehingga selalu dieksekusi |
| Target pertama | Menjadi tujuan bawaan saat make dipanggil tanpa argumen |
Penanda .PHONY krusial untuk target clean: tanpa penanda itu, bila suatu saat ada berkas bernama clean di direktori, make akan menganggap target sudah “selesai” dan menolak menjalankan recipe. Karena pengecualian ini mudah dilupakan, praktik amannya mendaftarkan seluruh target tugas ke .PHONY.
Makefile Lengkap untuk Proyek Go #
Chapter ini menyertakan module kecil example.com/aplikasikasir di bundle: program kasir pada main.go, test tabel pada main_test.go, dan go.mod sebagai penanda module. Seluruh tugas proyek dirangkum dalam satu Makefile:
# Makefile aplikasi kasir — otomasi tugas berulang.
# Jalankan "make <target>" dari direktori yang sama dengan berkas ini.
BINARY := bin/aplikasi
PACKAGES := ./...
# Target pertama menjadi tujuan default saat "make" dipanggil tanpa argumen.
.PHONY: run test build fmt lint clean
run:
go run .
test:
go test -v $(PACKAGES)
build:
@mkdir -p bin
go build -o $(BINARY) .
fmt:
gofmt -l -w .
lint:
go vet $(PACKAGES)
clean:
rm -rf bin
Mari kita bedah Makefile baris demi baris:
- Baris
BINARY := bin/aplikasi: variabel lokasi binary — mengubah nama program cukup mengubah satu baris. - Baris
PACKAGES := ./...: seluruh package di module; dipakai targettestdanlintagar cakupannya identik. - Baris
.PHONY: run test build fmt lint clean: keenam target dinyatakan sebagai tugas, bukan berkas. - Target
run: menjalankan program lewatgo run .— make meng-echo setiap baris recipe sebelum mengeksekusinya. - Target
build: baris@mkdir -p bindiawali@sehingga perintahnya tidak di-echo;go build -o $(BINARY) .mengompilasi binary kebin/. - Target
clean: menghapus hasil build agar gudang repositori tetap bersih.
Program utama module ini menghitung total belanja dengan potongan member:
package main
import "fmt"
func main() {
total := hitungTotal([]int{15000, 25000}, true)
fmt.Println("Total belanja:", total)
}
// hitungTotal menjumlahkan seluruh harga item, lalu memberi
// potongan 10 persen bila pembeli adalah member.
func hitungTotal(harga []int, member bool) int {
total := 0
for _, h := range harga {
total += h
}
if member {
total -= total / 10
}
return total
}Program tersebut dapat dijalankan langsung tanpa make:
go run main.goOutput yang Dihasilkan:
Total belanja: 36000Test-nya memakai pola tabel yang telah dibahas pada chapter Unit Test :
package main
import "testing"
func TestHitungTotal(t *testing.T) {
uji := []struct {
nama string
harga []int
member bool
ingin int
}{
{"member", []int{15000, 25000}, true, 36000},
{"bukan member", []int{15000, 25000}, false, 40000},
{"tanpa item", nil, true, 0},
}
for _, u := range uji {
dapat := hitungTotal(u.harga, u.member)
if dapat != u.ingin {
t.Errorf("%s: hitungTotal(%v, %v) = %d; ingin %d", u.nama, u.harga, u.member, dapat, u.ingin)
}
}
}Menjalankan Target #
Seluruh perintah berikut dijalankan nyata pada bundle chapter ini memakai GNU Make 4.4. Target paling sering dipakai saat pengembangan:
make runOutput yang Dihasilkan:
go run .
Total belanja: 36000Baris pertama adalah echo recipe dari make; baris kedua keluaran programnya. Pemeriksaan sebelum commit dijahit dalam satu kata:
make testOutput yang Dihasilkan:
go test -v ./...
=== RUN TestHitungTotal
--- PASS: TestHitungTotal (0.00s)
PASSTarget lint menjalankan go vet — recipe-nya tetap di-echo, tetapi karena tidak ada temuan, tidak ada baris lain yang tercetak dan status keluar 0:
make lintOutput yang Dihasilkan:
go vet ./...Pemformatan dijaga oleh target fmt — recipe-nya di-echo, lalu tidak ada berkas lain yang dicetak karena seluruh kode sudah terformat:
make fmtOutput yang Dihasilkan:
gofmt -l -w .Rangkaian rilis ditutup build dan bersih-bersih:
make build
ls bin
make cleanOutput yang Dihasilkan:
go build -o bin/aplikasi .
aplikasi
rm -rf binSetelah make build, binary aplikasi hadir di direktori bin (terbukti dari keluaran ls), lalu make clean menghapusnya. Terakhir, pemanggilan tanpa argumen menjalankan target pertama — run:
makeOutput yang Dihasilkan:
go run .
Total belanja: 36000Alternatif Task Runner Modern #
Make tersedia hampir di semua lingkungan Unix, tetapi sintaksnya tua: tanda $ harus di-escape untuk dipakai dalam recipe, dan penanganan parameter cukup kaku. Dua alternatif populer di komunitas Go:
| Alat | Konfigurasi | Keunggulan |
|---|---|---|
| Task (go-task) | Taskfile.yml berbasis YAML | Sintaks modern, dukungan variable dan watch bawaan |
| just | justfile | Ringan, recipe mirip make tanpa kebutuhan berkas nyata |
Keduanya diinstal sebagai binary tunggal dan menawarkan pengalaman task test atau just test yang setara. Pilihan alat tidaklah sepenting kesepakatannya: selama seluruh tim memanggil pintu yang sama, tugas berulang tidak lagi bergantung pada ingatan masing-masing orang. Pintu berikutnya yang perlu dibangun adalah menjalankan make test dan make lint secara otomatis di server — topik chapter Continuous Integration.
Referensi #
- Dokumentasi perintah go — seluruh perintah go yang dibungkus target Makefile.
- Dokumentasi resmi Go — titik awal perintah build, test, dan vet.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
main_test.go
Unduh berkaspackage main
import "testing"
func TestHitungTotal(t *testing.T) {
uji := []struct {
nama string
harga []int
member bool
ingin int
}{
{"member", []int{15000, 25000}, true, 36000},
{"bukan member", []int{15000, 25000}, false, 40000},
{"tanpa item", nil, true, 0},
}
for _, u := range uji {
dapat := hitungTotal(u.harga, u.member)
if dapat != u.ingin {
t.Errorf("%s: hitungTotal(%v, %v) = %d; ingin %d", u.nama, u.harga, u.member, dapat, u.ingin)
}
}
}
main.go
Unduh berkaspackage main
import "fmt"
func main() {
total := hitungTotal([]int{15000, 25000}, true)
fmt.Println("Total belanja:", total)
}
// hitungTotal menjumlahkan seluruh harga item, lalu memberi
// potongan 10 persen bila pembeli adalah member.
func hitungTotal(harga []int, member bool) int {
total := 0
for _, h := range harga {
total += h
}
if member {
total -= total / 10
}
return total
}