Go のクロスコンパイル環境構築

## Go でクロスコンパイル

Go の特徴であるクロスコンパイルの便利さと、その方法はよく語られますが、意外に「そのための準備工程」が知られてない気がしたので、ここで再度クロスコンパイル自体の便利さと、クロスコンパイル方法、そしてその準備方法を書いておきます。


## クロスコンパイルの旨味

Go は、 1 つのソースコードから様々な OS 向けのバイナリを生成するクロスコンパイルをサポートしています。しかも、対象の OS が無いとビルドできないわけではなく、例えば Mac で Windows 用、 Linux 用、  Plan9 用のバイナリを一気に生成するといったことができます。
しかも、 32bit マシンで 64bit 用のバイナリを生成することもできます。

"Write Once Run Anywhere" でお馴染みの Java との違いは、 Java の場合は Class ファイルという形式で生成し、環境ごとに VM (Java VM) を入れることで、その上で動かすことができるようになっています。プラットフォームごとの違いは Java VM が吸収するため、生成し配布するファイルは Class ファイルだけで良いという特徴があります。

一方 Go の場合は、生成するのが各環境で直接実行できるファイルなので、実行する人が何かを準備する必要(Java でいう JRE のインストールにあたる作業)がありません。

Mac で .exe ファイルを生成して Windows ユーザにそれを渡せば、受け取った瞬間にすぐに実行できるという点が非常に便利なため、コマンドラインツールのようなものをクロスコンパイルして配布するなどといった用途で、 Go が導入される場面も目立ってきました。(heroku の hk コマンドなど)


## クロスコンパイルサポート範囲

現在 Go 1.3 では、以下の組み合わせの OS と CPU アーキテクチャに対して、クロスコンパイルがサポートされています。(1.3 から NaCL もサポートされました。もちろん、試してませんが。。)

```
  $GOOS     $GOARCH
  darwin    386
  darwin    amd64
  freebsd   386
  freebsd   amd64
  freebsd   arm
  linux     386
  linux     amd64
  linux     arm
  netbsd    386
  netbsd    amd64
  netbsd    arm
  openbsd   386
  openbsd   amd64
  plan9     386
  plan9     amd64
  windows   386
  windows   amd64
  nacl      amd64
  nacl      386
```

## クロスコンパイル方法

実行する環境用のバイナリは、 go build コマンドを使って以下のように行います。

```sh
$ go build hello.go
```

環境を指定したい場合は、 GOOS と GOARCH という環境変数を先ほどのリストにある組み合わせで指定します。

```sh
$ GOOS=linux GOARCH=amd64 go build hello.go
```

これだけです！

## クロスコンパイルの準備

これだけです！というところまでは語られますが、実際は Go をインストールすればすぐにクロスコンパイルができるというわけではありません。

コンパイルしたい対象の環境ごとに、準備が必要です。ドキュメントは[こちら](http://golang.org/doc/install/source)

例えば、先ほどの Mac で Linux の 64bit 環境のバイナリを生成する先ほどのビルドをしたいなら、 Go をインストールしたディレクトリの src 以下にある、 make.bash というスクリプトに、 GOOS と GOARCH をつけて実行します。

```sh
$ cd $(go env GOROOT) # go をインストールしたディレクトリに移動
$ cd src              # その下の src
$ GOOS=linux GOARCH=amd64 ./make.bash
```

ちょっと時間がかかります。が、これを必要な環境ごとにやる必要があります。
最低で linux, darwin, windows の 64bit くらいはやっておくとしても 3 回。
全部サポートすれば 19 回変数を変えてコレをやります。地味に時間がかかる。

まあ、自動化しますよね。
シェルなりなんなり書けばよいのですが、そういうツールもいくつかあります。


## ビルド環境構築もろもろ

### homebrew

まず、 Mac で homebrew をつかっているなら [オプションがあるので](https://github.com/Homebrew/homebrew/blob/master/Library/Formula/go.rb#L16-L17) それをつけます。

- `--cross-compile-all`: 全部
- `--cross-compile-common`: Darwin, Linux, Windows だけ

ということでこんな感じです。

```sh
$ brew install go --cross-compile-all
```

### golang-crosscompile

homebrew とか使っていなければ、 bash で書かれたスクリプトが公開されています。

https://github.com/davecheney/golang-crosscompile

使いかたは [こちらのブログ](http://dave.cheney.net/2013/07/09/an-introduction-to-cross-compilation-with-go-1-1) にある通りです。

```shell-session
$ git clone git://github.com/davecheney/golang-crosscompile.git
$ source golang-crosscompile/crosscompile.bash
$ go-crosscompile-build-all
```

ちなみに、このスクリプトは build をするための shell の関数を作ってくれるのですが、 zsh となんか相性が悪いので使ってません。環境変数だけなんで、適当に自分で shell を書く程度で間に合いそうです。


### Windows

よく知りません。情報求む。



## 追記

そうえば、こういうの公式にサポートしないの？ っていう話を GoCon 2013 で来日してた Andrew と飲み会でして、 [ML に投稿](https://groups.google.com/forum/#!msg/golang-nuts/oFIs6Puj0_Q/0_seVYRtOyYJ)したら issue 立った。

https://code.google.com/p/go/issues/detail?id=6597

気がついたら Label が Maybe から Repo-Main になってるし、もしかしたら何か入るかも。
(そして、ここにも mattn 先生の姿がw)


## 追記2

コメントに @nikuyoshi さんが sh で全環境のビルド環境を構築するスクリプトを貼ってくださったので、掲載させていただきます。

```
素晴らしいエントリーありがとうございます。 非常に参考になりました。

私はhomebrewを使っておらず、Zsh使いなので簡単なスクリプトを自作してみました。
上記の $ GOOS=linux GOARCH=amd64 ./make.bash を19回分回してるだけです。
https://gist.github.com/nikuyoshi/a6c92119c59144d472ef

Jxckさんは既に不要かと思いますが、誰かのお役に立てれば幸いです。
```

https://gist.github.com/nikuyoshi/a6c92119c59144d472ef

@nikuyoshi さんありがとうございました！