😺
go buildのオプションを調べてみた
概要
- 最近goのコンパイラを追っていて
- 普段使ってるけどたまにこんなの有ったんだというのに遭遇するので調べてみました
- まだ全部じゃないはずなので多分後で追記等するはず
- https://pkg.go.dev/cmd/go
- ここのbuildの所にオプション一覧がある
go build [-o output] [build flags] [packages]
オプション
-a : 強制再ビルド
- cacheを使わずに全て強制再ビルドを行う
go build -a main.go
-buildmode : ビルドモードの指定
- https://pkg.go.dev/cmd/go#hdr-Build_modes
- staticかsharedを分けるものだと思えばだいたい合ってる
- ビルドするオブジェクトファイルの種類を指定できる
- archive
- 非メインパッケージを .aにビルド
- c-archive
- c-archiveにビルド
- オブジェクトをまとめて .aファイルにする
- http://menoie.cocolog-nifty.com/blog/2010/08/c-archive-files.html
- c-archiveにビルド
- c-shared
- 共有ライブラリにビルド
- 呼び出し可能なシンボルはcgo/exportコメントを使用してエクスポートされた関数のみ
- default
- メインパッケージを実行可能ファイルへ、非メインパッケージを.aへ組み込む
- shared
- リストされている非メインパッケージを共有ライブラリに結合します
- exe
- リストされたメインパッケージとそれらが実行可能ファイルにインポートするすべてのものをビルド
- pie
- PIE形式へコンパイルする
- 実行ファイル形式の一つ
- https://sugawarayusuke.hatenablog.com/entry/2018/01/22/025515
- http://0xcc.net/blog/archives/000109.html
- archive
-ldflags : リンク呼び出しで渡すフラグを指定
- リンクへ渡すフラグ
- よくある例としてはgdbでデバッグするときに
- こちらが詳しい
- https://kazuhira-r.hatenablog.com/entry/2021/03/08/003752
-installsuffix : インストール先を指定
- デフォルトと違う場所へインストールするための指定
- ほとんどの場合は$HOMEや/usr/local/などにインストールするが
- それ以外の場所にインストールしたい場合に指定する
-gccgoflags : gccgoへ渡すフラグ
- gccgoを使用してコンパイルする時に使用するフラグ
- 後述されるがgoには本家のgcコンパイラ以外に
- gccにあるgccgoというコンパイラが存在する
- そちらのコンパイラに渡すフラグ
-gcflags : gcへ渡すフラグ
- gcコンパイラへフラグを渡す
- よく知られているのが最適化を抑制してDebugするためのフラグ
- 詳細は tool compile --helpで見ることができる
- https://pkg.go.dev/cmd/compile
go tool compile -help
-compiler : コンパイラを指定
- gccgoとgoがある
- 基本はgoのほうが使われる
- gccgoはgccに含まれる方のgoのコンパイラ
- gccgo
-race : データ競合を抽出する
- データ競合を抽出する
- 上記の記事にかいてあるが
- 競合を検査するための処理を埋め込んでいるので
- 実行しないとエラーは出力されない
- またパフォーマンスに影響を及ぼすので本番環境向けのビルドでは使用すべきではない
go build -race main.go
raceの検出例
package main
import "context"
func main() {
ctx, cancel := context.WithCancel(context.Background())
var count int
go func() {
for i := 0; i < 10; i++ {
count++
}
cancel()
}()
go func() {
for i := 0; i < 10; i++ {
count++
}
cancel()
}()
select {
case <-ctx.Done():
return
}
}
$ go run main.go
go run -race main.go
==================
WARNING: DATA RACE
Read at 0x00c000124028 by goroutine 7:
main.main.func2()
/home/hiroyuki/tmp/mygobuild/main.go:16 +0x46
Previous write at 0x00c000124028 by goroutine 6:
main.main.func1()
/home/hiroyuki/tmp/mygobuild/main.go:10 +0x58
Goroutine 7 (running) created at:
main.main()
/home/hiroyuki/tmp/mygobuild/main.go:14 +0x1be
Goroutine 6 (finished) created at:
main.main()
/home/hiroyuki/tmp/mygobuild/main.go:8 +0x11c
==================
Found 1 data race(s)
exit status 66
-msan : 未初期化メモリを検出
- メモリサニタイザとの相互運用を可能にする
- メモリ未初期化を検出するツール
- memory sanitizer
- 使い方は下記が詳しい
検出の例
- 実際はオプション無しのビルドでは成功するが
- msanオプションをつけると初期化されてない変数を参照しているためエラーが起きる
package main
import "fmt"
func main() {
var a int
fmt.Println(a)
}
go build -msan
# runtime/cgo
gcc: error: unrecognized argument to '-fsanitize=' option: 'memory'
-work : build用の一時ディレクトリの場所を表示
- build用の一時ディレクトリの場所を表示してくれる
- その場所をみるとbuildされたファイルがある
$ go build -work main.go
WORK=/tmp/go-build3417807789
tree /tmp/go-build3417807789
/tmp/go-build3417807789
└── b001
├── _gomod_.go
├── _pkg_.a
├── exe
├── importcfg
└── importcfg.link
2 directories, 4 files
-asmflags : asm呼び出しに使う引数
- https://twitter.com/nobonobo/status/1065526650996027392
- ここを見るとtrimpathなどを渡したりするようです
-asmflags=-trimpath=$GOPATH
-x : ビルドによって実行されたコマンドを表示する
- ビルドによって実行されたコマンドを表示する
- こちらを観測するとGOROOTに置かれたコンパイル済みのファイルを
- linkerがリンクしていることが分かる
go build -x main.go
WORK=/var/folders/kr/4zffqydx6k1453qkpkmtcml00000gn/T/go-build778683197
mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime.modinfo
var __debug_modinfo__ = "0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tcommand-line-arguments\nmod\tcommand-line-arguments\t(devel)\t\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"
EOF
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
EOF
cd /Users/amesho/tmp/compildedebug
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.17 -complete -buildid 5BSOkrLpMKVTefZlhNku/5BSOkrLpMKVTefZlhNku -goversion go1.17.7 -D _/Users/amesho/tmp/compildedebug -importcfg $WORK/b001/importcfg -pack -c=4 ./main.go $WORK/b001/_gomod_.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cp $WORK/b001/_pkg_.a /Users/amesho/Library/Caches/go-build/2b/2b93eec6dcd1dea0c47d03c757b3b85fda9da04427527fa5995543181bc817e4-d # internal
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=$WORK/b001/_pkg_.a
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
packagefile errors=/usr/local/go/pkg/darwin_amd64/errors.a
packagefile internal/fmtsort=/usr/local/go/pkg/darwin_amd64/internal/fmtsort.a
packagefile io=/usr/local/go/pkg/darwin_amd64/io.a
packagefile math=/usr/local/go/pkg/darwin_amd64/math.a
packagefile os=/usr/local/go/pkg/darwin_amd64/os.a
packagefile reflect=/usr/local/go/pkg/darwin_amd64/reflect.a
packagefile strconv=/usr/local/go/pkg/darwin_amd64/strconv.a
packagefile sync=/usr/local/go/pkg/darwin_amd64/sync.a
packagefile unicode/utf8=/usr/local/go/pkg/darwin_amd64/unicode/utf8.a
packagefile internal/abi=/usr/local/go/pkg/darwin_amd64/internal/abi.a
packagefile internal/bytealg=/usr/local/go/pkg/darwin_amd64/internal/bytealg.a
packagefile internal/cpu=/usr/local/go/pkg/darwin_amd64/internal/cpu.a
packagefile internal/goexperiment=/usr/local/go/pkg/darwin_amd64/internal/goexperiment.a
packagefile runtime/internal/atomic=/usr/local/go/pkg/darwin_amd64/runtime/internal/atomic.a
packagefile runtime/internal/math=/usr/local/go/pkg/darwin_amd64/runtime/internal/math.a
packagefile runtime/internal/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile internal/reflectlite=/usr/local/go/pkg/darwin_amd64/internal/reflectlite.a
packagefile sort=/usr/local/go/pkg/darwin_amd64/sort.a
packagefile math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/itoa=/usr/local/go/pkg/darwin_amd64/internal/itoa.a
packagefile internal/oserror=/usr/local/go/pkg/darwin_amd64/internal/oserror.a
packagefile internal/poll=/usr/local/go/pkg/darwin_amd64/internal/poll.a
packagefile internal/syscall/execenv=/usr/local/go/pkg/darwin_amd64/internal/syscall/execenv.a
packagefile internal/syscall/unix=/usr/local/go/pkg/darwin_amd64/internal/syscall/unix.a
packagefile internal/testlog=/usr/local/go/pkg/darwin_amd64/internal/testlog.a
packagefile internal/unsafeheader=/usr/local/go/pkg/darwin_amd64/internal/unsafeheader.a
packagefile io/fs=/usr/local/go/pkg/darwin_amd64/io/fs.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.a
packagefile syscall=/usr/local/go/pkg/darwin_amd64/syscall.a
packagefile time=/usr/local/go/pkg/darwin_amd64/time.a
packagefile unicode=/usr/local/go/pkg/darwin_amd64/unicode.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
packagefile path=/usr/local/go/pkg/darwin_amd64/path.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=HPGTvia4hCV6Rsc9yJdV/5BSOkrLpMKVTefZlhNku/wflypVVZNnVC9uFBm6Ij/HPGTvia4hCV6Rsc9yJdV -extld=clang $WORK/b001/_pkg_.a
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out main
rm -r $WORK/b001/
追記: 20230913
デバックフラグ
- ドキュメントに載せてない unstableな debugging flagsがある
// Undocumented, unstable debugging flags.
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "")
-debug-actiongraph
- cmd/go/internal/work/exec.go
- 失敗して早期終了することを想定して、タイミング情報を入れずにアクショングラフを書くフラグ
// Write action graph, without timing information, in case we fail and exit early.
writeActionGraph := func() {
if file := cfg.DebugActiongraph; file != "" {
if strings.HasSuffix(file, ".go") {
// Do not overwrite Go source code in:
// go build -debug-actiongraph x.go
base.Fatalf("go: refusing to write action graph to %v\n", file)
}
js := actionGraphJSON(root)
if err := os.WriteFile(file, []byte(js), 0666); err != nil {
fmt.Fprintf(os.Stderr, "go: writing action graph: %v\n", err)
base.SetExitStatus(1)
}
}
}
writeActionGraph()
- 中を見る限りはこういう風に使うようだ
go build -debug-actiongraph x.go
サンプル
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
go build -debug-actiongraph hoge
- 以下の様なデータが出力される
- Arrayに入っている
{
"ID": 41,
"Mode": "built-in package",
"Package": "unsafe",
"Objdir": "/var/folders/kr/4zffqydx6k1453qkpkmtcml00000gn/T/go-build3242670013/b007/",
"Priority": 1,
"NeedBuild": true,
"TimeReady": "2023-01-31T16:42:41.389686+09:00",
"TimeStart": "2023-01-31T16:42:41.389718+09:00",
"TimeDone": "2023-01-31T16:42:41.389718+09:00",
"Cmd": null
}
-debug-trace
- trace機能があるのでこれを起動させる為のフラグ
func maybeStartTrace(pctx context.Context) context.Context {
if cfg.DebugTrace == "" {
return pctx
}
ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
if err != nil {
base.Fatalf("failed to start trace: %v", err)
}
base.AtExit(func() {
if err := close(); err != nil {
base.Fatalf("failed to stop trace: %v", err)
}
})
return ctx
}
サンプル
go build -debug-trace hoge
- jsonデータが出力される
- Buildの過程が出力されているようにみえる
,{"name":"Executing action (link-install brace)","ph":"E","ts":1675151297956658,"pid":0,"tid":8}
,{"name":"exec.Builder.Do (link-install brace)","ph":"E","ts":1675151297956712,"pid":0,"tid":0}
,{"name":"Running build command","ph":"E","ts":1675151297956727,"pid":0,"tid":0}
Chrome Tracing
- chrome tracing: chrome://tracing/
- 出力されたデータは
trace ファイル
というデータ構造をしていてchrome://tracing/
へD&Dすることでトレースデータを表示出来る。
Discussion