😺

go buildのオプションを調べてみた

2022/03/23に公開

概要

  • 最近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-shared
      • 共有ライブラリにビルド
      • 呼び出し可能なシンボルはcgo/exportコメントを使用してエクスポートされた関数のみ
    • default
      • メインパッケージを実行可能ファイルへ、非メインパッケージを.aへ組み込む
    • shared
      • リストされている非メインパッケージを共有ライブラリに結合します
    • exe
      • リストされたメインパッケージとそれらが実行可能ファイルにインポートするすべてのものをビルド
    • pie

-ldflags : リンク呼び出しで渡すフラグを指定

-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 : コンパイラを指定

-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 : 未初期化メモリを検出

検出の例

  • 実際はオプション無しのビルドでは成功するが
  • 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呼び出しに使う引数

-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

Discussion