[インデックス 15784] ファイルの概要
このコミットは、Go言語のビルドおよびインストールツールである cmd/dist
において、Plan 9オペレーティングシステム上での $GOBIN
環境変数の設定に関するユーザーへの指示を改善するものです。具体的には、Plan 9の環境特性に合わせて、PATH
への追加ではなく bind
コマンドを用いた適切な設定方法をユーザーに促すように変更されています。
コミット
commit d94da6fab584ebba9fd9b9adcecc173a4174932f
Author: Anthony Martin <ality@pbrane.org>
Date: Fri Mar 15 05:04:19 2013 +0100
cmd/dist: instruct the user to bind $GOBIN on Plan 9
R=seed, rminnich, bradfitz, r
CC=golang-dev
https://golang.org/cl/7395059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d94da6fab584ebba9fd9b9adcecc173a4174932f
元コミット内容
cmd/dist: instruct the user to bind $GOBIN on Plan 9
このコミットは、Goの配布ツール (cmd/dist
) が、Plan 9オペレーティングシステム上でGoをインストールした際に、ユーザーに対して $GOBIN
環境変数を適切に設定するよう指示を出すためのものです。従来のUnix系OSにおける PATH
環境変数への追加とは異なる、Plan 9特有の bind
コマンドの使用を促します。
変更の背景
Go言語のビルドプロセスでは、コンパイルされたバイナリが配置されるディレクトリを $GOBIN
環境変数で指定します。多くのUnix系オペレーティングシステムでは、この $GOBIN
ディレクトリをシステムの PATH
環境変数に追加することで、ユーザーはコマンドラインからGoのツールや自身がビルドしたプログラムを直接実行できるようになります。
しかし、Plan 9オペレーティングシステムは、Unixとは異なるファイルシステムとプロセス管理のパラダイムを持っています。特に、Plan 9にはUnixのようなグローバルな PATH
環境変数の概念がありません。代わりに、各プロセスの「名前空間 (namespace)」が重要であり、bind
コマンドを使ってディレクトリを特定のパスにマウント(バインド)することで、そのプロセスからファイルやコマンドにアクセスできるようになります。
この違いのため、Goの cmd/dist
ツールがPlan 9上でGoをインストールした際に、一般的なUnix系の「PATH
に $GOBIN
を追加してください」という指示を出すと、Plan 9ユーザーにとっては不適切であり、混乱を招く可能性がありました。このコミットは、この問題を解決し、Plan 9ユーザーに対してより正確で役立つ指示を提供することを目的としています。
前提知識の解説
Go言語のビルドとcmd/dist
Go言語のソースコードからGoツールチェイン自体をビルドする際には、src/cmd/dist
ディレクトリにあるツールが使用されます。これはGoのブートストラッププロセスの一部であり、Goのコンパイラ、リンカ、その他のツールを構築し、インストールする役割を担います。インストールが完了すると、Goのバイナリは $GOROOT/bin
に、ユーザーが go install
でビルドしたバイナリは $GOBIN
に配置されます。
$GOBIN
環境変数
$GOBIN
はGo言語の環境変数の一つで、go install
コマンドによってビルドされた実行可能ファイルが配置されるディレクトリを指定します。この変数が設定されていない場合、実行可能ファイルは $GOPATH/bin
(Go Modules以前) または $GOPATH
の最初のパスの bin
サブディレクトリ (Go Modules以降) に配置されます。ユーザーがビルドしたプログラムをコマンドラインから簡単に実行できるようにするためには、この $GOBIN
で指定されたディレクトリがシステムの実行パスに含まれている必要があります。
Plan 9オペレーティングシステム
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計されましたが、多くの点でUnixとは異なる設計思想を持っています。
- すべてがファイル: Plan 9の最も特徴的な設計原則は「すべてがファイル」であることです。デバイス、プロセス、ネットワーク接続など、システム内のあらゆるリソースがファイルとして表現され、ファイルシステムを通じてアクセスされます。
- 名前空間 (Namespace): 各プロセスは独自の「名前空間」を持ちます。これは、そのプロセスから見えるファイルシステムのビューです。
bind
コマンドを使って、異なるディレクトリやリソースをプロセスの名前空間内の特定のパスにマウントできます。これにより、各プロセスは自分にとって最適なファイルシステムの構成を持つことができます。 /proc
ファイルシステム: Plan 9の/proc
は、実行中のプロセスに関する情報を提供する仮想ファイルシステムです。各プロセスは/proc/<pid>
のようなディレクトリを持ち、その中にプロセスの状態、メモリマップ、そして重要な「名前空間」を表すns
ファイルなどが含まれます。#c/pid
は現在のプロセスのIDをファイルとして提供する特殊なパスです。bind
コマンド: Plan 9のbind
コマンドは、Unixのmount
コマンドに似ていますが、より柔軟です。特定のディレクトリを別のディレクトリにマウントしたり、既存のディレクトリの前に新しいディレクトリを挿入したり(bind -b
)、既存のディレクトリの後ろに新しいディレクトリを追加したりできます。これは、UnixのPATH
環境変数とは異なる方法で実行可能ファイルの検索パスを管理するために使用されます。
Unix系OSのPATH
環境変数
Unix系OSでは、PATH
環境変数は、シェルがコマンドを実行する際に実行可能ファイルを検索するディレクトリのリストをコロン (:
) で区切って指定します。例えば、/usr/local/bin:/usr/bin:/bin
のように設定されている場合、シェルはこれらのディレクトリを順番に検索し、最初に見つかった実行可能ファイルを実行します。
技術的詳細
このコミットの技術的な核心は、Goのインストールプロセスが実行されているオペレーティングシステムがPlan 9であるかどうかを検出し、その検出結果に基づいてユーザーへの指示を分岐させる点にあります。
従来のコードでは、gohostos
(Goが動作しているホストOS) がWindowsであるかどうかに基づいて PATH
環境変数の区切り文字 (:
または ;
) を選択し、その後、$GOBIN
が PATH
に含まれているかをチェックしていました。
この変更では、まず gohostos
が "plan9"
であるかを streq(gohostos, "plan9")
で確認します。
-
Plan 9の場合:
#c/pid
ファイルを読み込み、現在のプロセスのPID(プロセスID)を取得します。Plan 9では、#c/pid
は現在のプロセスのPIDを含む特殊なファイルです。- 取得したPIDを使って、現在のプロセスの名前空間ファイルへのパス
/proc/<pid>/ns
を構築します。このns
ファイルは、そのプロセスの現在の名前空間のバインド設定(どのディレクトリがどこにマウントされているか)を記述しています。 /proc/<pid>/ns
ファイルの内容を読み込みます。- 読み込んだ内容の中に、
bind -b %s /bin
という形式の文字列が含まれているかをチェックします。ここで%s
は$GOBIN
の値に置き換えられます。これは、$GOBIN
が/bin
の前にバインドされているかを確認するものです。Plan 9では、/bin
は標準的なシステムコマンドが置かれる場所であり、ユーザーがビルドしたGoプログラムがシステムコマンドよりも優先されるように、$GOBIN
を/bin
の前にバインドすることが推奨されます。 - もしこのバインドが見つからない場合、
*** You need to bind %s before /bin.\\n
という、Plan 9に特化した指示メッセージをユーザーに表示します。
-
Plan 9以外の場合 (Unix系、Windowsなど):
- 従来のロジックがそのまま適用されます。つまり、
$PATH
環境変数を読み込み、$GOBIN
がその中に含まれているかをチェックします。 - 含まれていない場合、
*** You need to add %s to your PATH.\\n
という一般的なPATH
への追加指示メッセージをユーザーに表示します。
- 従来のロジックがそのまま適用されます。つまり、
この変更により、Goのインストールツールは、実行環境の特性を正確に認識し、ユーザーに対してそのOSに最適な環境設定の指示を提供できるようになりました。これは、クロスプラットフォーム対応のソフトウェアが、各プラットフォームの慣習や技術的特性を尊重することの重要性を示す良い例です。
コアとなるコードの変更箇所
変更は src/cmd/dist/build.c
ファイルの cmdbanner
関数内で行われています。
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -1588,7 +1588,7 @@ cmdclean(int argc, char **argv)
void
cmdbanner(int argc, char **argv)
{
- char *pathsep;
+ char *pathsep, *pid, *ns;
Buf b, b1, search, path;
ARGBEGIN{
@@ -1612,15 +1612,28 @@ cmdbanner(int argc, char **argv)
xprintf("Installed Go for %s/%s in %s\\n", goos, goarch, goroot);
xprintf("Installed commands in %s\\n", gobin);
- // Check that gobin appears in $PATH.
- xgetenv(&b, "PATH");
- pathsep = ":";
- if(streq(gohostos, "windows"))
- pathsep = ";";
- bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);\
- bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);\
- if(xstrstr(bstr(&b1), bstr(&search)) == nil)\
- xprintf("*** You need to add %s to your PATH.\\n", gobin);
+ if(streq(gohostos, "plan9")) {
+ // Check that gobin is bound before /bin.
+ readfile(&b, "#c/pid");
+ bsubst(&b, " ", "");
+ pid = btake(&b);
+ bprintf(&b, "/proc/%s/ns", pid);
+ ns = btake(&b);
+ readfile(&b, ns);
+ bprintf(&search, "bind -b %s /bin\\n", gobin);
+ if(xstrstr(bstr(&b), bstr(&search)) == nil)
+ xprintf("*** You need to bind %s before /bin.\\n", gobin);
+ } else {
+ // Check that gobin appears in $PATH.
+ xgetenv(&b, "PATH");
+ pathsep = ":";
+ if(streq(gohostos, "windows"))
+ pathsep = ";";
+ bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
+ bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
+ if(xstrstr(bstr(&b1), bstr(&search)) == nil)
+ xprintf("*** You need to add %s to your PATH.\\n", gobin);
+ }
if(streq(gohostos, "darwin")) {
if(isfile(bpathf(&path, "%s/cov", tooldir)))
コアとなるコードの解説
cmdbanner
関数は、Goのインストールが完了した際に、インストールされたGoのバージョン情報や、$GOBIN
の設定に関するアドバイスなどを表示する役割を担っています。
変更の核心は、if(streq(gohostos, "plan9"))
という条件分岐の導入です。
-
char *pathsep, *pid, *ns;
:pathsep
は、Unix系OSやWindowsにおけるPATH
環境変数の区切り文字 (:
または;
) を格納するために使用されます。pid
は、Plan 9において現在のプロセスのIDを格納するために新しく追加されました。ns
は、Plan 9においてプロセスの名前空間ファイルへのパスを格納するために新しく追加されました。
-
if(streq(gohostos, "plan9")) { ... }
ブロック:- このブロックは、GoがPlan 9上で動作している場合にのみ実行されます。
readfile(&b, "#c/pid");
:特殊なファイル#c/pid
から現在のプロセスのIDを読み込みます。Plan 9では、このファイルが現在のプロセスのPIDを提供します。bsubst(&b, " ", "");
:読み込んだPID文字列からスペースを削除します。pid = btake(&b);
:整形されたPID文字列をpid
変数に格納します。bprintf(&b, "/proc/%s/ns", pid);
:取得したPIDを使って、現在のプロセスの名前空間ファイル/proc/<pid>/ns
へのパスを構築します。ns = btake(&b);
:構築された名前空間ファイルパスをns
変数に格納します。readfile(&b, ns);
:名前空間ファイルの内容を読み込みます。このファイルには、現在のプロセスの名前空間におけるディレクトリのバインド情報が記述されています。bprintf(&search, "bind -b %s /bin\\n", gobin);
:検索対象となる文字列を構築します。これは、$GOBIN
が/bin
の前にバインドされていることを示すPlan 9のbind
コマンドの形式です。if(xstrstr(bstr(&b), bstr(&search)) == nil)
:読み込んだ名前空間ファイルの内容の中に、構築した検索文字列(bind -b $GOBIN /bin
)が含まれているかをチェックします。xstrstr
は部分文字列の検索を行う関数です。nil
であれば見つからなかったことを意味します。xprintf("*** You need to bind %s before /bin.\\n", gobin);
:もし$GOBIN
が/bin
の前にバインドされていない場合、Plan 9ユーザーに対して適切なbind
コマンドの使用を促すメッセージを表示します。
-
else { ... }
ブロック:- このブロックは、GoがPlan 9以外のOS(Unix系、Windowsなど)で動作している場合に実行されます。
xgetenv(&b, "PATH");
:PATH
環境変数の値を取得します。pathsep = ":"; if(streq(gohostos, "windows")) pathsep = ";";
:ホストOSがWindowsであれば;
を、そうでなければ:
をPATH
の区切り文字として設定します。bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
:PATH
環境変数の値の前後に区切り文字を追加し、検索を容易にします。bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
:$GOBIN
のパスの前後に区切り文字を追加し、検索対象の文字列を構築します。if(xstrstr(bstr(&b1), bstr(&search)) == nil)
:PATH
環境変数の中に$GOBIN
が含まれているかをチェックします。xprintf("*** You need to add %s to your PATH.\\n", gobin);
:もし$GOBIN
がPATH
に含まれていない場合、一般的なPATH
への追加を促すメッセージを表示します。
この変更により、cmd/dist
はよりインテリジェントになり、異なるOS環境下でのGoの利用体験を向上させています。
関連リンク
- Go Change List 7395059: https://golang.org/cl/7395059
- Go言語の環境変数: https://go.dev/doc/goenv (現在のドキュメント)
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Plan 9のファイルシステムと名前空間に関する情報:
- The Plan 9 File System: https://9p.io/sys/doc/fs.html
- The Plan 9
bind
command: https://9p.io/man/1/bind.html - The Plan 9
/proc
file system: https://9p.io/man/4/proc.html
- Unixの
PATH
環境変数に関する一般的な情報 (例: Wikipedia): https://ja.wikipedia.org/wiki/PATH - Go言語の
cmd/dist
に関する情報 (Goのソースコードやドキュメント): https://go.dev/src/cmd/dist/ (Goのソースコードリポジトリ) - Go言語の
GOBIN
に関する情報: https://go.dev/doc/code (Goのコード構成に関するドキュメント) - Go言語の
go install
コマンドに関する情報: https://go.dev/cmd/go/#hdr-Compile_and_install_packages_and_dependencies