[インデックス 12282] ファイルの概要
このコミットは、Goコンパイラ(gc
)において、絶対パスでのインポートを禁止する変更を導入します。これは、既存の絶対パスのインポートが正しく機能していなかった問題に対処し、将来的なビルドツールの複雑性を回避することを目的としています。具体的には、src/cmd/gc/lex.c
に絶対パスを検出してエラーを発生させるロジックが追加され、test/import5.go
にその挙動を検証するテストケースが追加されています。
コミット
commit d0d251f858efa75fc67ab8804413edfec57cc2db
Author: Russ Cox <rsc@golang.org>
Date: Wed Feb 29 15:28:36 2012 -0500
gc: disallow absolute import paths
They are broken and hard to make work.
They have never worked: if you import "/tmp/x"
from "/home/rsc/p.c" then the compiler rewrites
this into import "/home/rsc/tmp/x", which is
clearly wrong.
Also we just disallowed the : character in import
paths, so import "c:/foo" is already not allowed.
Finally, in order to support absolute paths well in
a build tool we'd have to provide a mechanism to
instruct the compiler to resolve absolute imports
by looking in some other tree (where the binaries live)
and provide a mapping from absolute path to location
in that tree. This CL avoids adding that complexity.
This is not part of the language spec (and should not be),
so no spec change is needed.
If we need to make them work later, we can.
R=ken2
CC=golang-dev
https://golang.org/cl/5712043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d0d251f858efa75fc67ab8804413edfec57cc2db
元コミット内容
gc: disallow absolute import paths
They are broken and hard to make work.
They have never worked: if you import "/tmp/x"
from "/home/rsc/p.c" then the compiler rewrites
this into import "/home/rsc/tmp/x", which is
clearly wrong.
Also we just disallowed the : character in import
paths, so import "c:/foo" is already not allowed.
Finally, in order to support absolute paths well in
a build tool we'd have to provide a mechanism to
instruct the compiler to resolve absolute imports
by looking in some other tree (where the binaries live)
and provide a mapping from absolute path to location
in that tree. This CL avoids adding that complexity.
This is not part of the language spec (and should not be),
so no spec change is needed.
If we need to make them work later, we can.
R=ken2
CC=golang-dev
https://golang.org/cl/5712043
変更の背景
この変更の背景には、Goコンパイラにおける絶対パスでのインポートが抱えていた複数の問題があります。
- 既存の機能不全: コミットメッセージに明記されているように、絶対パスでのインポートはこれまでも正しく機能していませんでした。例えば、
/home/rsc/p.c
から/tmp/x
をインポートしようとすると、コンパイラはこれを誤って/home/rsc/tmp/x
として解釈していました。これは、インポートパスの解決ロジックが相対パスの解決に特化しており、絶対パスを適切に扱えていなかったためです。 - ビルドツールの複雑性回避: 絶対パスのインポートを適切にサポートするためには、ビルドツール側で、バイナリが存在する別のツリーを参照し、絶対パスからそのツリー内の実際の場所へのマッピングを提供するメカニズムが必要となります。このような機能を追加することは、ビルドシステムに不必要な複雑性をもたらすと考えられました。このコミットは、その複雑性の追加を回避することを目的としています。
- 関連する変更との整合性: このコミットの少し前に、インポートパスにおけるコロン(
:
)文字の使用が禁止されました。これにより、Windowsパスのようなc:/foo
といった形式の絶対パスも既に許可されていませんでした。今回の変更は、Unix-likeな絶対パス(/foo
)も禁止することで、インポートパスの制約をより一貫性のあるものにします。 - 言語仕様との関係: Go言語の仕様では、インポートパスの絶対パスに関する明確な規定がありませんでした。この変更は、言語仕様の一部ではないため、仕様の変更は不要と判断されました。これにより、将来的に必要になった場合にのみ、絶対パスのサポートを再検討する柔軟性が残されています。
これらの理由から、Go開発チームは、現状で機能しない上に複雑性を増す絶対パスのインポートを明示的に禁止する方針を採りました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンパイラに関する基本的な知識が必要です。
Go言語のパッケージとインポート
Go言語では、コードは「パッケージ」という単位で整理されます。他のパッケージのコードを利用するには、import
ステートメントを使用します。
- 標準パッケージ: Go言語に組み込まれているパッケージ(例:
fmt
,net/http
など)。これらはGoのインストールディレクトリ内の特定の場所に存在します。 - サードパーティパッケージ:
go get
などで取得される外部のパッケージ。これらは通常、GOPATH
またはGoモジュールキャッシュ内の特定の場所に配置されます。 - ローカルパッケージ: 開発中のプロジェクト内で定義されるパッケージ。これらは通常、現在のモジュールのルートからの相対パスで参照されます。
Goのインポートパスは、通常、パッケージの識別子として機能し、Goツールチェーンがそのパッケージのソースコードを見つけるためのヒントとなります。
Goコンパイラ(gc
)
gc
は、Go言語の公式コンパイラであり、Goのソースコードを機械語に変換する役割を担っています。Goのビルドプロセスにおいて中心的な役割を果たし、ソースファイルの解析、型チェック、最適化、コード生成などを行います。
lex.c
: Goコンパイラの字句解析(lexical analysis)を担当する部分のC言語ソースファイルです。字句解析は、ソースコードをトークン(キーワード、識別子、演算子など)のストリームに変換するプロセスです。このファイルには、インポートパスの処理に関連するロジックも含まれています。importfile
関数:lex.c
内に存在する関数で、import
ステートメントで指定されたパスを処理し、対応するパッケージファイルを特定しようとします。islocalname
関数: インポートパスがローカルな(相対的な)名前であるかどうかを判断するヘルパー関数です。yyerror
関数: コンパイラが構文エラーやその他の問題を発見した際に、エラーメッセージを出力するために使用される関数です。fakeimport
関数: エラーが発生した場合に、コンパイラの処理を継続させるために、あたかもインポートが成功したかのように振る舞う(ダミーのインポート情報を生成する)関数です。これにより、エラーが発生してもコンパイルプロセスが完全に停止せず、後続のエラーも検出できるようになります。
インポートパスの解決
Goコンパイラは、インポートパスを解決する際に、いくつかのルールに従います。通常、インポートパスは、GOPATH
やGoモジュールパスのルートからの相対パスとして解釈されます。例えば、import "github.com/user/repo/pkg"
のようなパスは、GOPATH/src/github.com/user/repo/pkg
やモジュールキャッシュ内の対応する場所を探します。
絶対パス(例: /home/user/myproject/mypkg
)は、Goの標準的なインポートパス解決メカニズムとは異なる振る舞いをします。コミットメッセージが示唆するように、コンパイラはこれを現在のファイルのディレクトリからの相対パスとして誤って解釈してしまうことがありました。
技術的詳細
このコミットの技術的詳細は、Goコンパイラがインポートパスをどのように処理し、絶対パスをどのように検出して拒否するかという点に集約されます。
Goコンパイラのsrc/cmd/gc/lex.c
ファイル内のimportfile
関数は、import
ステートメントで指定されたパスを処理する主要なロジックを含んでいます。この関数は、インポートパスがローカルな名前であるかどうかをislocalname
関数で判断します。
変更前のgc
の挙動では、islocalname
がtrue
を返した場合、コンパイラはインポートパスを現在のファイルのディレクトリからの相対パスとして扱おうとしました。しかし、もしインポートパスが/tmp/x
のような絶対パスであった場合、コンパイラはこれを現在のファイル(例えば/home/rsc/p.c
)のディレクトリと結合し、/home/rsc/tmp/x
という誤ったパスを生成していました。これは、Goのインポートパス解決の意図とは異なるものであり、機能的なバグでした。
このコミットでは、この誤った挙動を修正するために、importfile
関数内のislocalname(path)
チェックの直後に新しい条件が追加されました。
if(path->s[0] == '/') {
yyerror("import path cannot be absolute path");
fakeimport();
return;
}
このコードスニペットは、以下のロジックを実装しています。
- 絶対パスの検出:
path->s[0] == '/'
という条件は、インポートパス文字列の最初の文字がスラッシュ(/
)であるかどうかをチェックします。Unix-likeシステムにおいて、スラッシュで始まるパスは絶対パスを示します。 - エラーの発生: もしパスが絶対パスであると検出された場合、
yyerror("import path cannot be absolute path");
が呼び出されます。これは、コンパイラが「import path cannot be absolute path」(インポートパスは絶対パスにできません)というエラーメッセージを出力することを意味します。 - ダミーインポートの実行:
fakeimport();
が呼び出されます。これは、エラーが発生したにもかかわらず、コンパイルプロセスが停止しないようにするためのメカニズムです。fakeimport
は、あたかもインポートが成功したかのように、ダミーのパッケージ情報を生成し、コンパイラが後続のコードの解析を続けられるようにします。これにより、単一のエラーでコンパイルが中断されることなく、複数のエラーを一度に報告できるようになります。 - 関数の終了:
return;
ステートメントにより、importfile
関数の現在の実行が終了します。
この変更により、Goコンパイラは、絶対パスでのインポートを明示的に拒否し、開発者にその使用を警告するようになります。これは、既存のバグを修正し、ビルドシステムの複雑性を回避するための、シンプルかつ効果的な解決策です。
また、test/import5.go
に新しいテストケースが追加されたことも重要です。
// Invalid local imports.
import "/foo" // ERROR "import path cannot be absolute path"
import "c:/foo" // ERROR "import path contains invalid character"
これらのテストケースは、以下のことを確認します。
import "/foo"
が、新しいエラーメッセージ「import path cannot be absolute path」を生成すること。import "c:/foo"
が、以前の変更で導入されたエラーメッセージ「import path contains invalid character」を生成すること。これは、コロン(:
)文字がインポートパスで許可されなくなったことを再確認するものです。
これらのテストは、コンパイラの変更が意図した通りに機能し、絶対パスのインポートが正しく拒否されることを保証します。
コアとなるコードの変更箇所
このコミットにおける主要なコードの変更は、以下の2つのファイルにあります。
-
src/cmd/gc/lex.c
: Goコンパイラの字句解析器の一部であり、インポートパスの処理ロジックが含まれています。--- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -637,6 +637,11 @@ importfile(Val *f, int line) path = f->u.sval; if(islocalname(path)) { + if(path->s[0] == '/') { + yyerror("import path cannot be absolute path"); + fakeimport(); + return; + } cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); strcpy(cleanbuf, pathname); strcat(cleanbuf, "/");
-
test/import5.go
: Goのテストスイートの一部であり、インポートパスに関する様々なエラーケースをテストします。--- a/test/import5.go +++ b/test/import5.go @@ -49,3 +49,7 @@ import "\x80\x80" // ERROR "import path" import `\x80\x80` // ERROR "import path" import "\xFFFD" // ERROR "import path" import `\xFFFD` // ERROR "import path" + +// Invalid local imports. +import "/foo" // ERROR "import path cannot be absolute path" +import "c:/foo" // ERROR "import path contains invalid character"
コアとなるコードの解説
src/cmd/gc/lex.c
の変更
src/cmd/gc/lex.c
の変更は、importfile
関数内に新しい条件分岐を追加することで、絶対パスのインポートを検出して拒否します。
path = f->u.sval;
if(islocalname(path)) {
+ if(path->s[0] == '/') {
+ yyerror("import path cannot be absolute path");
+ fakeimport();
+ return;
+ }
cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2);
strcpy(cleanbuf, pathname);
strcat(cleanbuf, "/");
path = f->u.sval;
:import
ステートメントで指定されたインポートパスの文字列を取得し、path
変数に代入します。if(islocalname(path))
: この条件は、インポートパスがローカルな(相対的な)名前として扱われるべきかどうかを判断します。Goコンパイラは、特定の形式のパスをローカルとして扱います。if(path->s[0] == '/')
:islocalname
がtrue
を返したパスに対して、さらにそのパスがスラッシュ(/
)で始まるかどうかをチェックします。スラッシュで始まるパスは、Unix-likeシステムにおける絶対パスの慣習です。yyerror("import path cannot be absolute path");
: もしパスがスラッシュで始まる場合、コンパイラはyyerror
関数を呼び出して、指定されたエラーメッセージ「import path cannot be absolute path」を出力します。これは、絶対パスのインポートが許可されていないことを開発者に通知します。fakeimport();
: エラーが報告された後、fakeimport
関数が呼び出されます。この関数は、コンパイルプロセスがエラーで完全に停止するのを防ぎ、後続のコードの解析を継続できるように、ダミーのインポート情報を生成します。これにより、コンパイラは単一のインポートエラーで中断することなく、ファイル内の他のエラーも検出して報告できます。return;
:importfile
関数の実行を終了し、これ以上このインポートパスの処理を行わないようにします。
この変更により、コンパイラは絶対パスのインポートを早期に検出し、エラーとして処理することで、誤ったパス解決を防ぎ、ビルドの予測可能性を高めます。
test/import5.go
の変更
test/import5.go
の変更は、新しいエラーケースをテストスイートに追加することで、gc
の変更が正しく機能することを検証します。
+// Invalid local imports.
+import "/foo" // ERROR "import path cannot be absolute path"
+import "c:/foo" // ERROR "import path contains invalid character"
import "/foo" // ERROR "import path cannot be absolute path"
: この行は、スラッシュで始まる絶対パスのインポートが、src/cmd/gc/lex.c
で追加された新しいエラーメッセージ「import path cannot be absolute path」を生成することを期待しています。これは、今回のコミットの主要な変更が正しく機能していることを確認します。import "c:/foo" // ERROR "import path contains invalid character"
: この行は、コロン(:
)を含むインポートパスが、以前のコミットで導入されたエラーメッセージ「import path contains invalid character」を生成することを期待しています。これは、関連する以前の変更が引き続き有効であることを確認し、インポートパスの制約の一貫性を保ちます。
これらのテストケースは、コンパイラの変更が意図した通りに動作し、絶対パスや不正な文字を含むインポートパスが適切に拒否されることを保証するための重要な役割を果たします。
関連リンク
- Go CL (Change List): https://golang.org/cl/5712043 このリンクは、このコミットに対応するGoのコードレビューシステム(Gerrit)上の変更リストを示しています。ここには、コミットに至るまでの議論や、レビュー担当者からのコメントなどが含まれている場合があります。
参考にした情報源リンク
- コミットメッセージの内容
- Go言語の公式ドキュメント(パッケージとインポートに関する一般的な情報)
- Goコンパイラのソースコード(
src/cmd/gc/lex.c
) - Goのテストスイート(
test/import5.go
)