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

[インデックス 14137] ファイルの概要

このコミットは、Go言語のビルドツールである cmd/dist における、誤解を招く可能性のあるメッセージの修正と、デバッグを容易にするための make.bat スクリプトの改善を目的としています。具体的には、GOROOTGOROOT_FINAL が異なる場合に表示される「バイナリがコピーまたは移動されることを期待しています」というメッセージが、ファイルシステム上の実体が同じであるにも関わらず表示される問題を解決します。また、cmd/dist のデバッグを支援するため、make.bat--dist-tool フラグをサポートするように変更されています。

コミット

commit 19dc7bb18fc4e4dab937ce13d50a86db938ab744
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Fri Oct 12 13:35:05 2012 +0800

    cmd/dist: fix superfluous and confusing "binaries ... to be copied or moved" message
    Also, to aid debugging cmd/dist, make make.bat support --dist-tool flag.
    
    Fixes #3100.
    
    R=alex.brainman
    CC=golang-dev
    https://golang.org/cl/6637061

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/19dc7bb18fc4e4dab937ce13d50a86db938ab744

元コミット内容

cmd/dist: 不要で紛らわしい「バイナリがコピーまたは移動されることを期待しています」というメッセージを修正。 また、cmd/dist のデバッグを支援するため、make.bat--dist-tool フラグをサポートするように変更。

Go Issue #3100 を修正。

変更の背景

Go言語のビルドプロセスにおいて、cmd/dist ツールはGoのソースコードからバイナリをビルドし、適切な場所に配置する役割を担っています。このツールは、ビルド時に GOROOT (Goのソースコードのルートディレクトリ) と GOROOT_FINAL (最終的にGoのバイナリが配置されるディレクトリ) という2つのパスを扱います。

以前のバージョンでは、GOROOTGOROOT_FINAL が文字列として異なる場合、たとえそれらがファイルシステム上で同じ場所を指していても(例えば、シンボリックリンクや異なるパス表記のため)、cmd/dist は「The binaries expect %s to be copied or moved to %s」(バイナリは %s から %s へコピーまたは移動されることを期待しています)というメッセージを表示していました。このメッセージは、実際にファイルが移動される必要がない場合に表示されるため、ユーザーにとって紛らわしく、誤解を招くものでした。

この問題は Go Issue #3100 として報告されており、このコミットはその修正を目的としています。根本的な原因は、パスの文字列比較 streq(goroot_final, goroot) が、ファイルシステム上の同一性を正確に判断できないことにありました。

また、cmd/dist のデバッグは、その性質上、ビルドプロセス全体に影響を与えるため、困難な場合があります。このコミットは、make.bat--dist-tool フラグを追加することで、cmd/dist ツール自体をビルドディレクトリにコピーし、デバッグを容易にするためのメカニズムを提供します。

前提知識の解説

  • cmd/dist: Go言語のソースコードからGoツールチェイン(コンパイラ、リンカ、標準ライブラリなど)をビルドするための内部ツールです。Goの自己ホスト型コンパイラ(GoでGoをコンパイルする)のブートストラッププロセスにおいて重要な役割を果たします。
  • GOROOT: Goのソースコードが置かれているディレクトリのパスを示す環境変数です。Goのビルドシステムはこのパスを基にソースファイルを探します。
  • GOROOT_FINAL: ビルドされたGoのバイナリやライブラリが最終的に配置されるディレクトリのパスです。通常、GOROOT と同じか、Goがインストールされるシステム全体のパス(例: /usr/local/go)になります。
  • ファイルシステムの同一性: ファイルシステムにおいて、異なるパス表記(例: /a/b/c/a/./b/c、またはシンボリックリンクを介したパス)が、実際には同じファイルやディレクトリを指すことがあります。このような場合、文字列比較だけでは同一性を判断できません。ファイルシステム上の同一性を確認するには、ファイル記述子やinode番号などの低レベルな情報を使用する必要があります。
  • make.bat: Windows環境でGoのビルドプロセスを制御するためのバッチスクリプトです。Unix/Linux環境における make.bashall.bash に相当します。
  • Go Issue #3100: このコミットが修正するGoのバグトラッカー上の課題です。

技術的詳細

このコミットの主要な技術的変更点は、ファイルシステム上の同一性を判断するための新しい関数 xsamefile の導入と、make.bat スクリプトの機能拡張です。

  1. xsamefile 関数の導入:

    • この関数は、2つのパス f1f2 がファイルシステム上で同じファイルまたはディレクトリを指しているかどうかを判定します。
    • Unix/Plan 9 環境: これらの環境では、xsamefile は単純に文字列比較 streq(f1, f2) を行います。これは、これらのシステムではパスの正規化が比較的単純であり、シンボリックリンクなどの複雑なケースがこの文脈では問題にならないか、またはより高レベルで処理されるためと考えられます。コメントには「suffice for now」(今のところこれで十分)とあり、将来的に必要であればより堅牢な実装に置き換える可能性が示唆されています。
    • Windows 環境: Windowsではパスの表現がより複雑であり、シンボリックリンクやジャンクションポイント、大文字小文字の区別など、ファイルシステムの同一性を判断するのが難しい場合があります。そのため、Windows版の xsamefile はより高度なロジックを実装しています。
      • CreateFileW を使用して、両方のパスに対してファイルハンドルを取得します。FILE_FLAG_BACKUP_SEMANTICS フラグは、ディレクトリに対してもハンドルを取得できるようにするために使用されます。
      • GetFileInformationByHandle を使用して、各ファイルハンドルのファイル情報を取得します。この情報には、ボリュームシリアル番号 (dwVolumeSerialNumber)、ファイルインデックスの上位 (nFileIndexHigh)、およびファイルインデックスの下位 (nFileIndexLow) が含まれます。
      • これらの3つの値が両方のファイルで一致する場合、それらは同じファイルシステム上の同じ実体を指していると判断されます。これは、Windowsにおけるファイルシステム上の同一性を判断する標準的な方法です。
      • エラーハンドリングも含まれており、ファイルハンドルが取得できない場合(例: ファイルが存在しない)は 0(異なるファイル)を返します。
  2. cmd/dist/build.c の変更:

    • cmdbanner 関数内で、GOROOT_FINALGOROOT の比較に streq の代わりに新しく導入された xsamefile が使用されるようになりました。
    • これにより、パスの文字列が異なっていても、ファイルシステム上で同じ場所を指している場合には、紛らわしいメッセージが表示されなくなります。
  3. src/make.bat の変更:

    • --dist-tool という新しいコマンドライン引数が追加されました。
    • make.bat--dist-tool フラグを受け取ると、cmd/dist/dist.exeGOTOOLDIR (Goツールが配置されるディレクトリ) にコピーします。
    • この機能は、cmd/dist ツール自体をデバッグする際に、ビルドプロセス全体を実行せずに、特定のバージョンの dist.exe を簡単に利用できるようにするために役立ちます。

コアとなるコードの変更箇所

このコミットでは、以下のファイルが変更されています。

  • src/cmd/dist/a.h: xsamefile 関数のプロトタイプ宣言が追加されました。
  • src/cmd/dist/build.c: cmdbanner 関数内の GOROOT_FINALGOROOT の比較が streq から xsamefile に変更されました。
  • src/cmd/dist/plan9.c: Plan 9 環境向けの xsamefile の実装が追加されました。これは streq を使用したシンプルな実装です。
  • src/cmd/dist/unix.c: Unix 環境向けの xsamefile の実装が追加されました。これも streq を使用したシンプルな実装です。
  • src/cmd/dist/windows.c: Windows 環境向けの xsamefile の実装が追加されました。これは CreateFileWGetFileInformationByHandle を使用した詳細な実装です。
  • src/make.bat: --dist-tool フラグを処理するためのロジックが追加され、cmd\dist\dist.exeGOTOOLDIR にコピーする処理が実装されました。

コアとなるコードの解説

xsamefile 関数の導入と利用

src/cmd/dist/a.h に追加されたプロトタイプ宣言:

int     xsamefile(char*, char*);

src/cmd/dist/build.c での利用:

-	if(!streq(goroot_final, goroot)) {
+	if(!xsamefile(goroot_final, goroot)) {
 		xprintf("\n"
 			"The binaries expect %s to be copied or moved to %s\n",
 			goroot, goroot_final);

この変更により、GOROOT_FINALGOROOT のパスがファイルシステム上で同一である場合、たとえ文字列が異なっていても、誤解を招くメッセージが表示されなくなります。

Windows 環境における xsamefile の実装 (src/cmd/dist/windows.c)

// xsamefile returns whether f1 and f2 are the same file (or dir)
int
xsamefile(char *f1, char *f2)
{
	Rune *ru;
	HANDLE fd1, fd2;
	BY_HANDLE_FILE_INFORMATION fi1, fi2;
	int r;

	// trivial case
	if(streq(f1, f2))
		return 1;

	torune(&ru, f1);
	// refer to ../../pkg/os/stat_windows.go:/sameFile
	fd1 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
	xfree(ru);
	if(fd1 == INVALID_HANDLE_VALUE)
		return 0;
	torune(&ru, f2);
	fd2 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
	xfree(ru);
	if(fd2 == INVALID_HANDLE_VALUE) {
		CloseHandle(fd1);
		return 0;
	}
	r = GetFileInformationByHandle(fd1, &fi1) != 0 && GetFileInformationByHandle(fd2, &fi2) != 0;
	CloseHandle(fd2);
	CloseHandle(fd1);
	if(r != 0 &&
	   fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
	   fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
	   fi1.nFileIndexLow == fi2.nFileIndexLow)
	   	return 1;
	return 0;
}

このコードは、Windows API を使用してファイルの同一性を確認します。CreateFileW でファイルハンドルを取得し、GetFileInformationByHandle でファイルシステム上のユニークな識別子(ボリュームシリアル番号とファイルインデックス)を取得して比較します。これにより、シンボリックリンクや異なるパス表記でも、同じ実体を指しているかを正確に判断できます。

make.bat の変更 (src/make.bat)

+if x%1==x--dist-tool goto copydist
+if x%2==x--dist-tool goto copydist
+
...

+:copydist
+mkdir %GOTOOLDIR% 2>NUL
+copy cmd\dist\dist.exe %GOTOOLDIR%\
+goto end

この変更により、make.bat--dist-tool オプション付きで実行すると、cmd/dist/dist.exeGOTOOLDIR にコピーされます。これは、cmd/dist のデバッグや特定のバージョンの dist ツールをテストする際に非常に便利です。

関連リンク

参考にした情報源リンク