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

[インデックス 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といった低レベルのツールが直接使用されていました。

主な背景としては、以下の点が挙げられます。

  1. リンカの挙動の改善: アーカイブファイル(.a)内に含まれるオブジェクトが、そのオブジェクトが元々存在していたソースディレクトリ内のファイルを参照しようとした際に発生する問題を解決する必要がありました。これは、ビルドシステムが複雑な依存関係を解決する際に、誤ったパス解決や無限ループに陥る可能性を排除するためです。
  2. アーカイバの堅牢性向上: 空のアーカイブファイルを扱う際のバグが存在し、ビルドプロセスが予期せず失敗する可能性がありました。これを修正し、より安定したビルド環境を提供することが求められました。
  3. ビルドプロセスの標準化: gobuildツールが生成するMakefileの構造を改善し、ビルドされたアーカイブが適切な場所に配置されるように変更することで、パッケージ管理とインストールプロセスをより予測可能にすることが目的でした。特に、ビルド成果物をカレントディレクトリに生成し、その後GOROOT/pkg配下に移動させるという新しいフローが導入されました。
  4. テストの実行可能性の向上: テストファイルが単独で実行可能であること、およびテストの実行方法が明確に記述されていることが重要でした。これにより、開発者が個々のコンポーネントの変更を迅速に検証できるようになります。

これらの変更は、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言語のビルドシステムにおけるいくつかの重要な技術的課題に対処しています。

  1. 6l (リンカ) におけるアーカイブ内のオブジェクト参照の解決:

    • src/cmd/6l/obj.caddlib関数が変更されています。この関数は、リンカがライブラリ(アーカイブ)からシンボルを解決する際に呼び出されます。
    • 変更の核心は、アーカイブ内のオブジェクトが、そのオブジェクトが元々コンパイルされたソースディレクトリ内の他のファイルを参照しようとした場合に、その参照を無視するロジックが追加されたことです。
    • 具体的には、addlib関数にsrc引数が追加され、現在のソースファイル(またはアーカイブ内のオブジェクトの元ソース)のパスが渡されるようになりました。
    • リンカは、参照されるファイル名と現在のソースファイルのパスを比較し、もし両者が同じディレクトリ構造を持ち、かつ参照がアーカイブ内のオブジェクトから行われている場合(strchr(obj, '(')でアーカイブ内のオブジェクトであるかを判定)、その参照を無視します。
    • これは、アーカイブが自己完結型であるべきであり、アーカイブ内のオブジェクトが外部のソースディレクトリに依存するような不健全な参照を排除するためのものです。これにより、ビルドの循環参照や、アーカイブが移動された際に参照が壊れる問題を回避します。
    • また、copyhistfrogという新しい関数が追加され、現在のヒストリ(コンパイル中のファイルのパス情報)をsrcバッファにコピーするようになりました。これは、addlib関数が正確なソースパス情報に基づいて判断できるようにするためです。
  2. 6ar (アーカイバ) における空アーカイブのバグ修正:

    • src/cmd/ar/ar.cgetpkgdef関数に、pkgstmt == nilの場合に*datap*lenpnil0に設定して早期リターンするチェックが追加されました。
    • これにより、空のアーカイブを処理しようとした際に発生する可能性のあるクラッシュや不正な挙動が防止されます。
  3. gobuild におけるビルド成果物の管理:

    • src/cmd/gobuild/gobuild.cが変更され、gobuildが生成するMakefileのテンプレートが更新されました。
    • 以前はPKG=$(GOROOT)/pkg/%s.aのように、ビルドされたパッケージが直接GOROOT/pkg以下に配置されることを想定していましたが、新しい変更ではPKG=%s.aPKGDIR=$(GOROOT)/pkg%sが導入されました。
    • これにより、gobuildはまずカレントディレクトリにアーカイブファイル(例: math.a)を生成し、その後mv $(PKG) $(PKGDIR)/$(PKG)コマンドを使って、そのアーカイブを適切なGOROOT/pkg以下のディレクトリに移動させるようになりました。
    • この変更は、ビルドプロセスにおける中間成果物の管理を改善し、最終的なインストールパスをより柔軟に制御できるようにすることを目的としています。また、nukeターゲット(クリーンアップ)もPKGDIRを考慮するように更新されています。
  4. math パッケージのビルドプロセスの更新:

    • src/lib/math/Makefileが、新しいgobuildのMakefile構造に適合するように変更されました。
    • PKGPKGDIRの定義がgobuildの変更に合わせて更新され、ビルド後にmvコマンドでパッケージを移動するステップが追加されました。
    • また、各オブジェクトファイル(a1, a2, a3, a4)がアーカイブに追加された後に、それらの中間オブジェクトファイルを削除するrm -f $(O1)のような行が追加されました。これにより、ビルドディレクトリのクリーンアップが改善されます。
  5. test/math.go のテスト改善:

    • test/math.gotest/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関数(浮動小数点数の比較を行う関数)における許容誤差e1e-14からfloat64(1e-13)に調整されました。これは、浮動小数点演算の性質上、厳密な比較が困難であるため、より現実的な誤差範囲を設定するためです。また、panicメッセージもfmt.sprintfを使ってより詳細な情報(元の値)を出力するように変更されました。

これらの変更は、Go言語のビルドシステムがより堅牢で、予測可能で、開発者にとって使いやすいものになるための基盤を築きました。

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

このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。

  1. src/cmd/6l/obj.c:

    • addlib関数のシグネチャがaddlib(char *obj)からaddlib(char *src, char *obj)に変更され、ソースパスを引数として受け取るようになりました。
    • addlib関数内に、アーカイブ内のオブジェクトが元のソースディレクトリ内のファイルを指している場合にその参照を無視するロジックが追加されました。
    • 新しい関数copyhistfrogが追加され、現在のヒストリ(ソースパス)をバッファにコピーするようになりました。
    • ldobj関数内でsrcバッファが初期化され、AHISTORY命令の処理でaddlibが新しいシグネチャで呼び出されるようになりました。
  2. src/cmd/gobuild/gobuild.c:

    • Makefileのpreamble文字列が変更され、PKGPKGDIRの定義が更新されました。
    • installターゲットにmv $(PKG) $(PKGDIR)/$(PKG)という行が追加され、ビルドされたアーカイブを適切なGOROOT/pkgディレクトリに移動させるようになりました。
    • main関数内でpkgdir変数が導入され、パッケージ名からディレクトリパスを抽出するロジックが追加されました。
  3. src/lib/math/Makefile:

    • PKGPKGDIRの定義がgobuildの変更に合わせて更新されました。
    • installターゲットにmv $(PKG) $(PKGDIR)/$(PKG)が追加されました。
    • cleanターゲットに$(PKG)の削除が追加されました。
    • $(PKG)ターゲット内で、各オブジェクトファイルがアーカイブに追加された後に削除されるように変更されました(例: rm -f $(O1))。
  4. 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の構造を反映しています。PKGPKGDIRの定義が変更され、installターゲットでmvコマンドを使ってビルドされたmath.a$GOROOT/pkgに移動されるようになりました。また、各オブジェクトファイル(a1, a2など)がアーカイブに追加された後、それらの中間オブジェクトファイルが削除されるようにrm -fコマンドが追加されました。これにより、ビルドディレクトリがクリーンに保たれ、不要な中間ファイルが残らないようになります。

関連リンク

  • Go言語の初期のビルドシステムに関する議論やドキュメントは、現在のGoの公式ドキュメントからは見つけにくい場合があります。当時のGoのメーリングリストや、Goのソースコードリポジトリの初期のコミットログが参考になることがあります。
  • Go言語のビルドプロセスに関する一般的な情報は、現在のGoの公式ドキュメントを参照してください。

参考にした情報源リンク

  • Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
  • 一般的なコンパイルとリンクの概念に関する情報源 (例: Wikipedia, プログラミング言語のコンパイラに関する書籍など)
  • Makefileの基本的な構文と使用法に関する情報源 (例: GNU Makeのマニュアルなど)
  • Go言語の初期の設計に関するブログ記事や論文 (Russ Cox氏のブログなど)
  • この解説は、提供されたコミットログとGo言語の一般的な知識に基づいて生成されました。特定の外部ドキュメントや記事を直接参照したものではありません。