朝日ネット開発部の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を実運用するには早そうですが備えてはおきたいですね。
参考
採用情報
朝日ネットでは新卒採用・キャリア採用を行っております。
