はじめに
TIG真野です。
Go 1.16連載の2つ目となるこの記事では、Go 1.16で機能追加された go install について説明します。
go installの新機能
Go 1.16から go installに新機能が追加されました。go install 自体は従来から存在しているので全く新しいコマンドが増えたわけではなく、機能拡張されたイメージです。
go installの機能拡張部分ですが簡単に言うと、バージョン指定できるようになりました。言ってみればそれだけです。嬉しいこととして、それによってgo.modの書き換え無しにツールなどのインストールを行いやすくなりました。ちなみに、インストールとはコードをコンパイルして、$GOPATH/binとか$HOME/go/binにビルドしたバイナリを配備してくれることです。
従来はgo getでツールのインストールも行えていましたが、同時にgo.modも書き換わってしまいました。メインで開発するモジュールで利用するライブラリならgo.modに追記されることが自然ですが、例えばGoのLanguage Serverであるgoplsや、stringer といった開発系のコマンドラインツールの場合は少し困りました。
どういうことかと言うと、メインで開発するモジュール本体のgo.modで、例えばgo get golang.org/x/tools/cmd/stringerでインストールすると一時的に go.mod に追加されてしまうのです。もちろん、コードの中にはimportされていないため、go mod tidyすると消すことができますが、新規参画者の最初の環境構築で手順を間違えると不要な差分が出てきて少し手間でした。参画タイミングによって新しいバージョンが出たりすると、go getでgo.modのバージョンが書き換わるのもまた問題でした。また、逆に go mod tidy するとgo.modから削除されますが、つまり開発系ツール自体のバージョン管理ができなくなる問題もありました。そちらについては後述します。
今回追加された go install を用いればこういったストレスからは開放されるかと思います。なんとなく go getの機能が分割され、今後はGo Module追加編集のためのgo get、ツールなどのバイナリインストールのgo installと住み分けることができそうです。
利用方法
go install example.com/cmd@v1.0.0 の形式で利用可能です。go getと同じ指定方法ですね。
例えば、stringerであれば
go get golang.org/x/tools/cmd/stringer@v0.1.0です。バージョンは必須とのこと。最新で良い場合は@latestをつけます。
動かしてみる
Go 1.16のRelease Candidate1でgo installを動かしてみます。
>go version |
適当なフォルダで go mod init して、go get golang.org/x/tools/cmd/stringer@v0.1.0 と go install golang.org/x/tools/cmd/stringer@v0.1.0 の実行結果を比較してみます。
>go get golang.org/x/tools/cmd/stringer@v0.1.0 |
Go 1.16だとまだ go get でインストールもできるようですね(将来的に消える可能性があるので注意です)
続いて、go install で動かしてみます。
>go install golang.org/x/tools/cmd/stringer@v0.1.0 |
リリースノート通り、go.mod への副作用はありませんでした。
ローカルリポジトリに対してのgo install
こちらは従来どおり、そのまま利用可能です。
package main |
以下のような適当なmainパッケージなファイルを作成してinstallします。
$ go install mycmd.go |
また、巷では、toolsフォルダをつくって、そこでgo mod init toolsして、上記の記事のようなブランクインポートする流派もあります。こうすると開発しているモジュールが依存するパッケージと、ツールのバージョン別に管理できるプラクティスとして一定の広がりがあったと思います。
どちらにしても開発ツールのバージョンをgo.modに記載し、そのディレクトリでgo install golang.org/x/tools/cmd/stringer などとしていました。複数あるとそのまま複数個installコマンドを愚直に打つか、cat tools.go | grep _ | awk -F'"' '{print $2}' | xargs -tI % go install % などでシェル技が炸裂していました。
Go 1.16での変化ですが、RC1時点だと go getでインストールはまだ行ってくれます。しかし将来的に使えなくなる可能性があるので、素直に go install を使いましょう。従来のgo installはバージョン指定ができなかったため、わざわざGo Module管理するためにハックを繰り返していましたが、Go 1.16だとそもそも不要です。
素直に make install でセットアップするが正解になる気がします。
|
このあたりのプラクティスは、Go WikiのHow can I track tool dependencies for a module? がどう変わるかをウォッチしていこうと思います(多分、変わるはず..)
//go:generateディレクティブ
Go Modules が導入されてから、公開されている Go 製のツールは go run によるダウンロード・ビルド・実行が一度にできるようになっていたのですが、Go1.16だとgo.modが自動で更新されない影響か、//go:generate go run golang.org/x/tools/cmd/stringer -type=Pill でインストール無しで go generate するだけでコード生成するハックが使えなくなりました。
package generate |
上記のようにすると、各開発者が事前準備無しにgo generateさえ実行するだけで済んだのですが、Go1.16だとこうなります。
>go generate |
同じ開発体験を守りたいなら、複数行に記載することになると思います。
package generate |
>go generate |
無事生成はできました。//go:generateディレクティブに go run コマンドを書くのはちょっとしたハック感があって好きだったのですが、初見では混乱するので消えて良かったのかもしれませんね。
余談
go buildとかgo testで自動的にgo.modが更新されない変わりに、go mod tidyしてねってメッセージがでるようなりました。どのタイミングで表示してくれるのか細かくは良く分かりませんが、気がついたらバージョンを上げると良いかと思います。
まとめ
go installでバージョンを指定ができるようになった- いくつかのハックが陳腐化したり、使えなくなったが、試行錯誤した経験は我々の中にずっと残り続ける