TL;DR
golang のクロスコンパイルを準備する場合には、以下の点に留意してください。
- (Windows のみ) gccは32ビット版か64ビット版か、使いたい方を正しく選択する
- 2つ以上の環境へクロスコンパイルする場合には、
make.bat
/make.bash
実行時に--no-clean
を指定する
クロスコンパイルの準備をする
golang を用いるとクロスコンパイルが容易なことはよく知られています。例えば、Windows上のgolangであっても、OSX向けのバイナリを生成したり、EdisonやRaspberry Pi用のバイナリを生成できたりするのです。ただし、以下に示す、ちょっとした事前準備が必要です。
- 環境変数
GOOS
,GOARCH
を設定し %GOROOT%\src\make.bat
($GOROOT/src/make.bash
for not Windows) を実行する (2度目以降は不要)
本記事が取り上げるのは、この make.bat
です。
make.bat で体験したこと
まずは私が make.bat
を使った際に体験したエピソードを2つ紹介しましょう。
64ビットを使っていたと思ったら、いつの間にかのに32ビットだった
な…何を言っているのかわからねーと(ry
体験したことは以下のとおりです。
- Windows の64ビット版 golang をインストールして使用
- あるプログラムのWindows 64ビット版をビルドし、正しくビルドできた
- OSX向けのビルド環境を
make.bat
でセットアップ - 同プログラムのOSX版をビルドし、正しくビルドできた
- 再度、同プログラムのWindows 64ビット版のビルドをしたら、32ビットexeができてしまった (GOOSとGOARCHは未指定)
- GOOSとGOARCHでWinodws 64ビット版を明示してビルドしたら、エラーになった
クロスコンパイルを準備したと思ったら、いつの間にか使えなくなっていた
な…何を言っているのかわからねーと(ry
体験したことは以下のとおりです。
- Windows の64ビット版 golang をインストールして使用
- あるプログラムのWindows 64ビット版をビルドし、正しくビルドできた
- OSX向けのビルド環境を
make.bat
でセットアップ - 同プログラムのOSX版をビルドし、正しくビルドできた
- Linux向けのビルド環境を
make.bat
でセットアップ - 同プログラムのLinux版をビルドし、正しくビルドできた
- 再度、同プログラムのOSX版のビルドをしたら、エラーになった
原因調査と対策
この2つのエピソードは、両方共に、make.bat
が何をしているかを少し詳しく調べることで、解決方法が見いだせました。以下は make.bat
からの抜粋です。
echo # Building C bootstrap tool.
echo cmd/dist
if not exist ..\bin\tool mkdir ..\bin\tool
:: Windows has no glob expansion, so spell out cmd/dist/*.c.
gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
if errorlevel 1 goto fail
.\cmd\dist\dist env -wp >env.bat
if errorlevel 1 goto fail
call env.bat
ここでは dist.exe
というC言語で書かれたプログラムをコンパイルして、実行環境を出力するサブコマンド&オプション env -wp
を付与して実行しています。そうして得た env.bat
が以下です。
set CC=gcc
set CC_FOR_TARGET=gcc
set GOROOT=d:\Go\go1.4.2.windows-amd64
set GOBIN=d:\Go\go1.4.2.windows-amd64\bin
set GOARCH=386
set GOOS=windows
set GOHOSTARCH=386
set GOHOSTOS=windows
set GOTOOLDIR=d:\Go\go1.4.2.windows-amd64\pkg\tool\windows_386
set GOCHAR=8
set GO386=sse2
set PATH=(省略)
これは Windows 64ビット環境で実行した結果です。注目すべきは set GOHOSTARCH=386
の行です。一般にクロスコンパイル時には、ホスト(コンパイルを実行する)環境とターゲット(ビルドした実行ファイルを実際に使用する)環境を正しく指定される必要があります。Windows 64ビット環境がホストであるならば、ここは set GOHOSTARCH=amd64
になるはずです。これが「いつの間にか32ビットだった」ことに関係していそうです。
これに make.bat
もう1つの仕組みが加わります。一般的なC言語などのクロスコンパイル環境の構築では、ターゲット用のツール類だけをビルドします。しかしgolangの make.bat
は、ホスト用のツールもビルドするのです。よって、GOHOSTARCH
に想定外の値が入っているということは、ホスト用のツールが変わってしまう、すなわち32ビットになってしまうことを意味しています。
あとは cmd/dist/*.c
の内容を追えばわかるのですが、結論を急ぎましょう。使っているgccのバージョンが原因でした。PATHの通った gccが64ビット版だと GOHOSTARCH=amd64
になり、32ビット版だと GOHOSTARCH=386
になってしまいます。私の環境では32ビット版のMinGWが使われていました。
よって解決方法は簡単で、tdm-gcc から正しいバージョン(golangに合わせた)ものをダウンロードし、インストールましょう。make.bat
実行時に PATH を書き換えて、正しく選択するだけでも大丈夫です。
golang を使っているはずなのに、gcc のバージョンに依存するというは、盲点になりやすいですね。
残るは「(準備したはずのクロスコンパイル環境が)いつの間にか使えなくなっていた」のほうですが、これも make.bat
を読み進めることで解決できます。該当箇所を示しましょう。
echo # Building compilers and Go bootstrap tool.
set buildall=-a
if x%1==x--no-clean set buildall=
.\cmd\dist\dist bootstrap %buildall% -v
dist.exe
の bootstrap
サブコマンドに -a
オプションを指定すると、bootstrap
の本体処理の前に cmd/dist/build.c
内の clean()
関数が実行されます。過去にビルドしたツールやランタイムを、この関数が全て消し去ってしまいます。
よって2つ目以降のクロスコンパイル環境を整備する際には make.bat --no-clean
というように、make.bat
に --no-clean
フラグを指定しましょう。
まとめ
make.bat
は、何も指定しなければホスト環境用のツールとランタイムをビルドします。
ホスト環境はgccにより決定するので、意図と異なるgccを利用すると、誤ったホスト環境用のツールとランタイムに書き換わってしまいます。
またツールとランタイムのビルドでは、--no-clean
を指定しなければ過去のビルドの成果物を全て削除します。これは過去のビルドの残骸が、なにか悪さをするの — C言語あたりを使ったことがあれば、誰もが1度は経験するアレ — を防ぐ目的があるのかもしれません。
加えてクロスコンパイル時には、make.bat
はホスト環境用とターゲット環境用の、ツールとランタイムをビルドします。前述の削除と組み合わさって、2つめのターゲット環境用のツールとランタイムをビルドすると、1つめのそれらが消えてしまうという結果になります。