golang のサブコマンドで GetWild

この記事は [GetWild Advent Calendar 2016 - Qiita](http://qiita.com/advent-calendar/2016/getwild) の12日目の記事です。

~ get wild and tough ~ と言われたらもちろんシティーハンターですね。今回、記事を書くにあたって、AAを探しましたが、シティーハンターのあまりネタとしては使いにくいAAしかなくて「ぐぬぬ…」となりました…

<p align=center>
<img src="https://qiita-image-store.s3.amazonaws.com/0/3353/731c1e11-ea2f-8dec-aabf-f03f1547416d.png" />
</p>

---

## 「Get wild 禁断症状」とは

[業務中にGETWILD分が切れて禁断症状がでそうになった際に必死に耐えるための方法論~#ghq ないし go getで頑張る](http://qiita.com/MasashiSalvador57f/items/bf1395797397764ad9e1#ghq-ないし-go-getで頑張る)

> `go get` もしくは `ghq get` もGETWILDするチャンスかもしれません。出来る時にしておかないと頭の中をエンドレスでGETWILDが流れ、ゾーン状態はどこへやら、いつの間にか１日が終わってしまうかもしれません :scream:

なるほどなるほど。 `go get` という文脈にカスタマイズされた Get wild が効果を発揮すればいいんですね。わかります。

ー_**無いならば　作ってしまおう　ゲッワイルド**_

<p align=center>
<img src=https://qiita-image-store.s3.amazonaws.com/0/3353/29604f9f-25c9-334a-e7a6-986a65e91552.png />
</p>

## `go getwild` コマンド

単純に github.com/kaneshin/getwild のようなリポジトリを作って `getwild` コマンドを作ればいいと思うんですが、それだと何も面白くないので、今回はあまりやらないことをやろうと思います。

### go のサブコマンド

golang を使っている人はわかると思いますが、go コマンドには複数の（サブ）コマンドが存在しています。

```bash
$ go help
Go is a tool for managing Go source code.

Usage:

        go command [arguments]

The commands are:

        build       compile packages and dependencies
        clean       remove object files
        doc         show documentation for package or symbol
        env         print Go environment information
        fix         run go tool fix on packages
        fmt         run gofmt on package sources
        generate    generate Go files by processing source
        get         download and install packages and dependencies
        install     compile and install packages and dependencies
        list        list packages
        run         compile and run Go program
        test        test packages
        tool        run specified go tool
        version     print Go version
        vet         run go tool vet on packages

Use "go help [command]" for more information about a command.
```

今回のターゲットとして、ここに `getwild` を追加することを目標とします。

## go/src/cmd/go

go コマンドのカスタマイズ方法ですが、とても簡単です。まず、適当にファイルを用意します。今回は getwild.go を用意しています。

### go/src/cmd/go/getwild.go

```bash
$ cd go/src/cmd/go
$ vim getwild.go
```

### 実装

```go
package main

import (
	"fmt"
	"os"
)

var cmdGetWild = &Command{
	UsageLine: "getwild [-tough]",
	Short:     "get wild and tough",
	Long: `
Print just the 'get wild'.

The -tough flag prints more message we want.
	`,
}

var getTough = cmdGetWild.Flag.Bool("tough", false, "")

func init() {
	addBuildFlags(cmdGetWild)
	cmdGetWild.Run = runGetWild // break init loop
}

func runGetWild(cmd *Command, args []string) {
	fmt.Fprint(os.Stdout, textGet, textWild)
	args = append([]string{"-f", "-u"}, args...)
	if *getTough {
		args = append([]string{"-t"}, args...)
		fmt.Fprint(os.Stdout, textAnd, textTough)
	}

	cmdGet.Flag.Parse(args)
	args = cmdGet.Flag.Args()
	cmdGet.Run(cmdGet, args)
}

const (
	textGet = `
 ________  _______  _________
|\   ____\|\  ___ \|\___   ___\
\ \  \___|\ \   __/\|___ \  \_|
 \ \  \  __\ \  \_|/__  \ \  \
  \ \  \|\  \ \  \_|\ \  \ \  \
   \ \_______\ \_______\  \ \__\
    \|_______|\|_______|   \|__|
`

	textWild = `
 ___       __   ___  ___       ________
|\  \     |\  \|\  \|\  \     |\   ___ \
\ \  \    \ \  \ \  \ \  \    \ \  \_|\ \
 \ \  \  __\ \  \ \  \ \  \    \ \  \ \\ \
  \ \  \|\__\_\  \ \  \ \  \____\ \  \_\\ \
   \ \____________\ \__\ \_______\ \_______\
    \|____________|\|__|\|_______|\|_______|
`

	textAnd = `
 ________  ________   ________
|\   __  \|\   ___  \|\   ___ \
\ \  \|\  \ \  \\ \  \ \  \_|\ \
 \ \   __  \ \  \\ \  \ \  \ \\ \
  \ \  \ \  \ \  \\ \  \ \  \_\\ \
   \ \__\ \__\ \__\\ \__\ \_______\
    \|__|\|__|\|__| \|__|\|_______|
`

	textTough = `
 _________  ________  ___  ___  ________  ___  ___
|\___   ___\\   __  \|\  \|\  \|\   ____\|\  \|\  \
\|___ \  \_\ \  \|\  \ \  \\\  \ \  \___|\ \  \\\  \
     \ \  \ \ \  \\\  \ \  \\\  \ \  \  __\ \   __  \
      \ \  \ \ \  \\\  \ \  \\\  \ \  \|\  \ \  \ \  \
       \ \__\ \ \_______\ \_______\ \_______\ \__\ \__\
        \|__|  \|_______|\|_______|\|_______|\|__|\|__|
`
)
```

#### Commandの用意

```go
var cmdGetWild = &Command{
	UsageLine: "getwild [-tough]",
	Short:     "get wild and tough",
	Long: `
Print just the 'get wild'.

The -tough flag prints more message we want.
	`,
}
```

このように、cmd/go 内に定義されている `Command` 構造体をコマンド用に定義しておきます。

そして、このコマンドにフラグ（オプション）を定義することも可能です。

```go
var getTough = cmdGetWild.Flag.Bool("tough", false, "")
```

ここまで実装したらおまじないのように `init()` で初期化を行っておきます。

```
func init() {
	addBuildFlags(cmdGetWild)
	cmdGetWild.Run = runGetWild
}
```

実行されるのは `runGetWild` 関数で、下記のように定義しています。

```
func runGetWild(cmd *Command, args []string) {
	fmt.Fprint(os.Stdout, textGet, textWild)
	args = append([]string{"-f", "-u"}, args...)
	if *getTough {
		args = append([]string{"-t"}, args...)
		fmt.Fprint(os.Stdout, textAnd, textTough)
	}

	cmdGet.Flag.Parse(args)
	args = cmdGet.Flag.Args()
	cmdGet.Run(cmdGet, args)
}
```

特定の文字列を出現させて、そのあとは `go get -f -u` と同じ効果で go get を行うようにしています。

### go/src/cmd/go/main.go

main.goのコマンドのリストに定義したコマンドを設定します。

```go
// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'go help'.
var commands = []*Command{
        cmdBuild,
        cmdClean,
        cmdDoc,
        cmdEnv,
        cmdFix,
        cmdFmt,
        cmdGenerate,
        cmdGet,
        cmdGetWild,  // <= New!!
        cmdInstall,
        cmdList,
        cmdRun,
        cmdTest,
        cmdTool,
        cmdVersion,
        cmdVet,

        helpC,
        helpBuildmode,
        helpFileType,
        helpGopath,
        helpEnvironment,
        helpImportPath,
        helpPackages,
        helpTestflag,
        helpTestfunc,
}
```

### コンパイル

人によって環境が違うと思いますが、下記のようにコンパイルします。

```bash
$ cd go/src
$ GOROOT_BOOTSTRAP=~/go1.7.4 ./make.bash 
```

このようにコンパイルすることによって、 go/bin に成果物が出力されます。

### `go getwild` の実行

```
$ cd go/bin
$ ./go help
Go is a tool for managing Go source code.

Usage:

	go command [arguments]

The commands are:

	build       compile packages and dependencies
	clean       remove object files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         run go tool fix on packages
	fmt         run gofmt on package sources
	generate    generate Go files by processing source
	get         download and install packages and dependencies
	getwild     get wild and tough
	install     compile and install packages and dependencies
	list        list packages
	run         compile and run Go program
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         run go tool vet on packages

Use "go help [command]" for more information about a command.
```

```
$ cd go/bin
$ ./go getwild help
usage: getwild [-tough]

Print just the 'get wild'.

The -tough flag prints more message we want.
```

## 出力

-tough と -v オプションを付けて github.com/kaneshin/gate/cmd/gatecli をインストールします。

```
$ ./go getwild -tough -v github.com/kaneshin/gate/cmd/gatecli

 ________  _______  _________
|\   ____\|\  ___ \|\___   ___\
\ \  \___|\ \   __/\|___ \  \_|
 \ \  \  __\ \  \_|/__  \ \  \
  \ \  \|\  \ \  \_|\ \  \ \  \
   \ \_______\ \_______\  \ \__\
    \|_______|\|_______|   \|__|

 ___       __   ___  ___       ________
|\  \     |\  \|\  \|\  \     |\   ___ \
\ \  \    \ \  \ \  \ \  \    \ \  \_|\ \
 \ \  \  __\ \  \ \  \ \  \    \ \  \ \\ \
  \ \  \|\__\_\  \ \  \ \  \____\ \  \_\\ \
   \ \____________\ \__\ \_______\ \_______\
    \|____________|\|__|\|_______|\|_______|

 ________  ________   ________
|\   __  \|\   ___  \|\   ___ \
\ \  \|\  \ \  \\ \  \ \  \_|\ \
 \ \   __  \ \  \\ \  \ \  \ \\ \
  \ \  \ \  \ \  \\ \  \ \  \_\\ \
   \ \__\ \__\ \__\\ \__\ \_______\
    \|__|\|__|\|__| \|__|\|_______|

 _________  ________  ___  ___  ________  ___  ___
|\___   ___\\   __  \|\  \|\  \|\   ____\|\  \|\  \
\|___ \  \_\ \  \|\  \ \  \\\  \ \  \___|\ \  \\\  \
     \ \  \ \ \  \\\  \ \  \\\  \ \  \  __\ \   __  \
      \ \  \ \ \  \\\  \ \  \\\  \ \  \|\  \ \  \ \  \
       \ \__\ \ \_______\ \_______\ \_______\ \__\ \__\
        \|__|  \|_______|\|_______|\|_______|\|__|\|__|
github.com/kaneshin/gate (download)
github.com/BurntSushi/toml (download)
```

このように、GET WILD AND TOUGH を出力したうえで `go get -v` を行います。

## おわりに

go のコマンド自身は golang で書かれているのでカスタマイズは比較的簡単に行うことができるので、自身でカスタマイズしちゃいましょう！