Go1.16からは go get は使わず go install を使おう

この記事は[Go Advent Calendar 2020](https://qiita.com/advent-calendar/2020/go) 16日目の代打記事です。奇しくも16日目にGo1.16の話をすることになりました。

### 【追記】タイトル改題しました
状況が落ち着いてだいぶ経ったのと、未だに多くの方にこの記事を見ていただけていることから、Go1.16での変更というより、今を生きる私達がどうすればいいか、という点にフォーカスしたタイトルに改題しました。本文に変更はありません。一応注記すると、go get が廃止になったわけではなく、普段の開発フローで使うことはまずなくなった、という意味です。（一通り読んでいただければお分かりいただけるかと。）

### 【追記】Go1.18について
ついに待望のGo1.18がリリースされましたね！ https://go.dev/doc/go1.18#go-command
そして予告通り go get によるインストール機能は削除されました。どうしても従来のように go get でビルドとインストールを行いたい場合、`GO111MODULE` 環境変数を `off` にしてGOPATHモードにすることで可能です。

### 【追記】Go1.17について
Go1.17がリリースされました。 https://golang.org/doc/go1.17#go-get
Go1.17では、go get によるツールのインストールに警告が表示されるようになります。Go1.18 にて、go get によるツールのインストール機能が削除される予定です。（当初は1.17で削除される予定でしたが、変更されました。）

## はじめに
Go1.16では、Modulesに関する様々なアップデートが入ります。cf. https://tip.golang.org/doc/go1.16#tools

* ツールのグローバルインストールを簡単にするため、`go install` に新たな機能が追加されました。今後は「バイナリのビルドとインストールのための `go install`」、「`go.mod` 編集のための `go get`」と役割が整理されていくことになりました。（issueは[こちら。](https://github.com/golang/go/issues/40276))
* `GO111MODULE` はデフォルトでon、つまり常にModuleモードが有効になりました。GOPATHモードにお別れを。
* `go build` や `go test` で自動的に `go.mod` が更新されることがなくなりました。`go.mod` の編集は `go get` , `go mod tidy` , あるいは手作業で行います。

この記事では、Go1.16以降の`go get` / `go install` の使い方、抑えておきたいポイントを解説します。ちなみに、以下はすべてGOPATHモードではなくModuleモード前提で解説しています。（でないと複雑になりすぎるので）

### 抑えておきたいポイント
* 基本的に、ツールのグローバルインストールには `go install <package>@<version>` を使用します。
  * 例: `go install golang.org/x/tools/gopls@latest`
  * スクリプトなどに書いていた方はごめんなさい、修正をお勧めします。
* `go get` のバイナリインストールの機能は今後のバージョンで削除される予定です。
* `go get` は `go.mod` に依存性を追記するためだけのコマンドとして整理されていきますが、`go.mod` の編集には `go get` 以外の方法も用意されているので、`go get` を使う機会は今後少なくなっていきそうです。

## Go1.16で解決されるツールのインストール問題
今までGoでグローバル (`$GOPATH/bin`) にツールをインストールするにはお馴染みの `go get` コマンドが使われてきましたが、一つ問題を抱えていました。`go get` は `go.mod` を編集する役割も兼ねているため、`go.mod` を触らずにツールだけをインストールするには「`go.mod` のあるディレクトリを抜けて」「ツール側の `go.mod` を有効にし」`go get` する、という手順が必要でした。一つにまとめると以下のようになります。

```
$ cd $(mktemp -d); GO111MODULE=on go get golang.org/x/tools/gopls
```

流石にインストール一つにこんな手間は掛けたくないし、何よりModulesについてよく知らずに `go get` を行い不本意な `go.mod` の更新を行ってしまう事故が絶えませんでした。（ソースは筆者）

というわけで、Go1.16からは以下のようになります。

```
$ go install golang.org/x/tools/gopls@latest
```

とてもすっきりしましたね。

**実はこの `go install <package>@<version>` という書式がGo1.16で新たに導入されたものです**。この書式では、Module配下であろうとなかろうと、一貫して指定したversionを `$GOPATH/bin` にインストールします。

Go1.16では `GO111MODULE` はデフォルトでonになっているし、`go install` は `go.mod` を編集しなくなるため、もろもろの事故が起こることもなくなりました。

### 注意
* `@version` つきでインストールできるのはmain packageのみです。非main packageはこの書式の対象ではありません。

### 補足: version指定なしの `go install <package>` について

* Module外で `@version` なしの `go install <package>` は出来ません。
* Module配下では、`@version` なしのインストールも可能（`go.mod` のバージョンが反映される）ですが、main packageを `go.mod` に記載しておくには[ひと手間必要](https://qiita.com/nirasan/items/2bdbf0ada7b4182d56ce)なので、Module配下であろうと `go install <package>@<version>` を行うのがシンプルでよいかと思います。
  * 非main packageのインストールについては、次の項目をご覧ください。

## `go install` のその他の機能について
`go install` のその他の機能、つまり、

* 作業ディレクトリをビルドして `$GOPATH/bin` に配置する機能
* 非main packageをビルドしてキャッシュする機能（Module配下で、version指定せずに `go install <package>`）

については、従来のまま変わりありません。これらは無くても生きていける代物ではありますが、たまに思い出すと便利かもしれません。

## `go get`, `go.mod` について
一方の `go get` は、バイナリインストールの役割を `go install` に譲り、`go.mod` を編集するためだけのコマンドとして整理されていきます。Go1.16より後のリリース（Go1.17予定→Go1.18予定に変更）では、`go get` によるバイナリインストールの機能は削除され、`go get` はすべて `go get -d` 相当の、ソースをダウンロードし `go.mod` に追記するだけの挙動になる予定です。

### `go.mod` を編集する方法
Go1.16では他にも、`go build` や `go test` で自動的に `go.mod` が編集されないという挙動の変更がありました。これらを踏まえて、Go1.16で `go.mod` を扱う方法は以下の通りになります。

#### A) コードにimport文を書いてから `go.mod` に反映する方法
* `go get # 引数なし`
  * 新規モジュール追加のみ
* `go mod tidy`
  * 不要モジュールの削除と新規モジュールの追加を行ってくれる

#### B) importされていないモジュールを `go.mod` に追加する方法
* `go get <package>[@<version>]`
* 手作業で `go.mod` を編集

4つ挙げましたが、実際のところ `go mod tidy` がすべて賄っているので、`go mod tidy` 一本で行ってもよさそうです。

## Go1.15からの変更点
先に述べている通り、`go build` などによる `go.mod` の自動編集の機能はGo1.16から無効になるため、Go1.15までのユーザーは、`go.mod` が変更されるのに期待して `go build` するとあれっ？となるかもしれません。`go mod tidy` しておきましょう。

また、Go1.18以降で `go get` のバイナリインストールの機能が削除されるため、それまでに各種手順書やスクリプトを修正する必要がありそうです。

`gopls` も[v0.6.0からGo1.16に追従して](https://github.com/golang/tools/releases/tag/gopls%2Fv0.6.0) `go.mod` の自動編集を行わなくなる変更が入っています。順次開発フローに関しても、新しいModulesに寄せて整っていくのではないかと思われます。

## まとめ
ツール周りで一部非互換な変更が入りますが、Go1.16からのModulesは大幅に簡潔かつハマりどころも少なくなっているため、より初心者には勧めやすくなったのではないでしょうか。Goを布教したい気持ちが強い筆者としては、ハマりどころが少なくなると非常にやりやすくなるし、布教のモチベも高まります。

他にも `embed` やら `io/fs` やら目玉機能が目白押しのGo1.16が待ち遠しいところです。リリース予定は来年の2月頃です。