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

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

このコミットは、Go言語の公式リポジトリにおける cmd/api ツールに関連するものです。cmd/api は、Goの標準ライブラリのAPIサーフェスを検査し、互換性のない変更がないかを確認するために使用されるコマンドラインツールです。具体的には、goapi.go ファイルがこのツールの主要なロジックを含んでいます。このファイルは、異なるビルドコンテキスト(オペレーティングシステム、アーキテクチャ、Cgoの有効/無効など)におけるGoパッケージのAPIを分析し、APIの変更点を追跡する役割を担っています。

コミット

commit f430d0e6096093c9b21b05c48acabe4ab15f87cd
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Wed May 23 13:45:53 2012 -0700

    cmd/api: add flag to specify contexts
    
    I needed this to explore per-GOOS/GOARCH differences in pkg
    syscall for a recent CL.  Others may find it useful too.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6236046

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

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

元コミット内容

cmd/api: add flag to specify contexts

I needed this to explore per-GOOS/GOARCH differences in pkg
syscall for a recent CL.  Others may find it useful too.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6236046

変更の背景

この変更は、cmd/api ツールに新しいコマンドラインフラグ -contexts を追加するものです。コミットメッセージによると、作者のBrad Fitzpatrick氏は、pkg syscall の特定の変更セット(CL: Change List)において、異なる GOOS(オペレーティングシステム)と GOARCH(アーキテクチャ)の組み合わせによるAPIの違いを調査する必要がありました。

cmd/api ツールは、デフォルトで複数のビルドコンテキスト(例: Linux/386, Windows/amd64など)を考慮してAPIを分析します。しかし、特定のOS/アーキテクチャの組み合わせに限定して分析を行いたい場合や、デフォルトで含まれていない特定のコンテキストでAPIを検査したい場合に、既存の機能では対応できませんでした。

この新しいフラグ -contexts を導入することで、ユーザーはカンマ区切りの <goos>-<goarch>[-cgo] 形式で任意のビルドコンテキストを指定できるようになります。これにより、より柔軟かつ詳細なAPI分析が可能となり、特定の環境におけるAPIの挙動や互換性の問題を効率的に特定できるようになります。作者は、この機能が自分だけでなく、他の開発者にとっても有用であると考えています。

前提知識の解説

cmd/api ツール

cmd/api はGo言語のツールチェインの一部であり、Goの標準ライブラリのAPIサーフェスを検査するために使用されます。その主な目的は、Goのリリース間でAPIの互換性が維持されていることを確認することです。このツールは、Goのソースコードを解析し、エクスポートされた型、関数、メソッド、変数などのAPI要素を抽出し、それらを以前のバージョンのAPIと比較します。これにより、意図しないAPIの変更(特に破壊的変更)を検出することができます。

GOOSGOARCH

GOOS (Go Operating System) と GOARCH (Go Architecture) は、Goのビルドシステムで使用される環境変数です。これらは、GoプログラムがコンパイルされるターゲットのオペレーティングシステムとCPUアーキテクチャを指定します。

  • GOOS: linux, windows, darwin (macOS), freebsd, android, ios など。
  • GOARCH: amd64, 386, arm, arm64, ppc64, s390x など。

Goのソースコードには、これらの環境変数に基づいて条件付きでコンパイルされる部分(ビルドタグやファイル名サフィックスなど)が存在します。これにより、特定のOSやアーキテクチャに特化したコードを記述し、クロスプラットフォーム対応を実現しています。

build.Context

Goの go/build パッケージには Context という構造体があります。これは、Goのビルドプロセスにおける環境設定をカプセル化するものです。Context 構造体には、GOOS, GOARCH, CgoEnabled (Cgoが有効かどうか), Compiler (使用するコンパイラ) などのフィールドが含まれています。cmd/api ツールは、この build.Context のインスタンスを使用して、異なるターゲット環境でのAPIをシミュレートし、分析します。

cgo

cgo は、GoプログラムからC言語のコードを呼び出すためのGoの機能です。CgoEnabled フラグは、特定のビルドコンテキストで cgo が有効になっているかどうかを示します。cgo を使用するGoパッケージは、cgo が無効な環境ではコンパイルできないか、異なるAPIを提供する可能性があります。そのため、API分析において cgo の有効/無効を考慮することは重要です。

技術的詳細

このコミットの技術的詳細は、主に src/cmd/api/goapi.go ファイルへの変更に集約されます。

  1. 新しいフラグ -contexts の追加: flag.String を使用して、forceCtx という新しいグローバル変数が定義されました。これは、コマンドラインから -contexts フラグで渡される文字列(例: "linux-amd64,windows-386-cgo")を保持します。

    var (
        // ... 既存のフラグ定義 ...
        forceCtx  = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
    )
    
  2. デフォルトコンテキストの初期化ロジックの変更: 以前は init() 関数内で contexts スライス内の各 build.ContextCompiler フィールドが build.Default.Compiler に設定されていました。このコミットにより、この初期化ロジックは main() 関数内に移動され、-contexts フラグが指定された場合に新しいコンテキストが設定された後に実行されるようになりました。

    // 変更前:
    // func init() {
    // 	for _, c := range contexts {
    // 		c.Compiler = build.Default.Compiler
    // 	}
    // }
    
    // 変更後 (main関数内):
    // if *forceCtx != "" {
    // 	setContexts()
    // }
    // for _, c := range contexts {
    // 	c.Compiler = build.Default.Compiler
    // }
    
  3. parseContext 関数の追加: この新しい関数は、<goos>-<goarch>[-cgo] 形式の文字列を受け取り、対応する *build.Context オブジェクトを生成します。入力文字列をハイフンで分割し、GOOSGOARCH を抽出し、オプションで cgo が指定されていれば CgoEnabledtrue に設定します。不正な形式の文字列が渡された場合は log.Fatalf でプログラムを終了させます。

    func parseContext(c string) *build.Context {
        parts := strings.Split(c, "-")
        if len(parts) < 2 {
            log.Fatalf("bad context: %q", c)
        }
        bc := &build.Context{
            GOOS:   parts[0],
            GOARCH: parts[1],
        }
        if len(parts) == 3 {
            if parts[2] == "cgo" {
                bc.CgoEnabled = true
            } else {
                log.Fatalf("bad context: %q", c)
            }
        }
        return bc
    }
    
  4. setContexts 関数の追加: この関数は、-contexts フラグで指定されたカンマ区切りの文字列を解析し、グローバル変数 contexts を新しい *build.Context スライスで上書きします。strings.Split で文字列を分割し、各部分文字列を parseContext に渡して build.Context オブジェクトを生成し、それを contexts スライスに追加します。

    func setContexts() {
        contexts = []*build.Context{} // デフォルトコンテキストをクリア
        for _, c := range strings.Split(*forceCtx, ",") {
            contexts = append(contexts, parseContext(c))
        }
    }
    
  5. main 関数でのフラグ処理: main 関数内で flag.Parse() の後、*forceCtx (つまり -contexts フラグの値) が空文字列でない場合に setContexts() が呼び出されます。これにより、ユーザーが指定したコンテキストがデフォルトのコンテキストリストを上書きします。その後、contexts スライス内のすべての build.Context オブジェクトに対して Compiler フィールドが設定されます。

    func main() {
        flag.Parse()
    
        if *forceCtx != "" {
            setContexts()
        }
        for _, c := range contexts {
            c.Compiler = build.Default.Compiler
        }
    
        // ... 既存のロジック ...
    }
    

これらの変更により、cmd/api ツールは、ユーザーがコマンドラインから明示的に指定したビルドコンテキストに基づいてAPI分析を実行できるようになり、特定のプラットフォームやビルド設定に特化したAPIの差異を効率的に調査することが可能になりました。

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

diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 533636cd8a..b10a51c510 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -41,8 +41,11 @@ var (
 	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")
+	forceCtx  = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
 )
 
+// contexts are the default contexts which are scanned, unless
+// overridden by the -contexts flag.
 var contexts = []*build.Context{
 	{GOOS: "linux", GOARCH: "386", CgoEnabled: true},
 	{GOOS: "linux", GOARCH: "386"},
@@ -56,12 +59,6 @@ var contexts = []*build.Context{
 	{GOOS: "windows", GOARCH: "386"},
 }
 
-func init() {
-	for _, c := range contexts {
-		c.Compiler = build.Default.Compiler
-	}
-}
-
 func contextName(c *build.Context) string {
 	s := c.GOOS + "-" + c.GOARCH
 	if c.CgoEnabled {
@@ -70,9 +67,42 @@ func contextName(c *build.Context) string {
 	return s
 }
 
+func parseContext(c string) *build.Context {
+	parts := strings.Split(c, "-")
+	if len(parts) < 2 {
+		log.Fatalf("bad context: %q", c)
+	}
+	bc := &build.Context{
+		GOOS:   parts[0],
+		GOARCH: parts[1],
+	}
+	if len(parts) == 3 {
+		if parts[2] == "cgo" {
+			bc.CgoEnabled = true
+		} else {
+			log.Fatalf("bad context: %q", c)
+		}
+	}
+	return bc
+}
+
+func setContexts() {
+	contexts = []*build.Context{}
+	for _, c := range strings.Split(*forceCtx, ",") {
+		contexts = append(contexts, parseContext(c))
+	}
+}
+
 func main() {
 	flag.Parse()
 
+	if *forceCtx != "" {
+		setContexts()
+	}
+	for _, c := range contexts {
+		c.Compiler = build.Default.Compiler
+	}
+
 	var pkgs []string
  	if flag.NArg() > 0 {
  		pkgs = flag.Args()

コアとなるコードの解説

このコミットは、src/cmd/api/goapi.go ファイルに以下の主要な変更を加えています。

  1. forceCtx フラグの追加: var ( ブロック内に forceCtx という新しい string 型のフラグが追加されました。これは -contexts コマンドライン引数に対応し、ユーザーが指定するビルドコンテキストのリストをカンマ区切り文字列として受け取ります。

  2. init() 関数の削除と Compiler 設定の移動: 以前は init() 関数内で contexts スライス内の各 build.ContextCompiler フィールドを build.Default.Compiler に設定していました。この init() 関数は削除され、そのロジックは main() 関数内に移動されました。これにより、ユーザーが -contexts フラグで新しいコンテキストを指定した場合でも、正しく Compiler が設定されるようになります。

  3. parseContext(c string) *build.Context 関数の追加: この関数は、"goos-goarch" または "goos-goarch-cgo" 形式の文字列を解析し、対応する *build.Context オブジェクトを返します。

    • 入力文字列をハイフン (-) で分割します。
    • 少なくとも2つの部分(GOOSGOARCH)があることを確認します。そうでなければ、不正なコンテキストとしてプログラムを終了します。
    • build.Context の新しいインスタンスを作成し、GOOSGOARCH を設定します。
    • もし3番目の部分があり、それが "cgo" であれば、CgoEnabledtrue に設定します。それ以外の場合は、不正なコンテキストとしてプログラムを終了します。
  4. setContexts() 関数の追加: この関数は、forceCtx フラグ(ユーザーが -contexts で指定した文字列)を読み取り、それをカンマ (,) で分割します。分割された各部分文字列に対して parseContext を呼び出し、生成された *build.Context オブジェクトをグローバルな contexts スライスに追加します。これにより、デフォルトで定義されていた contexts スライスが、ユーザーが指定したコンテキストで完全に上書きされます。

  5. main() 関数内のロジック変更: flag.Parse() の呼び出し後、main() 関数はまず *forceCtx が空文字列でないか(つまり、ユーザーが -contexts フラグを指定したか)をチェックします。

    • もし指定されていれば、setContexts() を呼び出して、contexts スライスをユーザー定義のコンテキストで更新します。
    • その後、contexts スライス内のすべての build.Context オブジェクトに対して、Compiler フィールドを build.Default.Compiler に設定します。このステップは、init() 関数から移動されたものです。

これらの変更により、cmd/api ツールは、デフォルトのビルドコンテキストのセットに加えて、またはそれを完全に置き換えて、ユーザーが指定した特定のビルドコンテキストでAPI分析を実行する柔軟性を獲得しました。

関連リンク

参考にした情報源リンク