Goを初めて触る人向けにGo Modulesとは何かを解説する

2020-07-28

はじめに

この記事ではGoを初めて触る人向けに「Go Modules」の全体像・使い方を公式ブログを元にhands-on的な感じで掻い摘んで解説していきます。

Go Modulesとは

Go Modulesは外部パッケージを管理するシステムのことを指します。

Go modules in a nutshell is a built-in dependency versioning and dependency management feature for Go.

引用:「Getting started with Go modules

moduleについて

moduleはpackageのコレクションです。(コレクションというのは複数の要素を格納するためのデータ構造のことを指します)例えばhelloというpackageが存在するとします。このpackageはrsc.io/quote をimportして内部の関数で使用しています。

package hello import "rsc.io/quote" func Hello() string { return quote.Hello() }

rsc.io/quoteの中にさらにpackageがimportされているので依存関係が発生しています。この依存関係はgo.modでファイルツリー構造のデータで保持され、このデータをmoduleと呼びます。

Go 1.11からの挙動

Go 1.11から現在のディレクトリ階層または親のディレクトリ階層にgo.modファイルがあれば$GOPATH/src以外の階層にいてもGoコマンドがmoduleを使用することができるようになりました。(これはmodule modeと呼ばれるようです)またこのmodule modeはGo 1.13からデフォルトになりました。逆にGo 1.11以前は$GOPATH/src以外の階層ではmoduleを使用することができない、すわなち$GOPATH以下でしか作業用プロジェクトを作ることができませんでした。(以前のmodeをGOPATH modeと呼ぶそうです)またこのGOPATH modeでは

  • $GOPATH/src
  • $GOPATH/src/ 配下に置かれたvendorディレクトリ

からpackageを探しにいく挙動だったようです。

Go Moduleを手を動かして理解する

公式Blogの内容を元に実際に手を動かしながらGo Moduelsの使い方を理解していきましょう。

// hello.go package hello func Hello() string { return "Hello, world." } // hello_test.go package hello import "testing" func TestHello(t *testing.T) { want := "Hello, world." if got := Hello(); got != want { t.Errorf("Hello() = %q, want %q", got, want) } }

この状態でtestを実行します。

$ go test PASS ok _/<適当なディレクトリ> 0.403s

Go Moduleを使用するにはgo.modが必要です。なのでこの状態だとGo Moduleは使用されておらずテストを実行したディレクトリでpackageが動作します。(import pathがない場合は現在のディレクトリに仮のpathを生成・表示されるようです。)

ではGo Moduleを使うためにgo.modファイルを生成します。

$ go mod init example.com/hello // go.mod module example.com/hello go 1.13

では先ほど実施したテストを再実行してみます。

$ go test PASS ok example.com/hello 0.430s

テストの結果からgo.modのmoduleで指定したexample.com/helloでテストが実行されていることがわかります。ここでhello2というディレクトリを作成して、その中に内容の同じhello2というpackageを作ったとします。この場合packageのpathはexample.com/hello/hellow2になります。

ちなみにmodule名はドメインの形でなくとも問題なく生成されます。

$ go mod init hoge-pkg // go.mod module hoge-pkg go 1.13 $ go test PASS ok hoge-pkg 0.340s

ただ一般的にはmodule名はドメイン形式で定義するようです。(moduleはそもそもインターネット経由で配布・ダウンロードする前提だからなのでしょうか?正直わかってないのでわかったら再度更新します)

では次にrsc.io/quoteというpackageをimportして使ってみます。

// hello.go package hello import "rsc.io/quote" func Hello() string { return quote.Hello() }

Go Modulesを使った場合、packageは$GOPATH/pkg/modに配置されます。

the downloaded modules are cached locally (in $GOPATH/pkg/mod):

引用:「Using Go Modules

この状態でgo testを実行すると$GOPATH/pkg/modにこのpackageがまだダウンロードされていない場合、次のようにダウンロードが開始されます。

go: finding rsc.io/quote v1.5.2 go: downloading rsc.io/quote v1.5.2 go: extracting rsc.io/quote v1.5.2 go: finding rsc.io/sampler v1.3.0 go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c go: downloading rsc.io/sampler v1.3.0 go: extracting rsc.io/sampler v1.3.0 go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // go.mod module example.com/hello go 1.13 require rsc.io/quote v1.5.2

また同時に依存関係を保存するためにgo.sumが生成されます。(npmでいうpackage.lock.jsonみたいなものです)

// go.sum golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y= rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

依存関係も全て含んだpackageの一覧を確認したい場合go list -m allを使用します。

$ go list -m all example.com/hello golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c rsc.io/quote v1.5.2 rsc.io/sampler v1.3.0

これによりrsc.io/quotegolang.org/x/textrsc.io/samplerに依存していることがわかります。では最後に使用していないpackageの削除を行う方法について紹介します。hello.goを次のようにrsc.io/quoteを使わない1番初めの書き方に戻したとします。

// hello.go package hello func Hello() string { return "Hello, world." }

これによりrsc.io/quoteは不要なパッケージになりました。このように不要なpackageをGo Modulesの管理から除外したい場合go mod tidyを実行すればよいです。

$ go mod tidy $ go list -m all example.com/hello // go.mod module example.com/hello go 1.13

これによりrsc.io/quoteとこのpackageに依存するpackageも全て削除されました。また実行時に不足しているpackageがあればインストールしてくれます。(こちらゴリラさんが教えてくれました。ありがとうございます。)

おわりに

最低限の知っておきたいGo Modules周辺の使い方は紹介できたかなと思います。余談ですがGo 1.16からGo Modules周りでいくつか変更があったようなので余裕がある方はチェックしてみてください。

参考文献:

KATUO
Software Engineer