[インデックス 12858] ファイルの概要
このコミットは、Go言語のビルドシステム (cmd/dist
) におけるWindows環境特有の問題を解決するためのものです。具体的には、Mercurial(hg
コマンド)がWindows上でバッチファイルとしてインストールされている場合に発生する問題と、FormatMessageW
APIの稀なクラッシュを修正しています。
コミット
commit 0669261af107eddb13d71293d654b595417f8053
Author: Alexey Borzenkov <snaury@gmail.com>
Date: Mon Apr 9 15:39:59 2012 -0400
cmd/dist: don't fail when Mercurial is a batch file on Windows
On windows Mercurial installed with easy_install typically creates
an hg.bat batch file in Python Scripts directory, which cannot be used
with CreateProcess unless full path is specified. Work around by
launching hg via cmd.exe /c.
Additionally, fix a rare FormatMessageW crash.
Fixes #3093.
R=golang-dev, rsc, alex.brainman, aram, jdpoirier, mattn.jp
CC=golang-dev
https://golang.org/cl/5937043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0669261af107eddb13d71293d654b595417f8053
元コミット内容
cmd/dist: don't fail when Mercurial is a batch file on Windows
On windows Mercurial installed with easy_install typically creates
an hg.bat batch file in Python Scripts directory, which cannot be used
with CreateProcess unless full path is specified. Work around by
launching hg via cmd.exe /c.
Additionally, fix a rare FormatMessageW crash.
Fixes #3093.
変更の背景
このコミットは、主に2つの問題に対処しています。
-
Windows環境でのMercurial (
hg
) コマンドの実行問題: Windows環境において、easy_install
などのツールでMercurialをインストールすると、hg.exe
のような実行ファイルではなく、hg.bat
というバッチファイルが生成されることがありました。WindowsのCreateProcess
APIは、実行ファイル(.exe
)を直接指定する場合にはパス解決が容易ですが、バッチファイル(.bat
や.cmd
)を直接指定して実行しようとすると、完全なパスが指定されていない限り、正しく起動できないという挙動を示すことがあります。Goのビルドシステム (cmd/dist
) がMercurialを呼び出す際に、この挙動によってhg.bat
の起動に失敗し、ビルドプロセスが中断する問題が発生していました。 -
FormatMessageW
APIの稀なクラッシュ: Windows APIのFormatMessageW
関数は、システムエラーメッセージなどをフォーマットするために使用されます。この関数は、メッセージ文字列内の挿入シーケンス(例:%1
,%2
)を引数で置き換える機能を持っています。しかし、メッセージ文字列に挿入シーケンスが含まれているにもかかわらず、対応する引数が提供されない場合や、引数の型が不一致の場合に、稀にクラッシュ(アクセス違反など)を引き起こす可能性がありました。このコミットは、この潜在的なクラッシュを回避するための修正も含まれています。
これらの問題は、Goのビルドプロセスの安定性と信頼性に影響を与えるため、修正が必要とされました。特に、Fixes #3093
は、Mercurialのバッチファイルに関する具体的なバグ報告に対応しています。
前提知識の解説
- Go言語のビルドシステム (
cmd/dist
): Go言語のソースコードからコンパイラ、ツール、標準ライブラリなどをビルドするための内部ツールです。Goのソースコード管理にはGitとMercurialが使用されており、cmd/dist
はこれらのバージョン管理システムと連携して動作します。 - Mercurial (
hg
): 分散型バージョン管理システムの一つで、Gitと同様にソースコードの変更履歴を管理します。Goプロジェクトの初期にはMercurialが主要なバージョン管理システムとして利用されていました。 - Windowsバッチファイル (
.bat
,.cmd
): Windowsのコマンドプロンプトで実行されるスクリプトファイルです。複数のコマンドを記述し、自動実行するために使用されます。 CreateProcess
API: Windows APIの一つで、新しいプロセスを作成し、実行可能ファイルを起動するために使用されます。このAPIは、実行するプログラムのパスと引数を指定します。cmd.exe /c
: Windowsのコマンドプロンプト (cmd.exe
) を起動し、指定されたコマンドを実行した後に終了させるためのオプションです。バッチファイルや内部コマンドを確実に実行するためにしばしば使用されます。例えば、cmd.exe /c my_script.bat
とすることで、my_script.bat
がcmd.exe
によって解釈・実行されます。FormatMessageW
API: Windows APIの一つで、システムエラーコードやメッセージリソースからメッセージ文字列を取得し、フォーマットするために使用されます。W
サフィックスは、ワイド文字(Unicode)バージョンであることを示します。FORMAT_MESSAGE_ALLOCATE_BUFFER
:FormatMessageW
フラグの一つで、関数がメッセージバッファを自動的に割り当て、そのポインタを返すように指示します。FORMAT_MESSAGE_FROM_SYSTEM
:FormatMessageW
フラグの一つで、システムが定義するエラーメッセージテーブルからメッセージを検索するように指示します。FORMAT_MESSAGE_IGNORE_INSERTS
:FormatMessageW
フラグの一つで、メッセージ文字列内の挿入シーケンス(例:%1
)を無視するように指示します。このフラグが指定されると、FormatMessageW
は挿入シーケンスの引数を期待せず、メッセージをそのまま返します。これにより、引数不足や型不一致によるクラッシュを防ぐことができます。
技術的詳細
このコミットは、src/cmd/dist/windows.c
ファイルに対して2つの主要な変更を加えています。
-
Mercurialコマンドの実行方法の変更: GoのビルドシステムがMercurial (
hg
) コマンドを実行する際、Windows環境ではCreateProcess
APIを直接使用していました。しかし、hg
がhg.bat
のようなバッチファイルである場合、CreateProcess
は完全なパスが指定されていないと正しく起動できない問題がありました。 この修正では、hg
コマンドが呼び出される際に、そのコマンドの前に明示的にcmd.exe /c
を付加するように変更しています。これにより、hg
コマンドはcmd.exe
のコンテキスト内で実行されるため、バッチファイルであっても正しく解釈・実行されるようになります。これは、argv
(引数リスト)の最初の要素が"hg"
である場合にのみ適用されます。 -
FormatMessageW
のクラッシュ修正:errstr
関数内でFormatMessageW
が呼び出される際に、新しいフラグFORMAT_MESSAGE_IGNORE_INSERTS
が追加されました。 以前のコードでは、FormatMessageW
はFORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM
フラグを使用していました。システムエラーメッセージの中には、挿入シーケンスを含むものがあります。FormatMessageW
は、これらの挿入シーケンスに対応する引数が提供されない場合に、稀にクラッシュする可能性がありました。FORMAT_MESSAGE_IGNORE_INSERTS
フラグを追加することで、FormatMessageW
はメッセージ内の挿入シーケンスを無視し、引数を期待しなくなります。これにより、システムエラーメッセージの取得時に、メッセージのフォーマットに関する潜在的な問題を回避し、クラッシュを防ぐことができます。この関数はエラー文字列を取得する目的で使用されており、挿入シーケンスを埋める必要がないため、このフラグの追加は適切な修正です。
コアとなるコードの変更箇所
--- a/src/cmd/dist/windows.c
+++ b/src/cmd/dist/windows.c
@@ -115,7 +115,7 @@ errstr(void)
binit(&b);
code = GetLastError();
r = nil;
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\
\tnil, code, 0, (Rune*)&r, 0, nil);\
toutf(&b, r);\
return bstr(&b); // leak but we're dying anyway
@@ -285,9 +285,11 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)\
binit(&cmd);\
\n
for(i=0; i<argv->len; i++) {
+\t\tq = argv->p[i];
+\t\tif(i == 0 && streq(q, "hg"))
+\t\t\tbwritestr(&cmd, "cmd.exe /c ");
\tif(i > 0)\
\t\tbwritestr(&cmd, " ");
-\t\tq = argv->p[i];
\tif(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) {
\t\tbwritestr(&cmd, "\"");
\t\tnslash = 0;
コアとなるコードの解説
errstr
関数の変更
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\
nil, code, 0, (Rune*)&r, 0, nil);
errstr
関数は、Windowsのシステムエラーコードからエラーメッセージ文字列を取得するために使用されます。この変更では、FormatMessageW
関数の呼び出しにFORMAT_MESSAGE_IGNORE_INSERTS
フラグが追加されています。これにより、取得されるメッセージ文字列に挿入シーケンス(例: %1
)が含まれていても、FormatMessageW
はそれらを無視し、引数を期待せずにメッセージをそのまま返します。これにより、引数不足による稀なクラッシュを防ぎます。
genrun
関数の変更
for(i=0; i<argv->len; i++) {
+ q = argv->p[i];
+ if(i == 0 && streq(q, "hg"))
+ bwritestr(&cmd, "cmd.exe /c ");
if(i > 0)
bwritestr(&cmd, " ");
- q = argv->p[i];
if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) {
genrun
関数は、外部コマンドを実行するためのコマンドライン文字列を構築します。この変更は、コマンドライン引数を処理するループの内部で行われています。
q = argv->p[i];
の行がループの先頭に移動しました。これは、q
(現在の引数)がif
文の条件判定で使用される前に確実に初期化されるようにするためです。- 新しい
if
文if(i == 0 && streq(q, "hg"))
が追加されました。これは、現在の引数がコマンドラインの最初の要素(i == 0
)であり、かつそのコマンドが文字列"hg"
(Mercurialコマンド)である場合に真となります。 - この条件が真の場合、
bwritestr(&cmd, "cmd.exe /c ");
が実行され、構築中のコマンドライン文字列の先頭にcmd.exe /c
が追加されます。これにより、hg
コマンドがバッチファイルであっても、cmd.exe
によって正しく実行されるようになります。
関連リンク
- Go issue #3093: https://github.com/golang/go/issues/3093 (このコミットによって修正された問題のトラッカー)
- Go CL 5937043: https://golang.org/cl/5937043 (このコミットに対応するGoのコードレビューリンク)
参考にした情報源リンク
- Microsoft Docs:
FormatMessage
function: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage - Microsoft Docs:
CreateProcess
function: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw - Stack Overflow:
FormatMessageW
crash withFORMAT_MESSAGE_IGNORE_INSERTS
: https://stackoverflow.com/questions/tagged/formatmessagew (一般的な情報源として) - Stack Overflow: Running batch files with
CreateProcess
: https://stackoverflow.com/questions/tagged/createprocess (一般的な情報源として) - Go言語の公式ドキュメントおよびソースコード (Goのビルドシステムに関する一般的な理解のため)