複数プラットフォームにGoアプリケーションを配布する
最近試しにGo言語でCLIアプリケーションを作成した.joelthelion/autojumpをシンプルにしただけのツールで,ディレクトリを保存して,どこからでもその保存したディレクトリへの移動を可能にする.
Goの環境さえあれば,このようなGo言語のアプリケーションの配布はとても簡単で,インストールは以下のようにするだけでよい.
$ go get github.com/tcnksm/jj_
これだけではなく,Goはクロスコンパイルが簡単で,様々なプラットフォーム向けにバイナリを生成することができる.つまり,Goがインストールされていない環境に対しても簡単にツールを配布することができる.
Packerなどの最近のHashicorp制のツールは,Go言語で書かれており,OSX,Linux,Windows,FreeBSDなど様々なプラットフォーム向けにそれらを配布している.レポジトリを見てると,その辺をいい感じに自動化している.それらを参考にして,今回作成したツールを複数プラットフォーム向けに配布してみた.
TL;DR
以下のようにOXSとLinux,そしてWindowsのそれぞれ386とamd64に対してツールを配布する(まだ不安定なので使わないでください).
やったことは,
ソースは全て,tcnksm/jjのscripts以下にある.
なぜGoを使い始めたか
まず,簡単になぜGoを使い始めたか.理由は下のエントリと同じ.
- On Distributing Command line Applications: Why I switched from Ruby to Go - Code Gangsta
- Abandoning RubyGems | Mitchell Hashimoto
今まで簡単な便利コマンドラインツールは,Rubyを使ってさらっとつくってきた.他のひとも使えそうなものはRubyGemsで配布するようにつくった.しかし,いざチームの人に使ってもらう段階になると,そもそも自分の周りがジャバなので,gem
って何?となり,Rubyのインストールから初めてもらうということが起こった.
自分ならruby-build
やruby-install
でさらっと入れるが,Ruby使ったことないひとにとってはインストールさえも障壁が高い.その壁を超えてまで使ってくれるひとは実は少ない.
それはもったいない.たいしたツールしか作れないのであれば,せめて導入の障壁だけでも下げたい.使い手の環境にあったバイナリをつくって,はいどうぞ!としたい.Go言語の良さは,goroutine
とかいろいろあるだろうが,自分の中では,このクロスコンパイルのやりやすさが一番大きい.
クロスコンパイル
GO言語のクロスコンパイルはとても簡単で,以下のようにするだけでOSXでlinuxのamd64向けのバイナリをつくることができる.
$ GOOS=linux GOARCH=amd64 go build hello.go
複数プラットフォーム向けにクロスコンパイルする場合は,mitchellh/goxを使うともっと簡単にできる.Goxを使う利点は以下が挙げられる.
- シンプル
- 複数プラットフォームの並列ビルド
- 複数パッケージの並列ビルド
準備
まず,goxをインストールする.
$ go get github.com/mitchellh/gox
次にクロスコンパイル用のツールをインストールする.
$ gox -build-toolchain
使い方
使い方は,go build
と同じで,コンパイルしたいパッケージのディレクトリで以下を実行するだけ.
$ gox
--> darwin/386: github.com/mitchellh/gox
--> darwin/amd64: github.com/mitchellh/gox
--> linux/386: github.com/mitchellh/gox
...
何も指定しなければ,OSX,Linux,Windows,FreeBSD,そしてOpenBSDのそれぞれ386とamd64のバイナリが作成される.
今回は,OXSとLinux,Windowsの386とamd64に対してクロスコンパイルを実行する.これらを-os
,-arch
で指定するだけ.
自動化するために以下のようなスクリプトを書いておく.
#!/bin/bash
XC_ARCH=${XC_ARCH:-386 amd64}
XC_OS=${XC_OS:-linux darwin windows}
rm -rf pkg/
gox \
-os="${XC_OS}" \
-arch="${XC_ARCH}" \
-output "pkg/{{.OS}}_{{.Arch}}/{{.Dir}}"
出力先は,-output
で指定できる.フォーマットは,go templateに従う.
バイナリ配布
バイナリの置き場には,最近よくみるbintrayを使う.レポジトリ単位での複数パッケージ,バージョニングによる配置が可能.バイナリだけでなく,debやrpmパッケージ,mavenなどに対応している.もちろん無料.
{%img https://bintray.com/docs/help/repository_diagram.png %}
bintrayが良いと感じたのは,REST APIで操作できるところ.自動化がしやすい.例えばアップロードはcURLを使って以下のようにできる.
$ curl -T <FILE.EXT> -utcnksm:<API_KEY> https://api.bintray.com/content/tcnksm/jj/jj/<VERSION_NAME>/<FILE_TARGET_PATH>
準備
binaryにバイナリをアップロードするには以下の設定が必要.
- レポジトリの作成
- API Keyの取得
Githubとの連携も可能なので,READMEやCHANGELOGを紐づけておくと親切.
アップロード
これも自動化する.事前に複数のバイナリやその他のファイルをzipで固めてpkg/dist
以下に配置しておく.
VERSION=0.1.0
for ARCHIVE in ./pkg/dist/*; do
ARCHIVE_NAME=$(basename ${ARCHIVE})
echo Uploading: ${ARCHIVE_NAME}
curl \
-T ${ARCHIVE} \
-utcnksm:${BINTRAY_API_KEY} \
"https://api.bintray.com/content/tcnksm/jj/jj/${VERSION}/${ARCHIVE_NAME}"
done
これだけで,適切なバージョニングをしつつバイナリをホストしてくれる.バージョンに関しては,Packerとかだとversion.go
を準備してそれから読み取るなどしている.
クロスコンパイル用のcompile.sh
,zipで固めるdist.sh
,アップロードするためのupload.sh
を準備して,それらを一気に実行するようにしておけば便利.