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

[インデックス 13137] ファイルの概要

このコミットは、Go言語のAPI互換性チェックツールであるgo tool apiの動作を改善し、将来のAPI変更に対する警告のノイズを削減することを目的としています。具体的には、api/next.txtという新しいファイルを追加し、既知の将来のAPI変更をこのファイルに記述することで、all.bashスクリプト実行時に発生する不必要な警告を抑制します。

変更されたファイルは以下の通りです。

  • api/README: apiディレクトリ内のファイルの役割について説明する新しいREADMEファイルが追加されました。go1.txtのような凍結されたAPIファイルと、next.txtのような変更可能なAPIファイルの目的が明確にされています。
  • api/next.txt: 新規追加されたファイルで、Goの次期リリースで追加される予定のAPI機能がリストアップされています。このコミットでは、crypto/tls, crypto/x509, debug/elf, go/doc, math/big, regexp/syntax, syscall (darwin, windows向け) など、多数の新しいAPI要素が追加されています。
  • src/cmd/api/goapi.go: go tool apiコマンドの主要なロジックが含まれるGoのソースファイルです。このファイルが最も大きく変更されており、api/next.txtを読み込み、既存のAPIとの差分を比較する新しいロジックが追加されました。特に、-allow_new-nextという新しいコマンドラインフラグが導入されています。
  • src/run.bash: Goプロジェクトのテストとビルドを実行するシェルスクリプトです。go tool apiの呼び出しに、新しく追加された-next $GOROOT/api/next.txtオプションが追加されました。
  • src/run.bat: src/run.bashのWindows版バッチファイルです。こちらも同様にgo tool apiの呼び出しに-next ..\api\next.txtオプションが追加されました。

コミット

commit 71c1a7b77700ea8e549368c8606d74d7a7b5104f
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Tue May 22 18:41:20 2012 -0700

    cmd/api: add api/next.txt
    
    This quiets all.bash noise for upcoming features we know about.
    
    The all.bash warnings will now only print for things not in next.txt
    (or in next.txt but not in the API).
    
    Once an API is frozen, we rename next.txt to a new frozen file
    (like go1.txt)
    
    Fixes #3651
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6218069

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/71c1a7b77700ea8e549368c8606d74d7a7b5104f

元コミット内容

cmd/api: add api/next.txt

This quiets all.bash noise for upcoming features we know about.

The all.bash warnings will now only print for things not in next.txt
(or in next.txt but not in the API).

Once an API is frozen, we rename next.txt to a new frozen file
(like go1.txt)

Fixes #3651

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6218069

変更の背景

Go言語の開発プロセスにおいて、新しいAPIが追加される際、既存のAPIとの互換性をチェックするgo tool apiコマンドが使用されます。このツールは、GoのリリースサイクルにおいてAPIの安定性を保証するために非常に重要です。しかし、次期リリースで導入が予定されている新しいAPI機能が開発中にgo tool apiによって「新しいAPIの追加」として検出され、all.bashスクリプト(Goのビルドとテストを実行するスクリプト)の実行時に警告として出力されることがありました。

この警告は、開発者が意図的に追加している将来のAPI変更に対しても発生するため、開発プロセスにおいてノイズとなり、本当に互換性の問題がある場合にその警告を見落とす可能性がありました。このコミットの目的は、このような「既知の、意図された将来のAPI追加」に関する警告を抑制し、all.bashの出力をより意味のあるものにすることです。具体的には、次期リリースで追加される予定のAPIを事前に定義したファイル(next.txt)に記述することで、go tool apiがこれらの変更を「既知の追加」として認識し、警告を出さないようにします。これにより、開発者は本当に予期せぬAPIの変更や互換性の破壊にのみ注意を払うことができるようになります。

前提知識の解説

Go言語のAPI互換性ポリシー

Go言語は、後方互換性を非常に重視しています。特に、メジャーリリース(例: Go 1, Go 2)の間では、既存の公開API(エクスポートされた関数、型、変数など)の変更は厳しく制限されます。これは、Goで書かれたプログラムが新しいバージョンのGoでも問題なく動作することを保証するためです。go tool apiは、この互換性ポリシーを強制するための主要なツールの一つです。

go tool api

go tool apiは、Goの標準ライブラリの公開APIを抽出し、指定されたAPI定義ファイル(例: go1.txt)と比較することで、APIの変更を検出するコマンドラインツールです。

  • APIの抽出: Goのソースコードを解析し、エクスポートされたすべての型、関数、メソッド、変数、定数などの情報を収集します。
  • API定義ファイル: go1.txtのようなファイルには、特定のGoバージョンの公開APIの「スナップショット」がテキスト形式で記述されています。各行が1つのAPI要素を表します。
  • 互換性チェック: go tool apiは、現在のGoソースコードから抽出したAPIと、API定義ファイルに記述されたAPIを比較します。
    • API定義ファイルに存在するが、現在のコードに存在しないAPI(削除されたAPI)は、互換性の破壊とみなされ、エラーまたは警告として報告されます。
    • 現在のコードに存在するが、API定義ファイルに存在しないAPI(追加されたAPI)は、通常は警告として報告されます。GoのAPI互換性ポリシーでは、既存のAPIの削除や変更は厳しく制限されますが、新しいAPIの追加は許可される場合があります。

all.bashスクリプト

all.bashは、Go言語のソースツリーのルートにあるシェルスクリプトで、Goのビルド、テスト、リンティング、およびその他の検証プロセスを包括的に実行するために使用されます。Goの開発者が変更をコミットする前に、このスクリプトを実行して、変更が既存のシステムを破壊しないことを確認するのが一般的なプラクティスです。go tool apiのチェックもこのスクリプトの一部として実行され、APIの互換性に関する問題があれば、all.bashの出力に警告やエラーとして表示されます。

go1.txtnext.txtの役割

  • go1.txt: Go 1のリリース時に「凍結」されたAPIの定義ファイルです。Go 1のリリース以降、このファイルに記述されたAPIは変更または削除されてはならないという強い互換性保証があります。新しいGoのバージョンがリリースされるたびに、そのバージョンのAPIを定義する新しい凍結ファイル(例: go1.1.txt, go1.2.txtなど)が作成されることがあります。これらのファイルは、一度作成されると基本的に変更されません。
  • next.txt: このコミットで導入された新しい概念です。これは、次期Goリリースで導入される予定のAPI変更を一時的にリストアップするためのファイルです。go tool apiは、go1.txt(または現在の凍結されたAPIファイル)との比較に加えて、next.txtに記述されたAPIを「既知の追加」として扱います。これにより、next.txtにリストされているAPIが現在のコードベースに存在する場合でも、all.bashが警告を出さなくなります。次期リリースが凍結されると、next.txtの内容は新しい凍結ファイル(例: go1.1.txt)に統合され、next.txtはクリアされるか、次の開発サイクルのために更新されます。

Go言語の標準ライブラリとシステムコール

api/next.txtには、crypto/tlssyscallパッケージなど、Goの標準ライブラリの様々なAPIがリストされています。

  • crypto/tls: TLS (Transport Layer Security) プロトコルを実装するためのパッケージで、セキュアな通信を提供します。
  • syscall: オペレーティングシステムの低レベルな機能(システムコール)にアクセスするためのパッケージです。OSごとに異なる定数や構造体が含まれるため、darwin-386windows-amd64のようにプラットフォーム固有のAPIが多数追加されています。

技術的詳細

このコミットの技術的な核心は、go tool apiコマンドがAPIの互換性チェックを行う際に、go1.txtのような「凍結されたAPI」と、next.txtのような「次期リリースで追加予定のAPI」を区別して扱うようになった点です。

変更前は、go tool api -c <file>は、指定されたファイル(通常はgo1.txt)と現在のGoソースコードから抽出されたAPIを単純に比較し、差分があれば報告していました。新しいAPIが追加された場合、それは常に「+」プレフィックス付きで出力され、all.bashスクリプトがこれを検出して警告を発していました。

変更後、go tool apiには以下の新しいフラグが追加されました。

  • -next <filename>: このフラグで指定されたファイル(例: api/next.txt)は、次期リリースで追加される予定のAPIのリストとして扱われます。
  • -allow_new: このフラグはデフォルトでtrueに設定されており、新しいAPIの追加を許可します。ただし、next.txtに記載されていない新しいAPIが検出された場合は、引き続き警告が出力されます。このフラグをfalseに設定すると、next.txtに記載されているかどうかにかかわらず、新しいAPIの追加がすべてエラーとして扱われるようになります。これは、リリース前のAPI凍結期間などに使用される可能性があります。

goapi.go内のAPI比較ロジックは、以下の3つのAPIセットを考慮するように拡張されました。

  1. required: -cフラグで指定されたファイル(例: go1.txt)から読み込まれる、既存の安定版APIのセット。
  2. features: 現在のGoソースコードから動的に抽出される、現在のAPIのセット。
  3. optional: -nextフラグで指定されたファイル(例: next.txt)から読み込まれる、次期リリースで追加予定のAPIのセット。

比較ロジックは、これら3つのセットをソートし、線形に走査することで差分を検出します。

  • APIの削除: requiredに存在するがfeaturesに存在しないAPIは、互換性の破壊とみなされ、-プレフィックス付きで出力され、fail = trueが設定されます。これは、GoのAPI互換性ポリシーにおいて最も深刻な問題です。
  • APIの追加: featuresに存在するがrequiredに存在しないAPIは、新しいAPIとみなされます。
    • もしその新しいAPIがoptionalセットにも存在する場合(つまり、next.txtに記載されている場合)、それは「既知の追加」として扱われ、警告は出力されません。optionalマップからそのエントリは削除され、next.txtのクリーンアップに役立ちます。
    • もしその新しいAPIがoptionalセットに存在しない場合(つまり、next.txtに記載されていない場合)、それは予期せぬ新しいAPIとして扱われ、+プレフィックス付きで出力されます。この場合、-allow_newフラグがfalseであればfail = trueが設定されます。
  • next.txtに記載されているがAPIに存在しないもの: 比較の最後に、optionalマップに残っているAPI(つまり、next.txtには記載されているが、まだ実際のAPIには追加されていないもの)があれば、(in next file, but not in API) -%sという形式で出力されます。これは、next.txtが古くなっている可能性を示唆し、クリーンアップを促します。

この新しいロジックにより、all.bashは、next.txtに記載されている既知のAPI追加に対しては警告を出さなくなり、本当に予期せぬAPI変更や互換性の破壊があった場合にのみ、意味のある警告やエラーを出力するようになります。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更は、主にsrc/cmd/api/goapi.goファイルに集中しています。

  1. 新しいフラグの追加:

    // Flags
    var (
    	// TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated
    	// list of files, rather than just one.
    	checkFile = flag.String("c", "", "optional filename to check API against")
    	allowNew  = flag.Bool("allow_new", true, "allow API additions") // 新規追加
    	nextFile  = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") // 新規追加
    	verbose   = flag.Bool("v", false, "verbose debugging")
    )
    

    allowNewnextFileという2つの新しいコマンドラインフラグが追加されました。

  2. API比較ロジックの変更: main関数内のAPI比較ロジックが大幅に書き換えられました。 変更前は、*checkFile != ""の場合にのみ比較が行われ、単純な差分表示でした。 変更後は、*checkFileが指定されている場合に、required(既存API)、features(現在のAPI)、optional(次期API)の3つのセットを考慮した複雑な比較が行われます。

    	fail := false // 互換性破壊があった場合にtrueになるフラグ
    	defer func() {
    		if fail {
    			os.Exit(1) // 互換性破壊があれば終了コード1で終了
    		}
    	}()
    
    	// ... (既存のAPI抽出ロジック) ...
    
    	if *checkFile == "" { // -c フラグが指定されていない場合、現在のAPIをすべて出力して終了
    		for _, f := range features {
    			fmt.Fprintf(bw, "%s\\n", f)
    		}
    		return
    	}
    
    	var required []string // -c で指定されたファイルから読み込まれるAPI
    	for _, filename := range []string{*checkFile} {
    		required = append(required, fileFeatures(filename)...)
    	}
    	sort.Strings(required)
    
    	var optional = make(map[string]bool) // -next で指定されたファイルから読み込まれるAPI
    	if *nextFile != "" {
    		for _, feature := range fileFeatures(*nextFile) {
    			optional[feature] = true
    		}
    	}
    
    	take := func(sl *[]string) string { // スライスから先頭要素を取り出すヘルパー関数
    		s := (*sl)[0]
    		*sl = (*sl)[1:]
    		return s
    	}
    
    	// required, features の両方が空になるまで比較を続ける
    	for len(required) > 0 || len(features) > 0 {
    		switch {
    		case len(features) == 0 || required[0] < features[0]:
    			// required にあるが features にない (API削除)
    			fmt.Fprintf(bw, "-%s\\n", take(&required))
    			fail = true // 互換性破壊
    		case len(required) == 0 || required[0] > features[0]:
    			// features にあるが required にない (API追加)
    			newFeature := take(&features)
    			if optional[newFeature] {
    				// next.txt に記載されている既知の追加
    				delete(optional, newFeature) // next.txt から削除済みとしてマーク
    			} else {
    				// next.txt に記載されていない予期せぬ追加
    				fmt.Fprintf(bw, "+%s\\n", newFeature)
    				if !*allowNew {
    					fail = true // -allow_new=false の場合、互換性破壊
    				}
    			}
    		default:
    			// required と features の両方に存在する (変更なし)
    			take(&required)
    			take(&features)
    		}
    	}
    
    	// next.txt に記載されているが、まだAPIに存在しないもの
    	var missing []string
    	for feature := range optional {
    		missing = append(missing, feature)
    	}
    	sort.Strings(missing)
    	for _, feature := range missing {
    		fmt.Fprintf(bw, "(in next file, but not in API) -%s\\n", feature)
    	}
    } // main関数の終わり
    
  3. fileFeaturesヘルパー関数の追加:

    func fileFeatures(filename string) []string {
    	bs, err := ioutil.ReadFile(filename)
    	if err != nil {
    		log.Fatalf("Error reading file %s: %v", filename, err)
    	}
    	return strings.Split(strings.TrimSpace(string(bs)), "\\n")
    }
    

    これは、指定されたファイルからAPI機能を読み込み、文字列のスライスとして返す新しいヘルパー関数です。requiredoptionalのAPIリストを読み込むために使用されます。

  4. src/run.bashsrc/run.batの変更: go tool apiの呼び出しに-nextフラグが追加されました。

    • src/run.bash:
      --- a/src/run.bash
      +++ b/src/run.bash
      @@ -106,7 +106,7 @@ time go run run.go
       
       echo
       echo '# Checking API compatibility.'
      -go tool api -c $GOROOT/api/go1.txt
      +go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
       
       echo
       echo ALL TESTS PASSED
      
    • src/run.bat:
      --- a/src/run.bat
      +++ b/src/run.bat
      @@ -68,7 +68,7 @@ echo.
       if %FAIL%==1 goto fail
       
       echo # Checking API compatibility.
      -go tool api -c ..\api\go1.txt
      +go tool api -c ..\api\go1.txt -next ..\api\next.txt
       if errorlevel 1 goto fail
       echo.
      

コアとなるコードの解説

src/cmd/api/goapi.goの変更は、go tool apiのAPI互換性チェックの挙動を根本的に変えるものです。

  1. failフラグとdeferによる終了コード制御: fail := falsedefer func() { if fail { os.Exit(1) } }()の組み合わせは、API互換性の問題が検出された場合に、プログラムが終了コード1で終了するようにします。これにより、all.bashのようなスクリプトがAPIチェックの失敗を検出し、ビルドプロセスを停止できるようになります。

  2. checkFileが指定されない場合の挙動: if *checkFile == ""のブロックは、-cフラグが指定されていない場合(つまり、APIの比較ではなく、現在のAPIを単にリストアップしたい場合)の挙動を定義します。この場合、現在のGoソースコードから抽出されたすべてのAPI機能が標準出力に書き出され、プログラムは正常終了します。

  3. required APIの読み込み: requiredスライスは、-cフラグで指定されたファイル(例: go1.txt)から読み込まれるAPIのリストを保持します。これは、Goの安定版リリースで保証されているAPIのセットを表します。fileFeaturesヘルパー関数がこの読み込みに使用されます。

  4. optional APIの読み込みとマップへの格納: optionalマップは、-nextフラグで指定されたファイル(例: next.txt)から読み込まれるAPIのリストを保持します。マップを使用することで、APIの存在チェック(optional[newFeature])を高速に行うことができます。これは、次期リリースで追加される予定のAPIのセットを表します。

  5. API比較のメインループ: for len(required) > 0 || len(features) > 0ループは、required(既存API)とfeatures(現在のAPI)の両方のリストが空になるまで続きます。両方のリストは事前にソートされているため、線形走査で効率的に比較できます。

    • len(features) == 0 || required[0] < features[0]: これは、requiredリストの現在のAPIがfeaturesリストの現在のAPIよりも辞書順で小さいか、featuresリストがすでに空であることを意味します。つまり、requiredに存在するAPIがfeaturesに存在しない、すなわちAPIが削除されたことを示します。この場合、-%s形式で出力され、fail = trueが設定されます。これは互換性破壊とみなされます。

    • len(required) == 0 || required[0] > features[0]: これは、featuresリストの現在のAPIがrequiredリストの現在のAPIよりも辞書順で小さいか、requiredリストがすでに空であることを意味します。つまり、featuresに存在するAPIがrequiredに存在しない、すなわち新しいAPIが追加されたことを示します。

      • if optional[newFeature]の条件で、新しく追加されたAPIがnext.txtに記載されているかどうかをチェックします。もし記載されていれば、それは既知の追加であるため、警告は出力されず、optionalマップからそのエントリが削除されます(これにより、next.txtのクリーンアップが必要なAPIを後で特定できます)。
      • もしnext.txtに記載されていない場合、+プレフィックス付きで出力されます。さらに、-allow_newフラグがfalseであれば、fail = trueが設定され、互換性破壊とみなされます。
    • default: これは、required[0] == features[0]の場合、つまり両方のリストに同じAPIが存在する場合です。APIに変更がないため、両方のリストからそのAPIをtake(削除)して、次の比較に進みます。

  6. next.txtに記載されているがAPIに存在しないものの検出: メインループの終了後、optionalマップに残っているエントリは、next.txtには記載されているものの、まだ実際のAPIには追加されていないAPIを示します。これらは(in next file, but not in API) -%sという形式で出力され、next.txtのメンテナンスが必要であることを開発者に通知します。

これらの変更により、go tool apiは、GoのAPI互換性チェックをより柔軟かつインテリジェントに行えるようになり、開発プロセスにおける不必要な警告を削減し、本当に重要な互換性問題に焦点を当てることができるようになりました。

関連リンク

参考にした情報源リンク