gccgo のセットアップと使用方法
このドキュメントでは、Go 言語のコンパイラである gccgo の使用方法を説明します。gccgo コンパイラは、広く使用されている GNU コンパイラである GCC の新しいフロントエンドです。フロントエンド自体は BSD スタイルのライセンスの下にありますが、gccgo は通常 GCC の一部として使用され、その場合は GNU General Public License の対象となります(ライセンスは GCC の一部としての gccgo 自体を対象としており、gccgo によって生成されたコードは対象としていません)。
gccgo は gc コンパイラではないことに注意してください。そのコンパイラについては、Go のインストール の手順を参照してください。
リリース
gccgo をインストールする最も簡単な方法は、Go サポートを含むようにビルドされた GCC バイナリリリースをインストールすることです。GCC バイナリリリースは さまざまなウェブサイト から入手でき、通常は GNU/Linux ディストリビューションの一部として含まれています。これらのバイナリをビルドするほとんどの人は Go サポートを含めると予想されます。
GCC 4.7.1 リリースおよびそれ以降の 4.7 リリースには、完全な Go 1 コンパイラとライブラリが含まれています。
タイミング上の理由から、GCC 4.8.0 および 4.8.1 リリースは Go 1.1 に近いですが同一ではありません。GCC 4.8.2 リリースには完全な Go 1.1.2 実装が含まれています。
GCC 4.9 リリースには、完全な Go 1.2 実装が含まれています。
GCC 5 リリースには、Go 1.4 ユーザーライブラリの完全な実装が含まれています。Go 1.4 ランタイムは完全にマージされていませんが、Go プログラムからは見えないはずです。
GCC 6 リリースには、Go 1.6.1 ユーザーライブラリの完全な実装が含まれています。Go 1.6 ランタイムは完全にマージされていませんが、Go プログラムからは見えないはずです。
GCC 7 リリースには、Go 1.8.1 ユーザーライブラリの完全な実装が含まれています。以前のリリースと同様に、Go 1.8 ランタイムは完全にマージされていませんが、Go プログラムからは見えないはずです。
GCC 8 リリースには、Go 1.10.1 リリースの完全な実装が含まれています。Go 1.10 ランタイムは GCC 開発ソースに完全にマージされ、並行ガベージコレクションが完全にサポートされています。
GCC 9 リリースには、Go 1.12.2 リリースの完全な実装が含まれています。
GCC 10 リリースには、Go 1.14.6 リリースの完全な実装が含まれています。
GCC 11 リリースには、Go 1.16.3 リリースの完全な実装が含まれています。
GCC 12 および 13 リリースには、Go 1.18 標準ライブラリの完全な実装が含まれています。ただし、GCC はまだジェネリックのサポートを含んでいません。
ソースコード
リリースを使用できない場合、または自分で gccgo をビルドしたい場合は、gccgo のソースコードは Git 経由でアクセスできます。GCC のウェブサイトには GCC ソースコードの取得手順 があります。gccgo のソースコードも含まれています。便宜上、Go サポートの安定版は、メインの GCC コードリポジトリの devel/gccgo ブランチで利用できます: git://gcc.gnu.org/git/gcc.git。このブランチは定期的に安定した Go コンパイラのソースで更新されます。
gcc.gnu.org は Go フロントエンドのソースコードを入手するのに最も便利な方法ですが、マスターソースの場所ではないことに注意してください。Go フロントエンドコンパイラに貢献したい場合は、gccgo への貢献 を参照してください。
ビルド
gccgo のビルドは、1つまたは2つの追加オプションを除いて GCC のビルドと同じです。詳細は gcc ウェブサイトの指示 を参照してください。configure を実行するときは、オプション --enable-languages=c,c++,go (ビルドしたい他の言語も一緒に) を追加してください。32ビット x86 をターゲットにしている場合は、ロックされた比較交換命令をサポートするように gccgo をデフォルトでビルドしたいと思うでしょう。そのためには、configure オプション --with-arch=i586 (またはプログラムの実行が必要な場所に応じて新しいアーキテクチャ) も使用してください。64ビット x86 をターゲットにしているが、時々 -m32 オプションを使用したい場合は、configure オプション --with-arch-32=i586 を使用してください。
Gold
x86 GNU/Linux システムでは、gccgo コンパイラはゴルーチン用に小さな非連続スタックを使用できます。これにより、各ゴルーチンが比較的少量のスタックを使用できるため、プログラムはより多くのゴルーチンを実行できます。これには、gold リンカバージョン 2.22 以降を使用する必要があります。GNU binutils 2.22 以降をインストールするか、自分で gold をビルドすることができます。
自分で gold をビルドするには、configure スクリプトを実行するときに --enable-gold=default を使用して GNU binutils をビルドします。ビルドする前に、flex と bison パッケージをインストールする必要があります。一般的な手順は次のようになります (/opt/gold を書き込みアクセス権のある任意のディレクトリに置き換えることができます)
git clone git://sourceware.org/git/binutils-gdb.git mkdir binutils-objdir cd binutils-objdir ../binutils-gdb/configure --enable-gold=default --prefix=/opt/gold make make install
gold をどのようにインストールする場合でも、gccgo を構成するときは、オプション --with-ld=GOLD_BINARY を使用してください。
前提条件
GCC をビルドするには、gcc ウェブサイト に記載されているように、多くの前提条件が必要です。gcc configure スクリプトを実行する前に、すべての前提条件をインストールすることが重要です。前提条件ライブラリは、GCC ソースのスクリプト contrib/download_prerequisites を使用して簡単にダウンロードできます。
ビルドコマンド
すべての前提条件がインストールされたら、一般的なビルドおよびインストール手順は次のようになります(上記で説明したように gold リンカを使用している場合にのみ --with-ld オプションを使用してください)
git clone --branch devel/gccgo git://gcc.gnu.org/git/gcc.git gccgo mkdir objdir cd objdir ../gccgo/configure --prefix=/opt/gccgo --enable-languages=c,c++,go --with-ld=/opt/gold/bin/ld make make install
gccgo の使用
gccgo コンパイラは他の gcc フロントエンドと同様に動作します。GCC 5 以降、gccgo のインストールには go コマンドのバージョンも含まれており、https://go.dokyumento.jp/cmd/go で説明されているように Go プログラムをビルドするために使用できます。
go コマンドを使用せずにファイルをコンパイルする
gccgo -c file.go
これにより file.o が生成されます。ファイルをリンクして実行可能ファイルを作成する
gccgo -o file file.o
生成されたファイルを実行するには、コンパイルされた Go パッケージの場所をプログラムに伝える必要があります。これを行うにはいくつかの方法があります。
-
LD_LIBRARY_PATH環境変数を設定するLD_LIBRARY_PATH=${prefix}/lib/gcc/MACHINE/VERSION [or] LD_LIBRARY_PATH=${prefix}/lib64/gcc/MACHINE/VERSION export LD_LIBRARY_PATHここで
${prefix}は、gccgo をビルドするときに使用した--prefixオプションです。バイナリインストールの場合、通常は/usrです。libを使用するかlib64を使用するかはターゲットによって異なります。通常、x86_64 システムではlib64が正しく、他のシステムではlibが正しいです。libgo.soが見つかるディレクトリを指定するという考え方です。 -
リンク時に
-Wl,-Rオプションを渡す (システムに適している場合は lib を lib64 に置き換える)go build -gccgoflags -Wl,-R,${prefix}/lib/gcc/MACHINE/VERSION [or] gccgo -o file file.o -Wl,-R,${prefix}/lib/gcc/MACHINE/VERSION -
-static-libgoオプションを使用して、コンパイルされたパッケージに静的にリンクします。 -
-staticオプションを使用して、完全に静的なリンクを行います (gcコンパイラのデフォルト)。
オプション
gccgo コンパイラは、言語に依存しないすべての GCC オプション、特に -O および -g オプションをサポートしています。
-fgo-pkgpath=PKGPATH オプションは、コンパイル中のパッケージに一意のプレフィックスを設定するために使用できます。このオプションは go コマンドによって自動的に使用されますが、gccgo を直接呼び出す場合は使用したい場合があります。このオプションは、複数のパッケージが同じ識別子をパッケージ名として使用できるようにするために、多くのパッケージを含む大規模なプログラムで使用することを目的としています。PKGPATH は任意の文字列にすることができます。文字列の良い選択肢は、パッケージをインポートするために使用されるパスです。
コンパイラの同義語である -I および -L オプションは、インポートを見つけるための検索パスを設定するために使用できます。go コマンドでビルドする場合は、これらのオプションは必要ありません。
インポート
何かをエクスポートするファイルをコンパイルすると、エクスポート情報は直接オブジェクトファイルに保存されます。go コマンドではなく gccgo を直接使用してビルドする場合、パッケージをインポートするときに、ファイルの場所を gccgo に伝える必要があります。
gccgo でパッケージ FILE をインポートすると、以下のファイルでインポートデータを探し、最初に見つかったものを使用します。
FILE.goxlibFILE.solibFILE.aFILE.o
FILE.gox は、使用される場合、通常エクスポートデータのみを含みます。これは、FILE.o から以下のように生成できます。
objcopy -j .go_export FILE.o FILE.gox
gccgo コンパイラは、インポートファイルのために現在のディレクトリを検索します。より複雑なシナリオでは、-I または -L オプションを gccgo に渡すことができます。どちらのオプションも検索するディレクトリを受け取ります。-L オプションはリンカにも渡されます。
gccgo コンパイラは現在 (2015-06-15) オブジェクトファイルにインポートされたパッケージのファイル名を記録しません。インポートされたデータがプログラムにリンクされるように手配する必要があります。繰り返しますが、go コマンドでビルドする場合はこれは必要ありません。
gccgo -c mypackage.go # Exports mypackage gccgo -c main.go # Imports mypackage gccgo -o main main.o mypackage.o # Explicitly links with mypackage.o
デバッグ
コンパイル時に -g オプションを使用すると、実行可能ファイルに対して gdb を実行できます。デバッガは Go について限定的な知識しか持っていません。ブレークポイントの設定、シングルステップ実行などはできます。変数を表示することはできますが、C/C++ 型であるかのように表示されます。数値型の場合、これは問題ありません。Go の文字列とインターフェースは 2 要素の構造体として表示されます。Go のマップとチャネルは常にランタイム構造体への C ポインタとして表されます。
C 相互運用性
gccgo を使用する場合、C または extern "C" を使用してコンパイルされた C++ コードとの相互運用性は限られています。
型
基本型は直接マッピングされます。Go の int32 は C の int32_t、int64 は int64_t などです。Go の型 int はポインタと同じサイズの整数であり、C の型 intptr_t に対応します。Go の byte は C の unsigned char と同等です。Go のポインタは C のポインタです。Go の struct は、同じフィールドと型を持つ C の struct と同じです。
Go の string 型は現在、2 要素の構造体として定義されています (これは 変更される可能性があります)
struct __go_string {
const unsigned char *__data;
intptr_t __length;
};
C と Go の間で配列を渡すことはできません。ただし、Go の配列へのポインタは、要素型の同等物への C ポインタと同等です。たとえば、Go の *[10]int は C の int* と同等です (C ポインタが 10 個の要素を指していると仮定した場合)。
Go のスライスは構造体です。現在の定義は次のとおりです(これは 変更される可能性があります)
struct __go_slice {
void *__values;
intptr_t __count;
intptr_t __capacity;
};
Go 関数の型は構造体へのポインタです(これは変更される可能性があります)。構造体の最初のフィールドは関数のコードを指しており、これはパラメーター型が同等で、追加の末尾パラメーターを持つ C 関数へのポインタと同等になります。末尾パラメーターはクロージャーであり、渡す引数は Go 関数構造体へのポインタです。Go 関数が複数の値を返す場合、C 関数は構造体を返します。たとえば、これらの関数はほぼ同等です
func GoFunction(int) (int, float64)
struct { int i; float64 f; } CFunction(int, void*)
Go の interface、channel、および map 型には対応する C 型がありません (interface は 2 要素の構造体、channel と map は C の構造体へのポインタですが、構造体は意図的に文書化されていません)。C の enum 型はなんらかの整数型に対応しますが、具体的にどれかは一般的に予測が困難です。キャストを使用してください。C の union 型には対応する Go 型がありません。ビットフィールドを含む C の struct 型には対応する Go 型がありません。C++ の class 型には対応する Go 型がありません。
Go はガベージコレクションを使用するため、C と Go ではメモリ割り当てがまったく異なります。この分野の正確なガイドラインはまだ決定されていませんが、C から Go に割り当てられたメモリへのポインタを渡すことは許可される可能性が高いです。ポインタを最終的に解放する責任は C 側にあり、もちろん C 側がポインタを解放し、Go 側がまだコピーを持っている場合、プログラムは失敗します。Go から C にポインタを渡す場合、Go 関数は Go 変数にその目に見えるコピーを保持する必要があります。そうしないと、C 関数がまだ使用している間に Go ガベージコレクタがポインタを削除する可能性があります。
関数名
Go コードは、gccgo に実装された Go 拡張機能を使用して C 関数を直接呼び出すことができます。関数宣言の前に //extern NAME を付けることができます。たとえば、C 関数 open を Go で宣言する方法を次に示します。
//extern open func c_open(name *byte, mode int, perm int) int
C 関数は当然ながら NUL 終端文字列を期待しますが、Go ではこれは終端ゼロバイトを持つ byte の配列 (スライスではありません!) へのポインタと同等です。したがって、Go からの呼び出し例は次のようになります (syscall パッケージをインポートした後)。
var name = [4]byte{'f', 'o', 'o', 0};
i := c_open(&name[0], syscall.O_RDONLY, 0);
(これは例としてのみ機能し、Go でファイルを開くには、代わりに Go の os.Open 関数を使用してください)。
read の呼び出しのように、C 関数がブロックする可能性がある場合、C 関数を呼び出すと Go プログラムがブロックする可能性があることに注意してください。何をしているのかを明確に理解していない限り、C と Go の間のすべての呼び出しは、gc コンパイラの場合と同様に、cgo または SWIG を介して実装する必要があります。
C からアクセスされる Go 関数の名前は変更される可能性があります。現在、レシーバーを持たない Go 関数の名前は prefix.package.Functionname です。プレフィックスは、パッケージがコンパイルされたときに使用される -fgo-prefix オプションによって設定されます。このオプションが使用されない場合、デフォルトは go です。C から関数を呼び出すには、GCC 拡張機能を使用して名前を設定する必要があります。
extern int go_function(int) __asm__ ("myprefix.mypackage.Function");
C ソースコードからの Go 宣言の自動生成
GCC の Go バージョンは、C コードから Go 宣言を自動的に生成することをサポートしています。この機能はかなり扱いにくいため、ほとんどのユーザーは代わりに -gccgo オプション付きの cgo プログラムを使用する必要があります。
C コードを通常どおりコンパイルし、オプション -fdump-go-spec=FILENAME を追加します。これにより、コンパイルの副作用としてファイル FILENAME が作成されます。このファイルには、C コードで宣言された型、変数、および関数の Go 宣言が含まれます。Go で表現できない C 型は、Go コードにコメントとして記録されます。生成されたファイルには package 宣言はありませんが、それ以外は gccgo で直接コンパイルできます。
この手順には多くの言及されていない注意点と制限があり、将来変更されないという保証はありません。これは、通常のプロシージャとしてよりも、実際の Go コードの出発点としてより役立ちます。