[インデックス 1118] ファイルの概要
このコミットは、Go言語の初期のビルドシステムにおける複数の改善とバグ修正を統合したものです。特に、リンカ (6l
)、アーカイバ (6ar
)、ビルドツール (gobuild
) の挙動が調整され、パッケージのビルドとテストのプロセスがより堅牢になるように変更されています。また、math
パッケージのビルドプロセスが新しいgobuild
の仕組みに適合するように更新され、関連するテストファイルも修正されています。
コミット
commit c5f21c0dc2bccad03d076d4d56c073334e06a3fa
Author: Russ Cox <rsc@golang.org>
Date: Thu Nov 13 13:42:26 2008 -0800
* 6l:
if an object in an archive tries to refer
to a file in its original source directory,
ignore it.
* 6ar:
fix bug if archive is empty.
* gobuild:
build archive in current directory.
* math:
use new gobuild Makefile.
* test/math.go:
rename to mathest.go, add // run line, make it run.
R=r
DELTA=494 (277 added, 203 deleted, 14 changed)
OCL=19090
CL=19171
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c5f21c0dc2bccad03d076d4d56c073334e06a3fa
元コミット内容
* 6l:
if an object in an archive tries to refer
to a file in its original source directory,
ignore it.
* 6ar:
fix bug if archive is empty.
* gobuild:
build archive in current directory.
* math:
use new gobuild Makefile.
* test/math.go:
rename to mathest.go, add // run line, make it run.
変更の背景
このコミットは、Go言語の初期開発段階におけるビルドシステムの成熟化の一環として行われました。当時のGoのビルドプロセスは、現在のようなgo build
コマンドが確立される前であり、6l
(リンカ)、6ar
(アーカイバ)、gobuild
といった低レベルのツールが直接使用されていました。
主な背景としては、以下の点が挙げられます。
- リンカの挙動の改善: アーカイブファイル(
.a
)内に含まれるオブジェクトが、そのオブジェクトが元々存在していたソースディレクトリ内のファイルを参照しようとした際に発生する問題を解決する必要がありました。これは、ビルドシステムが複雑な依存関係を解決する際に、誤ったパス解決や無限ループに陥る可能性を排除するためです。 - アーカイバの堅牢性向上: 空のアーカイブファイルを扱う際のバグが存在し、ビルドプロセスが予期せず失敗する可能性がありました。これを修正し、より安定したビルド環境を提供することが求められました。
- ビルドプロセスの標準化:
gobuild
ツールが生成するMakefileの構造を改善し、ビルドされたアーカイブが適切な場所に配置されるように変更することで、パッケージ管理とインストールプロセスをより予測可能にすることが目的でした。特に、ビルド成果物をカレントディレクトリに生成し、その後GOROOT/pkg
配下に移動させるという新しいフローが導入されました。 - テストの実行可能性の向上: テストファイルが単独で実行可能であること、およびテストの実行方法が明確に記述されていることが重要でした。これにより、開発者が個々のコンポーネントの変更を迅速に検証できるようになります。
これらの変更は、Go言語のビルドシステム全体の信頼性と使いやすさを向上させるための重要なステップでした。
前提知識の解説
このコミットを理解するためには、Go言語の初期のビルドシステムと、一般的なコンパイル・リンクの概念に関する知識が必要です。
- Go言語の初期ビルドツール:
6l
(リンカ): Go言語の初期のリンカです。6
は当時のGoのターゲットアーキテクチャ(Plan 9 from Bell Labsの6
、つまりx86)を指します。コンパイルされたオブジェクトファイル(.6
)やアーカイブファイル(.a
)を結合して実行可能ファイルを生成する役割を担います。6ar
(アーカイバ): Go言語の初期のアーカイバです。複数のオブジェクトファイルを一つのアーカイブファイル(ライブラリファイル、通常は.a
拡張子)にまとめるツールです。C言語におけるar
コマンドに相当します。gobuild
: Go言語のパッケージをビルドするための高レベルなスクリプトまたはツールです。当時のGoのビルドプロセスを自動化し、適切なコンパイラ、リンカ、アーカイバを呼び出してパッケージを生成する役割を担っていました。現在のgo build
コマンドの前身のようなものです。
- アーカイブファイル (
.a
): 複数のオブジェクトファイル(コンパイル済みコード)を一つにまとめた静的ライブラリファイルです。リンカは、このアーカイブファイルから必要なオブジェクトコードを抽出し、最終的な実行可能ファイルに結合します。 - Makefile: ビルドプロセスを自動化するためのスクリプトファイルです。依存関係と、それらを解決するためのコマンドを記述します。
make
コマンドによって実行されます。 GOROOT
: Go言語のインストールディレクトリを指す環境変数です。Goの標準ライブラリやツールがこのディレクトリに配置されます。ビルドされたパッケージは通常、$GOROOT/pkg
以下にインストールされます。// run
ディレクティブ: Goのテストファイルやサンプルコードに記述される特別なコメント行で、そのファイルがどのように実行されるべきか、またはテストとしてどのように扱われるべきかを示すものです。これは、テストフレームワークやビルドシステムがファイルを適切に処理するために使用します。
技術的詳細
このコミットは、Go言語のビルドシステムにおけるいくつかの重要な技術的課題に対処しています。
-
6l
(リンカ) におけるアーカイブ内のオブジェクト参照の解決:src/cmd/6l/obj.c
のaddlib
関数が変更されています。この関数は、リンカがライブラリ(アーカイブ)からシンボルを解決する際に呼び出されます。- 変更の核心は、アーカイブ内のオブジェクトが、そのオブジェクトが元々コンパイルされたソースディレクトリ内の他のファイルを参照しようとした場合に、その参照を無視するロジックが追加されたことです。
- 具体的には、
addlib
関数にsrc
引数が追加され、現在のソースファイル(またはアーカイブ内のオブジェクトの元ソース)のパスが渡されるようになりました。 - リンカは、参照されるファイル名と現在のソースファイルのパスを比較し、もし両者が同じディレクトリ構造を持ち、かつ参照がアーカイブ内のオブジェクトから行われている場合(
strchr(obj, '(')
でアーカイブ内のオブジェクトであるかを判定)、その参照を無視します。 - これは、アーカイブが自己完結型であるべきであり、アーカイブ内のオブジェクトが外部のソースディレクトリに依存するような不健全な参照を排除するためのものです。これにより、ビルドの循環参照や、アーカイブが移動された際に参照が壊れる問題を回避します。
- また、
copyhistfrog
という新しい関数が追加され、現在のヒストリ(コンパイル中のファイルのパス情報)をsrc
バッファにコピーするようになりました。これは、addlib
関数が正確なソースパス情報に基づいて判断できるようにするためです。
-
6ar
(アーカイバ) における空アーカイブのバグ修正:src/cmd/ar/ar.c
のgetpkgdef
関数に、pkgstmt == nil
の場合に*datap
と*lenp
をnil
と0
に設定して早期リターンするチェックが追加されました。- これにより、空のアーカイブを処理しようとした際に発生する可能性のあるクラッシュや不正な挙動が防止されます。
-
gobuild
におけるビルド成果物の管理:src/cmd/gobuild/gobuild.c
が変更され、gobuild
が生成するMakefileのテンプレートが更新されました。- 以前は
PKG=$(GOROOT)/pkg/%s.a
のように、ビルドされたパッケージが直接GOROOT/pkg
以下に配置されることを想定していましたが、新しい変更ではPKG=%s.a
とPKGDIR=$(GOROOT)/pkg%s
が導入されました。 - これにより、
gobuild
はまずカレントディレクトリにアーカイブファイル(例:math.a
)を生成し、その後mv $(PKG) $(PKGDIR)/$(PKG)
コマンドを使って、そのアーカイブを適切なGOROOT/pkg
以下のディレクトリに移動させるようになりました。 - この変更は、ビルドプロセスにおける中間成果物の管理を改善し、最終的なインストールパスをより柔軟に制御できるようにすることを目的としています。また、
nuke
ターゲット(クリーンアップ)もPKGDIR
を考慮するように更新されています。
-
math
パッケージのビルドプロセスの更新:src/lib/math/Makefile
が、新しいgobuild
のMakefile構造に適合するように変更されました。PKG
とPKGDIR
の定義がgobuild
の変更に合わせて更新され、ビルド後にmv
コマンドでパッケージを移動するステップが追加されました。- また、各オブジェクトファイル(
a1
,a2
,a3
,a4
)がアーカイブに追加された後に、それらの中間オブジェクトファイルを削除するrm -f $(O1)
のような行が追加されました。これにより、ビルドディレクトリのクリーンアップが改善されます。
-
test/math.go
のテスト改善:test/math.go
がtest/mathtest.go
にリネームされました。これは、テストファイルの命名規則をより明確にするためと考えられます。- ファイルの先頭に
// $G $F.go && $L $F.$A && (./$A.out || echo BUG: math fails)
という// run
ディレクティブが追加されました。これは、このテストがどのようにコンパイル、リンク、実行されるべきかを示すもので、テストフレームワークが自動的にテストを実行するために使用します。 import
文がimport math "math"
からimport ("fmt"; "math";)
に変更され、fmt
パッケージが明示的にインポートされました。これは、panic
メッセージのフォーマットにfmt.sprintf
を使用するためです。ck
関数(浮動小数点数の比較を行う関数)における許容誤差e
が1e-14
からfloat64(1e-13)
に調整されました。これは、浮動小数点演算の性質上、厳密な比較が困難であるため、より現実的な誤差範囲を設定するためです。また、panic
メッセージもfmt.sprintf
を使ってより詳細な情報(元の値)を出力するように変更されました。
これらの変更は、Go言語のビルドシステムがより堅牢で、予測可能で、開発者にとって使いやすいものになるための基盤を築きました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
-
src/cmd/6l/obj.c
:addlib
関数のシグネチャがaddlib(char *obj)
からaddlib(char *src, char *obj)
に変更され、ソースパスを引数として受け取るようになりました。addlib
関数内に、アーカイブ内のオブジェクトが元のソースディレクトリ内のファイルを指している場合にその参照を無視するロジックが追加されました。- 新しい関数
copyhistfrog
が追加され、現在のヒストリ(ソースパス)をバッファにコピーするようになりました。 ldobj
関数内でsrc
バッファが初期化され、AHISTORY
命令の処理でaddlib
が新しいシグネチャで呼び出されるようになりました。
-
src/cmd/gobuild/gobuild.c
:- Makefileの
preamble
文字列が変更され、PKG
とPKGDIR
の定義が更新されました。 install
ターゲットにmv $(PKG) $(PKGDIR)/$(PKG)
という行が追加され、ビルドされたアーカイブを適切なGOROOT/pkg
ディレクトリに移動させるようになりました。main
関数内でpkgdir
変数が導入され、パッケージ名からディレクトリパスを抽出するロジックが追加されました。
- Makefileの
-
src/lib/math/Makefile
:PKG
とPKGDIR
の定義がgobuild
の変更に合わせて更新されました。install
ターゲットにmv $(PKG) $(PKGDIR)/$(PKG)
が追加されました。clean
ターゲットに$(PKG)
の削除が追加されました。$(PKG)
ターゲット内で、各オブジェクトファイルがアーカイブに追加された後に削除されるように変更されました(例:rm -f $(O1)
)。
-
test/math.go
(->test/mathtest.go
):- ファイル名が変更されました。
// run
ディレクティブが追加されました。import
文が変更され、fmt
パッケージが追加されました。ck
関数内の許容誤差とpanic
メッセージのフォーマットが変更されました。
コアとなるコードの解説
src/cmd/6l/obj.c
におけるリンカの挙動変更
この変更の目的は、Goのリンカ6l
がアーカイブファイル(.a
)内のオブジェクトを処理する際の、不健全な依存関係の解決を防ぐことです。
// addlib関数の変更点(抜粋)
void
addlib(char *src, char *obj) // src引数が追加された
{
// ...
p = strrchr(src, '/');
q = strrchr(name, '/');
if(p != nil && q != nil && p - src == q - name && memcmp(src, name, p - src) == 0) {
// leading paths are the same.
// if the source file refers to an object in its own directory
// and we are inside an archive, ignore the reference, in the hope
// that the archive contains that object too.
if(strchr(obj, '(')) { // objがアーカイブ内のオブジェクトであるかを判定
if(debug['v'])
Bprint(&bso, "%5.2f ignored srcdir object %s\n", cputime(), name);
return; // 参照を無視
}
}
// ...
}
// ldobj関数内の変更点(抜粋)
loop:
switch(p->as) {
case AHISTORY:
if(p->to.offset == -1) {
addlib(src, pn); // src引数を渡すように変更
histfrogp = 0;
goto loop;
}
if(src[0] == '\0') // srcが空の場合、現在のヒストリをコピー
copyhistfrog(src, sizeof src);
// ...
}
addlib
関数は、リンカが外部ライブラリへの参照を解決する際に呼び出されます。この変更により、addlib
は現在のコンパイル単位のソースパス(src
)も受け取るようになりました。関数内で、参照されるオブジェクトのパス(name
)と現在のソースパス(src
)の先頭部分が一致するかどうかをチェックします。もし一致し、かつ参照がアーカイブ内のオブジェクトから来ている場合(obj
文字列に(
が含まれることで判定)、その参照は無視されます。これは、アーカイブが自己完結型であるべきという原則に基づき、アーカイブ内のオブジェクトが自身の元ソースディレクトリ内の他のファイルに依存するような不健全な状況を避けるためです。これにより、ビルドの循環依存や、アーカイブの再配置によるリンクエラーを防ぎます。
src/cmd/gobuild/gobuild.c
におけるビルド成果物の管理変更
この変更は、gobuild
が生成するMakefileの構造を改善し、ビルドされたパッケージがより適切に管理されるようにすることを目的としています。
// preamble文字列の変更点(抜粋)
char preamble[] =
// ...
"PKG=%s.a\\n" // GOROOT/pkgからの相対パスに変更
"PKGDIR=$(GOROOT)/pkg%s\\n" // 新しいPKGDIR変数を導入
"\\n"
"install: $(PKG)\\n"
"\\tmv $(PKG) $(PKGDIR)/$(PKG)\\n" // ビルド後に移動するステップを追加
"\\n"
"nuke: clean\\n"
"\\trm -f $(PKGDIR)/$(PKG)\\n" // nukeターゲットもPKGDIRを考慮
// ...
以前は、PKG
変数が直接$(GOROOT)/pkg/
以下の絶対パスを指していましたが、この変更によりPKG
はカレントディレクトリに生成されるアーカイブのファイル名(例: math.a
)を指すようになり、PKGDIR
という新しい変数が$(GOROOT)/pkg
以下の最終的なインストールディレクトリを指すようになりました。install
ターゲットには、まずカレントディレクトリにビルドされた$(PKG)
を、$(PKGDIR)
に移動させるmv
コマンドが追加されました。これにより、ビルドプロセスとインストールプロセスが分離され、より柔軟なビルド環境が実現されます。
src/lib/math/Makefile
におけるビルドプロセスの更新
math
パッケージのMakefileは、上記のgobuild
の変更に合わせて更新されました。
# Makefileの変更点(抜粋)
PKG=math.a
PKGDIR=$(GOROOT)/pkg
install: $(PKG)
mv $(PKG) $(PKGDIR)/$(PKG)
nuke: clean
rm -f $(PKGDIR)/$(PKG)
clean:
rm -f *.$O *.a $(PKG)
$(PKG): a1 a2 a3 a4
a1: $(O1)
$(AR) grc $(PKG) $(O1)
rm -f $(O1) # オブジェクトファイル削除を追加
# ... 他のa2, a3, a4も同様
このMakefileは、gobuild
が生成する新しいMakefileの構造を反映しています。PKG
とPKGDIR
の定義が変更され、install
ターゲットでmv
コマンドを使ってビルドされたmath.a
が$GOROOT/pkg
に移動されるようになりました。また、各オブジェクトファイル(a1
, a2
など)がアーカイブに追加された後、それらの中間オブジェクトファイルが削除されるようにrm -f
コマンドが追加されました。これにより、ビルドディレクトリがクリーンに保たれ、不要な中間ファイルが残らないようになります。
関連リンク
- Go言語の初期のビルドシステムに関する議論やドキュメントは、現在のGoの公式ドキュメントからは見つけにくい場合があります。当時のGoのメーリングリストや、Goのソースコードリポジトリの初期のコミットログが参考になることがあります。
- Go言語のビルドプロセスに関する一般的な情報は、現在のGoの公式ドキュメントを参照してください。
- Go Modules Reference (現代のGoのビルドシステム)
参考にした情報源リンク
- Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
- 一般的なコンパイルとリンクの概念に関する情報源 (例: Wikipedia, プログラミング言語のコンパイラに関する書籍など)
- Makefileの基本的な構文と使用法に関する情報源 (例: GNU Makeのマニュアルなど)
- Go言語の初期の設計に関するブログ記事や論文 (Russ Cox氏のブログなど)
- Go at Google: Language Design in the Service of Software Engineering (Goの設計思想に関するRuss Cox氏の講演資料)
- A Tour of Go (Go言語の基本的な概念)
- この解説は、提供されたコミットログとGo言語の一般的な知識に基づいて生成されました。特定の外部ドキュメントや記事を直接参照したものではありません。