Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 cleanpackage 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 buildpackage 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 の初期化方法が変更され、ディレクトリ名と同じ名前の実行可能ファイル (elemelem + ".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 ファイルから生成される可能性のある実行可能ファイル(basebase + ".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 スライスへの要素追加ロジックの変更です。

  1. allRemove の初期化: 変更前は、allRemove が直接スライスリテラルで初期化されていました。 変更後は、var allRemove []string と宣言され、空のスライスとして初期化されます。

  2. 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" であれば、そのディレクトリ名と同じ名前の実行可能ファイル (elemelem + ".exe") を allRemove スライスに追加します。これにより、package main ではないディレクトリでは、これらのファイルが削除対象から除外されるようになります。

  3. テストバイナリの追加:

    	// Remove package test executables.
    	allRemove = append(allRemove,
    		elem+".test",
    		elem+".test.exe",
    	)
    

    この部分は、package main の条件分岐の外に配置されています。これは、テストバイナリはパッケージの種類に関わらず常にクリーンアップの対象となるべきであるため、無条件に allRemove に追加されることを意味します。

この変更により、go clean は、package main から生成された実行可能ファイルと、すべてのテストバイナリのみをターゲットとする、より正確なクリーンアップ動作を実現します。

関連リンク

参考にした情報源リンク