GoでCのライブラリを使ったプログラムをクロスコンパイルする

Goは1つのコードで複数のOS, アーキテクチャにクロスコンパイルすることが出来る。
それも面倒な事前準備とかが必要ないのも特徴でGo1.5より前はmake.bashコンパイルするプラットフォームで実行してみたいなことが必要だったけどGo1.5からは必要なくなって次のようなコマンドでコンパイル出来るようになっている。

$ GOOS=linux GOARCH=amd64 go build main.go
$ GOOS=linux GOARCH=386 go build main.go
$ GOOS=darwin GOARCH=amd64 go build main.go
$ GOOS=darwin GOARCH=386 go build main.go

※ 対応しているOS, ARCHはここから確認が出来る

Goではcgoを使ってCのコードを呼び出すことが出来るものがあるけど、これを使ったコードをクロスコンパイルする時にいくつか問題にあたった。
今回はUbuntuでビルドしてました。

ロスコンパイル時はCGO_ENABLEDはデフォルトでは有効にならない

Goではbuild時にCのコードをgccなどを使ってコンパイルしている。
それをCGO_ENABLEDという環境変数で管理していて、build時にその環境にあったものを指定した時(例えば64bitのlinuxマシンでGOOS=linux GOARCH=amd64を指定する)に自動でCGO_ENABLEDを有効にしてくれる。
ただクロスコンパイル時は無効になるので明示してあげないといけなかった。

これはドキュメントにも記載されている

The cgo tool is enabled by default for native builds on systems where it is expected to work. It is disabled by default when cross-compiling. You can control this by setting the CGO_ENABLED environment variable when running the go tool: set it to 1 to enable the use of cgo, and to 0 to disable it. The go tool will set the build constraint "cgo" if cgo is enabled.

有効にするにはbuild時にCGO_ENABLED=1を指定すれば有効になる。

$ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build main.go
$ GOOS=linux GOARCH=386 CGO_ENABLED=1 go build main.go
$ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build main.go
$ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go build main.go

32bit用のパッケージをインストールしてビルドする

64bitのLinuxマシンではGOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build main.goでビルドが出来るがGOOS=linux GOARCH=386 CGO_ENABLED=1 go build main.goの時は次のようなエラーになってしまった。

/usr/bin/ld: cannot find -xxxxx

これは使用するCのパッケージが64bitのものはインストールされているが32bitのものがないというだけの問題だった。
ただ32bitマシンを用意するのかというとそれは面倒すぎるので、次の方法で32bit用のものを用意した。

$ dpkg --add-architecture i386
$ apt-get update -y
$ apt-get install -y [package name]:i386

これで無事32, 64どちらでもビルド出来るようになった。

Macでもビルド出来るようにする

Macでビルドする時に-xするとclangを使ってビルドしているのでLinuxマシンでMac用にbuildする時はコンパイラgccではなくてclangを使いたかった。
コンパイラ自体はビルド時にCC=gccCC=clangのように変更は出来るが、32bitと64bitのMac向けにビルドする必要がある。

これに関してはMacOSSDKLinuxで使えるようにしたDocker公式で提供しているdockercore/golang-crossを使って32, 64ごとにビルド出来るようにした。
/osxcross/target/bin内を見ると32bit用のclangのo32-clang、64bit用のo64-clangが入っているのでビルド時にそれぞれを指定してあげることでCをコンパイルしてくれる。

GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 CC=o64-clang go build
GOOS=darwin GOARCH=386 CGO_ENABLED=1 CC=o32-clang go build

最後に

無事コンパイル通った時は嬉しかった。。。