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

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

このコミットは、Go言語のコマンドラインツールであるgoコマンドの一部であるcleanサブコマンドに関連する変更です。具体的には、src/cmd/go/clean.goファイルが修正されています。このファイルは、go cleanコマンドのロジックを実装しており、ビルドによって生成されたファイルやキャッシュを削除する役割を担っています。

コミット

go clean -nおよびgo clean -xオプション使用時のパニック(プログラムの異常終了)を修正し、さらにこれらのオプションが生成するスクリプトが、実際のgo cleanコマンドの実行により近い動作をするように改善しました。

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

https://github.com/golang/go/commit/39611ec880779c4e093a4789c7d1a16d25f50795

元コミット内容

cmd/go: fixed panic on `go clean -n` and `go clean -x`.
        also made generated scripts act more like running go clean itself

R=golang-dev
CC=golang-dev, rsc
https://golang.org/cl/5624049

変更の背景

このコミットが行われた背景には、go cleanコマンドの特定のオプション(-n-x)を使用した際に発生していたパニック(実行時エラー)の存在があります。

  • パニックの修正: go clean -ngo clean -xは、実際にファイルを削除する代わりに、削除されるであろうコマンドを表示したり、実行されるコマンドを表示したりするデバッグ用のオプションです。これらのオプションが特定の条件下でパニックを引き起こしていたため、その安定性を確保する必要がありました。パニックは、プログラムが予期せぬ状態に陥り、回復不能なエラーが発生した際にGoランタイムによって引き起こされるものです。
  • スクリプトの動作改善: go clean -ngo clean -xは、実際に実行されるコマンドを標準出力に出力します。この出力は、ユーザーが手動で実行したり、スクリプトに組み込んだりすることを想定しています。しかし、以前のバージョンでは、これらのオプションが生成するrmコマンドが、実際のgo cleanコマンドの動作と完全に一致していなかった可能性があります。例えば、存在しないファイルを削除しようとした場合にエラーになるなど、より堅牢な動作が求められていました。このコミットは、生成されるrmコマンドに-fオプションを追加することで、この問題を解決し、より実用的なスクリプトを生成するように改善しています。

前提知識の解説

  • go cleanコマンド: Go言語のビルドシステムが生成したファイル(実行可能ファイル、アーカイブファイル、テストキャッシュなど)や、go mod downloadでダウンロードされたモジュールキャッシュなどを削除するためのコマンドです。これにより、クリーンなビルド環境を維持したり、ディスクスペースを解放したりすることができます。
  • go clean -nオプション: このオプションは、実際にファイルを削除する代わりに、go cleanが実行するであろうコマンド(通常はrmコマンド)を標準出力に表示します。これにより、ユーザーは実際に何が削除されるのかを事前に確認できます。
  • go clean -xオプション: このオプションは、go cleanが実行するすべてのコマンド(ビルドコマンドや削除コマンドなど)を標準出力に表示します。これは、go cleanの内部動作をデバッグする際に非常に役立ちます。
  • panic (Go言語): Go言語におけるpanicは、プログラムが回復不能なエラーに遭遇した際に発生するランタイムエラーです。panicが発生すると、現在の関数の実行が停止し、遅延関数が実行された後、呼び出し元の関数にpanicが伝播していきます。最終的にmain関数までpanicが伝播すると、プログラムは終了します。通常、panicはプログラマーの論理的な誤りや、予期せぬ不正な状態によって引き起こされます。
  • rmコマンド: Unix系オペレーティングシステムでファイルやディレクトリを削除するためのコマンドです。
  • rm -fオプション: rmコマンドのオプションで、以下の動作をします。
    • 強制削除: 存在しないファイルを削除しようとしてもエラーを報告せず、プロンプトを表示せずに削除を実行します。
    • 確認なし: 通常、書き込み権限のないファイルを削除しようとすると確認を求められますが、-fオプションを使用すると確認なしで削除します。 これにより、スクリプトなどからrmを実行する際に、途中で停止することなく処理を続行できるようになります。

技術的詳細

このコミットの技術的な変更点は主に2つあります。

  1. fmtパッケージのインポートとbuilder.printフィールドの初期化: 以前のclean.goでは、コマンドの表示に内部的なメカニズムを使用していた可能性があります。このコミットでは、Go標準ライブラリのfmtパッケージをインポートし、builder構造体のprintフィールドにfmt.Print関数を割り当てています。 builder構造体は、goコマンドが内部的にコマンドを構築・実行する際に使用されるユーティリティの集合体です。b.print = fmt.Printという行は、builderがコマンドを表示する際にfmt.Printを使用するように設定していることを意味します。これにより、コマンドの出力方法が標準化され、デバッグオプション(-n, -x)使用時のパニックの原因となっていた可能性のある、出力に関する潜在的なバグが修正されたと考えられます。

  2. rmコマンドへの-fオプションの追加: cleanNまたはcleanX(それぞれgo clean -ngo clean -xに対応)が真の場合に表示されるrmコマンドに、-fオプションが追加されました。 変更前: b.showcmd(p.Dir, "rm %s", strings.Join(allRemove, " ")) 変更後: b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " ")) 同様に、ターゲットファイルの削除コマンドにも-fが追加されています。 変更前: b.showcmd("", "rm %s", p.target) 変更後: b.showcmd("", "rm -f %s", p.target) (※提供されたdiffの最後の行はp.Join(p.target)となっていますが、これは一般的なrmコマンドの引数としては不自然であり、元のコミットではp.targetである可能性が高いです。ここでは提供されたdiffに従います。) この変更により、go clean -ngo clean -xが生成するrmコマンドは、削除対象のファイルが存在しない場合でもエラーで停止することなく実行を試みるようになります。これは、実際のgo cleanコマンドが、削除対象のファイルが存在するかどうかに関わらず、指定されたパスの削除を試みるという動作に近づけるための重要な改善です。これにより、生成されたスクリプトの堅牢性が向上し、より実用的なデバッグ出力となります。

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

--- a/src/cmd/go/clean.go
+++ b/src/cmd/go/clean.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -112,6 +113,7 @@ func clean(p *Package) {
 	}
 
 	var b builder
+	b.print = fmt.Print
 
 	packageFile := map[string]bool{}
 	if p.Name != "main" {
@@ -146,7 +148,7 @@ func clean(p *Package) {
 		}
 	}
 	if cleanN || cleanX {
-\t\tb.showcmd(p.Dir, "rm %s", strings.Join(allRemove, " "))
+\t\tb.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
 	}
 
 	toRemove := map[string]bool{}
@@ -180,7 +182,7 @@ func clean(p *Package) {
 
 	if cleanI && p.target != "" {
 		if cleanN || cleanX {
-\t\t\tb.showcmd("", "rm %s", p.target)
+\t\t\tb.showcmd("", "rm -f %s", p.Join(p.target))
 		}
 		if !cleanN {
 			os.Remove(p.target)

コアとなるコードの解説

上記のdiffは、src/cmd/go/clean.goファイルに対する変更を示しています。

  1. import "fmt" の追加: importブロックに"fmt"パッケージが追加されています。これは、Go言語でフォーマットされたI/O(入出力)を行うための標準パッケージです。このパッケージの関数(例: fmt.Print, fmt.Printlnなど)は、文字列や変数の値を整形して出力するために広く使用されます。

  2. b.print = fmt.Print の追加: clean関数内でbuilder型の変数bが宣言された直後に、b.print = fmt.Printという行が追加されています。これは、builder構造体(おそらくgoコマンドの内部でコマンドの実行や表示を抽象化するためのもの)が持つprintというフィールドに、fmt.Print関数を代入しています。これにより、builderが何かを出力する必要がある場合に、fmt.Printの機能を利用するようになります。この変更は、デバッグオプション(-n, -x)使用時の出力処理を標準化し、以前のパニックの原因となっていた可能性のあるカスタム出力ロジックを置き換えることで、安定性を向上させる目的があります。

  3. rmコマンドへの -f オプションの追加: cleanNまたはcleanXフラグが設定されている場合(つまり、go clean -nまたはgo clean -xが実行された場合)に、b.showcmd関数が呼び出される箇所が2箇所変更されています。

    • 1つ目の変更は、allRemoveというスライスに含まれるすべてのファイルを削除するコマンドです。 -行: b.showcmd(p.Dir, "rm %s", strings.Join(allRemove, " ")) +行: b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
    • 2つ目の変更は、パッケージのターゲットファイル(ビルド成果物など)を削除するコマンドです。 -行: b.showcmd("", "rm %s", p.target) +行: b.showcmd("", "rm -f %s", p.Join(p.target)) これらの変更により、go clean -ngo clean -xが生成するrmコマンドの文字列に-fオプションが明示的に含まれるようになります。これにより、生成されたスクリプトが、削除対象のファイルが存在しない場合でもエラーで停止することなく、より堅牢に動作するようになります。これは、実際のgo cleanコマンドの動作(ファイルが存在しなくても削除を試みる)に近づけるための重要な改善です。

関連リンク

  • Go Issue Tracker: https://github.com/golang/go/issues (このコミットに関連する具体的なIssue番号はコミットメッセージに記載されていませんが、golang.org/cl/5624049がコードレビューへのリンクです。)
  • Go Code Review: https://golang.org/cl/5624049 (このコミットの元のコードレビューページ。詳細な議論や背景情報が含まれている可能性があります。)

参考にした情報源リンク