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

[インデックス 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.goinit 関数で 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 スクリプトの修正に関するものであったと推測されます。

前提知識の解説

このコミットを理解するためには、以下の前提知識があると役立ちます。

  1. Go 1リリースとその意義:

    • Go 1は、Go言語の最初の安定版リリースであり、言語仕様、標準ライブラリ、およびツールチェインの互換性を保証する最初のバージョンです。これにより、Go言語は実用的な開発に広く利用されるようになりました。
    • Go 1以前は、言語やツールの変更が頻繁に行われており、既存のコードが動作しなくなる可能性がありました。Go 1は、この不安定な状況を終わらせ、将来のバージョンアップでも既存のGo 1コードが動作し続けることを約束しました(Go 1互換性保証)。
  2. Go言語のビルドシステム:

    • Go 1以前: Goプログラムのビルドには、6g (コンパイラ)、6l (リンカ) といった低レベルのツールが直接使われたり、gomake のようなカスタムのMakefileベースのシステムが使われたりしていました。これらはプラットフォームやアーキテクチャに依存する部分があり、複雑でした。
    • Go 1以降: go コマンドが導入され、go buildgo rungo testgo get など、統一されたインターフェースでGoプロジェクトを管理できるようになりました。これは、Go開発の体験を劇的に簡素化しました。
  3. シェルスクリプトの基本:

    • set -e: スクリプト内でコマンドが失敗した場合(終了ステータスが0以外の場合)に、即座にスクリプトの実行を終了させる設定です。これにより、エラーが無視されて予期せぬ動作が続くことを防ぎます。
    • eval $(...): コマンドの出力を現在のシェルで評価(実行)します。環境変数の設定などによく使われます。
    • rm -f: ファイルを強制的に削除します。存在しない場合でもエラーになりません。
    • 2>&1 >"$TMPFILE": 標準エラー出力 (2) を標準出力 (1) にリダイレクトし、その両方を $TMPFILE にリダイレクトします。これにより、コマンドのすべての出力がファイルに保存されます。
    • || true: 直前のコマンドが失敗しても、スクリプトが set -e によって終了しないようにします。
    • grep: テキストから正規表現にマッチする行を検索するコマンドです。
    • > / >>: リダイレクト演算子。> は上書き、>> は追記です。
    • $(): コマンド置換。コマンドの出力を別のコマンドの引数として使用します。
  4. Go言語の log パッケージと fmt パッケージ:

    • log パッケージ: 主にアプリケーションのロギングに使用されます。タイムスタンプやファイル名などのメタデータを自動的に付加する機能があります。
    • fmt パッケージ: フォーマットされたI/O(入出力)を提供します。Printf 関数は、C言語の printf と同様に、指定されたフォーマットで文字列を出力します。
    • log.SetFlags(0): log パッケージの出力フラグを設定します。0 を指定すると、デフォルトで付加されるタイムスタンプやファイル名などの情報が一切出力されなくなります。これにより、fmt.Printf と同様のシンプルな出力になります。

これらの知識があると、コミットの変更内容がなぜ行われたのか、そしてそれがGoエコシステム全体にどのような影響を与えたのかを深く理解できます。

技術的詳細

このコミットの技術的詳細は、主に doc/progs/run スクリプトの変更に集約されます。

  1. ビルドシステムの移行:

    • 変更前:
      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 build $i.go
      # ...
      ./$1
      
      Go 1で導入された go build コマンドを使用するように変更されました。go build は、ソースファイルから実行可能ファイルを生成する際に、コンパイルとリンクのプロセスを自動的に処理します。これにより、ビルドスクリプトが大幅に簡素化され、Go 1以降の標準的なビルド方法に準拠するようになりました。
  2. テストアサーションの改善:

    • 変更前:
      if [ "$x" != "$3" ]
      then
          echo $1 failed: '"'$x'"' is not '"'$3'"'
      fi
      
      testit 関数は、プログラムの出力 $x が期待される厳密な文字列 $3 と一致するかどうかを比較していました。これは、出力にわずかな変更があった場合でもテストが失敗する可能性があるため、堅牢性に欠ける場合があります。
    • 変更後:
      if ! echo "$x" | grep "$2" > /dev/null
      then
          echo $1 failed: '"'$x'"' is not '"'$2'"'
      fi
      
      アサーションが grep コマンドを使用するように変更されました。echo "$x" | grep "$2" は、プログラムの出力 $x が、期待される文字列 $2 (現在は正規表現として扱われる) にマッチするかどうかをチェックします。> /dev/nullgrep の出力を破棄し、!grep の終了ステータスを反転させます(マッチしなかった場合に真となる)。これにより、テストは出力の厳密な一致ではなく、特定のパターンが含まれているかどうかを検証するようになり、より柔軟で堅牢なテストが可能になりました。 これに伴い、testit の呼び出しも正規表現を使用するように更新されています(例: testit defer '^0 3210 2$')。
  3. ロギングの正規化:

    • doc/go1.htmldoc/progs/go1.golog.Printffmt.Printf に変更されました。log.Printf はデフォルトでタイムスタンプなどのロギング情報を付加しますが、fmt.Printf は純粋なフォーマット済み出力を行います。
    • doc/progs/go1.goinit 関数に log.SetFlags(0) が追加されました。これは、log パッケージのデフォルトの出力フラグ(日付、時刻、ファイル名など)を無効にし、log.Printffmt.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のビルドツールチェインが大きく変更されたため、このスクリプトもそれに合わせて更新されました。

  1. ビルドコマンドの変更:

    • 変更前は、eval $(gomake ...) で環境を設定し、$GC (Goコンパイラ) を直接呼び出して .go ファイルをコンパイルしていました。これはGo 1以前の低レベルなビルド方法でした。
    • 変更後は、go build $i.go というコマンドに置き換えられました。go build はGo 1で導入された標準的なビルドコマンドであり、コンパイルとリンクを自動的に行い、実行可能ファイルを生成します。これにより、スクリプトがより簡潔になり、Go 1以降の標準的な開発ワークフローに準拠するようになりました。
  2. テストアサーションの変更:

    • 変更前は、testit 関数内でプログラムの出力と期待される文字列を if [ "$x" != "$3" ] のように厳密に比較していました。これは、出力にわずかな変更があった場合でもテストが失敗する原因となる可能性がありました。
    • 変更後は、if ! echo "$x" | grep "$2" > /dev/null という形式に変更されました。これは、プログラムの出力 $x が、期待されるパターン $2 (正規表現として渡される) にマッチするかどうかを grep コマンドを使って検証します。この変更により、テストはより柔軟になり、出力の細かな差異に影響されにくくなりました。例えば、Sleeping for 0.123s のように、時間によって変動する可能性のある出力に対しても、.* (任意の文字列) を使って柔軟にマッチさせることができます。
  3. ファイルリストの簡素化:

    • 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.goinit 関数に log.SetFlags(0) が追加されました。これは、log パッケージのデフォルトの出力フラグ(日付、時刻、ファイル名など)を無効にし、log.Printffmt.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.gofunc 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番号とは異なる場合があります。)

参考にした情報源リンク