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

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

このコミットは、Go言語のAPI互換性チェックツールである cmd/api に、互換性の例外を定義するための except.txt ファイルを追加するものです。これにより、特定のAPI要素が変更または削除されても、ビルドが失敗しないように調整されます。特に、text/template/parse パッケージ内の要素が例外として指定されています。

コミット

commit d87d48895337392c9e0fb455cc9e3b08f7f45ce4
Author: Rob Pike <r@golang.org>
Date:   Thu Oct 4 11:35:17 2012 +1000

    cmd/api: add exception file
    Fixes build.
    
    R=golang-dev, adg, bradfitz, dsymonds, dave
    CC=golang-dev
    https://golang.org/cl/6586074

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

https://github.com/golang/go/commit/d87d48895337392c9e0fb455cc9e3b08f7f45ce4

元コミット内容

diff --git a/api/README b/api/README
index 6adc55454c..34b86efd99 100644
--- a/api/README
+++ b/api/README
@@ -5,6 +5,9 @@ Each file is a list of of API features, one per line.
 go1.txt (and similarly named files) are frozen once a version has been
 shipped. Each file adds new lines but does not remove any.
 
+except.txt lists features that may disappear without breaking
+true compatibility. The only package there is text/template/parse.
+
 next.txt is the only file intended to be mutated. It's a list of
 features that may be added to the next version. It only affects
 warning output from the go api tool.
diff --git a/api/except.txt b/api/except.txt
new file mode 100644
index 0000000000..e9fb24b466
--- /dev/null
+++ b/api/except.txt
@@ -0,0 +1,2 @@
+pkg text/template/parse, type DotNode bool
+pkg text/template/parse, type Node interface { Copy, String, Type }
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 7463e20d6d..391cbe76fa 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -38,11 +38,12 @@ import (
 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.
-\tcheckFile = flag.String("c", "", "optional filename to check API against")
-\tallowNew  = flag.Bool("allow_new", true, "allow API additions")
-\tnextFile  = 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.")
-\tverbose   = flag.Bool("v", false, "verbose debugging")
-\tforceCtx  = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
+\tcheckFile  = flag.String("c", "", "optional filename to check API against")
+\tallowNew   = flag.Bool("allow_new", true, "allow API additions")
+\texceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
+\tnextFile   = 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.")
+\tverbose    = flag.Bool("v", false, "verbose debugging")
+\tforceCtx   = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
 )
 
 // contexts are the default contexts which are scanned, unless
@@ -198,6 +199,13 @@ func main() {
 		}
 	}
 
+\tvar exception = make(map[string]bool) // exception => true
+\tif *exceptFile != "" {
+\t\tfor _, feature := range fileFeatures(*exceptFile) {
+\t\t\texception[feature] = true
+\t\t}\
+\t}
+\
 \ttake := func(sl *[]string) string {\n \t\ts := (*sl)[0]\n \t\t*sl = (*sl)[1:]\n@@ -207,8 +215,13 @@ func main() {\n \tfor len(required) > 0 || len(features) > 0 {\n \t\tswitch {\n \t\tcase len(features) == 0 || required[0] < features[0]:\n-\t\t\tfmt.Fprintf(bw, "-%s\\n", take(&required))\n-\t\t\tfail = true // broke compatibility\n+\t\t\tfeature := take(&required)\n+\t\t\tif exception[feature] {\n+\t\t\t\tfmt.Fprintf(bw, "~%s\\n", feature)\n+\t\t\t} else {\n+\t\t\t\tfmt.Fprintf(bw, "-%s\\n", feature)\n+\t\t\t\tfail = true // broke compatibility\n+\t\t\t}\n \t\tcase len(required) == 0 || required[0] > features[0]:\n \t\t\tnewFeature := take(&features)\n \t\t\tif optional[newFeature] {\ndiff --git a/src/run.bash b/src/run.bash
index f379ff5a70..1859555fb1 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -112,7 +112,7 @@ time go run run.go
 
 echo
 echo '# Checking API compatibility.'
-go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
+go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
 
 echo
 echo ALL TESTS PASSED

変更の背景

Go言語は、その安定したAPI互換性を非常に重視しています。これは、Go 1のリリース以降、既存のGoプログラムが新しいバージョンのGoコンパイラでコンパイルできなくなるような破壊的変更を避けるというGoチームの強いコミットメントに基づいています。この互換性保証は、Goエコシステムの健全性と開発者の信頼を維持するために不可欠です。

cmd/api ツールは、このAPI互換性を自動的にチェックするために使用されます。このツールは、Goの標準ライブラリのAPI定義を読み込み、以前のバージョン(例えば go1.txt に定義されているGo 1のAPI)と比較して、互換性のない変更(APIの削除やシグネチャの変更など)がないかを検証します。もし互換性のない変更が検出された場合、ツールはエラーを報告し、ビルドプロセスを失敗させることがあります。

しかし、開発の過程で、特定のパッケージやAPI要素が、互換性保証の対象外として扱われる必要がある場合があります。これは、例えば、内部的な実装の詳細であり、外部のユーザーが直接依存すべきではないAPIや、まだ安定していない実験的なAPIなどが該当します。このようなAPIが変更または削除された場合でも、cmd/api ツールがエラーを報告してビルドを中断するのを避けたいというニーズが生じます。

このコミットの背景には、text/template/parse パッケージ内の特定の型(DotNodeNode インターフェース)が、API互換性チェックの例外として扱われる必要があったという具体的な問題があります。これらの要素が変更された際にビルドが失敗するのを防ぐため、例外メカニズムを導入する必要がありました。コミットメッセージの「Fixes build.」という記述は、この問題が実際にビルドの失敗を引き起こしていたことを示唆しています。

前提知識の解説

Go言語のAPI互換性

Go言語は「Go 1 Compatibility Promise」という強力な互換性保証を持っています。これは、Go 1.xのリリースにおいて、Go 1で書かれたプログラムがGo 1.xの新しいバージョンでもコンパイルされ、実行されることを保証するものです。この保証は、Goの標準ライブラリの公開APIに適用されます。

cmd/api ツール

cmd/api は、Goの標準ライブラリのAPIがGo 1 Compatibility Promiseに準拠しているかを検証するための内部ツールです。このツールは、GoのソースコードからAPI情報を抽出し、既知の安定したAPI定義ファイル(例: api/go1.txt)と比較します。

  • api/go1.txt: Go 1のリリース時に確定されたAPIのリスト。このファイルに記載されているAPIは、将来のGoのバージョンでも変更されないことが保証されます。
  • api/next.txt: 次のリリースで追加される可能性のあるAPIのリスト。これは開発中のAPIであり、まだ安定性が保証されていません。cmd/api ツールは、このファイルに記載されたAPIの追加については警告を出しません。

cmd/api ツールは、APIの削除やシグネチャの変更など、互換性を損なう変更を検出するとエラーを報告します。

text/template/parse パッケージ

text/template パッケージは、Go言語でテキストテンプレートを解析・実行するための標準ライブラリです。このパッケージは、WebアプリケーションのHTML生成や、設定ファイルの動的な生成など、様々な用途で利用されます。

text/template/parse パッケージは、text/template パッケージの内部実装の一部であり、テンプレートの構文木(AST: Abstract Syntax Tree)を構築するための機能を提供します。通常、このパッケージはGoアプリケーション開発者が直接利用することは少なく、text/template パッケージを介して間接的に利用されます。そのため、text/template/parse パッケージの内部的な型やインターフェースは、Go 1 Compatibility Promiseの対象外とされることがあります。

技術的詳細

このコミットの主要な目的は、cmd/api ツールに「例外」の概念を導入することです。これにより、特定のAPI要素が削除または変更されても、ツールが互換性違反として報告しないように設定できるようになります。

except.txt ファイルの導入

  • 新しく api/except.txt ファイルが追加されました。このファイルには、API互換性チェックの例外として扱われるAPI要素が記述されます。
  • api/README が更新され、except.txt の目的が説明されています。「except.txt は、真の互換性を損なうことなく消滅する可能性のある機能をリストアップします。現在、唯一のパッケージは text/template/parse です。」と明記されています。
  • except.txt の初期内容として、pkg text/template/parse, type DotNode boolpkg text/template/parse, type Node interface { Copy, String, Type } が追加されています。これは、text/template/parse パッケージ内の DotNode 型と Node インターフェースが、互換性チェックの例外として扱われることを意味します。

cmd/api ツールの変更

  1. -except フラグの追加: src/cmd/api/goapi.go に新しいコマンドラインフラグ -except が追加されました。このフラグは、例外を定義するファイル(例: api/except.txt)のパスを指定するために使用されます。

    exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
    
  2. 例外マップの生成: main 関数内で、exceptFile フラグで指定されたファイルからAPI要素を読み込み、それらをキーとする map[string]bool 型の exception マップが作成されます。このマップは、特定のAPI要素が例外であるかどうかを効率的にチェックするために使用されます。

    var exception = make(map[string]bool) // exception => true
    if *exceptFile != "" {
        for _, feature := range fileFeatures(*exceptFile) {
            exception[feature] = true
        }
    }
    
  3. 互換性チェックロジックの変更: API互換性チェックのコアロジックが変更されました。以前は、required リスト(Go 1のAPIなど、必須とされるAPI)に存在するが features リスト(現在のAPI)に存在しないAPI要素が見つかった場合、即座に互換性違反としてエラー (fail = true) を報告していました。 新しいロジックでは、required から取り出された featureexception マップに存在するかどうかをチェックします。

    • もし exception[feature]true であれば、そのAPI要素は例外として扱われ、互換性違反とは見なされません。出力には - の代わりに ~ が付加され、互換性違反ではないが変更があったことを示します。
    • exception[feature]false であれば、以前と同様に互換性違反としてエラーが報告され、fail = true が設定されます。
    		case len(features) == 0 || required[0] < features[0]:
    			feature := take(&required)
    			if exception[feature] {
    				fmt.Fprintf(bw, "~%s\n", feature)
    			} else {
    				fmt.Fprintf(bw, "-%s\n", feature)
    				fail = true // broke compatibility
    			}
    

src/run.bash の変更

  • Goのテストスクリプトである src/run.bash が更新され、go tool api コマンドの実行時に新しく追加された -except フラグが使用されるようになりました。
    -go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
    +go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
    
    これにより、ビルドプロセスの一部としてAPI互換性チェックが実行される際に、api/except.txt に定義された例外が考慮されるようになります。

これらの変更により、text/template/parse パッケージ内の特定の型が変更または削除されても、cmd/api ツールがビルドを失敗させることなく、Goのビルドプロセスが正常に完了するようになりました。

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

  • api/README: except.txt の説明を追加。
  • api/except.txt: 新規ファイルとして追加。text/template/parse の例外を定義。
  • src/cmd/api/goapi.go:
    • -except コマンドラインフラグを追加。
    • except.txt から例外を読み込み、exception マップに格納するロジックを追加。
    • API互換性チェックロジックにおいて、例外を考慮するように変更。
  • src/run.bash: go tool api コマンドに -except $GOROOT/api/except.txt を追加。

コアとなるコードの解説

api/README の変更

api/README は、api ディレクトリ内のファイル群の目的を説明するドキュメントです。このコミットでは、except.txt が追加されたことに伴い、その役割が追記されました。これにより、except.txt が「真の互換性を損なうことなく消滅する可能性のある機能」をリストアップするものであることが明確に示されています。

api/except.txt の新規追加

このファイルは、cmd/api ツールが互換性チェックを行う際に無視すべきAPI要素を明示的に指定するために導入されました。ファイルの内容は以下の通りです。

pkg text/template/parse, type DotNode bool
pkg text/template/parse, type Node interface { Copy, String, Type }

これは、text/template/parse パッケージ内の DotNode 型と Node インターフェースが、API互換性チェックの例外として扱われることを意味します。これらの要素がGoの将来のバージョンで変更または削除されても、cmd/api ツールはエラーを報告しません。

src/cmd/api/goapi.go の変更

このファイルは cmd/api ツールの主要なロジックを含んでいます。

  1. exceptFile フラグの追加:

    exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
    

    flag.String を使用して、コマンドライン引数 -except を定義しています。これにより、ユーザーは例外ファイルを指定できるようになります。

  2. 例外マップの初期化と読み込み:

    var exception = make(map[string]bool) // exception => true
    if *exceptFile != "" {
        for _, feature := range fileFeatures(*exceptFile) {
            exception[feature] = true
        }
    }
    

    exception という名前の map[string]bool を作成し、exceptFile フラグが指定されている場合に、そのファイルから読み込んだ各API要素をマップのキーとして追加しています。fileFeatures 関数は、指定されたファイルからAPI要素のリストを読み込むユーティリティ関数です。このマップにより、特定のAPI要素が例外であるかをO(1)の計算量で高速にルックアップできるようになります。

  3. 互換性チェックロジックの修正:

    		case len(features) == 0 || required[0] < features[0]:
    			feature := take(&required)
    			if exception[feature] {
    				fmt.Fprintf(bw, "~%s\n", feature)
    			} else {
    				fmt.Fprintf(bw, "-%s\n", feature)
    				fail = true // broke compatibility
    			}
    

    この部分が、API互換性チェックの核心です。required リストからAPI要素 (feature) を一つ取り出し、それが現在のAPI (features) に存在しない場合(つまり、APIが削除された場合)の処理です。

    • if exception[feature] の条件で、取り出した feature が例外マップに存在するかどうかをチェックします。
    • もし存在すれば、fmt.Fprintf(bw, "~%s\n", feature) を実行します。これは、APIが削除されたが、それが例外であるため互換性違反とは見なさないことを示すために、~ プレフィックスを付けて出力します。fail = true は設定されません。
    • 存在しなければ、fmt.Fprintf(bw, "-%s\n", feature) を実行し、fail = true を設定します。これは、APIが削除され、それが互換性違反であることを示すために、- プレフィックスを付けて出力し、ビルドを失敗させます。

src/run.bash の変更

src/run.bash はGoのビルドおよびテストプロセスを自動化するためのシェルスクリプトです。

-go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
+go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt

この変更により、go tool api コマンドが実行される際に、$GOROOT/api/except.txt ファイルが例外リストとして渡されるようになります。これにより、Goの公式ビルドプロセスにおいて、text/template/parse パッケージの特定のAPI変更が互換性違反として扱われなくなり、ビルドが正常に完了するようになります。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語のAPI互換性チェックツールである cmd/api に、互換性の例外を定義するための except.txt ファイルを追加するものです。これにより、特定のAPI要素が変更または削除されても、ビルドが失敗しないように調整されます。特に、text/template/parse パッケージ内の要素が例外として指定されています。

コミット

commit d87d48895337392c9e0fb455cc9e3b08f7f45ce4
Author: Rob Pike <r@golang.org>
Date:   Thu Oct 4 11:35:17 2012 +1000

    cmd/api: add exception file
    Fixes build.
    
    R=golang-dev, adg, bradfitz, dsymonds, dave
    CC=golang-dev
    https://golang.org/cl/6586074

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

https://github.com/golang/go/commit/d87d48895337392c9e0fb455cc9e3b08f7f45ce4

元コミット内容

diff --git a/api/README b/api/README
index 6adc55454c..34b86efd99 100644
--- a/api/README
+++ b/api/README
@@ -5,6 +5,9 @@ Each file is a list of of API features, one per line.
 go1.txt (and similarly named files) are frozen once a version has been
 shipped. Each file adds new lines but does not remove any.
 
+except.txt lists features that may disappear without breaking
+true compatibility. The only package there is text/template/parse.
+
 next.txt is the only file intended to be mutated. It's a list of
 features that may be added to the next version. It only affects
 warning output from the go api tool.
diff --git a/api/except.txt b/api/except.txt
new file mode 100644
index 0000000000..e9fb24b466
--- /dev/null
+++ b/api/except.txt
@@ -0,0 +1,2 @@
+pkg text/template/parse, type DotNode bool
+pkg text/template/parse, type Node interface { Copy, String, Type }
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 7463e20d6d..391cbe76fa 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -38,11 +38,12 @@ import (
 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.
-\tcheckFile = flag.String("c", "", "optional filename to check API against")
-\tallowNew  = flag.Bool("allow_new", true, "allow API additions")
-\tnextFile  = 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.")
-\tverbose   = flag.Bool("v", false, "verbose debugging")
-\tforceCtx  = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
+\tcheckFile  = flag.String("c", "", "optional filename to check API against")
+\tallowNew   = flag.Bool("allow_new", true, "allow API additions")
+\texceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
+\tnextFile   = 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.")
+\tverbose    = flag.Bool("v", false, "verbose debugging")
+\tforceCtx   = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
 )
 
 // contexts are the default contexts which are scanned, unless
@@ -198,6 +199,13 @@ func main() {
 		}
 	}
 
+\tvar exception = make(map[string]bool) // exception => true
+\tif *exceptFile != "" {
+\t\tfor _, feature := range fileFeatures(*exceptFile) {
+\t\t\texception[feature] = true
+\t\t}\
+\t}
+\
 \ttake := func(sl *[]string) string {\n \t\ts := (*sl)[0]\n \t\t*sl = (*sl)[1:]\n@@ -207,8 +215,13 @@ func main() {\n \tfor len(required) > 0 || len(features) > 0 {\n \t\tswitch {\n \t\tcase len(features) == 0 || required[0] < features[0]:\n-\t\t\tfmt.Fprintf(bw, "-%s\\n", take(&required))\n-\t\t\tfail = true // broke compatibility\n+\t\t\tfeature := take(&required)\n+\t\t\tif exception[feature] {\n+\t\t\t\tfmt.Fprintf(bw, "~%s\\n", feature)\n+\t\t\t} else {\n+\t\t\t\tfmt.Fprintf(bw, "-%s\\n", feature)\n+\t\t\t\tfail = true // broke compatibility\n+\t\t\t}\n \t\tcase len(required) == 0 || required[0] > features[0]:\n \t\t\tnewFeature := take(&features)\n \t\t\tif optional[newFeature] {\ndiff --git a/src/run.bash b/src/run.bash
index f379ff5a70..1859555fb1 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -112,7 +112,7 @@ time go run run.go
 
 echo
 echo '# Checking API compatibility.'
-go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
+go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
 
 echo
 echo ALL TESTS PASSED

変更の背景

Go言語は、その安定したAPI互換性を非常に重視しています。これは、Go 1のリリース以降、既存のGoプログラムが新しいバージョンのGoコンパイラでコンパイルできなくなるような破壊的変更を避けるというGoチームの強いコミットメントに基づいています。この互換性保証は、Goエコシステムの健全性と開発者の信頼を維持するために不可欠です。

cmd/api ツールは、このAPI互換性を自動的にチェックするために使用されます。このツールは、Goの標準ライブラリのAPI定義を読み込み、以前のバージョン(例えば go1.txt に定義されているGo 1のAPI)と比較して、互換性のない変更(APIの削除やシグネチャの変更など)がないかを検証します。もし互換性のない変更が検出された場合、ツールはエラーを報告し、ビルドプロセスを失敗させることがあります。

しかし、開発の過程で、特定のパッケージやAPI要素が、互換性保証の対象外として扱われる必要がある場合があります。これは、例えば、内部的な実装の詳細であり、外部のユーザーが直接依存すべきではないAPIや、まだ安定していない実験的なAPIなどが該当します。このようなAPIが変更または削除された場合でも、cmd/api ツールがエラーを報告してビルドを中断するのを避けたいというニーズが生じます。

このコミットの背景には、text/template/parse パッケージ内の特定の型(DotNodeNode インターフェース)が、API互換性チェックの例外として扱われる必要があったという具体的な問題があります。これらの要素が変更された際にビルドが失敗するのを防ぐため、例外メカニズムを導入する必要がありました。コミットメッセージの「Fixes build.」という記述は、この問題が実際にビルドの失敗を引き起こしていたことを示唆しています。

前提知識の解説

Go言語のAPI互換性

Go言語は「Go 1 Compatibility Promise」という強力な互換性保証を持っています。これは、Go 1.xのリリースにおいて、Go 1で書かれたプログラムがGo 1.xの新しいバージョンでもコンパイルされ、実行されることを保証するものです。この保証は、Goの標準ライブラリの公開APIに適用されます。

cmd/api ツール

cmd/api は、Goの標準ライブラリのAPIがGo 1 Compatibility Promiseに準拠しているかを検証するための内部ツールです。このツールは、GoのソースコードからAPI情報を抽出し、既知の安定したAPI定義ファイル(例: api/go1.txt)と比較します。

  • api/go1.txt: Go 1のリリース時に確定されたAPIのリスト。このファイルに記載されているAPIは、将来のGoのバージョンでも変更されないことが保証されます。
  • api/next.txt: 次のリリースで追加される可能性のあるAPIのリスト。これは開発中のAPIであり、まだ安定性が保証されていません。cmd/api ツールは、このファイルに記載されたAPIの追加については警告を出しません。

cmd/api ツールは、APIの削除やシグネチャの変更など、互換性を損なう変更を検出するとエラーを報告します。

text/template/parse パッケージ

text/template パッケージは、Go言語でテキストテンプレートを解析・実行するための標準ライブラリです。このパッケージは、WebアプリケーションのHTML生成や、設定ファイルの動的な生成など、様々な用途で利用されます。

text/template/parse パッケージは、text/template パッケージの内部実装の一部であり、テンプレートの構文木(AST: Abstract Syntax Tree)を構築するための機能を提供します。通常、このパッケージはGoアプリケーション開発者が直接利用することは少なく、text/template パッケージを介して間接的に利用されます。そのため、text/template/parse パッケージの内部的な型やインターフェースは、Go 1 Compatibility Promiseの対象外とされることがあります。

技術的詳細

このコミットの主要な目的は、cmd/api ツールに「例外」の概念を導入することです。これにより、特定のAPI要素が削除または変更されても、ツールが互換性違反として報告しないように設定できるようになります。

except.txt ファイルの導入

  • 新しく api/except.txt ファイルが追加されました。このファイルには、API互換性チェックの例外として扱われるAPI要素が記述されます。
  • api/README が更新され、except.txt の目的が説明されています。「except.txt は、真の互換性を損なうことなく消滅する可能性のある機能をリストアップします。現在、唯一のパッケージは text/template/parse です。」と明記されています。
  • except.txt の初期内容として、pkg text/template/parse, type DotNode boolpkg text/template/parse, type Node interface { Copy, String, Type } が追加されています。これは、text/template/parse パッケージ内の DotNode 型と Node インターフェースが、互換性チェックの例外として扱われることを意味します。

cmd/api ツールの変更

  1. -except フラグの追加: src/cmd/api/goapi.go に新しいコマンドラインフラグ -except が追加されました。このフラグは、例外を定義するファイル(例: api/except.txt)のパスを指定するために使用されます。

    exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
    
  2. 例外マップの生成: main 関数内で、exceptFile フラグで指定されたファイルからAPI要素を読み込み、それらをキーとする map[string]bool 型の exception マップが作成されます。このマップは、特定のAPI要素が例外であるかどうかを効率的にチェックするために使用されます。

    var exception = make(map[string]bool) // exception => true
    if *exceptFile != "" {
        for _, feature := range fileFeatures(*exceptFile) {
            exception[feature] = true
        }
    }
    
  3. 互換性チェックロジックの変更: API互換性チェックのコアロジックが変更されました。以前は、required リスト(Go 1のAPIなど、必須とされるAPI)に存在するが features リスト(現在のAPI)に存在しないAPI要素が見つかった場合、即座に互換性違反としてエラー (fail = true) を報告していました。 新しいロジックでは、required から取り出された featureexception マップに存在するかどうかをチェックします。

    • もし exception[feature]true であれば、そのAPI要素は例外として扱われ、互換性違反とは見なされません。出力には - の代わりに ~ が付加され、互換性違反ではないが変更があったことを示します。
    • exception[feature]false であれば、以前と同様に互換性違反としてエラーが報告され、fail = true が設定されます。
    		case len(features) == 0 || required[0] < features[0]:
    			feature := take(&required)
    			if exception[feature] {
    				fmt.Fprintf(bw, "~%s\n", feature)
    			} else {
    				fmt.Fprintf(bw, "-%s\n", feature)
    				fail = true // broke compatibility
    			}
    

src/run.bash の変更

  • Goのテストスクリプトである src/run.bash が更新され、go tool api コマンドの実行時に新しく追加された -except フラグが使用されるようになりました。
    -go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
    +go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
    
    これにより、ビルドプロセスの一部としてAPI互換性チェックが実行される際に、api/except.txt に定義された例外が考慮されるようになります。

これらの変更により、text/template/parse パッケージ内の特定の型が変更または削除されても、cmd/api ツールがビルドを失敗させることなく、Goのビルドプロセスが正常に完了するようになりました。

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

  • api/README: except.txt の説明を追加。
  • api/except.txt: 新規ファイルとして追加。text/template/parse の例外を定義。
  • src/cmd/api/goapi.go:
    • -except コマンドラインフラグを追加。
    • except.txt から例外を読み込み、exception マップに格納するロジックを追加。
    • API互換性チェックロジックにおいて、例外を考慮するように変更。
  • src/run.bash: go tool api コマンドに -except $GOROOT/api/except.txt を追加。

コアとなるコードの解説

api/README の変更

api/README は、api ディレクトリ内のファイル群の目的を説明するドキュメントです。このコミットでは、except.txt が追加されたことに伴い、その役割が追記されました。これにより、except.txt が「真の互換性を損なうことなく消滅する可能性のある機能」をリストアップするものであることが明確に示されています。

api/except.txt の新規追加

このファイルは、cmd/api ツールが互換性チェックを行う際に無視すべきAPI要素を明示的に指定するために導入されました。ファイルの内容は以下の通りです。

pkg text/template/parse, type DotNode bool
pkg text/template/parse, type Node interface { Copy, String, Type }

これは、text/template/parse パッケージ内の DotNode 型と Node インターフェースが、API互換性チェックの例外として扱われることを意味します。これらの要素がGoの将来のバージョンで変更または削除されても、cmd/api ツールはエラーを報告しません。

src/cmd/api/goapi.go の変更

このファイルは cmd/api ツールの主要なロジックを含んでいます。

  1. exceptFile フラグの追加:

    exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
    

    flag.String を使用して、コマンドライン引数 -except を定義しています。これにより、ユーザーは例外ファイルを指定できるようになります。

  2. 例外マップの初期化と読み込み:

    var exception = make(map[string]bool) // exception => true
    if *exceptFile != "" {
        for _, feature := range fileFeatures(*exceptFile) {
            exception[feature] = true
        }
    }
    

    exception という名前の map[string]bool を作成し、exceptFile フラグが指定されている場合に、そのファイルから読み込んだ各API要素をマップのキーとして追加しています。fileFeatures 関数は、指定されたファイルからAPI要素のリストを読み込むユーティリティ関数です。このマップにより、特定のAPI要素が例外であるかをO(1)の計算量で高速にルックアップできるようになります。

  3. 互換性チェックロジックの修正:

    		case len(features) == 0 || required[0] < features[0]:
    			feature := take(&required)
    			if exception[feature] {
    				fmt.Fprintf(bw, "~%s\n", feature)
    			} else {
    				fmt.Fprintf(bw, "-%s\n", feature)
    				fail = true // broke compatibility
    			}
    

    この部分が、API互換性チェックの核心です。required リストからAPI要素 (feature) を一つ取り出し、それが現在のAPI (features) に存在しない場合(つまり、APIが削除された場合)の処理です。

    • if exception[feature] の条件で、取り出した feature が例外マップに存在するかどうかをチェックします。
    • もし存在すれば、fmt.Fprintf(bw, "~%s\n", feature) を実行します。これは、APIが削除されたが、それが例外であるため互換性違反とは見なさないことを示すために、~ プレフィックスを付けて出力します。fail = true は設定されません。
    • 存在しなければ、fmt.Fprintf(bw, "-%s\n", feature) を実行し、fail = true を設定します。これは、APIが削除され、それが互換性違反であることを示すために、- プレフィックスを付けて出力し、ビルドを失敗させます。

src/run.bash の変更

src/run.bash はGoのビルドおよびテストプロセスを自動化するためのシェルスクリプトです。

-go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
+go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt

この変更により、go tool api コマンドが実行される際に、$GOROOT/api/except.txt ファイルが例外リストとして渡されるようになります。これにより、Goの公式ビルドプロセスにおいて、text/template/parse パッケージの特定のAPI変更が互換性違反として扱われなくなり、ビルドが正常に完了するようになります。

関連リンク

参考にした情報源リンク