[インデックス 11496] ファイルの概要
このコミットは、Goコマンドラインツールに対する複数の改善を導入しています。主な変更点として、go clean
コマンドの追加、go build
およびgo test -c
の出力ファイル名の変更、そしてgo install
におけるクロスコンパイルされたバイナリの配置場所の改善が含まれます。これらの変更は、Go開発者がビルド成果物をより効率的に管理し、異なる環境での開発をスムーズに行えるようにすることを目的としています。
コミット
commit 00e9a54dad85724961dce513efbc835fd8365d5e
Author: Russ Cox <rsc@golang.org>
Date: Mon Jan 30 23:42:41 2012 -0500
go: improvements
Add 'go clean'.
Make 'go build' write to pkgname, not a.out.
Make 'go test -c' write to pkgname.test, not test.out.
Make 'go install' write alternate binaries to .../bin/goos_goarch/.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5600048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/00e9a54dad85724961dce513efbc835fd8365d5e
元コミット内容
このコミットは、Goツールチェインの使いやすさと機能性を向上させるための複数の改善をまとめています。具体的には以下の点が挙げられます。
go clean
コマンドの追加: ビルドによって生成されたオブジェクトファイルや実行ファイルを削除するための新しいコマンドが導入されました。これにより、クリーンなビルド環境を維持しやすくなります。go build
の出力ファイル名の変更: 実行可能ファイルのデフォルト名が、従来のa.out
から、ビルド対象のパッケージ名(Windowsでは.exe
拡張子付き)に変更されました。これにより、生成されるバイナリの識別が容易になります。go test -c
の出力ファイル名の変更: テストバイナリのデフォルト名が、従来のtest.out
から、パッケージ名に.test
を付加した形式(例:pkgname.test
)に変更されました。これもまた、テストバイナリの識別性を高めます。go install
におけるクロスコンパイルされたバイナリの配置場所の改善: 異なるOSやアーキテクチャ向けにクロスコンパイルされたバイナリが、$GOBIN
配下のgoos_goarch
サブディレクトリに配置されるようになりました。これにより、複数のターゲット向けのバイナリが整理されて管理されます。
変更の背景
このコミットが行われた背景には、Go言語の初期段階におけるツールチェインの成熟と、開発者の利便性向上のニーズがありました。
- ビルド成果物の管理の複雑化: Goのビルドシステムは、一時ディレクトリに多くのオブジェクトファイルを生成しますが、手動でのビルドや他のツール(例: Makefile)によって生成された古い成果物が残ることがありました。これらが蓄積されると、ディスクスペースの消費や、意図しない古いバイナリの実行といった問題を引き起こす可能性がありました。
go clean
の導入は、これらの不要なファイルを一掃し、クリーンな状態を保つための標準的なメカニズムを提供します。 - 出力ファイル名の汎用性: 従来の
a.out
やtest.out
といった汎用的な出力ファイル名は、複数の実行可能ファイルやテストバイナリを扱う際に、名前の衝突や識別性の問題を引き起こす可能性がありました。特に、異なるパッケージのバイナリが同じディレクトリに置かれる場合、どのファイルがどのパッケージに属するのかを判別するのが困難でした。パッケージ名に基づいた命名規則への変更は、この問題を解決し、より直感的で管理しやすいファイル名を提供します。 - クロスコンパイルの利便性向上: Goは設計当初からクロスコンパイルを強力にサポートしていますが、異なるターゲット向けのバイナリがすべて同じ
$GOBIN
ディレクトリに置かれると、管理が煩雑になるという課題がありました。goos_goarch
サブディレクトリへの配置は、クロスコンパイルされたバイナリをターゲット環境ごとに明確に分離し、開発者が特定の環境向けのバイナリを容易に見つけられるようにするための改善です。これは、特に組み込みシステムや異なるプラットフォームへのデプロイを行う開発者にとって大きな利点となります。 - Goツールチェインの標準化: これらの変更は、Goの公式ツールチェインが提供する機能の範囲を広げ、より一貫性のある開発体験を提供するための取り組みの一環です。特に、
go clean
のような基本的なメンテナンスコマンドの追加は、Goエコシステム全体の健全性を高める上で重要でした。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語およびGoツールチェインに関する基本的な知識が必要です。
- Goパッケージとモジュール:
- パッケージ: Goのコードはパッケージにまとめられます。関連する機能は同じパッケージに属し、パッケージはディレクトリ構造に対応します。
main
パッケージは実行可能なプログラムのエントリポイントです。 - インポートパス: パッケージを一意に識別するためのパスです(例:
fmt
,github.com/user/repo/pkg
)。
- パッケージ: Goのコードはパッケージにまとめられます。関連する機能は同じパッケージに属し、パッケージはディレクトリ構造に対応します。
- Goツールチェインの基本コマンド:
go build
: ソースコードをコンパイルして実行可能バイナリを生成します。通常、カレントディレクトリのmain
パッケージをビルドし、実行可能ファイルを生成します。go test
: パッケージのテストを実行します。-c
フラグを付けると、テストを実行せずにテストバイナリをコンパイルして出力します。go install
: パッケージをコンパイルし、その結果生成される実行可能バイナリやアーカイブファイルを、$GOBIN
または$GOPATH/bin
(実行可能ファイルの場合)、$GOPATH/pkg
(ライブラリの場合)などの標準的なインストール場所に配置します。
- Go環境変数:
GOROOT
: Goのインストールディレクトリを指します。Goの標準ライブラリやツールチェインのバイナリがここに格納されています。GOBIN
:go install
コマンドによって生成された実行可能バイナリが配置されるディレクトリを指します。設定されていない場合、$GOPATH/bin
がデフォルトで使用されます。GOOS
とGOARCH
: ビルドターゲットのオペレーティングシステム(例:linux
,windows
,darwin
)とアーキテクチャ(例:amd64
,arm
,386
)を指定する環境変数です。これらの変数を設定することで、クロスコンパイルが可能になります。
- ビルド成果物の種類:
- オブジェクトファイル (
.o
,.5
,.6
,.8
): コンパイラによって生成される中間ファイルで、アセンブリコードや機械語に変換されたソースコードの断片が含まれます。 - アーカイブファイル (
.a
): 複数のオブジェクトファイルをまとめたライブラリファイルです。 - 実行可能ファイル (
.exe
または拡張子なし): リンクによって生成される、直接実行可能なプログラムです。 - テストバイナリ:
go test -c
によって生成される、テストコードを含む実行可能ファイルです。
- オブジェクトファイル (
- Makefile: 以前のGoプロジェクトでは、ビルドプロセスを管理するためにMakefileが使用されることがありました。このコミットの
go clean
は、Makefileによって残された古い成果物も対象としています。
技術的詳細
このコミットは、Goツールチェインの内部実装にいくつかの重要な変更を加えています。
-
go clean
コマンドの実装 (src/cmd/go/clean.go
の追加):- 新しいファイル
src/cmd/go/clean.go
が追加され、go clean
コマンドのロジックが実装されています。 - このコマンドは、指定されたインポートパスに対応するソースディレクトリから、以下の種類のファイルを削除します。
- 古いMakefileベースのビルドによって残されたディレクトリやファイル(例:
_obj/
,_test/
,_testmain.go
,test.out
,build.out
,*.[568ao]
)。 go build
によって生成された実行可能ファイル(例:DIR
,DIR.exe
)。ここでDIR
はディレクトリの最終要素名です。go test -c
によって生成されたテストバイナリ(例:DIR.test
,DIR.test.exe
)。go build MAINFILE.go
のように特定のGoソースファイルを指定してビルドした場合に生成される実行可能ファイル(例:MAINFILE
,MAINFILE.exe
)。
- 古いMakefileベースのビルドによって残されたディレクトリやファイル(例:
-i
フラグは、go install
によってインストールされたアーカイブやバイナリも削除します。-n
フラグは、削除コマンドを実行せずに表示します。-r
フラグは、指定されたパッケージの依存関係を再帰的にクリーンアップします。-x
フラグは、削除コマンドを実行しながら表示します。clean
関数は、パッケージのディレクトリを読み込み、削除対象のファイルを特定し、os.Remove
やos.RemoveAll
を使用して削除を実行します。
- 新しいファイル
-
go build
の出力ファイル名の変更 (src/cmd/go/build.go
):runBuild
関数内で、main
パッケージをビルドし、かつ-o
フラグが指定されていない場合、出力ファイル名がa.out
から変更されます。- 変更後のファイル名は、パッケージのインポートパスの最終要素(パッケージ名)になります。Windows環境では
.exe
拡張子が自動的に付加されます。 - これにより、
go build
はデフォルトで./myprogram
(または./myprogram.exe
)のような、より意味のある名前の実行可能ファイルを生成するようになります。
// src/cmd/go/build.go の変更点 if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" { _, *buildO = path.Split(pkgs[0].ImportPath) if b.goos == "windows" { *buildO += ".exe" } }
-
go test -c
の出力ファイル名の変更 (src/cmd/go/test.go
,src/cmd/go/doc.go
,src/cmd/go/testflag.go
):go test -c
コマンドの出力ファイル名がtest.out
からpkgname.test
に変更されました。src/cmd/go/test.go
のtest
関数内で、テストバイナリのターゲット名がtestBinary
変数によって決定されるようになり、これがパッケージのインポートパスの最終要素に.test
を付加したものになります。- ドキュメント (
src/cmd/go/doc.go
) やテストフラグの定義 (src/cmd/go/testflag.go
) も、この新しい命名規則に合わせて更新されています。
// src/cmd/go/test.go の変更点 // Use last element of import path, not package name. // They differ when package name is "main". _, elem := path.Split(p.ImportPath) testBinary := elem + ".test" // ... // Action for building pkg.test. pmain = &Package{ Name: "main", Dir: testDir, // ... } a := b.action(modeBuild, modeBuild, pmain) a.objdir = testDir + string(filepath.Separator) a.objpkg = filepath.Join(testDir, "main.a") a.target = filepath.Join(testDir, testBinary) + b.exe // ここで testBinary が使われる
-
go install
におけるクロスコンパイルされたバイナリの配置場所の改善 (src/cmd/go/pkg.go
):scanPackage
関数内で、go install
がクロスコンパイルを行う場合(ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH
)、バイナリのインストールパスが$GOBIN
直下ではなく、$GOBIN/goos_goarch/
サブディレクトリに設定されるようになりました。- これにより、例えばLinux上でWindows向けのバイナリをビルドしてインストールすると、
$GOBIN/windows_amd64/
のようなディレクトリに配置されます。
// src/cmd/go/pkg.go の変更点 if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH { // Install cross-compiled binaries to subdirectories of bin. elem = ctxt.GOOS + "_" + ctxt.GOARCH + "/" + elem } p.target = filepath.Join(t.BinDir(), elem)
-
GOROOT
とGOBIN
の扱いの一貫性向上 (src/cmd/go/build.go
):builder
構造体からgoroot
とgobin
フィールドが削除され、代わりにグローバル変数goroot
とgobin
が導入されました。- これにより、
GOROOT
やGOBIN
のパスを参照する際に、builder
インスタンスのフィールドではなく、グローバルな定義を使用するようになり、コード全体での一貫性が向上しました。特に、fmtcmd
関数でのパスの置換処理や、Goツールチェインのバイナリ(go-tool/6g
,go-tool/6a
など)へのパス構築において、この変更が適用されています。
// src/cmd/go/build.go の変更点 // builder struct から goroot と gobin フィールドが削除 type builder struct { work string // the temporary work directory (ends in filepath.Separator) arch string // e.g., "6" goarch string // the $GOARCH goos string // the $GOOS exe string // the executable suffix - "" or ".exe" gcflags []string // additional flags for Go compiler actionCache map[cacheKey]*action // a cache of already-constructed actions } // グローバル変数として定義 var ( gobin = build.Path[0].BinDir() goroot = build.Path[0].Path ) // fmtcmd 関数での使用例 // cmd = strings.Replace(cmd, b.gobin, "$GOBIN", -1) から cmd = strings.Replace(cmd, gobin, "$GOBIN", -1) // cmd = strings.Replace(cmd, b.goroot, "$GOROOT", -1) から cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
コアとなるコードの変更箇所
go build
の出力ファイル名変更
src/cmd/go/build.go
のrunBuild
関数内での変更。
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -113,7 +114,10 @@ func runBuild(cmd *Command, args []string) {
}
if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
- *buildO = "a.out"
+ _, *buildO = path.Split(pkgs[0].ImportPath)
+ if b.goos == "windows" {
+ *buildO += ".exe"
+ }
}
if *buildO != "" {
go clean
コマンドの追加
src/cmd/go/clean.go
の新規追加ファイルの一部。
// src/cmd/go/clean.go (抜粋)
package main
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var cmdClean = &Command{
UsageLine: "clean [-i] [-r] [-n] [-x] [importpath...]",
Short: "remove object files",
Long: `
Clean removes object files from package source directories.
The go command builds most objects in a temporary directory,
so go clean is mainly concerned with object files left by other
tools or by manual invocations of go build.
Specifically, clean removes the following files from each of the
source directories corresponding to the import paths:
_obj/ old object directory, left from Makefiles
_test/ old test directory, left from Makefiles
_testmain.go old gotest file, left from Makefiles
test.out old test log, left from Makefiles
build.out old test log, left from Makefiles
*.[568ao] object files, left from Makefiles
DIR(.exe) from go build
DIR.test(.exe) from go test -c
MAINFILE(.exe) from go build MAINFILE.go
In the list, DIR represents the final path element of the
directory, and MAINFILE is the base name of any Go source
file in the directory that is not included when building
the package.
The -i flag causes clean to remove the corresponding installed
archive or binary (what 'go install' would create).
The -n flag causes clean to print the remove commands it would execute,
but not run them.
The -r flag causes clean to be applied recursively to all the
dependencies of the packages named by the import paths.
The -x flag causes clean to print remove commands as it executes them.
`,
}
// ... (フラグ変数とinit関数、runClean関数など)
func clean(p *Package) {
// ... (ディレクトリの読み込み、削除対象の特定ロジック)
// go build や go test -c で生成されたファイルの削除ロジック
_, elem := filepath.Split(p.Dir)
allRemove := []string{
elem,
elem + ".exe",
elem + ".test",
elem + ".test.exe",
}
for _, dir := range dirs {
name := dir.Name()
if packageFile[name] {
continue
}
if !dir.IsDir() && strings.HasSuffix(name, ".go") {
base := name[:len(name)-len(".go")]
allRemove = append(allRemove, base, base+".exe")
}
}
// ... (実際のファイル削除処理)
}
go install
におけるクロスコンパイルバイナリの配置変更
src/cmd/go/pkg.go
のscanPackage
関数内での変更。
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -273,6 +275,10 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string
if t.Goroot && isGoTool[p.ImportPath] {
p.target = filepath.Join(t.Path, "bin/go-tool", elem)
} else {
+ if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
+ // Install cross-compiled binaries to subdirectories of bin.
+ elem = ctxt.GOOS + "_" + ctxt.GOARCH + "/" + elem
+ }
p.target = filepath.Join(t.BinDir(), elem)
}
if ctxt.GOOS == "windows" {
コアとなるコードの解説
go build
の出力ファイル名変更 (src/cmd/go/build.go
)
この変更は、go build
コマンドがmain
パッケージをビルドする際のデフォルトの出力ファイル名を制御します。
len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == ""
という条件は、単一のmain
パッケージがビルドされ、かつユーザーが-o
フラグで明示的な出力ファイル名を指定していない場合に真となります。path.Split(pkgs[0].ImportPath)
は、パッケージのインポートパス(例:github.com/user/myprogram
)から、最後の要素(myprogram
)を抽出します。これが新しいデフォルトの実行可能ファイル名となります。if b.goos == "windows"
の条件は、ビルドターゲットがWindowsである場合に、自動的に.exe
拡張子をファイル名に追加します。これにより、Windows上での実行可能ファイルの慣習に適合します。 この変更により、go build
はより直感的で、パッケージ名に紐づいた実行可能ファイルを生成するようになり、複数のプロジェクトやバイナリを扱う際の識別性が向上しました。
go clean
コマンドの追加 (src/cmd/go/clean.go
)
src/cmd/go/clean.go
は、Goプロジェクトのビルド成果物をクリーンアップするための新しいgo clean
コマンドを定義しています。
cmdClean
構造体は、コマンドの利用方法、短い説明、長い説明(詳細な動作とフラグの説明を含む)を定義しています。特に、_obj/
や_test/
のような古いMakefileベースの成果物、そしてgo build
やgo test -c
によって生成される特定のファイル(例:DIR
,DIR.test
)を削除対象とすることが明記されています。clean
関数は、実際にファイルの削除ロジックを実装しています。この関数は、パッケージのディレクトリをスキャンし、allRemove
スライスに削除対象のファイル名を収集します。これには、ディレクトリ名に基づく実行可能ファイルやテストバイナリ、および.go
ファイル名に基づく実行可能ファイルが含まれます。cleanDir
、cleanFile
、cleanExt
といったマップは、削除すべき特定のディレクトリ、ファイル、拡張子を定義しており、主に古いビルドシステムからの残骸をターゲットにしています。-i
,-n
,-r
,-x
といったフラグは、クリーンアップ動作を細かく制御するためのオプションを提供します。例えば、-n
はドライラン(削除せずに表示のみ)、-r
は依存関係の再帰的なクリーンアップを可能にします。 このコマンドの導入により、Go開発者はビルド環境を簡単にリセットし、不要なファイルを削除できるようになり、プロジェクトの管理がより容易になりました。
go install
におけるクロスコンパイルバイナリの配置変更 (src/cmd/go/pkg.go
)
この変更は、go install
コマンドがクロスコンパイルされたバイナリを配置する際の動作を改善します。
if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH
の条件は、現在の実行環境のOS/アーキテクチャと、ビルドターゲットのOS/アーキテクチャが異なる場合に真となります。これはクロスコンパイルが行われていることを意味します。- この条件が真の場合、
elem
変数(通常はパッケージ名)の前にctxt.GOOS + "_" + ctxt.GOARCH + "/"
というプレフィックスが追加されます。これにより、最終的なインストールパスは$GOBIN/goos_goarch/パッケージ名
のようになります。 この変更により、異なるターゲット向けのバイナリが$GOBIN
ディレクトリ内で明確に分離され、開発者は特定のプラットフォーム向けのバイナリを容易に識別し、管理できるようになりました。これは、特に複数の環境にデプロイするアプリケーションを開発する際に非常に有用です。
関連リンク
- Go Code Review 5600048: このコミットの元のコードレビューページ。詳細な議論や変更の背景が確認できます。 https://golang.org/cl/5600048
- Go Command Documentation: Goコマンド全般に関する公式ドキュメント。
go build
,go test
,go install
,go clean
などのコマンドの詳細が記載されています。 https://pkg.go.dev/cmd/go
参考にした情報源リンク
- Go公式ドキュメント: Go言語の公式ウェブサイトおよびドキュメントは、Goツールチェインの動作や環境変数に関する詳細な情報を提供しています。 https://go.dev/doc/
- Go Modules Reference: Goモジュールに関する公式ドキュメント。パッケージの解決やビルドプロセスに関する理解を深めるのに役立ちます。 https://go.dev/ref/mod
- Go Cross-compilation: Goのクロスコンパイル機能に関する情報源。 https://go.dev/doc/install/source#environment (環境変数 GOOS, GOARCH の説明) https://go.dev/doc/code (Goコードの構成とビルドに関する一般的な情報)
- Git Diff Format:
diff
コマンドの出力形式に関する一般的な情報。コードの変更点を理解するのに役立ちます。 https://git-scm.com/docs/git-diff https://www.gnu.org/software/diffutils/manual/html_node/diff-Format.html