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

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

このコミットは、Go言語のmisc/cgo/stdioパッケージにおいて、標準出力(stdout)と標準エラー出力(stderr)の扱いを改善するための変更です。特に、NetBSDやOpenBSDといった一部のプラットフォームでのCgo(GoとC言語の相互運用機能)の挙動に対応するため、stdoutstderrの定義を別のファイルに分離しています。これにより、プラットフォーム固有のビルドタグを用いて、異なる環境でのstdout/stderrの取り扱いを柔軟に調整できるようになります。

コミット

commit b945b741e15b09f98ae6298d9698bd50a806267a
Author: Joel Sing <jsing@google.com>
Date:   Tue Jun 5 01:38:55 2012 +1000

    misc/cgo/stdio: split stdout/stderr into a separate file
    
    Split stdout/stderr into a separate file so that can be handled
    differently on some platforms. Both NetBSD and OpenBSD have defines
    for stdout/stderr that require some coercion in order for cgo to
    handle them correctly.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6247062

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

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

元コミット内容

misc/cgo/stdio: split stdout/stderr into a separate file

Split stdout/stderr into a separate file so that can be handled differently on some platforms. Both NetBSD and OpenBSD have defines for stdout/stderr that require some coercion in order for cgo to handle them correctly.

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

変更の背景

この変更の主な背景は、Go言語のCgo機能が、NetBSDおよびOpenBSDという特定のオペレーティングシステム上で、C標準ライブラリのstdoutおよびstderrの定義を正しく扱えないという問題にありました。

通常、C言語ではstdio.hをインクルードすることでstdoutstderrというグローバルなファイルポインタが提供され、これらは標準出力と標準エラー出力へのアクセスを可能にします。しかし、NetBSDやOpenBSDのような一部のシステムでは、これらの定義が他のシステムとは異なる方法で、あるいはCgoが直接解釈しにくい形で提供されている場合があります。例えば、マクロとして定義されていたり、特定の型変換が必要であったりするケースが考えられます。

CgoはGoコードからCコードを呼び出すためのメカニズムであり、Cの型や変数、関数をGoの型にマッピングする必要があります。stdoutstderrのような特殊なグローバル変数に対して、プラットフォーム固有の定義が存在すると、CgoがそれらをGoの*C.FILE型に正しくバインドする際に問題が発生することがありました。

この問題を解決するため、コミットではstdoutstderrのGo側での定義を、プラットフォーム固有のビルドタグ(+buildディレクティブ)を使用して分離することを提案しています。これにより、NetBSD以外のシステムでは通常の定義を使用し、NetBSD(およびOpenBSD)では異なる、あるいは特別な処理を施した定義を使用できるようになります。このアプローチは、コードベース全体に複雑な条件分岐を導入することなく、特定のプラットフォームの差異を吸収するためのGoの標準的な手法です。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識が必要です。

  1. Cgo: Go言語には、C言語のコードをGoプログラムから呼び出すためのメカニズムであるCgoが組み込まれています。Cgoを使用すると、Goのソースファイル内にCのコードを直接記述したり、既存のCライブラリをリンクしたりすることができます。GoとCの間でデータをやり取りする際には、型変換やメモリ管理に関する考慮が必要です。Cgoは、import "C"という特別なインポート宣言によって有効になります。Cgoは、Goのビルドプロセス中にCコンパイラ(通常はGCCやClang)を呼び出し、Cコードをコンパイルし、Goのオブジェクトファイルとリンクします。

  2. 標準入出力(Standard I/O): ほとんどのプログラミング言語やオペレーティングシステムにおいて、プログラムはデフォルトで3つの標準ストリームにアクセスできます。

    • 標準入力 (stdin): プログラムがデータを読み込むための入力ストリーム。通常はキーボードに接続されています。
    • 標準出力 (stdout): プログラムが通常の出力(結果や情報)を書き込むための出力ストリーム。通常は画面(コンソール)に接続されています。
    • 標準エラー出力 (stderr): プログラムがエラーメッセージや診断情報を書き込むための出力ストリーム。通常は画面(コンソール)に接続されていますが、stdoutとは独立してリダイレクトできます。 C言語では、これらはFILE*型のグローバル変数stdin, stdout, stderrとしてstdio.hヘッダで定義されています。
  3. FILE: C言語の標準ライブラリにおいて、FILE型はファイルやストリームを表現するための構造体です。fopen関数などでファイルを開くと、このFILE型へのポインタが返され、fprintffreadなどの関数でそのファイルやストリームに対する操作を行います。stdoutstderrも内部的にはFILE*型として扱われます。

  4. ビルドタグ(Build Tags): Go言語には、特定の条件に基づいてソースファイルをコンパイルに含めるか除外するかを制御するための「ビルドタグ」という機能があります。ソースファイルの先頭に// +build tag_nameのようなコメントを記述することで、go buildコマンドがそのタグが有効な場合にのみファイルをコンパイル対象とします。これは、オペレーティングシステム(例: linux, windows, darwin, netbsd, openbsd)、アーキテクチャ(例: amd64, arm)、またはカスタムタグに基づいてコードを条件付きでコンパイルする際に非常に有用です。このコミットでは、+build !netbsdというタグが使用されており、これは「NetBSD以外のシステムでこのファイルをコンパイルする」という意味になります。

  5. C.stdoutC.stderr: Cgoを使用する際、import "C"と記述すると、C言語のグローバル変数や関数にC.プレフィックスを付けてGoコードからアクセスできるようになります。したがって、C.stdoutはC言語のstdoutグローバル変数に対応し、C.stderrstderrに対応します。Goの*File型(CのFILE型をラップしたもの)にキャストすることで、Goの型システム内でこれらのCのポインタを扱うことができます。

技術的詳細

このコミットの技術的な核心は、GoのCgoがC言語のstdoutおよびstderrを扱う際のプラットフォーム依存の問題を、Goのビルドタグとファイル分割によって解決する点にあります。

問題の特定: NetBSDやOpenBSDのような一部のUNIX系システムでは、stdio.hで定義されるstdoutstderrが、他のシステム(LinuxやmacOSなど)とは異なる方法で実装されていることがあります。例えば、これらが単なるFILE*型の変数ではなく、マクロや特殊なリンケージを持つシンボルとして定義されている場合、CgoがGoの型システムにこれらを正しくマッピングする際に「強制(coercion)」が必要になる、つまり、特別な型変換やリンケージの考慮が必要になることを意味します。Cgoの内部的な処理が、これらのプラットフォーム固有のstdout/stderrの定義をそのままではGoの*C.FILEとして解釈できない、あるいはコンパイルエラーを引き起こす可能性がありました。

解決策: コミットは、この問題を解決するために以下の戦略を採用しています。

  1. stdout/stderr定義の分離: 既存のmisc/cgo/stdio/file.goからStdoutStderrの定義を削除し、新たにmisc/cgo/stdio/stdio.goというファイルを作成してそこに移動させます。

  2. ビルドタグの活用: 新しく作成されたmisc/cgo/stdio/stdio.goファイルの先頭に、// +build !netbsdというビルドタグを追加します。このタグは、Goコンパイラに対して「このファイルはNetBSD以外のすべてのオペレーティングシステムでコンパイル対象とする」という指示を与えます。 これにより、NetBSDシステムでビルドされる際にはstdio.goはコンパイルされず、stdoutstderrの定義は別の方法で提供されるか、あるいはNetBSD固有のstdio.go(このコミットでは示されていないが、将来的に追加される可能性のあるファイル)がコンパイルされることになります。

  3. プラットフォーム固有の対応の抽象化: このアプローチにより、Goの標準ライブラリは、stdoutstderrの定義に関するプラットフォーム間の差異を透過的に扱うことができます。NetBSDやOpenBSDのような特殊なケースでは、これらのプラットフォーム向けに特別に調整されたCgoのバインディングや、Goのコードが提供されることになります。これにより、Goのコードベースはよりクリーンに保たれ、プラットフォーム固有の複雑さが特定のファイルにカプセル化されます。

技術的な影響:

  • クロスプラットフォーム互換性の向上: Cgoを使用するGoプログラムが、より多くのオペレーティングシステムで安定して動作するようになります。
  • コードの保守性の向上: プラットフォーム固有のロジックが分離されるため、コードの可読性と保守性が向上します。
  • ビルドプロセスの最適化: 不要なコードが特定のプラットフォームでコンパイルされないため、ビルド時間がわずかに短縮される可能性があります。

この変更は、Go言語が多様な環境で動作するための基盤を強化する、典型的なクロスプラットフォーム対応の改善例と言えます。

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

このコミットでは、以下の2つのファイルが変更されています。

  1. misc/cgo/stdio/file.go: このファイルからStdoutStderrの変数定義が削除されました。

    --- a/misc/cgo/stdio/file.go
    +++ b/misc/cgo/stdio/file.go
    @@ -23,9 +23,6 @@ import "unsafe"
     
     type File C.FILE
     
    -var Stdout = (*File)(C.stdout)
    -var Stderr = (*File)(C.stderr)
    -
     // Test reference to library symbol.
     // Stdout and stderr are too special to be a reliable test.
     //var  = C.environ
    
  2. misc/cgo/stdio/stdio.go: このファイルが新規作成され、削除されたStdoutStderrの変数定義がここに移されました。また、ファイルの先頭にはビルドタグ// +build !netbsdが追加されています。

    --- /dev/null
    +++ b/misc/cgo/stdio/stdio.go
    @@ -0,0 +1,15 @@
    +// Copyright 2009 The Go Authors.  All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +// +build !netbsd
    +
    +package stdio
    +
    +/*
    +#include <stdio.h>
    +*/
    +import "C"
    +
    +var Stdout = (*File)(C.stdout)
    +var Stderr = (*File)(C.stderr)
    

コアとなるコードの解説

misc/cgo/stdio/file.go からの変更

file.goは、C言語のFILE型をGoのFile型としてラップし、Cgoを通じてCのファイル操作を行うための基本的な定義を提供していたと考えられます。このコミット以前は、StdoutStderrというGoの変数もこのファイル内で定義され、それぞれCのstdoutstderrにCgo経由でバインドされていました。

// 削除されたコード
var Stdout = (*File)(C.stdout)
var Stderr = (*File)(C.stderr)

これらの行が削除されたのは、stdoutstderrの定義がプラットフォームによって異なる挙動を示すため、より柔軟な対応が必要になったからです。特に、NetBSDやOpenBSDのようなシステムでは、C.stdoutC.stderrの直接的なバインディングが問題を引き起こす可能性がありました。

misc/cgo/stdio/stdio.go の新規作成と内容

新しく作成されたstdio.goファイルは、stdoutstderrの定義を専門に扱うためのものです。

// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !netbsd

package stdio

/*
#include <stdio.h>
*/
import "C"

var Stdout = (*File)(C.stdout)
var Stderr = (*File)(C.stderr)

このファイルの重要な点は以下の通りです。

  1. // +build !netbsd: これはGoのビルドタグです。この行は、Goコンパイラに対して「このファイルはNetBSD以外のすべてのオペレーティングシステムでコンパイル対象とする」という指示を与えます。つまり、Linux、macOS、Windowsなどではこのファイルがコンパイルされますが、NetBSDシステムでGoプログラムをビルドする際には、このstdio.goファイルは無視されます。これにより、NetBSD固有のstdout/stderrの取り扱いが必要な場合に、別のファイル(このコミットでは示されていないが、将来的に追加される可能性のあるNetBSD専用のstdio.goのようなファイル)でそのロジックを実装できるようになります。

  2. package stdio: file.goと同じstdioパッケージに属しています。これにより、パッケージ内の他のファイルと連携して機能します。

  3. CgoインポートとCヘッダのインクルード:

    /*
    #include <stdio.h>
    */
    import "C"
    

    これはCgoの構文です。import "C"はCgoを有効にし、その直前のコメントブロック内に記述されたCコード(この場合は#include <stdio.h>)をGoコードから利用できるようにします。stdio.hをインクルードすることで、C言語のstdoutstderrといった標準入出力関連の定義がGoからアクセス可能になります。

  4. StdoutStderrの定義:

    var Stdout = (*File)(C.stdout)
    var Stderr = (*File)(C.stderr)
    

    これらの行は、C言語のstdoutstderrというグローバルなFILE*ポインタを、Goの*File型(file.goで定義されているtype File C.FILE)にキャストして、Goの変数StdoutStderrに割り当てています。これにより、Goプログラム内でCの標準出力/エラー出力ストリームをGoの型として扱うことができるようになります。

この変更により、GoのCgoは、プラットフォーム固有のstdout/stderrの定義の差異を、ビルドタグによるファイル選択というGoの標準的なメカニズムで吸収できるようになり、より堅牢なクロスプラットフォーム対応が実現されました。

関連リンク

参考にした情報源リンク

  • golang.org/cl/6247062: このコミットに対応するGoのコードレビューシステム(Gerrit)上のチェンジリストです。コミットメッセージに記載されています。 https://golang.org/cl/6247062

  • GitHub上のコミットページ: この解説の冒頭にも記載されている、GitHub上の実際のコミットページです。 https://github.com/golang/go/commit/b945b741e15b09f98ae6298d9698bd50a806267a

  • C言語の標準入出力に関する一般的な情報: stdout, stderr, stdio.hなどに関する一般的なC言語のドキュメントやチュートリアル。 (例: C言語の標準ライブラリに関するWikipedia記事やC言語のチュートリアルサイトなど)

  • NetBSD/OpenBSDのCライブラリに関する情報: NetBSDやOpenBSDにおけるstdio.hの実装や、stdout/stderrの定義に関する詳細な情報。これは通常、各OSのソースコードや開発者向けドキュメントで確認できます。 (例: NetBSDのlibcソースコード、OpenBSDのlibcソースコードなど) ※具体的なリンクは、特定のバージョンや実装に依存するため、ここでは一般的な情報源としています。