[インデックス 13447] ファイルの概要
このコミットは、Go言語のビルドおよび配布ツールである cmd/dist
における詳細な(verbose)メッセージの出力先を、標準出力(stdout)から標準エラー出力(stderr)に変更するものです。これにより、診断メッセージと通常のプログラム出力が分離され、スクリプト処理やログ解析が容易になります。
コミット
commit 34b10d7482b4a83cf066c313a201503126393293
Author: Pieter Droogendijk <pieter@binky.org.uk>
Date: Fri Jul 6 15:00:18 2012 +1000
cmd/dist: Make verbose messages print to stderr
Made the following changes:
- Export errprintf() from all three OS-specific modules
- Added errprintf() to a.h
- Moved errprintf() in windows.c under xprintf(), since they are so similar
- Replaced all instances of xprintf() with errprintf() where a vflag check is done
Fixes #3788.
R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/6346056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/34b10d7482b4a83cf066c313a201503126393293
元コミット内容
cmd/dist
: 詳細メッセージを標準エラー出力に表示する
以下の変更を行いました:
errprintf()
を3つのOS固有モジュールすべてからエクスポートしました。a.h
にerrprintf()
を追加しました。windows.c
のerrprintf()
をxprintf()
の下に移動しました。これらは非常に似ているためです。vflag
のチェックが行われるすべてのxprintf()
のインスタンスをerrprintf()
に置き換えました。 Issue #3788 を修正します。
変更の背景
この変更の背景には、コマンドラインツールの標準的な出力慣習と、cmd/dist
のようなビルドツールの特性があります。一般的に、UNIX系のシステムでは、プログラムの通常の出力は標準出力(stdout)に、エラーメッセージや診断情報、デバッグ情報などの補助的な出力は標準エラー出力(stderr)に送るのが慣例です。
cmd/dist
はGo言語のビルドと配布を管理する内部ツールであり、その実行中に多くの詳細なメッセージ(verbose messages)を出力することがあります。これらのメッセージが標準出力に混ざってしまうと、以下のような問題が発生します。
- スクリプト処理の困難さ:
cmd/dist
の出力を他のコマンドにパイプで渡して処理する場合、詳細メッセージが混入していると、意図したデータだけを抽出するのが難しくなります。 - ログ解析の複雑化: ビルドログを解析する際に、通常のビルド結果と診断情報が混在していると、エラーや警告の特定が困難になります。
- ユーザー体験の低下: ユーザーが通常のビルド結果のみを求めている場合でも、詳細メッセージが大量に表示されることで、必要な情報が見つけにくくなることがあります。
Issue #3788 は、まさにこの問題、つまり cmd/dist
の詳細メッセージが標準出力に表示されてしまうことに対する修正要求であったと考えられます。このコミットは、この問題を解決し、cmd/dist
の出力がより標準的な慣習に沿うようにすることで、ツールの使いやすさとスクリプトとの連携を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
-
cmd/dist
: Go言語のソースコードリポジトリ内に存在する内部ツールです。Go言語のコンパイラ、ツールチェイン、標準ライブラリなどのビルド、テスト、インストールといったGoディストリビューション全体の管理を行います。一般的なGo開発者が直接使用するツールではなく、Goプロジェクト自体の開発やメンテナンスにおいて重要な役割を果たします。 -
標準出力(stdout)と標準エラー出力(stderr): UNIX系OSにおけるプロセスが持つ2つの主要な出力ストリームです。
- 標準出力(stdout): プログラムの通常の出力(結果、データなど)が送られる場所です。デフォルトではターミナルに表示されますが、パイプ(
|
)やリダイレクト(>
)で他のプログラムやファイルに送ることができます。 - 標準エラー出力(stderr): プログラムのエラーメッセージ、警告、診断情報などが送られる場所です。デフォルトではターミナルに表示されますが、標準出力とは独立してリダイレクト(
2>
)することができます。これにより、通常の出力とエラー出力を分離して処理することが可能になります。
- 標準出力(stdout): プログラムの通常の出力(結果、データなど)が送られる場所です。デフォルトではターミナルに表示されますが、パイプ(
-
vflag
: 多くのコマンドラインツールで採用されている、詳細度(verbosity)を制御するためのフラグ(変数)です。通常、vflag
の値が大きいほど、より多くの詳細なメッセージが出力されます。このコミットでは、vflag
の値に基づいて出力されるメッセージがxprintf()
からerrprintf()
に切り替えられています。 -
xprintf()
とerrprintf()
: このコミットで変更の対象となっている、cmd/dist
内部で使用される出力関数です。xprintf()
: 従来の出力関数で、おそらく標準出力にメッセージを出力していました。errprintf()
: このコミットで導入または変更された関数で、標準エラー出力にメッセージを出力するように設計されています。
-
va_list
,vfprintf()
,WriteFile()
,GetStdHandle()
: C言語における可変引数関数(printf
のような関数)の実装に関連する概念です。va_list
: 可変引数リストを操作するための型です。vfprintf()
:fprintf()
の可変引数バージョンで、FILE*
ストリームにフォーマットされた文字列を出力します。このコミットではstderr
ストリーム(標準エラー出力)に対して使用されています。WriteFile()
: Windows API関数で、指定されたファイルまたはI/Oデバイスにデータを書き込みます。GetStdHandle()
: Windows API関数で、標準入力、標準出力、または標準エラー出力のハンドルを取得します。STD_ERROR_HANDLE
を引数に渡すことで、標準エラー出力のハンドルを取得できます。
これらの知識を前提として、コミットの変更内容を詳細に見ていきます。
技術的詳細
このコミットの技術的な核心は、cmd/dist
内部の診断メッセージの出力メカニズムを xprintf
から errprintf
へと切り替えることにあります。この変更は、Goがサポートする複数のOS(Plan 9, Unix, Windows)に対応するため、各OS固有のコードベースで同様の修正が施されています。
具体的な変更点は以下の通りです。
-
errprintf()
の導入とエクスポート:src/cmd/dist/plan9.c
とsrc/cmd/dist/unix.c
に、vfprintf(stderr, fmt, arg);
を使用して標準エラー出力にメッセージを書き込むerrprintf
関数が新しく追加されました。これは、C標準ライブラリの機能を利用した一般的な実装です。src/cmd/dist/windows.c
では、既存のerrprintf
関数がxprintf
関数の近くに移動され、その実装がWriteFile(GetStdHandle(STD_ERROR_HANDLE), p, n, &w, 0);
を使用してWindowsの標準エラーハンドルに直接書き込む形になっています。これはWindows APIに特化した実装です。- これらの
errprintf
関数は、各OS固有のモジュールから外部にエクスポート(利用可能に)されています。
-
a.h
へのerrprintf()
の宣言追加:src/cmd/dist/a.h
はcmd/dist
の共通ヘッダファイルであり、他のソースファイルから利用される関数のプロトタイプ宣言が含まれています。このファイルにvoid errprintf(char*, ...);
の宣言が追加され、cmd/dist
の他の部分からerrprintf
を呼び出せるようになりました。 -
xprintf()
からerrprintf()
への置き換え:src/cmd/dist/build.c
,src/cmd/dist/plan9.c
,src/cmd/dist/unix.c
,src/cmd/dist/windows.c
の各ファイルにおいて、vflag
(詳細度フラグ)のチェックが行われる箇所でxprintf()
の呼び出しがerrprintf()
に置き換えられました。vflag
は、cmd/dist
の実行時に-v
オプションなどで設定される詳細度レベルを制御する変数です。vflag > 1
やvflag > 2
のような条件分岐の内部にあるxprintf()
は、通常、デバッグ情報や詳細な実行ステップを示すメッセージを出力するために使われていました。これらのメッセージが標準エラー出力に送られるように変更されたのです。
この変更により、cmd/dist
が出力する通常のビルド結果は引き続き標準出力に送られ、詳細な診断メッセージやデバッグ情報は標準エラー出力に分離されることになります。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その中でのコアとなる変更箇所は以下の通りです。
-
src/cmd/dist/a.h
:--- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -108,6 +108,7 @@ void xmain(int argc, char **argv); // portability layer (plan9.c, unix.c, windows.c) bool contains(char *p, char *sep); +void errprintf(char*, ...); void fatal(char *msg, ...); bool hasprefix(char *p, char *prefix); bool hassuffix(char *p, char *suffix);
errprintf
関数のプロトタイプ宣言が追加されました。 -
src/cmd/dist/build.c
:vflag
のチェックが行われる複数の箇所でxprintf
がerrprintf
に置き換えられています。例:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -147,7 +147,7 @@ static void rmworkdir(void) { if(vflag > 1) - xprintf("rm -rf %s\n", workdir); + errprintf("rm -rf %s\n", workdir); xremoveall(workdir); }
rmworkdir
関数内で、作業ディレクトリ削除時の詳細メッセージの出力先が変更されています。同様の変更がinstall
関数やcopy
関数内でも行われています。 -
src/cmd/dist/plan9.c
:errprintf
関数の実装が追加され、vflag
のチェックが行われる箇所でxprintf
がerrprintf
に置き換えられています。--- a/src/cmd/dist/plan9.c +++ b/src/cmd/dist/plan9.c @@ -661,6 +661,17 @@ xprintf(char *fmt, ...) va_end(arg); } +// errprintf prints a message to standard output. +void +errprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); +} + // xsetenv sets the environment variable $name to the given value. void xsetenv(char *name, char *value)
errprintf
の実装が追加され、genrun
,xremove
,xremoveall
関数内でxprintf
がerrprintf
に置き換えられています。 -
src/cmd/dist/unix.c
:plan9.c
と同様に、errprintf
関数の実装が追加され、vflag
のチェックが行われる箇所でxprintf
がerrprintf
に置き換えられています。--- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -627,6 +627,17 @@ xprintf(char *fmt, ...)\n \tva_end(arg);\n }\n \n+// errprintf prints a message to standard output.\n+void\n+errprintf(char *fmt, ...)\n+{\n+\tva_list arg;\n+\t\n+\tva_start(arg, fmt);\n+\tvfprintf(stderr, fmt, arg);\n+\tva_end(arg);\n+}\n+\n // xsetenv sets the environment variable $name to the given value.\n void\n xsetenv(char *name, char *value)\n ``` `errprintf` の実装が追加され、`genrun`, `xremove`, `xremoveall` 関数内で `xprintf` が `errprintf` に置き換えられています。
-
src/cmd/dist/windows.c
: 既存のerrprintf
の位置が変更され、vflag
のチェックが行われる箇所でxprintf
がerrprintf
に置き換えられています。--- a/src/cmd/dist/windows.c +++ b/cmd/dist/windows.c @@ -121,22 +121,6 @@ errstr(void)\n \treturn bstr(&b); // leak but we\'re dying anyway\n }\n \n-static void\n-errprintf(char *fmt, ...) {\n-\tva_list arg;\n-\tchar *p;\n-\tDWORD n, w;\n-\n-\tva_start(arg, fmt);\n-\tn = vsnprintf(NULL, 0, fmt, arg);\n-\tp = xmalloc(n+1);\n-\tvsnprintf(p, n+1, fmt, arg);\n-\tva_end(arg);\n-\tw = 0;\n-\tWriteFile(GetStdHandle(STD_ERROR_HANDLE), p, n, &w, 0);\n-\txfree(p);\n-}\n-\n void\n xgetenv(Buf *b, char *name)\n {\
errprintf
の定義がxprintf
の近くに移動され、genrun
,readfile
,writefile
関数内でxprintf
がerrprintf
に置き換えられています。
コアとなるコードの解説
このコミットのコアとなる変更は、cmd/dist
が出力する詳細なメッセージのストリームを切り替えるための errprintf
関数の導入と、その関数への既存の xprintf
呼び出しの置き換えです。
errprintf
関数の役割:
errprintf
は、可変引数を受け取り、フォーマットされた文字列を標準エラー出力に書き込むためのユーティリティ関数です。
-
Unix/Plan 9 の実装:
void errprintf(char *fmt, ...) { va_list arg; va_start(arg, fmt); vfprintf(stderr, fmt, arg); // 標準エラー出力に書き込む va_end(arg); }
この実装は、C標準ライブラリの
vfprintf
関数をstderr
ストリーム(標準エラー出力)に対して使用しています。これは、Unix系システムでエラーメッセージを出力する際の標準的な方法です。 -
Windows の実装:
void errprintf(char *fmt, ...) { va_list arg; char *p; DWORD n, w; va_start(arg, fmt); n = vsnprintf(NULL, 0, fmt, arg); // 出力に必要なバッファサイズを計算 p = xmalloc(n+1); // バッファを確保 vsnprintf(p, n+1, fmt, arg); // フォーマットされた文字列をバッファに書き込む va_end(arg); w = 0; WriteFile(GetStdHandle(STD_ERROR_HANDLE), p, n, &w, 0); // 標準エラーハンドルに直接書き込む xfree(p); // バッファを解放 }
Windowsでは、
vfprintf
の代わりにWindows APIのWriteFile
関数を直接使用しています。これは、GetStdHandle(STD_ERROR_HANDLE)
で取得した標準エラー出力のハンドルに対して、フォーマットされた文字列を書き込むためです。vsnprintf
を使ってまず文字列をメモリに構築し、その内容をWriteFile
で出力するという手順を踏んでいます。これは、Windows環境での低レベルなI/O操作の一般的なパターンです。
xprintf
から errprintf
への置き換え:
cmd/dist
のコードベース全体で、vflag
の値(詳細度レベル)に基づいて出力されるメッセージのほとんどが xprintf
から errprintf
に変更されました。例えば、rm -rf %s
のようなファイル操作のログや、クロスコンパイル時のスキップメッセージ、ファイル生成のログなどがこれに該当します。
この変更により、cmd/dist
の実行結果をスクリプトで処理する際に、通常の成功メッセージやビルド成果物のパスなどは標準出力から取得し、デバッグや診断に役立つ詳細なログは標準エラー出力から取得するといった、より柔軟な処理が可能になります。これは、コマンドラインツールの設計におけるベストプラクティスの一つであり、ツールの使いやすさと自動化のしやすさを向上させます。
関連リンク
- Go CL (Code Review) ページ: https://golang.org/cl/6346056
- GitHub Issue #3788 (関連する可能性のあるGoプロジェクトのIssue): このコミットメッセージに記載されている
Fixes #3788
は、GoプロジェクトのIssueトラッカーにおける特定の課題を指しています。具体的な内容はコミットメッセージからは読み取れませんが、通常は詳細メッセージの出力に関する問題であったと推測されます。
参考にした情報源リンク
- Go言語の
cmd/dist
の目的:- go.dev - cmd/dist (Go言語の公式ドキュメントやソースコードリポジトリ内の情報)
- alexedwards.net - Go: The
dist
command (Goのdist
コマンドに関する解説記事)
- 標準出力と標準エラー出力:
- C言語の可変引数関数 (
va_list
,vfprintf
): - Windows API (
WriteFile
,GetStdHandle
): - Go Issue #3788 (検索結果から関連性の高いものを推測):
- github.com - golang/vscode-go Issue #3788 (VS Code Go拡張機能のIssue、直接の関連は低いが番号が一致)
- github.com - gin-gonic/gin Issue #3788 (GinフレームワークのIssue、直接の関連は低いが番号が一致)
- jetbrains.com - YouTrack Issue GO-3788 (JetBrains GoLand IDEのIssue、直接の関連は低いが番号が一致)
- 注:
Fixes #3788
はGoプロジェクトの公式Issueトラッカー(現在はGitHubに移行)のIssueを指している可能性が高いですが、古いIssueは直接検索でヒットしにくい場合があります。コミットメッセージの文脈から、詳細メッセージの出力に関する問題であったと推測されます。