[インデックス 17513] ファイルの概要
コミット
commit 5b04d670912aaaa87fdc9a8547a7e71094150661
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 9 13:04:14 2013 -0400
cmd/cgo: record full source path to input .go files
Fixes #5122.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13395046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5b04d670912aaaa87fdc9a8547a7e71094150661
元コミット内容
cmd/cgo: record full source path to input .go files
このコミットは、cmd/cgo
(GoのC言語連携ツール)が入力の.go
ファイルの完全なソースパスを記録するように変更するものです。これにより、エラーメッセージやデバッグ時の行番号情報に、より正確なファイルパスが使用されるようになります。
変更の背景
この変更は、GoのIssue 5122「go build
error messages should use full path names」を修正するために行われました。
Goのツールチェイン、特にcgo
を使用する際に、コンパイルエラーやデバッグ情報において、ソースファイルのパスが相対パスで表示されることがありました。これは、特に複雑なプロジェクト構造や、異なるディレクトリからビルドを実行する場合に、エラーの発生源を特定しにくくするという問題を引き起こしていました。
例えば、go build
コマンドがエラーを報告する際に、./foo.go:10: syntax error
のように表示されると、foo.go
がどのディレクトリにあるのかがすぐに分かりません。しかし、go build
の他の部分では絶対パスが使用されることがあり、cgo
だけが相対パスを使用していると、ツールチェイン全体での一貫性が損なわれ、ユーザー体験が悪化します。
この不整合を解消し、エラーメッセージやデバッグ情報の一貫性と正確性を向上させるために、cgo
が入力ファイルの絶対パスを記録するように変更する必要がありました。
前提知識の解説
- Go言語 (Golang): Googleによって開発されたオープンソースのプログラミング言語。シンプルさ、効率性、並行処理のサポートが特徴です。
- cgo: GoプログラムからC言語のコードを呼び出すためのGoツール。C言語のライブラリを利用したり、既存のC言語コードベースと連携したりする際に使用されます。
cgo
は、Goのソースコード内にC言語のコードを埋め込むことを可能にし、GoとCの間のデータ変換や関数呼び出しを処理します。 - ツールチェイン (Toolchain): ソフトウェア開発に必要な一連のツール群(コンパイラ、リンカ、デバッガなど)を指します。Go言語の場合、
go build
、go run
、go test
などのコマンドがGoツールチェインの一部です。 - 絶対パス (Absolute Path): ファイルシステムにおけるファイルの完全な位置を示すパス。ルートディレクトリから始まり、すべてのディレクトリ階層を含みます(例:
/home/user/project/main.go
)。 - 相対パス (Relative Path): 現在の作業ディレクトリからの相対的な位置を示すパス(例:
./main.go
、../src/utils.go
)。 go/token
パッケージ: Goのソースコードを解析する際に、トークン(キーワード、識別子、演算子など)の位置情報(ファイル、行番号、列番号)を管理するためのパッケージ。go/ast
パッケージ: Goのソースコードの抽象構文木(AST: Abstract Syntax Tree)を表現するためのパッケージ。コンパイラやツールがソースコードを解析し、構造を理解するために使用します。filepath.Abs
関数: Goのpath/filepath
パッケージに含まれる関数で、与えられたパスを絶対パスに変換します。
技術的詳細
このコミットの技術的な核心は、cgo
がGoのソースファイルを読み込む際に、そのファイルのパスを絶対パスに変換してから処理するように変更することです。
具体的には、src/cmd/cgo/ast.go
ファイルのFile.ReadGo
関数が変更されています。この関数は、Goのソースファイルを読み込み、その内容を解析して抽象構文木(AST)を構築する役割を担っています。
変更前は、File.ReadGo
関数に渡されたファイル名(name
引数)がそのまま使用されていました。このname
は、go build
コマンドがcgo
を呼び出す際の引数として渡されるもので、相対パスである可能性がありました。
変更後は、File.ReadGo
関数の冒頭で、filepath.Abs(name)
を呼び出すことで、入力されたファイル名name
を絶対パスに変換しようと試みます。もし変換が成功し、エラーが発生しなければ、変換された絶対パスが新しいname
として使用されます。これにより、cgo
の内部処理、特にエラーメッセージの生成やデバッグ情報の記録において、常にファイルの絶対パスが参照されるようになります。
この変更は、Goツールチェイン全体でのパスの取り扱いの一貫性を向上させ、ユーザーがエラーメッセージから問題の箇所をより正確に特定できるようにすることを目的としています。
コアとなるコードの変更箇所
src/cmd/cgo/ast.go
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -13,6 +13,7 @@ import (
"go/scanner"
"go/token"
"os"
+ "path/filepath"
"strings"
)
@@ -44,6 +45,13 @@ func sourceLine(n ast.Node) int {
// a list of exported functions, and the actual AST, to be rewritten and
// printed.
func (f *File) ReadGo(name string) {
+ // Create absolute path for file, so that it will be used in error
+ // messages and recorded in debug line number information.
+ // This matches the rest of the toolchain. See golang.org/issue/5122.
+ if aname, err := filepath.Abs(name); err == nil {
+ name = aname
+ }
+
// Two different parses: once with comments, once without.
// The printer is not good enough at printing comments in the
// right place when we start editing the AST behind its back,
src/cmd/go/test.bash
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -463,6 +463,26 @@ import "C"
rm -rf $d
unset GOPATH
+TEST cgo shows full path names
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/x/y/dirname
+echo '
+package foo
+import "C"
+func f() {
+' >$d/src/x/y/dirname/foo.go
+if ./testgo build x/y/dirname >$d/err 2>&1; then
+ echo build succeeded unexpectedly.
+ ok=false
+elif ! grep x/y/dirname $d/err >/dev/null; then
+ echo error did not use full path.
+ cat $d/err
+ ok=false
+fi
+rm -rf $d
+unset GOPATH
+
# clean up
if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
コアとなるコードの解説
src/cmd/cgo/ast.go
の変更
import "path/filepath"
の追加: ファイルパス操作のための標準ライブラリパッケージpath/filepath
がインポートされました。File.ReadGo
関数の変更:if aname, err := filepath.Abs(name); err == nil { name = aname }
の行が追加されました。- このコードは、
filepath.Abs(name)
を呼び出して、入力されたファイル名name
の絶対パスを取得しようとします。 filepath.Abs
は、パスを絶対パスに変換する際に、シンボリックリンクの解決やカレントディレクトリの考慮などを行います。- もしエラーが発生せず(
err == nil
)、絶対パスaname
が正常に取得できた場合、元のname
変数をこの絶対パスaname
で上書きします。 - これにより、
File.ReadGo
関数以降の処理(ASTの構築、エラーメッセージの生成、デバッグ情報の記録など)では、常にファイルの絶対パスが使用されるようになります。 - コメント
// This matches the rest of the toolchain. See golang.org/issue/5122.
は、この変更がGoツールチェイン全体の一貫性を保つためのものであり、関連するIssue 5122に言及しています。
src/cmd/go/test.bash
の変更
- 新しいテストケースの追加:
TEST cgo shows full path names
という新しいテストケースが追加されました。 - テストの目的: このテストは、
cgo
がエラーメッセージで完全なパス名を表示するかどうかを検証します。 - テストの手順:
- 一時ディレクトリを作成し、
GOPATH
として設定します。 $d/src/x/y/dirname/foo.go
というパスに、意図的に構文エラーを含むGoソースファイルを作成します(func f() {
の後に閉じ括弧がない)。./testgo build x/y/dirname
コマンドを実行し、ビルドエラーを発生させます。- ビルドが成功した場合はテストを失敗とします(エラーが期待されるため)。
- エラー出力
$d/err
の中に、x/y/dirname
という部分パスが含まれているかどうかをgrep
で確認します。 - もし
x/y/dirname
が含まれていない場合(つまり、完全なパスが表示されていない場合)、テストを失敗とします。これは、エラーメッセージに相対パスではなく、ディレクトリ名を含む完全なパスが表示されることを期待しているためです。 - 一時ディレクトリをクリーンアップします。
- 一時ディレクトリを作成し、
このテストケースは、cgo
がIssue 5122で報告された問題を修正し、エラーメッセージに絶対パス(または少なくともディレクトリ名を含むより詳細なパス)を正しく表示するようになったことを確認するために非常に重要です。
関連リンク
- Go Issue 5122: https://github.com/golang/go/issues/5122
- Go Change List 13395046: https://golang.org/cl/13395046
参考にした情報源リンク
- Go言語公式ドキュメント: https://golang.org/doc/
path/filepath
パッケージドキュメント: https://pkg.go.dev/path/filepathgo/token
パッケージドキュメント: https://pkg.go.dev/go/tokengo/ast
パッケージドキュメント: https://pkg.go.dev/go/ast- cgoドキュメント: https://pkg.go.dev/cmd/cgo
- GoのIssueトラッカー: https://github.com/golang/go/issues
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/
mktemp
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/mktemp.1.htmlgrep
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/grep.1.htmlrm
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/rm.1.htmlmkdir
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/mkdir.1.htmlexport
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/export.1p.htmlunset
コマンド (Linux man page): https://man7.org/linux/man-pages/man1/unset.1p.html- シェルスクリプトの基本 (例:
2>&1
): https://www.gnu.org/software/bash/manual/html_node/Redirections.html - Goのテストフレームワーク (Goのテストの書き方): https://go.dev/doc/tutorial/add-a-test
- Goのビルドプロセス: https://go.dev/doc/code
- GoのGOPATH: https://go.dev/doc/gopath_code
- Goのモジュール (Go Modules): https://go.dev/blog/using-go-modules (このコミット時点ではGo Modulesは存在しないが、Goの依存関係管理の進化を理解する上で参考になる)
- Goのコンパイラとツールチェインの内部構造に関する一般的な情報源 (例: Goのソースコードリポジトリ): https://github.com/golang/go
- Goのデバッグ情報 (DWARFなど): https://go.dev/doc/gdb (Goのデバッグに関する一般的な情報)
- GoのAST (抽象構文木) の概念: https://go.dev/blog/go-ast