Menu

go run・go build・go installの違い:Goのコンパイルと実行

go run、go build、go installがそれぞれ何をするのか、Goがバイナリにどう名前を付けるのか、GOOSとGOARCHでのクロスコンパイル、そしてコミット前に実行したいgo fmtとgo vetのチェックを解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

go run はプログラムのコンパイルと実行を一度に行い、バイナリを残しません。go build はコンパイルして、カレントディレクトリに実行ファイルを残します。go install はコンパイルして、実行ファイルを $HOME/go/bin に置きます。3つともネイティブのマシンコードにコンパイルし、何かを解釈実行するものはありません。

コマンドコンパイル実行バイナリを残すバイナリの置き場所
go run .するする残さない一時ディレクトリ、実行後に削除
go buildするしない残すカレントディレクトリ(または -o path
go installするしない残す$GOBIN、なければ $GOPATH/bin

以下のプログラムは、このページの例で使うものです。エディタは go run と同じように実行します。

go run

go run はパッケージ(通常はカレントディレクトリを表す .)か、.go ファイルのリストを受け取ります。

go run .
go run main.go
go run ./cmd/server

パッケージより後ろに書いたものはすべて、引数としてプログラムに渡されます。

go run . --port 8080 verbose
version: dev
built for: linux/amd64
args: [--port 8080 verbose]

go run main.go より go run . を使いましょう。ファイル名を指定するとそのファイルだけがコンパイルされるので、パッケージに2つ目のファイルができた途端、そちらで定義した関数が undefined と報告されます。このエラーはパッケージとインポートのページで詳しく扱っています。

go run は、リモートのプログラムを特定のバージョンでインストールせずに実行することもできます。コード生成ツールで便利です。

go run golang.org/x/tools/cmd/stringer@v0.30.0 -type=Color

go build

go build はカレントディレクトリのパッケージをコンパイルします。main パッケージなら実行ファイルを書き出し、ライブラリのパッケージならコンパイルしてエラーを報告し、結果を捨てます。

go mod init example.com/hello
go build
ls
go.mod  hello  main.go

バイナリの名前は次のルールで決まります。

実行したコマンドバイナリ名
モジュール example.com/hellogo buildhello
go build ./cmd/serverserver
go build main.gomain(最初のファイル名から)
go build -o bin/app .bin/app
上記いずれかを GOOS=windows同じ名前に .exe を付けたもの

いつも使うことになる形があと2つあります。

go build ./...      # compile every package in the module
go vet ./...        # static checks, covered below

./... は「このディレクトリとその下のすべてのディレクトリ」という意味です。main パッケージが複数ある状態で go build ./... を実行してもバイナリは書き出されません。「全部コンパイルできるか」をすばやく確認する用途になります。

便利なビルドフラグ

フラグ効果
-o name出力ファイルまたはディレクトリ
-vコンパイル中のパッケージ名を表示
-raceデータ競合検出器付きでビルド(遅く、バイナリも大きくなる。テスト用)
-trimpath再現可能なビルドのため、ローカルのファイルシステムのパスをバイナリから除く
-ldflags "-s -w"シンボルテーブルとDWARFのデバッグ情報を除き、バイナリを小さくする
-ldflags "-X main.version=1.4.0"リンク時に文字列変数を設定する
-tags name//go:build name 制約で守られたファイルを含める

多くのGoプロジェクトは、この -X フラグでバイナリにバージョンを埋め込みます。使えるのはパッケージレベルの string 変数だけです(定数には使えません)。

go build -o app -ldflags "-X main.version=1.4.0" .
./app
version: 1.4.0
built for: linux/amd64
args: []

バイナリには、モジュールのバージョンと、Go 1.18以降はビルド元のVCSのコミットも記録されます。go version -m ./app でそれらを表示でき、プログラムからは runtime/debug.ReadBuildInfo で読み取れます。

go install

go installgo build とまったく同じようにビルドし、実行ファイルを $GOBIN に移動します。GOBIN が設定されていなければ $GOPATH/bin(デフォルトは $HOME/go/bin)です。

go install .
go env GOPATH

最もよくある用途は、Goで書かれたツールのインストールです。@version を付けると、go.mod に触れずにプログラムをインストールできます。

go install golang.org/x/tools/gopls@latest
go install honnef.co/go/tools/cmd/staticcheck@latest

インストール後にシェルがツールを見つけられない場合は、$HOME/go/binPATH に入っていません。シェルのプロファイルに export PATH=$PATH:$(go env GOPATH)/bin を追加してください。

GOOSとGOARCHでクロスコンパイルする

Goは、どのマシンからでも、追加のツールチェーンなしで別のOSやCPU向けにビルドできます。ビルドコマンドに2つの環境変数を設定します。

GOOS=linux GOARCH=amd64 go build -o app-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o app-linux-arm64 .
GOOS=darwin GOARCH=arm64 go build -o app-macos .
GOOS=windows GOARCH=amd64 go build -o app.exe .

PowerShellでは環境変数の設定方法が異なります。

$env:GOOS = "linux"; $env:GOARCH = "amd64"; go build -o app-linux .

よく使う組み合わせは次のとおりです。

GOOSGOARCH対象
linuxamd64大半のサーバーとコンテナ
linuxarm64AWS Graviton、64ビットOSのRaspberry Pi
darwinarm64AppleシリコンのMac
darwinamd64IntelのMac
windowsamd6464ビットWindows
jswasmブラウザで動くWebAssembly

go tool dist list はサポートされている組み合わせをすべて表示します(Go 1.24では48個)。

クロスコンパイルがこれほど簡単なのは、純粋なGoのコードの場合だけです。cgoを使うパッケージ(Cのコード、たとえば一部のSQLiteドライバ)には、対象向けのCクロスコンパイラが必要です。クロスコンパイル時はcgoがデフォルトで無効になり、CGO_ENABLED=0 を明示的に設定すると完全に静的なLinuxバイナリも得られます。最小限の scratch やdistrolessのコンテナイメージではこれが欲しいところです。

go fmtとgo vet

どのワークフローにも、そしてCIにも入れておきたいチェックが2つあります。

go fmt はファイルを公式の唯一のスタイルに書き換えます。タブ、そろえたフィールド、正規化された空白です。これは gofmt -l -w のラッパーです。

go fmt ./...
gofmt -l .     # list files that are not formatted; empty output means clean

go vet は、コンパイルは通るもののほぼ確実に間違っているコードを報告します。Printfの書式の不一致が典型例です。

package main

import "fmt"

func main() {
	count := 3
	fmt.Printf("%s items\n", count)
}
go vet ./...
# example.com/hello
# [example.com/hello]
./main.go:7:2: fmt.Printf format %s has arg count of wrong type int

このプログラムはコンパイルでき、%!s(int=3) items と表示します。vetが捕まえるためにあるのは、まさにこの種のバグです。他にも、sync.Mutex の値コピー、到達不能なコード、構文が間違った構造体タグ、呼ばれない context.CancelFunc などをチェックします。go test はこれらのチェックの一部を自動で実行しますが、go build は何も実行しません。

その他のgoサブコマンド

コマンド用途
go test ./...テストを実行する
go mod tidy足りない依存関係を追加し、使っていないものを削除する
go get pkg@version依存関係を追加または変更する
go clean -cacheビルドキャッシュを空にする
go envGoの設定を表示する
go doc fmt.Printlnターミナルでドキュメントを表示する
go list -m allビルドに含まれるすべてのモジュールを一覧表示する

2回目のビルドが速い理由

Goはコンパイル済みのパッケージをビルドキャッシュ(go env GOCACHE)に保存します。再ビルドでは、ソースか依存関係が変わったパッケージだけを再コンパイルするので、変更のないプロジェクトで go run . を実行するとほぼ即座に始まります。Goのバージョンや環境変数を変えた後にビルドの挙動がおかしくなったら、go clean -cache でキャッシュを消せます。必要になることはめったにありません。

よくある質問

go runとgo buildの違いは何ですか?

go run はプログラムを一時ディレクトリにコンパイルして実行し、バイナリを捨てます。go build はプログラムをコンパイルしてカレントディレクトリにバイナリを書き出すので、もう一度実行したり別の場所にコピーしたりできます。開発中は go run、実行ファイルが必要なときは go build を使います。

go buildの出力ファイル名はどう指定しますか?

-o を使います。go build -o myapp .myapp を書き出します(Windowsでは -o myapp.exe と自分で書きます)。-o がない場合、go build はパッケージのインポートパスの最後の要素か、.go ファイルを渡したときは最初のファイルの名前をバイナリ名にし、Windows向けのビルドでは .exe を付けます。

GoをLinuxやWindows向けにクロスコンパイルするには?

ビルド時に GOOSGOARCH を設定します:GOOS=linux GOARCH=amd64 go build -o app-linux .GOOS=windows GOARCH=amd64 go build .(こちらは .exe を生成します)。純粋なGoのコードなら追加のツールチェーンは不要です。go tool dist list でサポートされている組み合わせがすべて表示されます。

go installはバイナリをどこに置きますか?

設定されていれば $GOBIN に、そうでなければ $GOPATH/bin(デフォルトは $HOME/go/bin)に置きます。インストールしたツールを名前で実行するには、そのディレクトリを PATH に追加します。go install example.com/tool@latest なら、モジュールに追加せずにツールをインストールできます。

go buildはgo vetを実行しますか?

しません。go build はコンパイルするだけです。go testgo vet のチェックの一部を自動で実行しますが、すべてのチェックを行うには go vet ./... を自分で実行します。一般的にはCIで gofmt -l . と並べて実行します。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める