[インデックス 12359] ファイルの概要
このコミットは、Go言語の公式ドキュメントに含まれるプログラム例を、Go 1のリリースに合わせて更新するものです。具体的には、Go 1で導入された新しいツールチェインの挙動や、標準ライブラリの変更に適合させるための修正が含まれています。特に、Goプログラムのビルドと実行方法を管理するスクリプト doc/progs/run の大幅な改訂が中心となっています。
コミット
commit f5a1dd888def229b7ce2f522ab8ef4083715efa6
Author: Rob Pike <r@golang.org>
Date: Mon Mar 5 12:49:31 2012 +1100
doc/progs: update for go 1
Fixes #3076.
R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/5727056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f5a1dd888def229b7ce2f522ab8ef4083715efa6
元コミット内容
このコミットは、Go 1のリリースに向けて、ドキュメント内のプログラム例を更新するものです。主な変更点は以下の通りです。
doc/go1.htmlおよびdoc/progs/go1.goにおいて、log.Printfの代わりにfmt.Printfを使用するように変更し、出力に改行を追加。また、go1.goのinit関数でlog.SetFlags(0)を呼び出し、ロギングのフォーマットを正規化。doc/progs/error.goおよびdoc/progs/slices.goに、例としてのみ機能し、直接実行を意図しない関数にプレースホルダーの本体を追加。doc/progs/runスクリプトの大幅な改訂。これは、Goプログラムのビルドとテストの方法を、Go 1で導入された新しいgoコマンドラインツールに適合させるためのものです。- 従来の
gomakeや$GC(Goコンパイラ) および$LD(Goリンカ) の直接呼び出しを削除し、標準のgo buildコマンドを使用するように変更。 - テストのアサーション方法を、厳密な文字列比較から
grepを用いた正規表現マッチングに変更し、テストの堅牢性を向上。 - 一時ファイルのクリーンアップ方法を更新。
- 従来の
src/run.bashからdoc/progs/runの実行に関する記述を削除。
このコミットは、Go 1のリリースに伴うツールチェインと標準ライブラリの変更に対応し、ドキュメントの正確性と整合性を保つことを目的としています。
変更の背景
このコミットの背景には、Go言語の歴史における重要なマイルストーンである「Go 1」のリリースがあります。Go 1は、言語仕様、標準ライブラリ、およびツールチェインの安定化を目的とした最初のメジャーリリースでした。それまでのGoは活発に開発が進められており、後方互換性のない変更が頻繁に行われていました。Go 1の目標は、開発者が安心してGo言語を使用できるよう、安定した基盤を提供することでした。
この安定化の一環として、Go 1では新しい公式のビルドツールである go コマンドが導入されました。それ以前は、6g (コンパイラ)、6l (リンカ) といったアーキテクチャ固有のツールや、gomake のようなビルドスクリプトを直接使用することが一般的でした。go コマンドは、これらの複雑なビルドプロセスを抽象化し、統一されたインターフェースを提供することで、開発者の利便性を大幅に向上させました。
このコミットは、まさにこの go コマンドの導入と、それに伴うビルド・実行環境の変化に対応するために行われました。ドキュメント内のプログラム例が、新しい標準的な方法でビルド・実行できるように更新される必要があったのです。また、log パッケージの挙動や、テストの堅牢性に関する細かな調整も、Go 1の安定化と品質向上の一環として行われました。
Fixes #3076 は、このコミットがGoのIssueトラッカーで報告された特定の課題を解決することを示しています。このIssueは、Go 1のリリースに伴うドキュメントの更新、特に doc/progs/run スクリプトの修正に関するものであったと推測されます。
前提知識の解説
このコミットを理解するためには、以下の前提知識があると役立ちます。
-
Go 1リリースとその意義:
- Go 1は、Go言語の最初の安定版リリースであり、言語仕様、標準ライブラリ、およびツールチェインの互換性を保証する最初のバージョンです。これにより、Go言語は実用的な開発に広く利用されるようになりました。
- Go 1以前は、言語やツールの変更が頻繁に行われており、既存のコードが動作しなくなる可能性がありました。Go 1は、この不安定な状況を終わらせ、将来のバージョンアップでも既存のGo 1コードが動作し続けることを約束しました(Go 1互換性保証)。
-
Go言語のビルドシステム:
- Go 1以前: Goプログラムのビルドには、
6g(コンパイラ)、6l(リンカ) といった低レベルのツールが直接使われたり、gomakeのようなカスタムのMakefileベースのシステムが使われたりしていました。これらはプラットフォームやアーキテクチャに依存する部分があり、複雑でした。 - Go 1以降:
goコマンドが導入され、go build、go run、go test、go getなど、統一されたインターフェースでGoプロジェクトを管理できるようになりました。これは、Go開発の体験を劇的に簡素化しました。
- Go 1以前: Goプログラムのビルドには、
-
シェルスクリプトの基本:
set -e: スクリプト内でコマンドが失敗した場合(終了ステータスが0以外の場合)に、即座にスクリプトの実行を終了させる設定です。これにより、エラーが無視されて予期せぬ動作が続くことを防ぎます。eval $(...): コマンドの出力を現在のシェルで評価(実行)します。環境変数の設定などによく使われます。rm -f: ファイルを強制的に削除します。存在しない場合でもエラーになりません。2>&1 >"$TMPFILE": 標準エラー出力 (2) を標準出力 (1) にリダイレクトし、その両方を$TMPFILEにリダイレクトします。これにより、コマンドのすべての出力がファイルに保存されます。|| true: 直前のコマンドが失敗しても、スクリプトがset -eによって終了しないようにします。grep: テキストから正規表現にマッチする行を検索するコマンドです。>/>>: リダイレクト演算子。>は上書き、>>は追記です。$(): コマンド置換。コマンドの出力を別のコマンドの引数として使用します。
-
Go言語の
logパッケージとfmtパッケージ:logパッケージ: 主にアプリケーションのロギングに使用されます。タイムスタンプやファイル名などのメタデータを自動的に付加する機能があります。fmtパッケージ: フォーマットされたI/O(入出力)を提供します。Printf関数は、C言語のprintfと同様に、指定されたフォーマットで文字列を出力します。log.SetFlags(0):logパッケージの出力フラグを設定します。0を指定すると、デフォルトで付加されるタイムスタンプやファイル名などの情報が一切出力されなくなります。これにより、fmt.Printfと同様のシンプルな出力になります。
これらの知識があると、コミットの変更内容がなぜ行われたのか、そしてそれがGoエコシステム全体にどのような影響を与えたのかを深く理解できます。
技術的詳細
このコミットの技術的詳細は、主に doc/progs/run スクリプトの変更に集約されます。
-
ビルドシステムの移行:
- 変更前:
ここでは、eval $(gomake --no-print-directory -f ../../src/Make.inc go-env) # ... $GC $i # Goコンパイラを直接呼び出し # ... $LD $1.$O # Goリンカを直接呼び出しgomakeを介して環境変数を設定し、$GC(Goコンパイラ) や$LD(Goリンカ) といった低レベルのツールを直接呼び出してGoプログラムをビルド・リンクしていました。これはGo 1以前の典型的なビルドワークフローでした。 - 変更後:
Go 1で導入されたgo build $i.go # ... ./$1go buildコマンドを使用するように変更されました。go buildは、ソースファイルから実行可能ファイルを生成する際に、コンパイルとリンクのプロセスを自動的に処理します。これにより、ビルドスクリプトが大幅に簡素化され、Go 1以降の標準的なビルド方法に準拠するようになりました。
- 変更前:
-
テストアサーションの改善:
- 変更前:
if [ "$x" != "$3" ] then echo $1 failed: '"'$x'"' is not '"'$3'"' fitestit関数は、プログラムの出力$xが期待される厳密な文字列$3と一致するかどうかを比較していました。これは、出力にわずかな変更があった場合でもテストが失敗する可能性があるため、堅牢性に欠ける場合があります。 - 変更後:
アサーションがif ! echo "$x" | grep "$2" > /dev/null then echo $1 failed: '"'$x'"' is not '"'$2'"' figrepコマンドを使用するように変更されました。echo "$x" | grep "$2"は、プログラムの出力$xが、期待される文字列$2(現在は正規表現として扱われる) にマッチするかどうかをチェックします。> /dev/nullはgrepの出力を破棄し、!はgrepの終了ステータスを反転させます(マッチしなかった場合に真となる)。これにより、テストは出力の厳密な一致ではなく、特定のパターンが含まれているかどうかを検証するようになり、より柔軟で堅牢なテストが可能になりました。 これに伴い、testitの呼び出しも正規表現を使用するように更新されています(例:testit defer '^0 3210 2$')。
- 変更前:
-
ロギングの正規化:
doc/go1.htmlとdoc/progs/go1.goでlog.Printfがfmt.Printfに変更されました。log.Printfはデフォルトでタイムスタンプなどのロギング情報を付加しますが、fmt.Printfは純粋なフォーマット済み出力を行います。doc/progs/go1.goのinit関数にlog.SetFlags(0)が追加されました。これは、logパッケージのデフォルトの出力フラグ(日付、時刻、ファイル名など)を無効にし、log.Printfがfmt.Printfと同様にシンプルなメッセージのみを出力するようにします。これにより、ドキュメントの例における出力がより予測可能でクリーンになります。
これらの変更は、Go 1のリリースに伴うGoエコシステムの成熟と、より標準化された開発ワークフローへの移行を反映しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に doc/progs/run スクリプトです。
--- a/doc/progs/run
+++ b/doc/progs/run
@@ -5,63 +5,49 @@
set -e
-eval $(gomake --no-print-directory -f ../../src/Make.inc go-env)
-\
-if [ -z "$O" ]; then
-\techo 'missing $O - maybe no Make.$GOARCH?' 1>&2
-\texit 1
-fi
-\
-rm -f *.$O
-\
defer_panic_recover="
-\tdefer.go
-\tdefer2.go
+\tdefer
+\tdefer2
"
effective_go="
-\teff_bytesize.go
-\teff_qr.go
-\teff_sequence.go
+\teff_bytesize
+\teff_qr
+\teff_sequence
"
error_handling="
-\terror.go
-\terror2.go
-\terror3.go
-\terror4.go
+\terror
+\terror2
+\terror3
+\terror4
"
-for i in \
-\t$defer_panic_recover \
-\t$effective_go \
-\t$error_handling \
-\tslices.go \
-\tgo1.go \
-; do
-\t$GC $i
+all=$(echo $defer_panic_recover $effective_go $error_handling slices go1)
+\
+for i in $all; do
+\tgo build $i.go
done
# Write to temporary file to avoid mingw bash bug.
TMPFILE="/tmp/gotest3.$USER"
function testit {
-\t$LD $1.$O
-\t./$O.out $2 2>&1 >"$TMPFILE" || true
+\t./$1 >"$TMPFILE" 2>&1 || true
\tx=$(echo $(cat "$TMPFILE")) # extra echo canonicalizes
-\tif [ "$x" != "$3" ]
+\tif ! echo "$x" | grep "$2" > /dev/null
\tthen
-\t\techo $1 failed: '"'$x'"' is not '"'$3'"'
+\t\techo $1 failed: '"'$x'"' is not '"'$2'"'
\tfi
}
-testit defer "" "0 3210 2"
-testit defer2 "" "Calling g. Printing in g 0 Printing in g 1 Printing in g 2 Printing in g 3 Panicking! Defer in g 3 Defer in g 2 Defer in g 1 Defer in g 0 Recovered in f 4 Returned normally from f."
+testit defer '^0 3210 2$'
+testit defer2 '^Calling g. Printing in g 0 Printing in g 1 Printing in g 2 Printing in g 3 Panicking! Defer in g 3 Defer in g 2 Defer in g 1 Defer in g 0 Recovered in f 4 Returned normally from f.$'
-testit eff_bytesize "" "1.00YB 9.09TB"
-testit eff_sequence "" "[-1 2 6 16 44]"
+testit eff_bytesize '^1.00YB 9.09TB$'
+testit eff_sequence '^\\[-1 2 6 16 44\\]$'
-testit go1 "" "Christmas is a holiday: true"
+testit go1 '^Christmas is a holiday: true Sleeping for 0.123s.*go1.go already exists$'
-rm -f $O.out $O.out.exe *.$O "$TMPFILE"
+rm -f $all "$TMPFILE"
その他の重要な変更箇所は以下の通りです。
doc/go1.html--- a/doc/go1.html +++ b/doc/go1.html @@ -953,7 +953,7 @@ func sleepUntil(wakeup time.Time) { return } delta := wakeup.Sub(now) // A Duration. - log.Printf("Sleeping for %.3fs", delta.Seconds()) + fmt.Printf("Sleeping for %.3fs\n", delta.Seconds()) time.Sleep(delta) }</pre>doc/progs/go1.go--- a/doc/progs/go1.go +++ b/doc/progs/go1.go @@ -35,6 +35,11 @@ func main() { var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion") +func init() { + // canonicalize the logging + log.SetFlags(0) +} + func mapDelete() { m := map[string]int{"7": 7, "23": 23} k := "7" @@ -177,7 +182,7 @@ func sleepUntil(wakeup time.Time) { return } delta := wakeup.Sub(now) // A Duration. - log.Printf("Sleeping for %.3fs", delta.Seconds()) + fmt.Printf("Sleeping for %.3fs\n", delta.Seconds()) time.Sleep(delta) }doc/progs/error.go--- a/doc/progs/error.go +++ b/doc/progs/error.go @@ -102,7 +102,10 @@ func decodeError(dec *json.Decoder, val struct{}) error { // OMIT return nil } -func findLine(os.FileInfo, int64) (int, int) +func findLine(os.FileInfo, int64) (int, int) { + // place holder; no need to run + return 0, 0 +}doc/progs/slices.go--- a/doc/progs/slices.go +++ b/doc/progs/slices.go @@ -57,3 +57,7 @@ func CopyDigits(filename string) []byte { } // STOP OMIT +\ +func main() { + // place holder; no need to run +}
コアとなるコードの解説
doc/progs/run スクリプトの変更
このスクリプトは、Go言語のドキュメントに含まれる様々なプログラム例をビルドし、実行し、その出力を検証するためのものです。Go 1のリリースに伴い、Goのビルドツールチェインが大きく変更されたため、このスクリプトもそれに合わせて更新されました。
-
ビルドコマンドの変更:
- 変更前は、
eval $(gomake ...)で環境を設定し、$GC(Goコンパイラ) を直接呼び出して.goファイルをコンパイルしていました。これはGo 1以前の低レベルなビルド方法でした。 - 変更後は、
go build $i.goというコマンドに置き換えられました。go buildはGo 1で導入された標準的なビルドコマンドであり、コンパイルとリンクを自動的に行い、実行可能ファイルを生成します。これにより、スクリプトがより簡潔になり、Go 1以降の標準的な開発ワークフローに準拠するようになりました。
- 変更前は、
-
テストアサーションの変更:
- 変更前は、
testit関数内でプログラムの出力と期待される文字列をif [ "$x" != "$3" ]のように厳密に比較していました。これは、出力にわずかな変更があった場合でもテストが失敗する原因となる可能性がありました。 - 変更後は、
if ! echo "$x" | grep "$2" > /dev/nullという形式に変更されました。これは、プログラムの出力$xが、期待されるパターン$2(正規表現として渡される) にマッチするかどうかをgrepコマンドを使って検証します。この変更により、テストはより柔軟になり、出力の細かな差異に影響されにくくなりました。例えば、Sleeping for 0.123sのように、時間によって変動する可能性のある出力に対しても、.*(任意の文字列) を使って柔軟にマッチさせることができます。
- 変更前は、
-
ファイルリストの簡素化:
defer_panic_recoverなどの変数で定義されていたファイルリストが、.go拡張子なしで記述されるようになりました(例:defer.goからdefer)。これは、go build $i.goのように、スクリプト内で.go拡張子を明示的に追加するようになったためです。
doc/go1.html および doc/progs/go1.go の変更
これらのファイルでは、log.Printf の呼び出しが fmt.Printf に変更され、出力に改行 (\n) が追加されました。
log.Printfは、デフォルトでタイムスタンプやソースファイル情報などのロギングメタデータを付加します。ドキュメントの例では、これらのメタデータは不要であり、純粋なプログラム出力のみを表示したい場合が多いです。fmt.Printfは、指定されたフォーマット文字列に従って純粋なテキストを出力します。doc/progs/go1.goのinit関数にlog.SetFlags(0)が追加されました。これは、logパッケージのデフォルトの出力フラグ(日付、時刻、ファイル名など)を無効にし、log.Printfがfmt.Printfと同様にシンプルなメッセージのみを出力するようにします。これにより、ドキュメントの例における出力がより予測可能でクリーンになります。
doc/progs/error.go および doc/progs/slices.go の変更
これらのファイルでは、一部の関数にプレースホルダーの本体が追加されました。
func findLine(os.FileInfo, int64) (int, int)に{ // place holder; no need to run return 0, 0 }が追加されました。doc/progs/slices.goにfunc main() { // place holder; no need to run }が追加されました。
これらの変更は、これらのファイルがドキュメントの例としてのみ機能し、doc/progs/run スクリプトによって直接実行されることを意図していないことを明確にするためのものです。これにより、スクリプトがこれらのファイルをビルドしようとした際に、関数本体がないことによるコンパイルエラーを防ぎます。
全体として、これらの変更はGo 1のリリースに伴うGo言語のツールチェインと標準ライブラリの進化を反映しており、ドキュメントのプログラム例が新しい標準に準拠し、正確に動作するようにするための重要な更新です。
関連リンク
- Go 1 Release Notes: https://go.dev/doc/go1
- Go Command Documentation: https://go.dev/cmd/go/
- Go Issue 3076 (関連する可能性のあるIssue): https://github.com/golang/go/issues/3076 (ただし、このIssueは現在存在しないか、番号が再利用されている可能性があります。当時のGoのIssueトラッカーはGoogle Code上にあったため、現在のGitHubのIssue番号とは異なる場合があります。)
参考にした情報源リンク
- Go 1 Release Notes: https://go.dev/doc/go1
- Go Command Documentation: https://go.dev/cmd/go/
logpackage documentation: https://pkg.go.dev/logfmtpackage documentation: https://pkg.go.dev/fmt- シェルスクリプトの基本知識 (set -e, リダイレクト, grepなど)
- Go言語の歴史とGo 1の意義に関する一般的な知識
- コミットメッセージと差分から読み取れる情報
- GitHubのコミットページ: https://github.com/golang/go/commit/f5a1dd888def229b7ce2f522ab8ef4083715efa6