[インデックス 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 -gpwd| grep -v '^test.*\\.go:':6covツールを使ってカバレッジ情報を生成します。-gpwd`` は現在の作業ディレクトリをカバレッジ計測の対象として指定していると考えられます。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 -gpwd| 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リポジトリのコミット履歴
- 一般的なソフトウェア開発におけるコードカバレッジの概念