[インデックス 17061] ファイルの概要
このコミットは、Go言語の標準ライブラリos
パッケージにおけるGetwd
(現在の作業ディレクトリを取得する関数)のPlan 9ビルドに関するバグ修正です。以前のコミットでmacOS (Darwin) 向けの変更が導入された際に、Plan 9環境でsyscall.ENOTSUP
エラーが未定義であるためにビルドが壊れた問題を解決します。
コミット
commit ad119b9c4dcae8389a3700c245a923b0ebe449cd
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Tue Aug 6 12:04:08 2013 -0700
os: fix plan9 build
I broke it with the darwin getwd attrlist stuff (0583e9d36dd).
plan9 doesn't have syscall.ENOTSUP.
It's in api/go1.txt as a symbol always available (not context-specific):
pkg syscall, const ENOTSUP Errno
... but plan9 isn't considered by cmd/api, so it only looks
universally available. Alternatively, we could add a fake ENOTSUP
to plan9, but they were making efforts earlier to clean their
syscall package, so I'd prefer not to dump more in it.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12509044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ad119b9c4dcae8389a3700c245a923b0ebe449cd
元コミット内容
このコミットは、os
パッケージのGetwd
関数がPlan 9環境でビルドできない問題を修正します。問題は、以前のコミット0583e9d36dd
("darwin: use getattrlist for Getwd")でmacOS (Darwin) 向けのGetwd
実装が変更された際に発生しました。この変更は、syscall.ENOTSUP
というエラーコードを使用しましたが、Plan 9システムにはこのエラーコードが存在しないため、ビルドエラーが発生しました。
コミットメッセージでは、syscall.ENOTSUP
がapi/go1.txt
で常に利用可能なシンボルとして定義されているにもかかわらず、cmd/api
ツールがPlan 9を考慮していないため、普遍的に利用可能であると誤認されていたことが指摘されています。修正アプローチとして、Plan 9に偽のENOTSUP
を追加するのではなく、プラットフォーム固有のロジックでsyscall.ENOTSUP
のチェックを分離する方法が選択されました。これは、Plan 9のsyscall
パッケージをクリーンに保つという以前の取り組みに沿ったものです。
変更の背景
この変更の背景には、Go言語のクロスプラットフォーム対応における課題があります。Goは様々なオペレーティングシステムをサポートしていますが、OS固有のシステムコールやエラーコードの差異を吸収する必要があります。
具体的には、以下の経緯があります。
- macOS (Darwin) の
Getwd
最適化: 以前のコミット0583e9d36dd
では、macOSのGetwd
実装がgetattrlist
システムコールを使用するように変更されました。これは、シンボリックリンクを解決せずに現在の作業ディレクトリのパスを取得するための最適化でした。このgetattrlist
がENOTSUP
(Operation not supported)を返す可能性があるため、そのエラーを適切に処理する必要がありました。 syscall.ENOTSUP
の利用: macOSの新しいGetwd
実装では、syscall.ENOTSUP
を使用して、特定の操作がサポートされていない場合にエラーを処理していました。- Plan 9での問題: しかし、Plan 9オペレーティングシステムには
ENOTSUP
というエラーコードが存在しませんでした。Goのビルドシステムは、api/go1.txt
に定義されているシンボルを普遍的に利用可能であると見なす傾向がありましたが、cmd/api
ツールがPlan 9の特殊性を完全に考慮していなかったため、この差異が見過ごされました。 - ビルドの破損: 結果として、Plan 9環境でGoの
os
パッケージをビルドしようとすると、syscall.ENOTSUP
が未定義であるためにコンパイルエラーが発生し、ビルドが壊れてしまいました。 - 修正の必要性: この問題を解決し、Goのクロスプラットフォーム互換性を維持するために、このコミットが導入されました。特に、Plan 9の
syscall
パッケージに余計なものを追加したくないという開発者の意向も考慮されました。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
- Go言語のクロスコンパイルとビルドタグ: Goは異なるOSやアーキテクチャ向けにコードをコンパイルする機能(クロスコンパイル)を強力にサポートしています。これには、ファイル名に
_os.go
(例:getwd_darwin.go
)のようなサフィックスを付けることで、特定のOS向けにのみコンパイルされるファイルを指定する仕組みがあります。 syscall
パッケージ: Goのsyscall
パッケージは、オペレーティングシステムの低レベルなプリミティブ(システムコール)へのインターフェースを提供します。OSによって利用可能なシステムコールやエラーコードは異なります。Getwd
関数:os.Getwd()
は、現在の作業ディレクトリの絶対パスを返すGoの標準関数です。この関数の実装は、OSによって異なる場合があります。syscall.ENOTSUP
: これは、"Operation not supported"(操作がサポートされていません)を意味するエラーコードです。多くのPOSIX準拠システム(Linux, macOSなど)で定義されていますが、Plan 9のような非POSIXシステムでは存在しない場合があります。getattrlist
(macOS): macOSで利用可能なシステムコールで、ファイルシステムオブジェクトの属性を効率的に取得するために使用されます。os.Getwd
の実装で、シンボリックリンクを解決せずに現在のディレクトリのパスを取得するために利用されることがあります。- Plan 9: ベル研究所で開発された分散オペレーティングシステムです。Unixとは異なる設計思想を持ち、ファイルシステム中心の哲学が特徴です。Go言語はPlan 9の影響を強く受けており、Plan 9向けのビルドもサポートしています。
api/go1.txt
とcmd/api
:api/go1.txt
は、Go 1の互換性保証の一部として、Goの標準ライブラリが提供するAPIのリストを定義したファイルです。cmd/api
ツールは、このファイルと実際のGoソースコードを比較し、APIの互換性が維持されているかを確認します。
技術的詳細
このコミットの技術的な解決策は、syscall.ENOTSUP
の存在をプラットフォーム固有のロジックにカプセル化することです。
-
useSyscallwd
変数の導入:src/pkg/os/getwd.go
に、useSyscallwd
というfunc(error) bool
型のグローバル変数(関数ポインタ)が導入されました。この変数は、syscall.Getwd()
が返したエラーに基づいて、その戻り値を使用すべきかどうかを決定するロジックをカプセル化します。 初期値としては、エラーが何であれ常にtrue
を返す匿名関数が設定されています。これは、デフォルトの挙動として、syscall.Getwd
がエラーを返してもその結果を使用しようとすることを意味します。// useSyscallwd determines whether to use the return value of // syscall.Getwd based on its error. var useSyscallwd = func(error) bool { return true }
-
Getwd
関数の変更:src/pkg/os/getwd.go
のGetwd
関数内で、syscall.Getwd()
からのエラーチェックが変更されました。以前はe != syscall.ENOTSUP
という直接的な比較が行われていましたが、これがuseSyscallwd(e)
の呼び出しに置き換えられました。// 変更前 // if e != syscall.ENOTSUP { // 変更後 if useSyscallwd(e) {
これにより、
syscall.ENOTSUP
の存在しないプラットフォーム(Plan 9など)では、この比較が直接行われることがなくなります。 -
macOS (Darwin) 固有の実装:
src/pkg/os/getwd_darwin.go
という新しいファイルが追加されました。このファイルは、Goのビルドタグの仕組みにより、macOS (Darwin) 環境でのみコンパイルされます。 このファイルには、useSyscallwdDarwin
という関数が定義されています。この関数は、引数として受け取ったエラーがsyscall.ENOTSUP
と異なる場合にtrue
を返します。func useSyscallwdDarwin(err error) bool { return err != syscall.ENOTSUP }
そして、
init
関数内で、グローバル変数useSyscallwd
にこのuseSyscallwdDarwin
関数が代入されます。func init() { useSyscallwd = useSyscallwdDarwin }
これにより、macOS環境では
useSyscallwd
がsyscall.ENOTSUP
を考慮したロジックを持つようになり、他のプラットフォームではデフォルトの(常にtrue
を返す)ロジックが適用されることになります。
このアプローチにより、syscall.ENOTSUP
が存在しないPlan 9のようなシステムでは、syscall.ENOTSUP
への参照がコンパイル時に含まれなくなり、ビルドエラーが解消されます。同時に、syscall.ENOTSUP
が存在するmacOSのようなシステムでは、そのエラーを適切に処理する以前のロジックが維持されます。
コアとなるコードの変更箇所
src/pkg/os/getwd.go
--- a/src/pkg/os/getwd.go
+++ b/src/pkg/os/getwd.go
@@ -14,6 +14,10 @@ var getwdCache struct {
dir string
}
+// useSyscallwd determines whether to use the return value of
+// syscall.Getwd based on its error.
+var useSyscallwd = func(error) bool { return true }
+
// Getwd returns a rooted path name corresponding to the
// current directory. If the current directory can be
// reached via multiple paths (due to symbolic links),
@@ -22,7 +26,7 @@ func Getwd() (pwd string, err error) {
// If the operating system provides a Getwd call, use it.
if syscall.ImplementsGetwd {
s, e := syscall.Getwd()
- if e != syscall.ENOTSUP {
+ if useSyscallwd(e) {
return s, NewSyscallError("getwd", e)
}
}
src/pkg/os/getwd_darwin.go
(新規ファイル)
--- /dev/null
+++ b/src/pkg/os/getwd_darwin.go
@@ -0,0 +1,15 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+func init() {
+ useSyscallwd = useSyscallwdDarwin
+}
+
+func useSyscallwdDarwin(err error) bool {
+ return err != syscall.ENOTSUP
+}
コアとなるコードの解説
src/pkg/os/getwd.go
の変更点
-
useSyscallwd
変数の追加:var useSyscallwd = func(error) bool { return true }
この行は、useSyscallwd
という関数型の変数を宣言し、初期化しています。この変数は、syscall.Getwd()
から返されたエラーe
を受け取り、そのエラーがGetwd
の戻り値を使用すべきではないことを示すかどうかを判断するロジックをカプセル化します。デフォルトでは、どのようなエラーが返されてもtrue
を返す(つまり、syscall.Getwd
の戻り値を使用する)ように設定されています。これは、syscall.ENOTSUP
が存在しないシステム(Plan 9など)でのフォールバックの挙動となります。 -
Getwd
関数内の条件式の変更:if useSyscallwd(e) {
以前はif e != syscall.ENOTSUP {
という直接的な比較が行われていました。この変更により、エラーの評価ロジックがuseSyscallwd
関数に委譲されます。これにより、syscall.ENOTSUP
が定義されていない環境でもコンパイルエラーが発生しなくなります。
src/pkg/os/getwd_darwin.go
の追加と解説
-
ファイル名とビルドタグ: ファイル名が
getwd_darwin.go
であるため、Goのビルドシステムは、このファイルがmacOS (Darwin) 環境でのみコンパイルされることを認識します。これにより、macOS固有のコードが他のOSのビルドに影響を与えることを防ぎます。 -
useSyscallwdDarwin
関数の定義:func useSyscallwdDarwin(err error) bool { return err != syscall.ENOTSUP }
この関数は、macOS環境におけるuseSyscallwd
の具体的な実装を提供します。macOSではsyscall.ENOTSUP
が定義されているため、この関数は、syscall.Getwd()
が返したエラーがsyscall.ENOTSUP
ではない場合にtrue
を返します。これは、getattrlist
がENOTSUP
を返した場合に、そのエラーを特別扱いしてGetwd
の戻り値を使用しないという、以前のmacOS固有のロジックを維持するためのものです。 -
init
関数でのuseSyscallwd
のオーバーライド:func init() { useSyscallwd = useSyscallwdDarwin }
Goのinit
関数は、パッケージが初期化される際に自動的に実行されます。macOS環境でこのファイルがコンパイルされると、このinit
関数が実行され、src/pkg/os/getwd.go
で定義されたグローバル変数useSyscallwd
が、useSyscallwdDarwin
関数で上書きされます。これにより、macOSではsyscall.ENOTSUP
を考慮したエラーハンドリングが有効になります。
この変更の全体的な効果は、syscall.ENOTSUP
の存在に依存するロジックをプラットフォーム固有のファイルに分離し、syscall.ENOTSUP
が存在しないプラットフォームではそのロジックがコンパイルされないようにすることです。これにより、Goのクロスプラットフォーム互換性が向上し、Plan 9でのビルドが再び可能になりました。
関連リンク
-
元の問題を引き起こしたコミット (0583e9d36dd): https://github.com/golang/go/commit/0583e9d36dd このコミットは、"darwin: use getattrlist for Getwd" というタイトルで、macOSの
Getwd
実装をgetattrlist
を使用するように変更しました。 -
Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall -
Go言語の
os
パッケージのドキュメント: https://pkg.go.dev/os
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語の公式ドキュメント: https://go.dev/
- Plan 9 from Bell Labs: https://9p.io/plan9/
- POSIX (Portable Operating System Interface): https://ja.wikipedia.org/wiki/POSIX
getattrlist
man page (macOS):man getattrlist
(ターミナルで実行)