Edited at
GetWildDay 12

golang のサブコマンドで GetWild

More than 1 year has passed since last update.

この記事は GetWild Advent Calendar 2016 - Qiita の12日目の記事です。

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



「Get wild 禁断症状」とは

業務中にGETWILD分が切れて禁断症状がでそうになった際に必死に耐えるための方法論~#ghq ないし go getで頑張る


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

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

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


go getwild コマンド

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


go のサブコマンド

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

$ 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

$ cd go/src/cmd/go

$ vim getwild.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の用意

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 構造体をコマンド用に定義しておきます。

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

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のコマンドのリストに定義したコマンドを設定します。

// 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,
}


コンパイル

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

$ 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 で書かれているのでカスタマイズは比較的簡単に行うことができるので、自身でカスタマイズしちゃいましょう!