[インデックス 16548] ファイルの概要
このコミットは、Go言語のツールチェインの一部であるcmd/go
パッケージ内のbuild.go
ファイルに対する変更です。具体的には、Goのコードカバレッジツール(go tool cover
)の呼び出し方法が更新され、その出力処理が改善されています。
コミット
commit bc7e26621e12796ca9284e3fc3b4d91761c221c6
Author: Rob Pike <r@golang.org>
Date: Tue Jun 11 20:47:35 2013 -0700
cmd/go: use -o option of cover tool
Separates correct from erroneous output so errors running the tool will appear
in the log.
R=rsc
CC=golang-dev
https://golang.org/cl/10191043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bc7e26621e12796ca9284e3fc3b4d91761c221c6
元コミット内容
cmd/go: use -o option of cover tool
Separates correct from erroneous output so errors running the tool will appear in the log.
このコミットは、go tool cover
コマンドの-o
オプションを使用するように変更することで、ツールの正常な出力とエラー出力を分離し、エラーがログに明確に表示されるようにすることを目的としています。
変更の背景
Go言語のgo tool cover
は、ソースコードにカバレッジ計測用のコードを挿入し、その結果を新しいソースファイルとして出力するツールです。このツールは、テスト実行時にどのコードが実行されたかを追跡するために使用されます。
このコミット以前は、go tool cover
の出力は標準出力(stdout)に直接書き込まれていました。go
コマンドがこのツールを実行する際、b.runOut
というヘルパー関数を使用してgo tool cover
の標準出力をキャプチャし、その内容をioutil.WriteFile
で目的のファイルに書き込んでいました。
この方式の問題点は、go tool cover
が正常な処理結果(カバレッジ計測コードが挿入されたソースコード)を出力する場合も、何らかのエラーが発生した場合の診断メッセージを出力する場合も、すべて同じ標準出力ストリームに混在してしまうことでした。これにより、go
コマンドの実行ログを解析する際に、ツールの実行が成功したのか、それともエラーが発生したのかを判別しにくくなる可能性がありました。特に、エラーメッセージが大量の正常な出力の中に埋もれてしまうと、問題の特定が困難になります。
この問題を解決するため、go tool cover
が提供する-o
オプションを利用する変更が導入されました。-o
オプションは、ツールの出力を標準出力ではなく、指定されたファイルに直接書き込む機能を提供します。これにより、go tool cover
の正常な出力は指定されたファイルに送られ、エラーメッセージや警告は引き続き標準エラー出力(stderr)に送られるようになります。結果として、go
コマンドの実行ログにはエラーメッセージのみが残り、ツールの実行状態をより明確に把握できるようになります。
前提知識の解説
-
go tool cover
: Go言語の標準ツールチェーンに含まれるコマンドで、ソースコードのカバレッジを計測するために使用されます。具体的には、Goのソースファイルを受け取り、各ステートメントの実行回数を記録するためのコード(プロファイリングフック)を挿入した新しいソースファイルを生成します。この生成されたファイルをコンパイルして実行することで、テストがどのコードパスをカバーしたかを後で分析できます。-mode
: カバレッジの計測モードを指定します(例:set
,count
,atomic
)。-count
: カバレッジ計測用のカウンタ変数の名前を指定します。-pos
: カバレッジ計測用の位置情報変数の名前を指定します。-o
: 出力ファイルを指定します。このオプションが指定されない場合、go tool cover
は結果を標準出力に書き込みます。
-
標準入出力 (Standard I/O): Unix系システムにおけるプログラムの基本的な入出力メカニズムです。
- 標準入力 (stdin): プログラムがデータを読み込むためのデフォルトの入力ストリーム。通常はキーボードに接続されています。
- 標準出力 (stdout): プログラムが正常な結果を出力するためのデフォルトの出力ストリーム。通常はターミナルに表示されます。
- 標準エラー出力 (stderr): プログラムがエラーメッセージや診断情報を出力するためのデフォルトの出力ストリーム。通常はターミナルに表示されますが、stdoutとは独立しており、リダイレクトなどで分離して扱うことができます。
-
os.FileMode
: Go言語のos
パッケージで定義されている型で、ファイルのパーミッション(読み取り、書き込み、実行権限など)を表します。 -
ioutil.WriteFile
(Go 1.16でos.WriteFile
に移行): Go言語のio/ioutil
パッケージ(現在はos
パッケージに統合)の関数で、指定されたバイトスライスをファイルに書き込みます。ファイルが存在しない場合は作成し、存在する場合は上書きします。 -
builder
構造体とrun
/runOut
メソッド: Goのビルドプロセスを管理する内部的な構造体とメソッドです。b.runOut(dir, cmd, env, args...)
: 指定されたディレクトリでコマンドを実行し、その標準出力をバイトスライスとして返します。コマンドの実行中にエラーが発生した場合、エラーを返します。b.run(dir, cmd, env, args...)
: 指定されたディレクトリでコマンドを実行します。runOut
とは異なり、標準出力をキャプチャせず、コマンドの実行が成功したかどうかのエラーのみを返します。
技術的詳細
このコミットの技術的な核心は、src/cmd/go/build.go
ファイル内のbuilder
構造体のcover
メソッドの変更にあります。
変更前は、cover
メソッドはb.runOut
を呼び出してgo tool cover
を実行していました。b.runOut
は実行されたコマンドの標準出力をout
変数にキャプチャし、そのout
変数の内容をioutil.WriteFile
を使ってdst
(出力ファイルパス)に書き込んでいました。
// 変更前
func (b *builder) cover(a *action, dst, src string, perm os.FileMode, count, pos string) error {
// go tool cover の標準出力をキャプチャ
out, err := b.runOut(a.objdir, "cover "+a.p.ImportPath, nil, tool("cover"), "-mode="+a.p.coverMode, "-count="+count, "-pos="+pos, src)
if err != nil {
return err // エラーがあれば即座に返す
}
// キャプチャした出力をファイルに書き込む
return ioutil.WriteFile(dst, out, perm)
}
この方式では、go tool cover
がエラーメッセージを標準出力に書き込んだ場合、そのエラーメッセージもout
変数にキャプチャされ、dst
ファイルに書き込まれてしまう可能性がありました。また、b.runOut
はコマンドの終了コードに基づいてエラーを検出しますが、標準出力に混在するエラーメッセージを区別することはできません。
変更後は、cover
メソッドはb.run
を呼び出すように変更され、go tool cover
コマンドの引数に新たに-o dst
が追加されました。
// 変更後
func (b *builder) cover(a *action, dst, src string, perm os.FileMode, count, pos string) error {
// go tool cover に -o オプションを渡し、出力を直接 dst ファイルに書き込ませる
return b.run(a.objdir, "cover "+a.p.ImportPath, nil,
tool("cover"),
"-mode", a.p.coverMode,
"-count", count,
"-pos", pos,
"-o", dst, // ここが新しい引数
src)
}
この変更により、go tool cover
はカバレッジ計測コードが挿入されたソースコードを直接dst
ファイルに書き込むようになります。b.run
はコマンドの標準出力をキャプチャしないため、go tool cover
が標準エラー出力に書き込むエラーメッセージは、b.run
の呼び出し元(go
コマンドのログシステムなど)によって適切に処理され、ログに記録されるようになります。これにより、正常な出力とエラー出力が明確に分離され、エラーの検出と診断が容易になります。
引数の渡し方も、"-mode="+a.p.coverMode
のような単一の文字列から、"-mode", a.p.coverMode
のようにオプションと値を別々の引数として渡す形式に変更されています。これは、コマンドライン引数のパースをより堅牢にするための一般的なプラクティスです。
コアとなるコードの変更箇所
src/cmd/go/build.go
ファイルのcover
関数が変更されています。
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1110,14 +1110,15 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error {
}
// cover runs, in effect,
-// go tool cover -mode=b.coverMode -count="count" -pos="pos" src.go >dst.go
+// go tool cover -mode=b.coverMode -count="count" -pos="pos" -o dst.go src.go
func (b *builder) cover(a *action, dst, src string, perm os.FileMode, count, pos string) error {
- out, err := b.runOut(a.objdir, "cover "+a.p.ImportPath, nil, tool("cover"), "-mode="+a.p.coverMode, "-count="+count, "-pos="+pos, src)
- if err != nil {
- return err
- }
- // Output is processed source code. Write it to destination.
- return ioutil.WriteFile(dst, out, perm)
+ return b.run(a.objdir, "cover "+a.p.ImportPath, nil,
+ tool("cover"),
+ "-mode", a.p.coverMode,
+ "-count", count,
+ "-pos", pos,
+ "-o", dst,
+ src)
}
var objectMagic = [][]byte{
コアとなるコードの解説
変更の核心は、cover
関数内でgo tool cover
コマンドを実行する方法の変更です。
-
b.runOut
からb.run
への変更:- 変更前:
out, err := b.runOut(...)
を使用していました。runOut
は実行したコマンドの標準出力をout
変数にキャプチャします。 - 変更後:
return b.run(...)
を使用しています。run
はコマンドの標準出力をキャプチャせず、コマンドの実行が成功したかどうかのエラーのみを返します。これにより、go tool cover
の正常な出力がgo
コマンドの内部で一時的に保持される必要がなくなり、直接ファイルに書き込まれるようになります。
- 変更前:
-
-o dst
オプションの追加:- 変更前:
go tool cover
の出力は標準出力に送られ、それをioutil.WriteFile(dst, out, perm)
でファイルに書き込んでいました。 - 変更後:
go tool cover
の引数リストに"-o", dst
が追加されました。これにより、go tool cover
自身が、カバレッジ計測コードが挿入されたソースコードを直接dst
パスのファイルに書き込むようになります。
- 変更前:
-
ioutil.WriteFile
の削除:go tool cover
が直接ファイルに書き込むようになったため、以前のioutil.WriteFile
による書き込み処理は不要となり、削除されました。
これらの変更により、go tool cover
の正常な出力は-o
オプションによって指定されたファイルに直接送られ、エラーや警告メッセージは標準エラー出力に分離されます。これにより、go
コマンドのログからエラーをより容易に識別できるようになり、デバッグや自動化されたビルドシステムの信頼性が向上します。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
go tool cover
に関するドキュメント(Goのバージョンによって異なる場合がありますが、基本的な機能は共通です):go doc cmd/go
またはgo help cover
コマンドで詳細を確認できます。- Goのソースコードリポジトリ: https://github.com/golang/go
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/go/build.go
): https://github.com/golang/go/blob/master/src/cmd/go/build.go - Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/10191043
go tool cover
の基本的な使い方に関する記事やドキュメント (一般的な情報源):- A Tour of Go: https://go.dev/tour/
- Go Blog: The cover story: https://go.dev/blog/cover
go help cover
コマンドの出力
os
パッケージおよびio/ioutil
パッケージのドキュメント (Go言語の標準ライブラリ): https://pkg.go.dev/os , https://pkg.go.dev/io/ioutil (現在はos
に統合)- 標準入出力に関する一般的なUnix/Linuxの概念。