【Go言語】modulesについて理解するために過去から調べてみた

# はじめに
go1.11から導入されたmodulesを使うにあたって、あまりよく分かっていなかったので
**ちょっと過去からmodulesに至るまでの問題や詳細な動きなどを調べてみました。**

その時の備忘録を残しておきます。
理解するだけなので、情報が無い部分については自分で調べて、すでに調査済みの箇所は
多くの先人たちの記事を参考にさせていただきました。ありがとうございます。

# ライブラリのインストール　Go get
```go get https://github.com/...```で```$GOPATH/src```配下にライブラリをダウンロードする事で、
importで指定してライブラリを使用する事が出来ます。

srcやpkg、binの詳細は以下を参照
[プログラムの全体構成](https://astaxie.gitbooks.io/build-web-application-with-golang/ja/01.2.html)

```
bin/
    mathapp
pkg/
    プラットフォーム名/ 例：darwin_amd64、linux_amd64
         mymath.a
         github.com/
              astaxie/
                   beedb.a
src/
    mathapp
          main.go
      mymath/
          sqrt.go
      github.com/
           astaxie/
                beedb/
                    beedb.go
                    util.go
```

>上述の構成から明確に判断できるのは、binディレクトリの下にコンパイル後の実行可能ファイルが保存され、pkgの下に関数パッケージが保存され、srcの下にアプリケーションのソースコードが保存されているということです。

# GOPATH問題
GOPATHによる問題がいくつかあるようです。
（modulesによってこの問題が解決されます。）

### 複数プロジェクト
```go get```によって全てのライブラリが```$GOPATH/src```配下にダウンロードされます。
GOPATH配下のライブラリは複数プロジェクトで共通です。
つまり、複数のプロジェクトで同じライブラリ（バージョンが違う）を使う時などに問題が生じます。
プロジェクト毎にGOPATHを変更する手段もありますが、複雑になってしまいます。

### バージョン差異
go getではバージョンの指定ができないため、AさんBさんの環境でライブラリの差異が出てしまう可能性があります。

### GOPATH配下の限定
作成したいプロジェクトを```$GOPATH/src```配下に配置して```github.com/...```など
PATHをちゃんと設定しないといけません。
GOPATH配下に限定されるので、初めてGoを動かすときにかなり苦戦した覚えがあります。

### 参考記事
以下の記事を参考にしました。

- [GOPATH 汚染問題](http://text.baldanders.info/golang/gopath-pollution/)
- [Go言語のDependency/Vendoringの問題と今後．gbあるいはGo1.5](https://deeeet.com/writing/2015/06/26/golang-dependency-vendoring/)

# Vendoringの導入
go1.5でVendoringの導入がされました。
```$GOPATH/src```配下にライブラリをインストールするのではなく、
プロジェクト直下のvendorディレクトリ配下にライブラリをインストールできるようになりました。
何が嬉しいのかというと、上述した複数プロジェクトでのライブラリの共有などの問題が解消されます。

また**Vendor tree → GOROOT → GOPATH**の順でパッケージを探します。

## depで依存関係の管理
依存管理ツールのdep。
vendor配下にライブラリをインストールしたりしてくれます。
dep以外にも色々似たようなライブラリはありますが、実際に自分が使ってきたのがdepなので
ここでは一例としてdepについて調べます。

### Gopkg.toml
直接編集して依存ライブラリを決めたり出来ます。
詳細な書き方は以下の記事を参考にしたところ、すぐ分かりました。

- [golang/dep](https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md)
- [Goオフィシャルチーム作成の依存関係管理ツール dep を試してみた](https://dev.classmethod.jp/go/dep/)

### Gopkg.Lock
アプリケーションに記載したimportなどを解析して、どのリビジョンを使うかのSHAやgitのURLなどが指定されます。
以下実際のプロジェクトのechoの情報です。
ここでバージョンを指定するため、複数人で使用しても同じバージョンのライブラリがdep ensureでダウンロードされます。

```golang
[[projects]]
  digest = "1:87f801436ec4799e0e043ab348ff9f3e95b1b3761138158f0950ac67695cc272"
  name = "github.com/labstack/echo"
  packages = [
    ".",
    "middleware",
  ]
  pruneopts = "UT"
  revision = "6d227dfea4d2e52cb76856120b3c17f758139b4e"

```

# vendor問題
原因が分かっていないですが、たまに既存の空のvendorディレクトリがあるときに、
dep ensureをすると失敗になったりして少し苦戦した覚えがあります。
ですが、これといった問題はあまり無いように思います。
（改良されてvendorディレクトリが廃止になるので何かしら問題ありそうですが。。。）

**ご存知の方がいましたらコメントで教えていただけると助かります。**

# modulesとは？
ここから本題のgo 1.11で導入されたmodulesについてです。

## 大きく変わった点
### GOPATHの配下へのプロジェクト配置が不要
**モジュールと呼ばれる新しいコンセプト**を導入したそうです。
>an alternative to GOPATH with integrated support for versioning and package distribution.

>A module is a collection of related Go packages. Modules are the unit of source code interchange and versioning. The go command has direct support for working with modules, including recording and resolving dependencies on other modules. Modules replace the old GOPATH-based approach to specifying which source files are used in a given build.

**モジュールとは、GOPATHの代わりとなるバージョン管理とパッケージ配布のサポートを統合したもの。**だそうです。
（パッケージの集合体みたいなイメージ）
そのモジュールの情報はmoduleファイルの(go.mod)に記載されます。
結果として、どのライブラリを使うかが明確に特定できるようになったので、
GOPATH配下にプロジェクト配置しなくても動くようになったそうです。

### vendorディレクトリの廃止
depを使っていたときは、vendorディレクトリに各々の必要なライブラリを落として使用していました。
go 1.11からはvendorディレクトリが廃止になりました。
どうして廃止に出来たのかわからなかったので自分なりに調べてみました。
（間違ってるかもしれないので、間違っていましたらご指摘ください。）

調べるために、以下のようなディレクトリ構成でプロジェクトを作ってみました。

```

.
├── fuga
│   ├── go.mod
│   ├── go.sum
│   └── main.go
└── hoge
    ├── go.mod
    ├── go.sum
    └── main.go
```

main.goの中身は同じですが、go.modファイルのrsc.io/quoteのバージョンをfugaとhogeで変更してみました。
結果として、2つのバージョンが```$GOPATH/pkg/mod/rsc.io```配下にダウンロードされていました。

```
~/go $ ll pkg/mod/rsc.io/
quote@v1.4.0/     quote@v1.5.2/     sampler@v1.99.99/ 
~/go $ ll pkg/mod/rsc.io/
```

つまり**ダウンロード先は共通だが、使うモジュールのバージョンなどは各プロジェクトのgo.modファイルが特定してくれるため、
vendorディレクトリを持つ必要がなくなったのかなと思います。**


### Semantic Versioning
Semantic Versioningのルールに沿ってバージョンを決めてよ！って認識です。

- 後方互換性の無い変更の場合は、メジャーバージョンを上げる
- 後方互換性が取れた変更などはマイナーバージョンを上げる
- 不具合修正などはパッチバージョンを上げる

※メジャーバージョンを上げるときは、ブランチを変えたりもするみたいです。

以下の記事を参考にしました。

- [セマンティック バージョニング 2.0.0](https://semver.org/lang/ja/)
- [GOPATH モードからモジュール対応モードへ移行せよ](https://qiita.com/spiegel-im-spiegel/items/5cb1587cb55d6f6a34d7#semantic-versioning)



## モジュールファイルについて
### go.mod
ここにモジュール関連の情報が記載されます。
ここではrequiredしかありませんが、replace、excludeなど他にも指定できます。
詳細は参考資料にありましたので、そちらを参照。

```golang
# go.mod
module github.com/yoshinorihisakawa/hello

require (
        github.com/labstack/echo v1.4.4
        github.com/labstack/gommon v0.2.1 // indirect
)
```

### go.sum
go.modファイルをみて実際にダウンロードしたモジュールが全て記載される？
上記のhogeパッケージのgo.modファイルのrsc.ioのバージョンを変更してgo buildしてみました。
結果として2つのバージョンが記載されています。

```golang
# go.sum
rsc.io/quote v1.4.0 h1:tYuJspOzwTRMUOX6qmSDRTEKFVV80GM0/l89OLZuVNg=
rsc.io/quote v1.4.0/go.mod h1:S2vMDfxMfk+OGQ7xf1uNqJCSuSPCW5QC127LHYfOJmQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
```

以下の記事を参考にしました。

- [モジュール対応モードへの移行を検討する](http://text.baldanders.info/golang/go-module-aware-mode/)

### ローカルのパッケージを参照させる場合
depを使っていたときは、ローカルで開発中のライブラリをいちいちリモートに上げて、
dep ensure -updateを行い、更新されたライブラリを落としてきていて面倒でしたが、
以下の方法を使えば、ローカルのライブラリを参照してくれるみたいです。

- [非公開モジュールやローカルで変更したモジュールをGo Modules(vgo)環境で利用する3つの方法](https://engineers.fenrir-inc.com/entry/2018/10/19/170613)

### depからの移行
ちゃんとサポートされているようです。
Gopkg.lockファイルがあればgo mod init で指定ライブラリを取ってきてくれるみたいです。

ここについてはいつか実施すると思うので、その時またまとめます。

### メモ書き
- Gopkg.lockファイルがあれば、指定されたバージョンのライブラリをダウンロードしてくれる
    - depなどのツールからの移行をサポートしてくれている
- Golandを使ってる人はバージョンを上げないとうまくライブラリを参照してくれないです。
    - 地味にハマりました。。。
- 今は試験的なので、GO111MODULEという環境変数をonにする必要あり
- $GOPATH/pkg/mod/配下にライブラリがダウンロードされる。
- $GOPATH/pkg/mod/cache/download/配下に上記のライブラリのキャッシュが置かれる
    - pkg/mod配下に対象のライブラリが無いときは、cacheから落としてくるため、ビルドスピードは早い。
    また、cacheに存在しないときは、githubなどからダウンロードしてくる。


# 最後に
今まで無意識にしていたことを、意識的に考える事で、色々背景や問題を見ることができました。
まだ、こういった問題があるならこうすれば良いなど提案できるレベルでは全く無いのでいつかそうなれるようになりたいなと思いました。

# その他の参考記事
- [Go 1.11 の modules・vgo を試す - 実際に使っていく上で考えないといけないこと #golang](https://www.wantedly.com/companies/wantedly/post_articles/132270)

- [Go & Versioning(vgo)を読んで大きな変更が入ったなと思った](https://qiita.com/lufia/items/67701e2f927c77a75d6e)

- [和訳: Go += Package Versioning (Go & Versioning, Part 1)](https://qiita.com/nekketsuuu/items/60634417e6279ccfd95b)

- [和訳: A Tour of Versioned Go (vgo) (Go & Versioning, Part 2)](https://qiita.com/nekketsuuu/items/589bc29f00b507492a96)

- [和訳: Semantic Import Versioning (Go & Versioning, Part 3](https://qiita.com/nekketsuuu/items/2dcad7dde29171e1fe3f)

- [go modules(vgo)でlabstack/echo依存のプロジェクトをビルドする](https://himetani.cafe/posts/vgo-echo-sample/)