gccgo の設定と使用方法
このドキュメントでは、Go 言語のコンパイラである gccgo の使用方法について説明します。gccgo コンパイラは広く使用されている GNU コンパイラである GCC の新しいフロントエンドです。フロントエンド自体は BSD スタイルのライセンスになっていますが、gccgo は通常 GCC の一部として使用され、GNU General Public License(このライセンスは、GCC の一部としての gccgo 自体をカバーしますが、gccgo によって生成されたコードはカバーしません) によってカバーされます。
gccgo はgc
コンパイラではないことに注意してください。そのコンパイラの詳細については Go のインストール の指示を参照してください。
リリース
gccgo をインストールする最も簡単な方法は、Go サポートを含めるように構築された GCC バイナリリリースをインストールすることです。GCC バイナリリリースは さまざまな Web サイト から入手可能であり、通常は 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 を構築したい場合は、Git を介して gccgo のソースコードにアクセスできます。GCC Web サイトには、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 Web サイトの指示を参照してください。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 Web サイトで説明されているように、GCC のビルドには多くの前提条件が必要です。gcc configure
スクリプトを実行する前に、すべての前提条件をインストールすることが重要です。前提条件ライブラリは、GCC ソースにあるスクリプト contrib/download_prerequisites
を使用してダウンロードできます。
ビルドコマンド
すべての前提条件がインストールされたら、一般的なビルドとインストール手順は次のようになります (--with-ld
オプションは上記のように gold リンカーを使用する場合にのみ使用してください)
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
のどちらを使用するかは、ターゲットによって異なります。通常は、lib64
は x86_64 システムで、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.gox
libFILE.so
libFILE.a
FILE.o
FILE.gox
を使用すると、通常はエクスポートデータのみが含まれます。これは FILE.o
から次によって生成できます。
objcopy -j .go_export FILE.o FILE.gox
gccgo コンパイラは現在のディレクトリでインポートファイルを探します。複雑なシナリオの場合は、-I
または -L
オプションを gccgo に渡すことができます。どちらのオプションでも、検索するディレクトリを指定します。-L
オプションはリンカにも渡されます。
gccgo コンパイラは現在(2015 年 6 月 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 を使用すると、extern "C"
を使用してコンパイルされた 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 ポインターと同じです。たとえば、C ポインターが 10 要素を指すと仮定すると、Go の *[10]int
は C の int*
と等価です。
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 の enum
型は一部の整数型に対応していますが、一般的にどれに正確に対応するかは予測が難しいです。キャストを使用してください。C の union
型には、対応する Go 型がありません。ビットフィールドを含む C の struct
型には、対応する Go 型がありません。C++ の class
型には、対応する Go 型がありません。
C と Go では、Go がガベージコレクションを使用するため、メモリ割り当てが完全に異なります。この分野の正確なガイドラインは未定ですが、C から Go に割り当てられたメモリのポインターを渡すことが許可される可能性があります。ポインターを最終的に解放する責任は C 側にあるままであり、C 側がポインターを解放したときに Go 側がまだコピーを持っている場合、もちろんプログラムは失敗します。Go から C にポインターを渡す場合、Go 関数はそれを Go 変数内に保持する必要があります。それ以外の場合は、Go ガベージコレクタが、C 関数がまだ使用している間にポインターを削除する可能性があります。
ファクションネーム
Go コードは、gccgo で実装された Go の拡張を使用して C 関数を直接呼び出すことができます: 関数宣言の前に //extern NAME
が付けられます。たとえば、C 関数の open
を Go で宣言する方法を次に示します。
//extern open func c_open(name *byte, mode int, perm int) int
C 関数は当然、Go では末尾ゼロバイトの byte
配列 (スライスではありません!) へのポインターと同じである、NULL で終了する文字列を想定しています。したがって、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 プログラムがブロックする可能性があることに注意してください。何をやっているのかを明確に理解していない限り、gc
コンパイラと同様に、C と Go の間のすべての呼び出しは cgo または SWIG を介して実装する必要があります。
C からアクセスした Go 関数の名前は変更される場合があります。現在、レシーバーを持たない Go 関数の名前は prefix.package.Functionname
です。prefix はパッケージがコンパイルするときに使用される -fgo-prefix
オプションによって設定されます。このオプションが使用されない場合、デフォルトは go
になります。C から関数を呼び出すには、GCC 拡張機能を使用して名前を設定する必要があります。
extern int go_function(int) __asm__ ("myprefix.mypackage.Function");
C ソース コードからの Go 宣言の自動生成
Go バージョンの GCC は、C コードから Go 宣言を自動生成できます。この機能は非常に利便性が高く、ほとんどのユーザーは代わりに -gccgo
オプションを使用した cgo プログラムを使用する必要があります。
通常どおりに C コードをコンパイルし、オプション -fdump-go-spec=FILENAME
を追加します。これにより、コンパイルの副作用としてファイル FILENAME
が作成されます。このファイルには、C コードで宣言された型、変数、および関数の Go 宣言が含まれます。Go では表せない C タイプは、Go コード内のコメントとして記録されます。生成されたファイルには package
宣言はありませんが、gccgo によって直接コンパイルできます。
この手順には、述べられていない注意点と制限事項が数多くあります。将来的に変更されないという保証はありません。これは、定期的な手順というよりも、実際の Go コードの出発点としてより役立ちます。