[インデックス 1803] ファイルの概要
このコミットは、Goコンパイラ(gc
)に-I
オプションのサポートを追加するものです。このオプションを使用すると、ユーザーはパッケージ(具体的には.a
および.6
ファイル)を検索するための追加のディレクトリを指定できるようになります。これにより、Goコンパイラがパッケージを解決する際の柔軟性が向上します。
コミット
commit 062d6998abfa3fd59d47ed86547783db25ab066b
Author: Ian Lance Taylor <iant@golang.org>
Date: Tue Mar 10 20:03:31 2009 -0700
Add support for a -I option. -I DIR searches for packages in
DIR.
R=ken,rsc
DELTA=49 (41 added, 2 deleted, 6 changed)
OCL=26057
CL=26092
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/062d6998abfa3fd59d47ed86547783db25ab066b
元コミット内容
-I
オプションのサポートを追加しました。-I DIR
は、DIR
内でパッケージを検索します。
変更の背景
Go言語の初期段階において、コンパイラがパッケージを検索するメカニズムは主にGOROOT/pkg
ディレクトリに依存していました。しかし、開発者がプロジェクト固有のパッケージや、標準ライブラリとは異なる場所に配置されたパッケージを使用する場合、この固定された検索パスだけでは不十分でした。
このコミットは、C/C++コンパイラにおけるインクルードパスの指定(例: gcc -I/path/to/includes
)と同様の機能を提供することで、この問題を解決しようとしています。-I
オプションを導入することで、ユーザーはコンパイラに対して、指定されたディレクトリもパッケージ検索の対象に含めるように指示できるようになります。これにより、ビルドシステムや開発環境の柔軟性が向上し、より複雑なプロジェクト構成に対応できるようになります。
前提知識の解説
- Goコンパイラ (
gc
): Go言語の初期の公式コンパイラはgc
と呼ばれていました。これはPlan 9 Cコンパイラをベースにしており、Goのソースコードを機械語に変換する役割を担っていました。現在のGoコンパイラはより統合されたツールチェーンの一部ですが、このコミットが作成された時点ではgc
が主要なコンパイラでした。 - Goパッケージ: Go言語では、コードはパッケージにまとめられます。他のパッケージの関数や型を使用するには、
import
ステートメントを使ってそのパッケージをインポートする必要があります。コンパイラはインポートされたパッケージの定義を見つけるために、特定の検索パスをたどります。 - .a ファイルと .6 ファイル: Go言語の初期のビルドシステムでは、コンパイルされたパッケージは主に
.a
(アーカイブ)ファイルまたは.6
(オブジェクト)ファイルとして保存されていました。.a
ファイルは、複数のオブジェクトファイルやその他のリソースをまとめたアーカイブファイルで、ライブラリとして機能します。.6
ファイルは、Goのソースコードがコンパイルされた後のオブジェクトファイルで、Goの初期のツールチェーンでは、ターゲットアーキテクチャが64ビットの場合に.6
という拡張子が使われていました(例:8
は32ビットIntel、5
はARMなど)。これらのファイルには、パッケージの型情報や関数定義などが含まれており、他のGoプログラムからインポートされる際にコンパイラによって参照されます。
GOROOT
環境変数:GOROOT
はGoのインストールディレクトリを指す環境変数です。Goの標準ライブラリパッケージは通常、$GOROOT/pkg
以下に配置されており、コンパイラはデフォルトでこのパスを検索します。access(namebuf, 0)
: これはC言語のシステムコールaccess()
の呼び出しです。access(path, mode)
は、指定されたパスpath
に対して、mode
で指定されたアクセス権があるかどうかをチェックします。0
はF_OK(ファイルが存在するかどうか)を意味し、ファイルが存在すれば0を返し、存在しなければ-1を返します。このコミットでは、パッケージファイル(.a
または.6
)が存在するかどうかを確認するために使用されています。- リンクリスト:
Idir
構造体とlink
フィールドは、ディレクトリパスを格納するための単純なリンクリストを形成しています。これにより、複数の-I
オプションが指定された場合でも、それらのディレクトリパスを効率的に管理できます。
技術的詳細
このコミットの主要な技術的変更点は以下の通りです。
-
Idir
構造体の導入:src/cmd/gc/go.h
にIdir
という新しい構造体が定義されました。typedef struct Idir Idir; struct Idir { Idir* link; // 次のディレクトリへのポインタ char* dir; // ディレクトリパス文字列へのポインタ };
この構造体は、
-I
オプションで指定された各ディレクトリパスを格納するためのノードとして機能します。link
フィールドはリンクリストを構築するために使用され、dir
フィールドは実際のディレクトリパスを保持します。 -
グローバルリンクリスト
idirs
の追加:src/cmd/gc/go.h
にEXTERN Idir* idirs;
が追加されました。これは、-I
オプションで指定されたすべてのディレクトリパスを格納するリンクリストのヘッドポインタとなるグローバル変数です。 -
addidir
関数の追加:src/cmd/gc/go.h
にプロトタイプが、src/cmd/gc/lex.c
に実装が追加されました。void addidir(char* dir) { Idir** pp; if(dir == nil) return; for(pp = &idirs; *pp != nil; pp = &(*pp)->link) ; *pp = mal(sizeof(Idir)); (*pp)->link = nil; (*pp)->dir = dir; }
この関数は、引数として受け取ったディレクトリパス
dir
を新しいIdir
ノードに格納し、それをグローバルなidirs
リンクリストの末尾に追加します。mal
はメモリ割り当て関数です。 -
mainlex
での-I
オプションの処理:src/cmd/gc/lex.c
のmainlex
関数(コマンドライン引数パーサー)に、-I
オプションを処理するロジックが追加されました。case 'I': addidir(ARGF()); break;
-I
オプションが検出されると、その直後の引数(ディレクトリパス)をARGF()
で取得し、addidir
関数に渡してリンクリストに追加します。 -
findpkg
関数におけるパッケージ検索ロジックの変更:src/cmd/gc/lex.c
のfindpkg
関数は、指定されたパッケージ名に対応するパッケージファイル(.a
または.6
)を検索する役割を担っています。この関数が大幅に修正されました。- 優先順位の変更: 以前は、現在のディレクトリ、次に
GOROOT/pkg
の順で検索していました。変更後、まずidirs
リンクリストに登録されたすべてのディレクトリを順番に検索するようになりました。 GOROOT
の扱い:GOROOT
が設定されていない場合にfindpkg
が即座に0を返すロジックが削除され、GOROOT
の検索はif(goroot != nil)
ブロック内に移動しました。これにより、-I
で指定されたパスがあれば、GOROOT
が設定されていなくてもパッケージ検索が試行されるようになりました。- 検索パスの構築: 各ディレクトリに対して、パッケージ名と
.a
または.6
拡張子を結合したパス(例:/path/to/dir/packagename.a
)をsnprint
で構築し、access
関数でファイルの存在を確認します。
- 優先順位の変更: 以前は、現在のディレクトリ、次に
これらの変更により、Goコンパイラは-I
オプションで指定されたパスを優先的に検索し、パッケージの解決を行うようになります。
コアとなるコードの変更箇所
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -455,6 +455,13 @@ struct Dlist
Type* field;
};
+typedef struct Idir Idir;
+struct Idir
+{
+ Idir* link;
+ char* dir;
+};
+
EXTERN Dlist dotlist[10]; // size is max depth of embeddeds
EXTERN Io curio;
@@ -482,6 +489,7 @@ EXTERN int tptr; // either TPTR32 or TPTR64
extern char* sysimport;
extern char* unsafeimport;
EXTERN char* filename; // name to uniqify names
+EXTERN Idir* idirs;
EXTERN Type* types[NTYPE];
EXTERN uchar simtype[NTYPE];
@@ -552,6 +560,7 @@ int yyparse(void);\
*/
int mainlex(int, char*[]);
void setfilename(char*);
+void addidir(char*);\
void importfile(Val*);\
void cannedimports(char*, char*);\
void unimportfile();
src/cmd/gc/lex.c
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -35,6 +35,10 @@ mainlex(int argc, char *argv[])
case 'k':
package = ARGF();
break;
+\
+\tcase 'I':
+\t\taddidir(ARGF());
+\t\tbreak;
} ARGEND
if(argc != 1)
@@ -109,6 +113,7 @@ mainlex(int argc, char *argv[])
usage:
print("flags:\n");
+\tprint(" -I DIR search for packages in DIR\n");
print(" -d print declarations\n");
print(" -f print stack frame structure\n");
print(" -k name specify package name\n");
@@ -175,15 +180,29 @@ skiptopkgdef(Biobuf *b)\
return 1;\
}\
+void
+addidir(char* dir)\
+{\
+\tIdir** pp;\
+\
+\tif(dir == nil)\
+\t\treturn;\
+\
+\tfor(pp = &idirs; *pp != nil; pp = &(*pp)->link)\
+\t\t;\
+\t*pp = mal(sizeof(Idir));\
+\t(*pp)->link = nil;\
+\t(*pp)->dir = dir;\
+}\
+\
int
findpkg(String *name)\
{\
static char* goroot;\
+\tIdir* p;\
\
\tif(goroot == nil) {\
\t\tgoroot = getenv("GOROOT");
-\t\tif(goroot == nil)
-\t\t\treturn 0;
\t}\
\
\t// BOTCH need to get .6 from backend
@@ -191,18 +210,29 @@ findpkg(String *name)\
\t// try .a before .6. important for building libraries:
\t// if there is an array.6 in the array.a library,
\t// want to find all of array.a, not just array.6.
+\tfor(p = idirs; p != nil; p = p->link) {\
+\t\tsnprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name);\
+\t\tif(access(namebuf, 0) >= 0)\
+\t\t\treturn 1;\
+\t\tsnprint(namebuf, sizeof(namebuf), "%s/%Z.6", p->dir, name);\
+\t\tif(access(namebuf, 0) >= 0)\
+\t\t\treturn 1;\
+\t}\
+\
\tsnprint(namebuf, sizeof(namebuf), "%Z.a", name);\
\tif(access(namebuf, 0) >= 0)\
\t\treturn 1;\
\tsnprint(namebuf, sizeof(namebuf), "%Z.6", name);\
\tif(access(namebuf, 0) >= 0)\
\t\treturn 1;\
-\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.a", goroot, name);\
-\tif(access(namebuf, 0) >= 0)\
-\t\treturn 1;\
-\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.6", goroot, name);\
-\tif(access(namebuf, 0) >= 0)\
-\t\treturn 1;\
+\tif(goroot != nil) {\
+\t\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.a", goroot, name);\
+\t\tif(access(namebuf, 0) >= 0)\
+\t\t\treturn 1;\
+\t\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.6", goroot, name);\
+\t\tif(access(namebuf, 0) >= 0)\
+\t\t\treturn 1;\
+\t}\
\treturn 0;\
}\
コアとなるコードの解説
-
src/cmd/gc/go.h
:Idir
構造体は、-I
オプションで指定される各ディレクトリパスを保持するためのデータ構造を定義しています。link
はリンクリストの次の要素へのポインタ、dir
はディレクトリパス文字列へのポインタです。EXTERN Idir* idirs;
は、Idir
構造体のリンクリストの先頭を指すグローバル変数idirs
を宣言しています。この変数は、コンパイラがパッケージを検索する追加のディレクトリパスを管理するために使用されます。void addidir(char*);
は、新しいディレクトリパスをidirs
リンクリストに追加するための関数のプロトタイプです。
-
src/cmd/gc/lex.c
:mainlex
関数内のcase 'I':
ブロックは、コマンドライン引数から-I
オプションを解析し、その引数(ディレクトリパス)をaddidir
関数に渡してidirs
リンクリストに追加します。usage
メッセージに-I
オプションの説明が追加され、ユーザーがこのオプションの存在と目的を理解できるようになりました。addidir
関数は、与えられたディレクトリパスを新しいIdir
ノードとしてidirs
リンクリストの末尾に動的に追加します。これにより、複数の-I
オプションが指定されても、すべてのパスが適切に処理されます。findpkg
関数は、Goコンパイラのパッケージ検索ロジックの核心です。このコミットにより、findpkg
はまずidirs
リンクリスト内のすべてのディレクトリを順番に検索し、指定されたパッケージ名に対応する.a
または.6
ファイルが存在するかどうかを確認します。この検索が失敗した場合にのみ、現在のディレクトリ、そしてGOROOT/pkg
ディレクトリを検索するようになります。これにより、ユーザーが指定したパスが最も高い優先順位で検索されるようになり、パッケージの解決がより柔軟に行えるようになりました。また、GOROOT
が設定されていない場合でも、-I
で指定されたパスがあれば検索が続行されるように変更されています。
関連リンク
- Go言語の初期のコンパイラツールチェーンに関する情報: https://go.dev/doc/go1.0#tool (Go 1.0のドキュメントですが、当時のツールチェーンの概念を理解するのに役立ちます)
- Go言語のパッケージとモジュールの概念: https://go.dev/doc/code
参考にした情報源リンク
access()
システムコールに関する情報: https://man7.org/linux/man-pages/man2/access.2.html- Go言語の初期のビルドプロセスとファイル拡張子に関する一般的な知識。
- C言語におけるリンクリストの実装に関する一般的な知識。
- Go言語のソースコードリポジトリ (golang/go) のコミット履歴。
- Go言語の公式ドキュメント。
- Go言語のコンパイラ設計に関する一般的な情報。
snprintf
関数に関する情報: https://man7.org/linux/man-pages/man3/snprintf.3.htmlgetenv
関数に関する情報: https://man7.org/linux/man-pages/man3/getenv.3.html