Binary hasil go build pada chapter sebelumnya sudah bisa berjalan, tetapi masih membawa tiga bekal yang tidak selalu diinginkan di production: tabel simbol dan informasi debug (memperbesar ukuran), path direktori kerja mesin build (membocorkan struktur folder), dan versi yang tertulis manual di source. Perintah go build menerima flag yang mengendalikan ketiganya sekaligus. Chapter ini mempraktikkan -ldflags untuk memangkas binary dan menyuntik nomor versi, -trimpath untuk membersihkan path, serta build tags untuk memilih kode mana yang ikut dikompilasi.

Memangkas Ukuran dengan -s -w #

Compiler Go menyertakan symbol table dan data debug DWARF di dalam binary — bahan yang dibutuhkan Delve dan profiler saat development. Flag -ldflags "-s -w" menghapus keduanya saat linking: -s membuang symbol table, -w membuang data debug. Kita buktikan dengan program versi-inject.go di bagian bawah halaman (salin isinya sebagai main.go pada modul example.com/appflag):

bash
go build -o app-plain
go build -ldflags "-s -w" -o app-strip
ls -lh app-plain app-strip

Output yang Dihasilkan:

text
-rwxrwxr-x 1 z z 2.3M Oct 13 13:22 app-plain
-rwxrwxr-x 1 z z 1.5M Oct 13 13:22 app-strip

Ukuran turun dari 2.3M menjadi 1.5M — pemangkasan 35 persen untuk satu flag. Kedua binary menghasilkan output program yang sama persis:

bash
./app-plain
./app-strip

Output yang Dihasilkan:

text
aplikasi contoh build-flags
versi: dev
aplikasi contoh build-flags
versi: dev

Menyuntik Versi dengan -X #

Menulis nomor versi di source lalu meng-commit setiap rilis rawan tertinggal. Flag -X importpath.name=nilai mengisi variabel string dari luar saat build. Berikut program lengkapnya — berkas versi-inject.go di bagian bawah halaman:

go
package main

import "fmt"

// version diisi lewat flag -ldflags "-X main.version=..." saat build.
var version = "dev"

func main() {
	fmt.Println("aplikasi contoh build-flags")
	fmt.Println("versi:", version)
}

Output yang Dihasilkan:

text
aplikasi contoh build-flags
versi: dev

Mari kita telaah baris demi baris program di atas:

  1. Baris 5 (var version = "dev"): mendeklarasikan variabel package-level bertipe string. Nilai "dev" hanya berlaku bila tidak ada -X yang menimpanya — misalnya saat go run di mesin development.
  2. Baris 5 (penamaan version): flag -X menunjuk variabel dengan path lengkapnya; karena variabel ini berada di package main, targetnya adalah main.version.
  3. Baris 9 (fmt.Println("versi:", version)): mencetak isi variabel saat binary dijalankan, sehingga nilai hasil inject terlihat langsung.

Sekarang kita bangun dengan versi yang disuntikkan:

bash
go build -ldflags "-X main.version=1.2.3" -o app-inject
./app-inject

Output yang Dihasilkan:

text
aplikasi contoh build-flags
versi: 1.2.3

-trimpath: Membersihkan Path Build #

Tanpa flag tambahan, binary menyimpan path absolut direktori tempat ia dibangun — misalnya /home/z/proyek/appflag — di metadata dan stack trace. Flag -trimpath mengganti seluruh path berkas dengan path relatif module. Kita bisa menghitung jejaknya di dalam binary dengan grep:

bash
go build -o app-plain
go build -trimpath -o app-bersih
grep -ao "$(pwd)" app-plain | wc -l
grep -ao "$(pwd)" app-bersih | wc -l

Output yang Dihasilkan:

text
1
0

Binary pertama memuat path direktori kerja satu kali; binary kedua tidak sama sekali. Dua manfaatnya: binary tidak membocorkan struktur direktori mesin CI, dan dua orang yang membangun source sama dari folder berbeda menghasilkan binary identik — fondasi build yang dapat direproduksi.

Build Tags: Memilih Kode Saat Kompilasi #

Build tags memilih berkas mana yang ikut dikompilasi — kebalikan dari if runtime.GOOS == "linux" yang memutus saat runtime. Constraint ditulis sebagai komentar //go:build di baris pertama berkas. Siapkan modul kedua (go mod init example.com/tagdemo) berisi tiga berkas: main.go yang memanggil namaFitur(), lalu dua definisi fungsinya.

go
package main

import "fmt"

func main() {
	fmt.Println(namaFitur())
}
go
//go:build ekstra

package main

// namaFitur versi penuh, hanya ikut build jika tag "ekstra" aktif.
func namaFitur() string {
	return "fitur A: implementasi penuh (tag ekstra)"
}
go
//go:build !ekstra

package main

// namaFitur versi stub, dipakai ketika tag "ekstra" tidak aktif.
func namaFitur() string {
	return "fitur A: stub (tanpa tag)"
}

Dua berkas tersebut mendefinisikan fungsi bernama sama, tetapi constraint-nya saling melengkapi (ekstra dan !ekstra) sehingga tepat satu definisi aktif pada setiap build. Bukti nyatanya:

bash
go run .
go run -tags ekstra .

Output yang Dihasilkan:

text
fitur A: stub (tanpa tag)
fitur A: implementasi penuh (tag ekstra)

Mekanisme yang sama bekerja untuk platform: constraint //go:build linux membuat berkas hanya terkompilasi untuk target Linux, dan pasangan //go:build !linux menyediakan implementasi pengganti. Bedanya dengan pemeriksaan runtime.GOOS: kode di berkas non-aktif tidak dikompilasi sama sekali — berguna saat berkas itu memakai syscall atau library yang hanya ada di satu platform.

Kombinasi Lengkap untuk Rilis #

Satu perintah build production biasanya menggabungkan seluruh kendali di atas:

bash
go build -trimpath -ldflags "-s -w -X main.version=1.2.3" -o app-rilis
ls -lh app-rilis
./app-rilis

Output yang Dihasilkan:

text
-rwxrwxr-x 1 z z 1.5M Oct 13 13:28 app-rilis
aplikasi contoh build-flags
versi: 1.2.3
KendaliEfek ke binary
-ldflags "-s -w"Ukuran turun 35 persen, simbol dan debug hilang
-ldflags "-X main.version=..."Nomor versi tertanpa tanpa mengubah source
-trimpathPath build bersih, binary identik lintas mesin
-tags <nama>Memilih berkas sumber yang ikut terkompilasi

Binary app-rilis yang ramping dan beridentitas ini adalah kandidat sempurna untuk dikirim ke berbagai platform sekaligus. Chapter Cross Compilation membangun binary yang sama untuk Linux ARM, Windows, dan macOS.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

versi-inject.go

Unduh berkas
go
package main

import "fmt"

// version diisi lewat flag -ldflags "-X main.version=..." saat build.
var version = "dev"

func main() {
	fmt.Println("aplikasi contoh build-flags")
	fmt.Println("versi:", version)
}