Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 環境変数の区切り文字 (: または ;) を選択し、その後、$GOBINPATH に含まれているかをチェックしていました。

この変更では、まず gohostos"plan9" であるかを streq(gohostos, "plan9") で確認します。

  1. 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に特化した指示メッセージをユーザーに表示します。
  2. 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")) という条件分岐の導入です。

  1. char *pathsep, *pid, *ns;:

    • pathsep は、Unix系OSやWindowsにおける PATH 環境変数の区切り文字 (: または ;) を格納するために使用されます。
    • pid は、Plan 9において現在のプロセスのIDを格納するために新しく追加されました。
    • ns は、Plan 9においてプロセスの名前空間ファイルへのパスを格納するために新しく追加されました。
  2. 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 コマンドの使用を促すメッセージを表示します。
  3. 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);:もし $GOBINPATH に含まれていない場合、一般的な PATH への追加を促すメッセージを表示します。

この変更により、cmd/dist はよりインテリジェントになり、異なるOS環境下でのGoの利用体験を向上させています。

関連リンク

参考にした情報源リンク