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

[インデックス 16438] ファイルの概要

このコミットは、Go言語のディストリビューションビルドプロセスにおいて、Windows環境で発生する一時ファイル(go.exe~)が残ってしまう問題を解決するためのものです。具体的には、misc/dist/bindist.goファイルに、Windows環境でのみgo.exe~という一時ファイルを削除する処理を追加しています。これにより、ビルド後のクリーンアップが適切に行われ、不要なファイルがシステムに残ることを防ぎます。

コミット

commit 77d4347bf26b61bf95a842ff25c579d86b78d4c3
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Thu May 30 12:11:17 2013 +1000

    misc/dist: remove lingering ~ file
    
    Fixes #5405.
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/9856043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/77d4347bf26b61bf95a842ff25c579d86b78d4c3

元コミット内容

misc/dist: remove lingering ~ file

Fixes #5405.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/9856043

変更の背景

この変更は、Go言語のビルドシステムがWindows環境で実行される際に発生する特定の副作用に対処するために導入されました。Goのビルドプロセス、特にgo installコマンドが実行される際、既存の実行ファイルを新しいバージョンで上書きする際に、一時的に元のファイルのリネームされたコピーが作成されることがあります。Windowsでは、この一時ファイルが~サフィックス(例: go.exe~)を持つことが一般的です。

コミットメッセージにあるFixes #5405は、Goプロジェクトの公式イシュートラッカーで報告された問題5405番を修正することを示しています。この問題は、Goのビルドプロセスが完了した後も、go.exe~のような一時ファイルが残存し、ディスクスペースを占有したり、ユーザーを混乱させたりする可能性があったことを示唆しています。特に、Goのディストリビューションをビルドするmisc/distパッケージのコンテキストでは、クリーンなビルド成果物を提供することが重要であり、このような一時ファイルが残ることは望ましくありませんでした。

このコミットの目的は、ビルドプロセスが完了した後に、これらの残存する一時ファイルを明示的に削除することで、ビルド環境をクリーンに保ち、ユーザーエクスペリエンスを向上させることにあります。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

  • Go言語のビルドシステム: Go言語は、go buildgo installといったコマンドを通じて、ソースコードから実行ファイルを生成します。go installは、コンパイルされたパッケージや実行ファイルをGOPATH/bin(またはGOBIN)やGOROOT/binといった標準の場所に配置します。
  • GOROOT: Goのインストールディレクトリを指す環境変数です。Goの標準ライブラリやツール群がここに配置されます。
  • misc/distパッケージ: Goのソースツリー内にあるmisc/distディレクトリは、Go言語の公式ディストリビューション(バイナリ配布版)をビルドするためのスクリプトやツールが含まれています。これらは、Goのリリース版を作成する際に使用されます。
  • go installの動作(Windowsにおける一時ファイル): Windows環境では、実行中のプログラムが自身のファイルを上書きしようとすると、ファイルロックの問題が発生することがあります。これを回避するため、多くのビルドシステムやインストーラーは、既存のファイルを一時的な名前にリネーム(例: original.exeoriginal.exe~に)してから、新しいファイルを書き込むという戦略をとります。Goのビルドシステムも同様のメカニズムを使用しており、$GOROOT/src/cmd/go/build.go内の(*builder).copyFile関数でこの挙動が実装されています。通常、この一時ファイルは処理が完了した後に削除されるべきですが、何らかの理由で残ってしまうことがありました。
  • os.Remove: Go言語の標準ライブラリosパッケージに含まれる関数で、指定されたパスのファイルや空のディレクトリを削除するために使用されます。

技術的詳細

このコミットは、Goのディストリビューションビルドプロセスの中核を担うmisc/dist/bindist.goファイルに焦点を当てています。このファイルは、Goのバイナリディストリビューションを作成する際の主要なロジックを含んでいます。

変更が加えられたのは、Do()メソッド内のgo install -a stdコマンドの実行後です。このgo install -a stdコマンドは、Goの標準ライブラリを再インストールする目的で実行されます。特に、-raceフラグなしでstdを再インストールすることで、cmd/gocmd/godocなどのコマンドが、より低速なレース検出機能が有効なバージョンではなく、通常のバージョンでビルドされるようにします。

問題は、このgo installプロセスがWindows上で実行されると、既存のgo.exe(Goコマンドの実行ファイル)を新しいバージョンで上書きする際に、一時的にgo.exe~というファイルが作成され、それが残存してしまうことでした。これは、$GOROOT/src/cmd/go/build.go内の(*builder).copyFile関数が、ファイルをコピーする際に古いファイルをリネームする挙動に起因します。

このコミットでは、この問題を解決するために、以下の条件付きロジックが追加されました。

  1. if b.OS == "windows": ビルドターゲットのOSがWindowsである場合にのみ、この処理を実行します。これは、~ファイルが残る問題がWindows特有のものであるためです。
  2. os.Remove(goCmd + "~"): goCmdはGoコマンドのパス(通常はgo.exe)を指します。この行は、go.exe~という名前の一時ファイルを明示的に削除します。os.Removeは、ファイルが存在しない場合でもエラーを返さないため、安全に呼び出すことができます。

この変更により、GoのディストリビューションビルドプロセスがWindows上で実行された際に、不要な一時ファイルが残存することがなくなり、よりクリーンなビルド成果物が生成されるようになりました。

コアとなるコードの変更箇所

--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -216,6 +216,12 @@ func (b *Build) Do() error {
 			// Re-install std without -race, so that we're not left
 			// with a slower, race-enabled cmd/go, cmd/godoc, etc.
 			_, err = b.run(src, goCmd, "install", "-a", "std")
+			// Re-building go command leaves old versions of go.exe as go.exe~ on windows.
+			// See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details.
+			// Remove it manually.
+			if b.OS == "windows" {
+				os.Remove(goCmd + "~")
+			}
 		}
 		if err != nil {
 			return err

コアとなるコードの解説

変更はmisc/dist/bindist.goファイルのDo()メソッド内、go install -a stdコマンドの実行直後に追加されています。

  • if b.OS == "windows" { ... }: この条件文は、現在のビルドターゲットのオペレーティングシステムがWindowsであるかどうかをチェックしています。bBuild構造体のインスタンスであり、そのフィールドOSはターゲットOSを示します。この一時ファイルの問題はWindowsに特有であるため、このチェックは不要なファイル削除操作を他のOSで実行しないようにするために重要です。

  • os.Remove(goCmd + "~"): この行が、残存する一時ファイルを削除する核心部分です。

    • goCmd: これは、Goコマンドの実行ファイルへのパス(例: C:\Go\bin\go.exe)を保持する変数です。
    • + "~": goCmdのパスに文字列~を連結することで、一時ファイルの名前(例: C:\Go\bin\go.exe~)を構築しています。
    • os.Remove(...): Goの標準ライブラリosパッケージのRemove関数を呼び出し、指定されたパスのファイルを削除します。この関数は、ファイルが存在しない場合でもエラーを返さないため、冪等性(何度実行しても同じ結果になること)が保証され、安全に利用できます。

このコードブロックは、Goコマンドの再ビルドによってWindows上に生成される可能性のあるgo.exe~ファイルを、ビルドプロセスが完了した直後に自動的にクリーンアップすることを保証します。これにより、ビルド成果物の整合性が保たれ、ユーザーが手動で一時ファイルを削除する必要がなくなります。

関連リンク

  • Go CL (Code Review) リンク: https://golang.org/cl/9856043
  • Go Issue Tracker (問題 #5405): このコミットが修正している問題の詳細は、Goプロジェクトの公式イシュートラッカーで「Issue 5405」として検索することで確認できます。

参考にした情報源リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/77d4347bf26b61bf95a842ff25c579d86b78d4c3
  • Go言語の公式ドキュメント(go installGOROOTosパッケージなどに関する情報)
  • Windowsにおけるファイル操作と一時ファイルの挙動に関する一般的な知識
  • Go言語のソースコード(特に$GOROOT/src/cmd/go/build.go内の(*builder).copyFile関数)