Go言語 + cli-init でコマンドラインツールを作る
よく訓練されたアップル信者、都元です。最近社内のメンバーがみんなGo言語の世界で楽しそうなので、私も混ざってみることにしました。最初のセットアップや基礎文法等は、私も平行して急いで学ぶGo langシリーズで勉強中です。
コマンドラインツールが作りたい
と思っています。ちょっとしたものを作るとしたらPythonなのかな、と思って友達のPythonistaにインタビューをしたところ、「ちょっとしたツール作るとかって用途の人は Golangに移行した(えっ」という衝撃的なコメントを貰い、もうこの際だからGo勉強すっかという空気になった次第。
具体的な環境構築
基本的には急いで学ぶGo langシリーズを読めばいいのですが、一点迷ったのがディレクトリ構成です。
Go言語で幸せになれる10のテクニックでは「GOPATHは一つだけ (Use a single GOPATH)」という指針が紹介されています。当初はこの通りで行こうと考えたのですが、IDE(IntelliJ IDEA)と組み合わせる構成を考えた際に矛盾が発生してしまいました。
- GOPATHは一つだけ。(指針)
- 1つの環境で、複数の独立したプロジェクトを並行開発したい。(直近ではやらないしろ、当然出来るようにはしておきたい。)
- そしてもちろん、プロジェクト単位でgitリポジトリを構成したい。(要求)
- git管理の対象となるルート・ディレクトリは$GOPATHではなく、$GOPATH/src/github.com/(username)/(repository)である。(慣例)
- IDEAは(Eclipseと違って)Workspaceという考え方がなく、プロジェクト単位でウィンドウを開く。(仕様)
- IDEAのGo language support pluginでは、GOPATHをプロジェクトとして扱う。(仕様)
といった感じで、もうプロジェクト毎にGOPATH作ってしまったほうが良い、という判断をしました。間違ってるかもしれませんが。ひとまず判断。
というわけで、作業ディレクトリとしてプロジェクト(ここではusername/foobar)を作り、その直下はこのような構成にすることにしました。
. ├── .idea ├── bin ├── pkg └── src ├── github.com │ ├── username │ │ └── foobar │ │ ├── .git │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ └── *.go │ └── (other-dependencies) └── (other-dependencies)
GitHubで新規プロジェクトを作り、.gitignoreやLICENSE等を作ってもらいましょう。
その上でGitHub上のリソースをcloneします。
$ cd $GOPATH/src/github.com/username $ git clone git@github.com:username/foobar.git
cli-init
cli-initというツールがあります。詳しくは作者さんの高速にGo言語のCLIツールをつくるcli-initというツールをつくったをご覧いただければ分かりますが、この手順に則って、プロジェクトのスケルトンを作ってみます。
まずcli-initのインストール
$ cd $GOPATH $ go get -d github.com/tcnksm/cli-init $ cd $GOPATH/src/github.com/tcnksm/cli-init $ make install $ cd $GOPATH $ cli-init --version cli-init v0.1.0
以上です。ちなみにこのツールはグローバルにインストールされる訳ではなく、実行ファイルは$GOPATH/bin/cli-initに入ります。
スケルトンの作成
$ cd $GOPATH/src/github.com/username $ mv foobar foobar-tmp $ cli-init -s foo,bar,baz,qux foobar $ mv foobar/* foobar-tmp/ $ rmdir foobar $ mv foobar-tmp foobar
GitHubに作ってもらった.gitignore等を活かすために、少々ゴニョゴニョしてますが、主要なコマンドは3行目だけです。ちなみに既存ディレクトリにcli-initを実行してしまうと、下記のような確認の上で、中身を綺麗さっぱり消されてしまいます。
foobar is already exists, overwrite it? [Y/n]: Y
ビルドとインストール
任意のディレクトリで下記コマンドを実行することで、カレントディレクトリにfoobar実行ファイルが生成されます。
$ go build github.com/username/foobar $ ./foobar NAME: foobar - USAGE: foobar [global options] command [command options] [arguments...] VERSION: 0.1.0 AUTHOR: Daisuke Miyamoto - <miyamoto.daisuke@example.com> COMMANDS: foo bar baz qux help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help --version, -v print the version
流行りの(?)メイン+サブコマンド形式のCLI実行ファイルが出来ました。
本格的にインストールする場合は下記コマンド。これは$GOPATH/bin/foobarが生成されます。
$ go install github.com/username/foobar
ソースコード
さて、cli-initが生成してくれたコードをざっと見ていきましょう。まずはfoobar.goを見てみます。このスケルトンではgithub.com/codegangsta/cliというパッケージライブラリを使っています。CLIコマンドを作るためのフレームワークですね。ヘルプに表示するための情報を諸々設定しているのが分かります。
package main import ( "os" "github.com/codegangsta/cli" ) func main() { app := cli.NewApp() app.Name = "foobar" app.Version = Version app.Usage = "" app.Author = "Daisuke Miyamoto" app.Email = "miyamoto.daisuke@example.com" app.Commands = Commands app.Run(os.Args) }
各コマンドの実装はcommands.goに有ります。複雑なものであれば、コマンド毎にファイル分割するのもいいですね。
package main import ( "log" "os" "github.com/codegangsta/cli" ) var Commands = []cli.Command{ commandFoo, commandBar, commandBaz, commandQux, } var commandFoo = cli.Command{ Name: "foo", Usage: "", Description: ` `, Action: doFoo, } var commandBar = cli.Command{ Name: "bar", Usage: "", Description: ` `, Action: doBar, } var commandBaz = cli.Command{ Name: "baz", Usage: "", Description: ` `, Action: doBaz, } var commandQux = cli.Command{ Name: "qux", Usage: "", Description: ` `, Action: doQux, } func debug(v ...interface{}) { if os.Getenv("DEBUG") != "" { log.Println(v...) } } func assert(err error) { if err != nil { log.Fatal(err) } } func doFoo(c *cli.Context) { } func doBar(c *cli.Context) { } func doBaz(c *cli.Context) { } func doQux(c *cli.Context) { }
まとめ
というわけで、Goを使ってコマンドラインツールがさらっと書けるようになりました。近々何か作ってみたいと思います。