[インデックス 1185] ファイルの概要
このコミットは、Go言語の初期のビルドツールである gobuild
に、コードカバレッジ計測のためのルールを追加し、main
パッケージに属するファイルの取り扱いを改善するものです。具体的には、gobuild
がパッケージリストを生成する際に main
パッケージを無視するように変更され、また、Goの標準ライブラリの各Makefileにコードカバレッジを計測するための coverage
ターゲットが追加されています。
コミット
commit 87b112440739f219f706bd7e48bc05327d88eee6
Author: Russ Cox <rsc@golang.org>
Date: Wed Nov 19 12:52:30 2008 -0800
gobuild: add coverage rule, ignore files in package main.
R=r
DELTA=55 (41 added, 11 deleted, 3 changed)
OCL=19594
CL=19598
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/87b112440739f219f706bd7e48bc05327d88eee6
元コミット内容
gobuild: add coverage rule, ignore files in package main.
このコミットメッセージは、gobuild
ツールにカバレッジ計測のルールを追加し、main
パッケージ内のファイルを無視するように変更したことを簡潔に示しています。
変更の背景
このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の非常に初期の段階です。当時のGoのビルドシステムは現在とは異なり、gobuild
のようなツールが使われていました。
変更の背景には以下の点が考えられます。
- コード品質の向上: ソフトウェア開発において、テストカバレッジはコードの品質と信頼性を測る重要な指標です。Go言語の開発初期段階から、テストとカバレッジ計測の仕組みをビルドシステムに組み込むことで、開発者がより堅牢なコードを書くことを奨励し、バグの早期発見に繋げようとしたと考えられます。
main
パッケージの特殊性への対応: Go言語において、package main
は実行可能なプログラムのエントリポイントとなる特殊なパッケージです。通常のライブラリパッケージとは異なり、他のパッケージからインポートされることを意図していません。gobuild
がパッケージリストを処理する際にmain
パッケージを通常のライブラリパッケージと同様に扱ってしまうと、不必要なビルド依存関係や誤解を招く挙動が発生する可能性があります。そのため、main
パッケージに属するファイルを適切に無視することで、ビルドプロセスの正確性と効率性を向上させる必要がありました。- ビルドシステムの進化: Go言語のビルドシステムは、
gobuild
からgo build
コマンドへと進化していきます。このコミットは、その進化の過程で、ビルドツールがより洗練され、Go言語の特性(特にパッケージシステム)をより適切に扱うように改善されていく一環と見ることができます。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびビルドシステムに関する初期の概念を理解しておく必要があります。
- Go言語のパッケージシステム: Go言語はコードをパッケージという単位で管理します。関連する機能は同じパッケージにまとめられ、他のパッケージからインポートして利用できます。
package main
: 特別なパッケージで、実行可能なプログラムのエントリポイント(main
関数)を含みます。main
パッケージは通常、他のパッケージからインポートされることはありません。
gobuild
: このコミットが行われた2008年当時のGo言語の初期のビルドツールです。現在のgo build
コマンドの前身にあたります。Goのソースコードをコンパイルし、実行可能ファイルやライブラリを生成する役割を担っていました。- Makefile:
make
ユーティリティが使用するビルド自動化スクリプトです。Go言語の初期のプロジェクトでは、ビルドプロセスを定義するために広く使われていました。ターゲット(例:all
,clean
,test
,install
)と、そのターゲットをビルドするためのコマンドが記述されます。 gotest
: Go言語のテストフレームワークを実行するためのコマンドです。現在のgo test
コマンドに相当します。Goのテストコード(_test.go
ファイルに記述)を実行し、テスト結果を出力します。6cov
: Go言語の初期のコードカバレッジツールです。当時のGoコンパイラが6g
(64-bit Go compiler) などと呼ばれていたことに由来します。gotest
と組み合わせて使用され、テストが実行された際に、どのコード行が実行されたか(カバレッジ情報)を収集・報告します。- コードカバレッジ: ソフトウェアテストにおいて、テストケースが実行された際に、プログラムのソースコードのうちどれくらいの割合が実行されたかを示す指標です。カバレッジが高いほど、テストがコードの多くの部分を網羅していることを意味し、潜在的なバグを見つけやすくなります。
技術的詳細
このコミットの技術的な変更点は大きく分けて二つあります。
-
gobuild
におけるmain
パッケージの無視:src/cmd/gobuild/gobuild.c
内のgetpkg
関数が変更されています。この関数は、与えられたファイルがどのGoパッケージに属するかを判断し、そのパッケージ名を返す役割を担っています。- 変更前は、
main
パッケージも他の通常のパッケージと同様にパッケージリストに追加されていました。 - 変更後は、
strcmp(p, "main") == 0
という条件が追加され、もしパッケージ名が"main"
であれば、そのパッケージをパッケージリストに追加せず、単に"main"
という文字列を返すように変更されています。 - さらに、
main
関数内でジョブを処理するループにおいても、job[njob].pkg
が"main"
であれば、そのジョブをスキップするcontinue
文が追加されています。 - これにより、
gobuild
は実行可能ファイルのエントリポイントであるmain
パッケージを、ライブラリパッケージとは異なる特殊なものとして扱い、ビルドプロセスにおいて不必要な処理や依存関係の生成を避けることができます。
-
コードカバレッジルールの追加:
src/cmd/gobuild/gobuild.c
のpreamble
(これはおそらく、gobuild
が生成するMakefileのテンプレートの一部) に、coverage
という新しいターゲットが追加されています。- この
coverage
ターゲットは、以下のコマンドを実行します。gotest
: テストを実行します。6cov -g
pwd| grep -v '^test.*\\.go:'
:6cov
ツールを使ってカバレッジ情報を生成します。-g
pwd`` は現在の作業ディレクトリをカバレッジ計測の対象として指定していると考えられます。grep -v '^test.*\\.go:'
は、テストファイル自体のカバレッジ情報(テストコードが実行されたことによるカバレッジ)を除外するためのフィルタリングです。これにより、純粋にプロダクションコードのカバレッジのみを報告しようとしています。
- 同様に、
src/lib/fmt/Makefile
、src/lib/http/Makefile
、src/lib/math/Makefile
、src/lib/net/Makefile
、src/lib/os/Makefile
、src/lib/reflect/Makefile
、src/lib/strconv/Makefile
、src/lib/syscall/Makefile
といったGo標準ライブラリの各Makefileにも、このcoverage
ターゲットが追加されています。これにより、各ライブラリのテストカバレッジを簡単に計測できるようになりました。 src/lib/http/Makefile
では、main.a
やtriv.$O
といった、おそらくmain
パッケージや簡単な実行可能ファイルに関連するビルドターゲットが削除されています。これは、main
パッケージのビルドをgobuild
の一般的なパッケージビルドフローから切り離し、より適切に処理するための変更の一部である可能性が高いです。
コアとなるコードの変更箇所
src/cmd/gobuild/gobuild.c
// getpkg 関数内
@@ -227,6 +227,9 @@ getpkg(char *file)
return pkg[i];
}
}
+ // don't put main in the package list
+ if(strcmp(p, "main") == 0)
+ return "main";
npkg++;
pkg = erealloc(pkg, npkg*sizeof pkg[0]);
pkg[i] = emalloc(strlen(p)+1);
// preamble 配列内 (Makefile テンプレート)
@@ -285,6 +288,10 @@ char preamble[] =
"test: packages\\n"
"\\tgotest\\n"
"\\n"
+ "coverage: packages\\n"
+ "\\tgotest\\n"
+ "\\t6cov -g `pwd` | grep -v '^test.*\\\\.go:'\\n"
+ "\\n"
"%%.$O: %%.go\\n"
"\\t$(GC) $*.go\\n"
"\\n"
// main 関数内
@@ -485,6 +492,8 @@ main(int argc, char **argv)
job[njob].name = argv[i];
job[njob].pass = -1;
job[njob].pkg = getpkg(argv[i]);
+ if(job[njob].pkg && strcmp(job[njob].pkg, "main") == 0)
+ continue;
njob++;
}
src/lib/fmt/Makefile
(他のライブラリのMakefileも同様)
@@ -18,6 +18,10 @@ clean:
test: packages
gotest
+coverage: packages
+ gotest
+ 6cov -g `pwd` | grep -v '^test.*\.go:'
+
%.$O: %.go
$(GC) $*.go
src/lib/http/Makefile
(一部削除)
@@ -39,11 +43,7 @@ O3=\
O4=\
server.$O\
-O5=\
-\ttriv.$O\\\
-\n-http.a: a1 a2 a3 a4 a5
-main.a: a1 a2 a3 a4 a5
+http.a: a1 a2 a3 a4
a1: $(O1)\
$(AR) grc http.a url.$O
@@ -61,26 +61,19 @@ a4:\t$(O4)\
$(AR) grc http.a server.$O\
rm -f $(O4)\
-a5:\t$(O5)\
-\t$(AR) grc main.a triv.$O\
-\trm -f $(O5)\
-\
newpkg: clean
$(AR) grc http.a
-\t$(AR) grc main.a
$(O1): newpkg
$(O2): a1
$(O3): a2
$(O4): a3
-$(O5): a4
nuke: clean
-\trm -f $(GOROOT)/pkg/http.a $(GOROOT)/pkg/main.a
+\trm -f $(GOROOT)/pkg/http.a
-packages: http.a main.a
+packages: http.a
install: packages
\tcp http.a $(GOROOT)/pkg/http.a
-\tcp main.a $(GOROOT)/pkg/main.a
コアとなるコードの解説
src/cmd/gobuild/gobuild.c
の変更
getpkg
関数の変更:if(strcmp(p, "main") == 0) return "main";
の追加は、gobuild
がソースファイルからパッケージ名を抽出する際に、もしそれが"main"
パッケージであれば、特別な処理を行うことを示しています。これにより、main
パッケージが通常のライブラリパッケージのリストに誤って含まれることを防ぎます。これは、main
パッケージが実行可能ファイルのエントリポイントであり、他のパッケージからインポートされることを意図していないというGoの設計思想に合致させるための重要な変更です。
preamble
(Makefile テンプレート) へのcoverage
ルールの追加:- この変更は、
gobuild
が生成するMakefileに、コードカバレッジ計測のための標準的なターゲットを組み込むものです。 coverage: packages
は、カバレッジ計測を行う前に、まずpackages
ターゲット(通常はすべてのパッケージをビルドする)を実行することを意味します。gotest
はGoのテストを実行します。6cov -g
pwd| grep -v '^test.*\\.go:'
は、テスト実行後に6cov
ツールを使ってカバレッジレポートを生成します。grep -v '^test.*\\.go:'
は、テストコード自体のカバレッジ情報(テストコードが実行されたことによるカバレッジ)を除外するためのフィルタリングです。これにより、純粋にプロダクションコードのカバレッジのみを報告しようとしています。これは、テストコードのカバレッジは通常、プロダクションコードの品質指標としては重要ではないため、ノイズを除去する目的があります。
- この変更は、
main
関数内のジョブ処理の変更:if(job[njob].pkg && strcmp(job[njob].pkg, "main") == 0) continue;
の追加は、gobuild
がビルドジョブを処理する際に、main
パッケージに属するファイル(ジョブ)をスキップすることを意味します。これはgetpkg
関数の変更と連携しており、main
パッケージが通常のライブラリパッケージとは異なる方法で扱われるべきであることを強調しています。これにより、main
パッケージのビルドが重複したり、不適切な依存関係が生成されたりするのを防ぎます。
src/lib/*/Makefile
の変更
- Go標準ライブラリの各Makefileに
coverage
ターゲットが追加されたことは、Go言語の初期段階から、各ライブラリのテストカバレッジを簡単に計測できる環境を整備しようとしていたことを示しています。これは、ライブラリの品質保証と継続的な改善にとって不可欠な要素です。
src/lib/http/Makefile
の変更
O5
変数からのtriv.$O
の削除、http.a
およびmain.a
のビルドターゲットからのa5
の削除、そしてmain.a
関連のクリーンアップやインストール処理の削除は、http
パッケージがmain
パッケージや特定の「trivial」な実行可能ファイルと密接に結合してビルドされる必要がなくなったことを示唆しています。これは、main
パッケージのビルドがgobuild
のより一般的なメカニズムによって処理されるようになったか、あるいはhttp
パッケージのビルドプロセスがよりモジュール化され、独立性が高まったことを意味する可能性があります。
これらの変更は、Go言語のビルドシステムが初期段階から、パッケージの特性を考慮し、テストと品質保証の仕組みを組み込みながら進化していった過程を示しています。
関連リンク
- Go言語の初期の歴史に関する情報: https://go.dev/doc/history
- Go言語のパッケージに関する公式ドキュメント: https://go.dev/doc/code
- Go言語のテストに関する公式ドキュメント: https://go.dev/doc/tutorial/add-a-test
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のGitHubリポジトリのコミット履歴
- 一般的なソフトウェア開発におけるコードカバレッジの概念