[インデックス 13638] ファイルの概要
このコミットは、Go言語のテストインフラストラクチャの一部である test/run.go
ファイルに対する変更です。具体的には、Windows環境でのコンパイラ出力に含まれるキャリッジリターン (\r
) 文字を適切に処理することで、ビルドが失敗する問題を修正しています。
コミット
- コミットハッシュ:
42534cbc29de9bb645b06dd7f9b1c7bee72fbe2d
- 作者: Alex Brainman alex.brainman@gmail.com
- コミット日時: 2012年8月16日 木曜日 16:46:59 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/42534cbc29de9bb645b06dd7f9b1c7bee72fbe2d
元コミット内容
test: change run.go to ignore \r in compiler output (fixes windows build)
R=golang-dev, dave, minux.ma, remyoudompheng
CC=golang-dev
https://golang.org/cl/6460093
変更の背景
この変更の背景には、異なるオペレーティングシステム間でのテキストファイルの行末表現の違いが深く関わっています。
- Unix/Linux系システム: 行の終わりを単一のラインフィード (
\n
、LF) で表現します。 - Windows系システム: 行の終わりをキャリッジリターンとラインフィードの組み合わせ (
\r\n
、CRLF) で表現します。
Go言語のコンパイラ(特に当時の 6g
など)がWindows環境でエラーメッセージを出力する際、その行末に \r
が含まれることがありました。一方、test/run.go
内の errorCheck
関数は、コンパイラの出力を \n
で分割して各行を処理するように設計されていました。
この設計では、Windowsコンパイラの出力が ...エラーメッセージ\r\n
の形式である場合、strings.Split(outStr, "\n")
によって分割された各行の末尾に \r
が残ってしまいます。例えば、"エラーメッセージ\r"
のような文字列が生成されます。
errorCheck
関数は、エラーメッセージの解析や比較を行う際に、この余分な \r
が存在すると、期待される文字列と実際の文字列が一致せず、テストが誤って失敗する原因となっていました。特に、エラーメッセージの正確なマッチングを必要とするテストにおいて、この問題はWindowsビルドの不安定性や失敗を引き起こしていました。
このコミットは、このクロスプラットフォーム間の行末表現の違いに起因する問題を解決し、Windows環境でもテストが正しく実行されるようにするために導入されました。
前提知識の解説
1. 行末文字 (Line Endings)
テキストファイルにおいて、行の終わりを示す特殊な文字シーケンスを行末文字と呼びます。主要な行末文字には以下のものがあります。
- LF (Line Feed,
\n
): Unix、Linux、macOSなどのシステムで主に使用されます。カーソルを次の行の同じ桁に移動させます。 - CRLF (Carriage Return + Line Feed,
\r\n
): Windowsシステムで主に使用されます。カーソルを行の先頭に移動させ(キャリッジリターン)、その後次の行に移動させます(ラインフィード)。 - CR (Carriage Return,
\r
): 古いMac OS(Mac OS 9以前)などで使用されていました。カーソルを行の先頭に移動させます。
これらの違いは、特に異なるOS間でファイルを共有したり、プログラムの出力を解析したりする際に問題となることがあります。
2. コンパイラの出力
コンパイラは、ソースコードを機械語に変換する過程で、エラーや警告メッセージを標準出力や標準エラー出力に表示します。これらのメッセージのフォーマットはコンパイラによって異なりますが、通常は人間が読みやすいように整形されており、各メッセージは改行で区切られています。しかし、その改行が \n
なのか \r\n
なのかは、コンパイラが動作するOSやコンパイラ自体の実装に依存します。
3. Go言語の strings
パッケージ
Go言語の標準ライブラリには、文字列操作のための strings
パッケージが含まれています。このコミットで利用されている主要な関数は以下の通りです。
strings.Split(s, sep string) []string
: 文字列s
を区切り文字sep
で分割し、部分文字列のスライスを返します。このコミットでは"\n"
を区切り文字として使用し、コンパイラの出力を各行に分割しています。strings.HasSuffix(s, suffix string) bool
: 文字列s
が指定されたsuffix
で終わるかどうかを判定します。このコミットでは、行の末尾が"\r"
であるかをチェックするために使用されています。
技術的詳細
test/run.go
ファイル内の errorCheck
関数は、Goコンパイラ(当時の 6g
など)の出力文字列を解析し、エラーメッセージを抽出・整形する役割を担っていました。この関数は、まずコンパイラの出力全体を \n
で分割し、各行を個別に処理します。
for _, line := range strings.Split(outStr, "\n") {
// ...
}
問題は、Windows環境でコンパイラが出力するエラーメッセージの行末が \r\n
であったことです。strings.Split(outStr, "\n")
を実行すると、例えば "エラーメッセージ\r\n"
という文字列は ["エラーメッセージ\r", ""]
のように分割されます。結果として、line
変数には "\r"
が末尾に残った文字列が代入されていました。
この "\r"
が残った文字列は、後続のエラーメッセージの比較ロジックにおいて、期待される値と一致しない原因となっていました。例えば、期待されるエラーメッセージが "syntax error"
であったとしても、実際のコンパイラ出力から抽出された行が "syntax error\r"
であれば、両者は異なるものと判断され、テストが失敗します。
このコミットは、この問題を解決するために、各行を処理するループの冒頭で、行の末尾に \r
が存在するかどうかをチェックし、存在する場合はその \r
を削除するというシンプルなロジックを追加しました。これにより、後続の処理が常にクリーンな行末を持つ文字列に対して行われるようになり、Windows環境でのテストの信頼性が向上しました。
コアとなるコードの変更箇所
変更は test/run.go
ファイルの errorCheck
関数内、strings.Split(outStr, "\n")
で分割された各行を処理するループの冒頭に追加されました。
--- a/test/run.go
+++ b/test/run.go
@@ -400,6 +400,9 @@ func (t *test) errorCheck(outStr string, full, short string) (err error) {
// 6g error messages continue onto additional lines with leading tabs.
// Split the output at the beginning of each line that doesn\'t begin with a tab.
for _, line := range strings.Split(outStr, \"\\n\") {\n+\t\tif strings.HasSuffix(line, \"\\r\") {\t// remove \'\\r\', output by compiler on windows\n+\t\t\tline = line[:len(line)-1]\n+\t\t}\n \t\tif strings.HasPrefix(line, \"\\t\") {\
\t\t\tout[len(out)-1] += \"\\n\" + line\
\t\t} else {\
具体的には、以下の3行が追加されました。
if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows
line = line[:len(line)-1]
}
コアとなるコードの解説
追加されたコードは、以下の処理を行います。
-
if strings.HasSuffix(line, "\r") {
:strings.HasSuffix(line, "\r")
は、現在のline
変数の文字列が"\r"
で終わるかどうかをチェックします。- この条件が
true
である場合、その行はWindowsコンパイラによって出力され、末尾に余分なキャリッジリターン文字が含まれていると判断されます。
-
line = line[:len(line)-1]
:- この行は、
line
文字列の最後の文字(つまり\r
)を削除します。 len(line)
は文字列line
の長さを返します。line[:len(line)-1]
は、文字列line
のインデックス0からlen(line)-1
の直前までのスライス(部分文字列)を作成します。これにより、最後の文字が取り除かれた新しい文字列がline
に再代入されます。
- この行は、
この変更により、errorCheck
関数は、Windowsコンパイラの出力から得られた行であっても、常に \r
が除去されたクリーンな文字列として処理できるようになりました。これにより、エラーメッセージの比較が正しく行われ、Windows環境でのテストの誤った失敗が解消されました。これは、クロスプラットフォーム互換性を確保するための、小さくも重要な修正です。
関連リンク
- Go Code Review (Gerrit) Change-Id:
https://golang.org/cl/6460093
参考にした情報源リンク
- Wikipedia: Newline
- Go言語 strings パッケージ ドキュメント (当時のバージョンに基づく)
- Go言語のクロスプラットフォーム開発に関する一般的な情報 (一般的な情報源として)
- Go言語のテストに関する一般的な情報 (一般的な情報源として)
- Go言語のコンパイラに関する一般的な情報 (当時のコンパイラに関する一般的な情報として)```markdown
[インデックス 13638] ファイルの概要
このコミットは、Go言語のテストインフラストラクチャの一部である test/run.go
ファイルに対する変更です。具体的には、Windows環境でのコンパイラ出力に含まれるキャリッジリターン (\r
) 文字を適切に処理することで、ビルドが失敗する問題を修正しています。
コミット
- コミットハッシュ:
42534cbc29de9bb645b06dd7f9b1c7bee72fbe2d
- 作者: Alex Brainman alex.brainman@gmail.com
- コミット日時: 2012年8月16日 木曜日 16:46:59 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/42534cbc29de9bb645b06dd7f9b1c7bee72fbe2d
元コミット内容
test: change run.go to ignore \r in compiler output (fixes windows build)
R=golang-dev, dave, minux.ma, remyoudompheng
CC=golang-dev
https://golang.org/cl/6460093
変更の背景
この変更の背景には、異なるオペレーティングシステム間でのテキストファイルの行末表現の違いが深く関わっています。
- Unix/Linux系システム: 行の終わりを単一のラインフィード (
\n
、LF) で表現します。 - Windows系システム: 行の終わりをキャリッジリターンとラインフィードの組み合わせ (
\r\n
、CRLF) で表現します。
Go言語のコンパイラ(特に当時の 6g
など)がWindows環境でエラーメッセージを出力する際、その行末に \r
が含まれることがありました。一方、test/run.go
内の errorCheck
関数は、コンパイラの出力を \n
で分割して各行を処理するように設計されていました。
この設計では、Windowsコンパイラの出力が ...エラーメッセージ\r\n
の形式である場合、strings.Split(outStr, "\n")
によって分割された各行の末尾に \r
が残ってしまいます。例えば、"エラーメッセージ\r"
のような文字列が生成されます。
errorCheck
関数は、エラーメッセージの解析や比較を行う際に、この余分な \r
が存在すると、期待される文字列と実際の文字列が一致せず、テストが誤って失敗する原因となっていました。特に、エラーメッセージの正確なマッチングを必要とするテストにおいて、この問題はWindowsビルドの不安定性や失敗を引き起こしていました。
このコミットは、このクロスプラットフォーム間の行末表現の違いに起因する問題を解決し、Windows環境でもテストが正しく実行されるようにするために導入されました。
前提知識の解説
1. 行末文字 (Line Endings)
テキストファイルにおいて、行の終わりを示す特殊な文字シーケンスを行末文字と呼びます。主要な行末文字には以下のものがあります。
- LF (Line Feed,
\n
): Unix、Linux、macOSなどのシステムで主に使用されます。カーソルを次の行の同じ桁に移動させます。 - CRLF (Carriage Return + Line Feed,
\r\n
): Windowsシステムで主に使用されます。カーソルを行の先頭に移動させ(キャリッジリターン)、その後次の行に移動させます(ラインフィード)。 - CR (Carriage Return,
\r
): 古いMac OS(Mac OS 9以前)などで使用されていました。カーソルを行の先頭に移動させます。
これらの違いは、特に異なるOS間でファイルを共有したり、プログラムの出力を解析したりする際に問題となることがあります。
2. コンパイラの出力
コンパイラは、ソースコードを機械語に変換する過程で、エラーや警告メッセージを標準出力や標準エラー出力に表示します。これらのメッセージのフォーマットはコンパイラによって異なりますが、通常は人間が読みやすいように整形されており、各メッセージは改行で区切られています。しかし、その改行が \n
なのか \r\n
なのかは、コンパイラが動作するOSやコンパイラ自体の実装に依存します。
3. Go言語の strings
パッケージ
Go言語の標準ライブラリには、文字列操作のための strings
パッケージが含まれています。このコミットで利用されている主要な関数は以下の通りです。
strings.Split(s, sep string) []string
: 文字列s
を区切り文字sep
で分割し、部分文字列のスライスを返します。このコミットでは"\n"
を区切り文字として使用し、コンパイラの出力を各行に分割しています。strings.HasSuffix(s, suffix string) bool
: 文字列s
が指定されたsuffix
で終わるかどうかを判定します。このコミットでは、行の末尾が"\r"
であるかをチェックするために使用されています。
技術的詳細
test/run.go
ファイル内の errorCheck
関数は、Goコンパイラ(当時の 6g
など)の出力文字列を解析し、エラーメッセージを抽出・整形する役割を担っていました。この関数は、まずコンパイラの出力全体を \n
で分割し、各行を個別に処理します。
for _, line := range strings.Split(outStr, "\n") {
// ...
}
問題は、Windows環境でコンパイラが出力するエラーメッセージの行末が \r\n
であったことです。strings.Split(outStr, "\n")
を実行すると、例えば "エラーメッセージ\r\n"
という文字列は ["エラーメッセージ\r", ""]
のように分割されます。結果として、line
変数には "\r"
が末尾に残った文字列が代入されていました。
この "\r"
が残った文字列は、後続のエラーメッセージの比較ロジックにおいて、期待される値と一致しない原因となっていました。例えば、期待されるエラーメッセージが "syntax error"
であったとしても、実際のコンパイラ出力から抽出された行が "syntax error\r"
であれば、両者は異なるものと判断され、テストが失敗します。
このコミットは、この問題を解決するために、各行を処理するループの冒頭で、行の末尾に \r
が存在するかどうかをチェックし、存在する場合はその \r
を削除するというシンプルなロジックを追加しました。これにより、後続の処理が常にクリーンな行末を持つ文字列に対して行われるようになり、Windows環境でのテストの信頼性が向上しました。
コアとなるコードの変更箇所
変更は test/run.go
ファイルの errorCheck
関数内、strings.Split(outStr, "\n")
で分割された各行を処理するループの冒頭に追加されました。
--- a/test/run.go
+++ b/test/run.go
@@ -400,6 +400,9 @@ func (t *test) errorCheck(outStr string, full, short string) (err error) {
// 6g error messages continue onto additional lines with leading tabs.
// Split the output at the beginning of each line that doesn\'t begin with a tab.
for _, line := range strings.Split(outStr, \"\\n\") {\n+\t\tif strings.HasSuffix(line, \"\\r\") {\t// remove \'\\r\', output by compiler on windows\n+\t\t\tline = line[:len(line)-1]\n+\t\t}\n \t\tif strings.HasPrefix(line, \"\\t\") {\
\t\t\tout[len(out)-1] += \"\\n\" + line\
\t\t} else {\
具体的には、以下の3行が追加されました。
if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows
line = line[:len(line)-1]
}
コアとなるコードの解説
追加されたコードは、以下の処理を行います。
-
if strings.HasSuffix(line, "\r") {
:strings.HasSuffix(line, "\r")
は、現在のline
変数の文字列が"\r"
で終わるかどうかをチェックします。- この条件が
true
である場合、その行はWindowsコンパイラによって出力され、末尾に余分なキャリッジリターン文字が含まれていると判断されます。
-
line = line[:len(line)-1]
:- この行は、
line
文字列の最後の文字(つまり\r
)を削除します。 len(line)
は文字列line
の長さを返します。line[:len(line)-1]
は、文字列line
のインデックス0からlen(line)-1
の直前までのスライス(部分文字列)を作成します。これにより、最後の文字が取り除かれた新しい文字列がline
に再代入されます。
- この行は、
この変更により、errorCheck
関数は、Windowsコンパイラの出力から得られた行であっても、常に \r
が除去されたクリーンな文字列として処理できるようになりました。これにより、エラーメッセージの比較が正しく行われ、Windows環境でのテストの誤った失敗が解消されました。これは、クロスプラットフォーム互換性を確保するための、小さくも重要な修正です。
関連リンク
- Go Code Review (Gerrit) Change-Id:
https://golang.org/cl/6460093
参考にした情報源リンク
- Wikipedia: Newline
- Go言語 strings パッケージ ドキュメント (当時のバージョンに基づく)
- Go言語のクロスプラットフォーム開発に関する一般的な情報 (一般的な情報源として)
- Go言語のテストに関する一般的な情報 (一般的な情報源として)
- Go言語のコンパイラに関する一般的な情報 (当時のコンパイラに関する一般的な情報として)