Edited at

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


はじめに

go1.11から導入されたmodulesを使うにあたって、あまりよく分かっていなかったので

ちょっと過去からmodulesに至るまでの問題や詳細な動きなどを調べてみました。

その時の備忘録を残しておきます。

理解するだけなので、情報が無い部分については自分で調べて、すでに調査済みの箇所は

多くの先人たちの記事を参考にさせていただきました。ありがとうございます。


ライブラリのインストール Go get

go get https://github.com/...$GOPATH/src配下にライブラリをダウンロードする事で、

importで指定してライブラリを使用する事が出来ます。

srcやpkg、binの詳細は以下を参照

プログラムの全体構成

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を動かすときにかなり苦戦した覚えがあります。


参考記事

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


Vendoringの導入

go1.5でVendoringの導入がされました。

$GOPATH/src配下にライブラリをインストールするのではなく、

プロジェクト直下のvendorディレクトリ配下にライブラリをインストールできるようになりました。

何が嬉しいのかというと、上述した複数プロジェクトでのライブラリの共有などの問題が解消されます。

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


depで依存関係の管理

依存管理ツールのdep。

vendor配下にライブラリをインストールしたりしてくれます。

dep以外にも色々似たようなライブラリはありますが、実際に自分が使ってきたのがdepなので

ここでは一例としてdepについて調べます。


Gopkg.toml

直接編集して依存ライブラリを決めたり出来ます。

詳細な書き方は以下の記事を参考にしたところ、すぐ分かりました。


Gopkg.Lock

アプリケーションに記載したimportなどを解析して、どのリビジョンを使うかのSHAやgitのURLなどが指定されます。

以下実際のプロジェクトのechoの情報です。

ここでバージョンを指定するため、複数人で使用しても同じバージョンのライブラリがdep ensureでダウンロードされます。

[[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のルールに沿ってバージョンを決めてよ!って認識です。


  • 後方互換性の無い変更の場合は、メジャーバージョンを上げる

  • 後方互換性が取れた変更などはマイナーバージョンを上げる

  • 不具合修正などはパッチバージョンを上げる

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

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


モジュールファイルについて


go.mod

ここにモジュール関連の情報が記載されます。

ここではrequiredしかありませんが、replace、excludeなど他にも指定できます。

詳細は参考資料にありましたので、そちらを参照。

# 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つのバージョンが記載されています。

# 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=

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


ローカルのパッケージを参照させる場合

depを使っていたときは、ローカルで開発中のライブラリをいちいちリモートに上げて、

dep ensure -updateを行い、更新されたライブラリを落としてきていて面倒でしたが、

以下の方法を使えば、ローカルのライブラリを参照してくれるみたいです。


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などからダウンロードしてくる。




最後に

今まで無意識にしていたことを、意識的に考える事で、色々背景や問題を見ることができました。

まだ、こういった問題があるならこうすれば良いなど提案できるレベルでは全く無いのでいつかそうなれるようになりたいなと思いました。


その他の参考記事