[インデックス 17324] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
における go clean
コマンドの挙動を修正するものです。具体的には、実行可能ファイルのクリーンアップ処理が package main
の場合にのみ適用されるように変更されています。これにより、package main
ではないディレクトリ名と同じ名前のファイルが誤って削除されることを防ぎます。
コミット
commit 2f6e9a1e243c64f22d0f0542a05343bacf6d580b
Author: Andrew Gerrand <adg@golang.org>
Date: Mon Aug 19 16:22:33 2013 +1000
cmd/go: only try to clean executables for package main
Fixes #5665.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12807044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2f6e9a1e243c64f22d0f0542a05343bacf6d580b
元コミット内容
cmd/go: only try to clean executables for package main
Fixes #5665.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12807044
変更の背景
この変更は、GoのIssue #5665 を修正するために行われました。
Issue #5665 は、「go clean
が package main
ではないディレクトリで、ディレクトリ名と同じ名前のファイルを削除してしまう」というバグを報告しています。
go build
コマンドは、package main
の場合、デフォルトでディレクトリ名と同じ名前の実行可能ファイルを生成します。しかし、package main
ではないパッケージ(例えばライブラリパッケージ)の場合、go build
は実行可能ファイルを生成しません。
従来の go clean
コマンドは、無条件にディレクトリ名と同じ名前のファイル(およびその .exe
拡張子付きのファイル)を削除対象に含めていました。このため、もしディレクトリ内に package main
ではないにも関わらず、ディレクトリ名と同じ名前の非実行ファイル(例えばデータファイルやドキュメントファイル)が存在した場合、go clean
がそれらを誤って削除してしまうという問題がありました。
このコミットは、go clean
がディレクトリ名と同じ名前の実行可能ファイルを削除する際に、そのパッケージが package main
であるかどうかを確認することで、この誤削除を防ぐことを目的としています。
前提知識の解説
go clean
コマンド: Go言語のビルドキャッシュや生成された実行可能ファイル、テストバイナリなどを削除するためのコマンドです。クリーンな状態からビルドをやり直したい場合や、不要なファイルを削除したい場合に使用します。package main
: Goプログラムのエントリポイントとなるパッケージです。main
パッケージ内のmain
関数がプログラムの実行開始点となります。go build
コマンドでpackage main
のソースコードをビルドすると、通常は実行可能ファイルが生成されます。- 実行可能ファイル: コンピュータ上で直接実行できる形式のファイルです。Goの場合、
go build
でpackage main
をビルドすると、OSに応じた実行可能ファイル(例: Linux/macOSでは拡張子なし、Windowsでは.exe
)が生成されます。 - テストバイナリ:
go test
コマンドを実行すると、テストコードがコンパイルされ、テストを実行するためのバイナリが生成されます。これらのバイナリは通常、元のパッケージ名に.test
や.test.exe
といったサフィックスが付加されます。 filepath.Split
: Go標準ライブラリpath/filepath
パッケージの関数で、与えられたパスをディレクトリ部分とファイル名部分に分割します。strings.HasSuffix
: Go標準ライブラリstrings
パッケージの関数で、文字列が特定のサフィックス(接尾辞)で終わるかどうかを判定します。
技術的詳細
このコミットの主要な変更は、src/cmd/go/clean.go
ファイル内の clean
関数にあります。
clean
関数は、指定されたパッケージ p
に対してクリーンアップ操作を実行します。
変更前は、allRemove
という文字列スライスに、無条件に以下のファイル名を追加していました。
elem
(ディレクトリ名と同じ名前のファイル)elem + ".exe"
elem + ".test"
elem + ".test.exe"
変更後は、allRemove
の初期化方法が変更され、ディレクトリ名と同じ名前の実行可能ファイル (elem
と elem + ".exe"
) を削除対象に含めるかどうかの条件が追加されました。
具体的には、以下の条件が追加されています。
// Remove dir-named executable only if this is package main.
if p.Name == "main" {
allRemove = append(allRemove,
elem,
elem+".exe",
)
}
このコードブロックにより、p.Name
(パッケージ名) が "main"
である場合にのみ、ディレクトリ名と同じ名前のファイル (elem
) とその .exe
バージョン (elem + ".exe"
) が削除対象リスト allRemove
に追加されるようになりました。
一方、テストバイナリ (elem + ".test"
と elem + ".test.exe"
) は、package main
であるかどうかにかかわらず、引き続き無条件で削除対象に含まれます。これは、テストバイナリは常に go test
によって生成され、クリーンアップの対象となるべきだからです。
また、ディレクトリ内の個々の .go
ファイルから生成される可能性のある実行可能ファイル(base
と base + ".exe"
)についても、引き続き削除対象に含まれます。ただし、元のコードには TODO
コメントがあり、これらのファイルが実際に package main
であり、実行可能ファイルとしてビルド可能であるかどうかのチェックが将来的に必要であることが示唆されています。このコミットでは、その TODO
は変更されていません。
この修正により、go clean
はより正確に、package main
から生成された実行可能ファイルのみを削除するようになり、意図しないファイルの削除を防ぐことができます。
コアとなるコードの変更箇所
--- a/src/cmd/go/clean.go
+++ b/src/cmd/go/clean.go
@@ -137,22 +137,38 @@ func clean(p *Package) {
}
_, elem := filepath.Split(p.Dir)
- allRemove := []string{
- e
- elem + ".exe",
- elem + ".test",
- elem + ".test.exe",
- }
+ var allRemove []string
+
+ // Remove dir-named executable only if this is package main.
+ if p.Name == "main" {
+ allRemove = append(allRemove,
+ elem,
+ elem+".exe",
+ )
+ }
+
+ // Remove package test executables.
+ allRemove = append(allRemove,
+ elem+".test",
+ elem+".test.exe",
+ )
+
+ // Remove a potental executable for each .go file in the directory that
+ // is not part of the directory\'s package.
for _, dir := range dirs {
name := dir.Name()
if packageFile[name] {
continue
}
if !dir.IsDir() && strings.HasSuffix(name, ".go") {
- // TODO(adg,rsc): check that this .go file is actually
- // in "package main", and therefore capable of building
- // to an executable file.
base := name[:len(name)-len(".go")]
allRemove = append(allRemove, base, base+".exe")
}
}
+
if cleanN || cleanX {
b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
}
コアとなるコードの解説
変更の核心は、allRemove
スライスへの要素追加ロジックの変更です。
-
allRemove
の初期化: 変更前は、allRemove
が直接スライスリテラルで初期化されていました。 変更後は、var allRemove []string
と宣言され、空のスライスとして初期化されます。 -
package main
の条件分岐: 新しく追加された以下のコードブロックが最も重要です。// Remove dir-named executable only if this is package main. if p.Name == "main" { allRemove = append(allRemove, elem, elem+".exe", ) }
ここで、現在のパッケージ
p
の名前 (p.Name
) が"main"
であるかどうかをチェックしています。もし"main"
であれば、そのディレクトリ名と同じ名前の実行可能ファイル (elem
とelem + ".exe"
) をallRemove
スライスに追加します。これにより、package main
ではないディレクトリでは、これらのファイルが削除対象から除外されるようになります。 -
テストバイナリの追加:
// Remove package test executables. allRemove = append(allRemove, elem+".test", elem+".test.exe", )
この部分は、
package main
の条件分岐の外に配置されています。これは、テストバイナリはパッケージの種類に関わらず常にクリーンアップの対象となるべきであるため、無条件にallRemove
に追加されることを意味します。
この変更により、go clean
は、package main
から生成された実行可能ファイルと、すべてのテストバイナリのみをターゲットとする、より正確なクリーンアップ動作を実現します。
関連リンク
- Go Issue #5665: https://github.com/golang/go/issues/5665
- Gerrit Change-Id:
I2f6e9a1e243c64f22d0f0542a05343bacf6d580b
(Goの内部コードレビューシステムであるGerritの変更ID) - Go CL 12807044: https://golang.org/cl/12807044 (Gerrit上の変更リストへのリンク)
参考にした情報源リンク
- Go Issue #5665 の議論内容
- Go言語の
go clean
コマンドに関する公式ドキュメント (当時のバージョン) - Go言語の
package main
と実行可能ファイルの生成に関する一般的な知識 path/filepath
およびstrings
パッケージのGoドキュメントgo build
およびgo test
コマンドの動作に関する一般的な知識- Go言語のソースコード
src/cmd/go/clean.go
(コミット前後の差分) - https://github.com/golang/go/commit/2f6e9a1e243c64f22d0f0542a05343bacf6d580b (GitHub上のコミットページ)
- https://golang.org/cl/12807044 (Gerrit上の変更リスト)
- https://github.com/golang/go/issues/5665 (Go Issue #5665)