朝日ネット開発部のmuratamです。 先日Go 1.12もリリースされたことなので、今回はGo modulesについて書きます。
これまでのGoのパッケージ管理
これまでは環境変数GOPATH
の下にソースコード等を配置し、そこから使用するパッケージを探して使用するようにしていました。
GitHubなどからgo get
したパッケージは$GOPATH/src
の下にダウンロードされ、
Goのソースの中でimport
文を書いて使います。
自分で作るライブラリもGOPATH
の下に作っておけば同じように$GOPATH/src
からの相対パスでimport
ができます。
例えばgo get github.com/foo/bar
とすれば $GOPATH/src/github.com/foo/bar
にダウンロードされ、import "github.com/foo/bar"
で使うことができます。
dep
depはGoでパッケージの依存関係を自動で解決して管理するソフトウェアのひとつです。 Goでは<パッケージのルート>/vendor/をimport pathとみなすことができるので、 vendor/ にパッケージを保存して特定のバージョンを固定して使うことができます(vendoring)。
Modulesの導入
これまでのGOPATH
に代わってパッケージ管理する仕組みとしてGo 1.11からmodulesが追加されました。
今後はこちらに移っていく意向で、modulesは1.13 (2019年8月リリース予定)でデフォルトになる模様です。
moduleを使うときは、go.modファイルをGoプログラムのルートディレクトリに置きます。 go.modファイルではモジュール名の宣言と、依存モジュールを記述することができます。
go build
などモジュールのimport
が必要になれば$GOPATH/pkg/mod/
以下にバージョン別に整理されてダウンロードされ、参照できるようになります。
自分のプログラム内でimport
するときもGOPATH
の下で作業をしなくてもモジュール名を使ってimport
ができます。
go.modには依存パッケージのバージョンを書いて使用するライブラリのバージョンを指定することができます。 これによりdepなど既存のパッケージ管理ツールを置き換えることになりそうです。 実際、go.modを使うときはvendor/ 以下にあるパッケージを見ない挙動になっているようです。
使ってみる
ここではgo 1.11.5を使います。
$ go version go version go1.11.5 linux/amd64
GOPATH
の外でディレクトリを作ります。 1
$ mkdir newmod $ cd newmod
go mod init <module名>
でmodule名が設定されたgo.modファイルを作ることができます。
$ go mod init newmod go: creating new go.mod: module newmod $ cat go.mod module newmod
golang.org/x/text/width を使って全角文字を半角文字に変換するプログラムを作ってみます。 別のパッケージ libに、変換を行う関数を置きます。
main.go
package main import ( "fmt" "newmod/lib" ) func main() { s1 := "abc123-~アイウ|" s2 := "abc123-~アイウ|" fmt.Println(s1) ns := lib.Convert(s2) fmt.Println(ns) }
lib/lib.go
package lib import "golang.org/x/text/width" func Convert(s string) string { return width.Narrow.String(s) }
main.goから、newmod/lib
という名前でimport
しています。
これまでは$GOPATH/src/newmod/lib
にあるものをimport
していましたが、
go.modファイルのある場所からlibを探してimport
しています。
go build
すると自動でimport
文を解決して必要なパッケージをダウンロードします。
$ go build newmod go: finding golang.org/x/text/width latest go: finding golang.org/x/text v0.3.0 go: downloading golang.org/x/text v0.3.0
go.modに依存モジュールを要求するrequire
が書き込まれています。
$ cat go.mod module newmod require golang.org/x/text v0.3.0 // indirect
ファイルそのものはGOPATH
の下にダウンロードされています。
ls $GOPATH/pkg/mod/golang.org/x/text@v0.3.0/
確認のためビルドしたバイナリを実行すると期待通りに動いています。
$ ./newmod abc123-~アイウ| abc123-~アイウ|
depからの移行
go.modファイルを生成するgo mod init
コマンドではdepで使われるGopkg.lockファイルがあればそれを解釈してgo.modに反映させる機能があるので使ってみます。
先ほどと同じソースコードをGOPATH
の下に用意してdep init
します。
$ dep init Using ^0.3.0 as constraint for direct dep golang.org/x/text Locking in v0.3.0 (f21a4df) for direct dep golang.org/x/text
Gopkg.lockはこのような感じ
[[projects]] digest = "1:fc3e61b10647ddcee206a5431707878c043faeb5a1cef6de1e0f1d3a2147bcfa" name = "golang.org/x/text" packages = [ "internal/gen", "internal/triegen", "internal/ucd", "transform", "unicode/cldr", "width", ] pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = ["golang.org/x/text/width"] solver-name = "gps-cdcl" solver-version = 1
GO111MODULE=on
でmodulesを有効にしてgo mod init
をしてみます。
$ GO111MODULE=on go mod init newmod go: creating new go.mod: module newmod go: copying requirements from Gopkg.lock $ cat go.mod module newmod require golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38
バージョン指定がv0.0.0
(未指定)ですが末尾のcommit hashは同じように見えます。2
おわりに
まだmodulesを実運用するには早そうですが備えてはおきたいですね。
参考
採用情報
朝日ネットでは新卒採用・キャリア採用を行っております。