TL;DR
go get
で複数のツールを同時にインストールすると 意図しないバージョンのパッケージが使われることがある- ツールは1つずつ
go get
したほうが良い
経緯
go get
がモジュールおよびパッケージを取得・更新するサブコマンドだということはご存じかと思います。
一方でモジュールモードでは go install
の代わりに go get
がGo製コマンドのインストールコマンドとして機能します。
さらにコマンドインストールのユースケースにおいてバージョンを指定すると、 依存するモジュール群のバージョンを完全に固定してビルドできるという特徴があります。 以下はそのコマンド例です。
$ GOBIN=`pwd` GO111MODULE=on go get github.com/golang/protobuf/protoc-gen-go@v1.4.2
go: found github.com/golang/protobuf/protoc-gen-go in github.com/golang/protobuf v1.4.2
環境変数 GOBIN
でビルド済みバイナリのインストール場所を指定し、
GO111MODULE
で GOPATH
内でなくても強制的にモジュールモードにしています。
この例では現在のディレクトリに protoc-gen-go v1.4.2 のビルド済みバイナリが配置されます。
protoc-gen-go@v1.4.2 は google.golang.com/protobuf@v1.23.0 に依存しています。 そのことは protoc-gen-go@v1.4.2 の go.mod に宣言されています。 参照
ビルド済みバイナリからこのことを確認してみましょう。
$ strings protoc-gen-go | grep '^dep\s'
dep google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
確かに golang.golang.protobuf@v1.23.0 を用いています。
さてここで protoc-gen-go@v1.4.2 と一緒にGo製のポピュラーなswaggerツール
github.com/go-swagger/go-swagger@v0.25.0 を同時に go get
してみましょう。
以下はそのコマンドです。
$ GOBIN=`pwd` GO111MODULE=on go get \
github.com/golang/protobuf/protoc-gen-go@v1.4.2 \
github.com/go-swagger/go-swagger/cmd/swagger@v0.25.0
go: found github.com/go-swagger/go-swagger/cmd/swagger in github.com/go-swagger/go-swagger v0.25.0
go: found github.com/golang/protobuf/protoc-gen-go in github.com/golang/protobuf v1.4.2
そしてビルドされた protoc-gen-go の依存を調べるとなんと protobuf@1.25.0 に強制的に変更されてしまっています。 見てみましょう。
$ strings protoc-gen-go | grep '^dep\s'
dep google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
protoc-gen-go@v1.4.2 の go.mod が明示的に指定したバージョンを用いてないこと、
また go get
をモジュールモードで明示的なバージョン指定を用いたことを考えると、
直感に反した振る舞いと言えるでしょう。
これは go-swagger@v0.25.0 の go.mod が protobuf@v1.25.0 を要求していることが原因です。 参照
go.mod で管理されたモジュール内で複数のモジュールを go get
した時の動作だと考えればこれは理に適っています。
Goは依存モジュールについて複数のバージョンが指定されると、それらのうちもっとも新しいものを選択します。
しかしこの動作はGo製コマンドのインストールという観点では go.mod に指定されたバージョンが使われないという直感に反する事態を引き起こします。
これを回避するためにGo製コマンドは1つずつ go get
でインストールするほうが良いでしょう。
なお go install
をモジュールモードに対応させようという話もあるので
(参照)
近い将来、改善されるかもしれません。
知らんけど。
以上今日ハマった go get
でのコマンドインストールについてのわかりにくい落とし穴でした。