Golang のパッケージがよくわかってなかったんで自分なりにまとめてみました。
この記事は Go7 Advent Calendar 2019 3日目の記事です。 https://qiita.com/advent-calendar/2019/go7
この記事中で使われる環境は以下の docker-compose.yaml で用意しました。 使わなくても検証できますが、実行環境がない人やホスト環境を汚したくない人は使ってください。
version: '3.7'
services:
go113:
image: golang:1.13-stretch
container_name: go113
tty: true
volumes:
- .:/root/
working_dir: /root/
go112:
image: golang:1.12-stretch
container_name: go112
tty: true
volumes:
- .:/root/
working_dir: /root/
go111:
image: golang:1.11-stretch
container_name: go111
tty: true
volumes:
- .:/root/
working_dir: /root/
- info
- 当該Docker環境での
$GOPATH
は/go
に設定されています。
- 当該Docker環境での
import 文
基本的な構文であるimport文についても軽く整理しておきます。
Golangでは自身とは異なるパッケージを利用できるようにするために import文を使います。
パッケージはディレクトリ単位で区切られ import もこの単位で行われます。 つまり同一パッケージ内のGoファイルはすべて同じパッケージ名が指定されていなければいけません。
パッケージ名はディレクトリ名と同じにすることが強く推奨されており、短くわかりやすい名前にすべきです。 別名として参照できるので外部パッケージと名前の衝突を気にする必要はありません。
http://golang.jp/effective_go#package-names
Target directory
import文がパッケージを探すディレクトリは 標準パッケージを置くための $GOROOT と、ユーザが任意でダウンロードするパッケージを配置するディレクトリに分かれます。 前者は固定ですが後者については後述する「GOPATHモード」か「モジュールモード」で異なります。
モジュールモードについては後述するとして、GOPATHモード、つまり通常では
「 $GOPATH/src
」、「 $GOPATH/src/
配下のプロジェクトに置かれた vendor/
」のいずれかから該当モジュールを探します.
- info
- ユーザが任意でダウンロードするパッケージを大別すると、
golang.org/x/
で始まる準標準パッケージと、その他のサードパーティパッケージとがあります。
- ユーザが任意でダウンロードするパッケージを大別すると、
go get in GOPATH mode
go get
はGo においてパッケージをダウンロード(インストール)する最も一般的な方法です。
- info
- このセクションでは GOPATH モードの話をします。
- モードに関する話はモジュールモードのセクションで後述します。
これはとても便利ですが、バージョン管理の仕組みを持っていませんでした。 最新バージョンを取得してしまうし、プロジェクトごとに異なるバージョンのパッケージを利用することもできません。
たとえば ProjectA と ProjectB というプロジェクトがあり、ともに
golang.org/x/text
という準標準パッケージを利用していたとします。
しかし、 ProjectA では 0.3.2
を、 ProjectB では
0.3.1
を使いたいとしたらどうでしょうか。
$GOPATH/src
にダウンロードされるパッケージのパスはバージョン情報を含まないため、どちらか片方のバージョンしか使うことが出来ないのです。
多くの場合最新版を使うのに越したことはありませんが、動作確認が取れていなければ簡単にアップグレードすることはできません。
docker とか使うとその辺はカバーできそうですが、それはまた別の話です。
このような問題を解決すべく後述する dep や go.mod などの パッケージマネージャが登場していくわけです。
dep
- info
- パッケージについて広く浅く知ることを目的としているため、
dep
についても軽く触れますが、 現在では後述するGo modules
が使われる流れになってきているので、dep
に興味ない方は各自読み飛ばしてください。
- パッケージについて広く浅く知ることを目的としているため、
depは Golang が提供するパッケージマネージャです https://github.com/golang/dep
確証はありませんが、おそらく今後 dep は非推奨になっていくのではないかと思っています。 https://github.com/golang/dep/blob/1066608457fa1d1f8f2e1417098914f88d0ca777/website/blog/2018-07-25-announce-v0.5.0.md#dep-vgomodules-and-beyond
Go1.9以上のバージョンで利用可能です。 Mac では Homebrew で、Linuxでは
go get
でインストールしましょう。
$ go get -u github.com/golang/dep/cmd/dep
dep はプロジェクト毎に vendor
というディレクトリを作りその中でパッケージ管理を行うことで、
従来の go get
におけるバージョン重複問題を解決しています。
dep では go get
は使わずに dep ensure
コマンドでパッケージを追加していきます。
# 初期化
root@0c287973b452:/go/src/test# dep init
root@5b464397924b:/go/src/test# dep ensure -add golang.org/x/text
Fetching sources...
"golang.org/x/text" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.
root@5b464397924b:/go/src/test# ls vendor/golang.org/x/
text
# $GOPATH/pkg/dep 配下にもある(キャッシュかな?)
root@5b464397924b:/go/src/test# ls /go/pkg/dep/sources/
https---go.googlesource.com-text
# $GOPATH/src には保存されない
root@5b464397924b:/go/src/test# ls /go/src/
github.com test
- 作られたファイル
-
-
Gopkg.toml
# Gopkg.toml example # # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html # for detailed Gopkg.toml documentation. # # required = ["github.com/user/thing/cmd/thing"] # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] # # [[constraint]] # name = "github.com/user/project" # version = "1.0.0" # # [[constraint]] # name = "github.com/user/project2" # branch = "dev" # source = "github.com/myfork/project2" # # [[override]] # name = "github.com/x/y" # version = "2.4.0" # # [prune] # non-go = false # go-tests = true # unused-packages = true [prune] go-tests = true unused-packages = true [[constraint]] name = "golang.org/x/text" version = "0.3.2"
-
Gopkg.lock
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] digest = "1:88f793426b704818112a477c640b1c3419b3b525f30b96b6d65b297df4ec8568" name = "golang.org/x/text" packages = [ ".", "collate", "collate/build", "internal/colltab", "internal/gen", "internal/language", "internal/language/compact", "internal/tag", "internal/triegen", "internal/ucd", "language", "transform", "unicode/cldr", "unicode/norm", ] pruneopts = "UT" revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" version = "v0.3.2" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = ["golang.org/x/text"] solver-name = "gps-cdcl" solver-version = 1
-
-
- warning
$GOPATH/src
配下のプロジェクトであること、Goのプログラムファイルが存在することが dep を使うための条件です。これを満たさないとエラーになります。
検証めんどくさくて画面グーパンしそうになった# $GOPATH/src 配下じゃないとだめ root@5b464397924b:~# dep init init failed: unable to detect the containing GOPATH: /root is not within a known GOPATH/src # $GOPATH/src 直下だとだめなので root@5b464397924b:~# cd /go/src/ root@5b464397924b:/go/src# dep init init failed: unable to determine the import path for the root project /go/src: dep does not currently support using GOPATH/src as the project root # 移動して test ディレクトリを作って移動 root@5b464397924b:/go/src# mkdir test root@5b464397924b:/go/src# cd test/ # dep init はできた root@5b464397924b:/go/src/test# dep init # goのファイルがないと dep ensure できないそうだ root@5b464397924b:/go/src/test# dep ensure -add golang.org/x/text no dirs contained any Go code # 空のファイルじゃだめぽ.. root@5b464397924b:/go/src/test# touch main.go root@5b464397924b:/go/src/test# dep ensure -add golang.org/x/text all dirs contained build errors # package文があればGoファイルとして認識される root@5b464397924b:/go/src/test# echo package main > main.go
Go modules
Go modules は 1.11 から使えるようになった公式のパッケージマネージャです。前は
vgo
とも呼ばれていたそうです。
1.13 からはデフォルトで利用可能ですが、それ未満では環境変数で
GO111MODULE=on
もしくは GO111MODULE=auto
のようにすると使えるようになります。1.13 以上と それ未満の Go modules
では Go modules が有効になる要件が異なるようですが、ここでは 1.13を対象とした話をします。
Go modules は dep のようにパッケージの依存関係やバージョンを保存することができますが、
dep で使っていた Gopkg.toml
, Gopkg.lock
は使わず、 go.mod
で管理します。
また、別途インストールする必要はありません。
Go modules によるパッケージ管理では go.mod
と同じ階層に go.sum
と呼ばれるファイルが作られますが、これはパッケージごとのチェックサムを記録したファイルであって、ロックファイルではありません。
(go.mod
だけで依存バージョンまで再現可能です)
go.mod
からパッケージが削除されても go.sum
から自動的に消えることはなく使用が再開されたときにもチェックサムの検証が可能となります。
https://github.com/golang/go/wiki/Modules#is-gosum-a-lock-file-why-does-gosum-include-information-for-module-versions-i-am-no-longer-using
公式には go.sum
は go.mod
と同様にリポジトリにコミットすることが推奨されていますが、チェックサムの計算方法がこれまでに何度か変わっているらしく、
これにより checksum mismatch
が発生するため、 go.sum
をコミットしないという対応をしているところもあるようです。
(今の事情はよくわからないのでキャッチアップしたら追記します)
https://medium.com/@ponde_m/go-1-11-4-%E4%BB%A5%E4%B8%8A%E3%81%A7-go-modules-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E4%BE%9D%E5%AD%98%E9%96%A2%E4%BF%82%E8%A7%A3%E6%B1%BA%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E3%82%89-checksum-mismatch-c5528eaebd72 概要はこのくらいにして動作を見ていきましょう。
root@039e0fbdc156:~# go mod init
go: cannot determine module path for source directory /root (outside GOPATH, module path must be specified)
Example usage:
'go mod init example.com/m' to initialize a v0 or v1 module
'go mod init example.com/m/v2' to initialize a v2 module
Run 'go help mod init' for more information.
root@039e0fbdc156:~# go mod init root
go: creating new go.mod: module root
root@039e0fbdc156:~# ls
docker-compose.yml go.mod index.rst main.go
root@039e0fbdc156:~# go get golang.org/x/text
go: finding golang.org/x/text v0.3.2
go: downloading golang.org/x/text v0.3.2
go: extracting golang.org/x/text v0.3.2
root@039e0fbdc156:~# ls /go/src/
root@039e0fbdc156:~# ls /go/pkg/mod/golang.org/x
[email protected]
go mod init
コマンドで go.mod
ファイルが作成されます。
$GOPATH/src の中ではモジュール名の指定が不要ですが、それ以外の場所で go mod init
する場合は モジュール名の指定が必須です。
この例では /root
ディレクトリでモジュール名の指定なしに実行したため初回実行でエラーになっています。
dep からの移行措置として Gopkg.lock があるとそれをもとに go.mod を作成するようです。
モジュールモード
Go modules が有効な状態を モジュールモード
と表現します。
英語だと module-aware mode
ともいうそうです。
これに対して従来の $GOPATH/src
に保存する方式を GOPATHモード
といいます。
Go1.13 では go.mod
があるとモジュールモードになります。
環境変数 GO111MODULE
でいうところの auto
のようですが、1.12以前とは
auto
の定義が違い go.mod
ファイルの存在だけがモジュールモードの有効/無効を決めるようになっています。
なお、 GO111MODULE
の設定は Go1.13 でも有効で GO111MODULE=off
にセットしておけば go.mod
があっても GOPATHモードとして動作します。
モジュールモードでは既存の go get
の挙動が変わり、ダウンロードしたパッケージを $GOPATH/src
配下ではなく $GOPATH/pkg/mod
配下にバージョンを含めて配置するようになります。
そして import も $GOPATH/pkg/mod
から探します。(プロジェクト自身のモジュールは除く)
また、GOPATH モードの go get
では最新のパッケージを取得しましたが、
モジュールモードでは go get golang.org/x/[email protected]
のようにバージョンを指定してインストールできるようになりました。
go.mod
go.mod ファイルの中身を見てみると、依存パッケージを指定する
require
ディレクティブの他に、 module
というディレクティブが含まれます。
これは自身が置かれているディレクトリ配下のファイル郡を識別するためのモジュール名を自称します。
module root
go 1.13
require golang.org/x/text v0.3.2
GOPATHモードでは $GOPATH/src
配下にプロジェクトを配置しなければ
import
で検出できずビルドできなかったのですが、モジュールモードでは
go.mod
で指定したモジュール名がインポート対象になるためどこにおいてもビルドできるようになります。やったね。
認識されているモジュール一覧は以下のように取得できます。
go list -m all
root
golang.org/x/text v0.3.1
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
これはコマンドを実行したプロジェクトディレクトリ(が対応するgo.mod)ごとに違う結果を返すようになっており、
単に $GOPATH/pkg/mod
以下を一覧しているというわけではありません。
また、自身のモジュールも認識できているというのも注目するポイントですね。
- warning
go mod init
でgo.mod
を作成すると言いましたが、モジュール名を指定しないとモジュール名は自動的にディレクトリ名になります。github.com/username/package
のようにホスト名を含み import する場合は、モジュール名もこれに合わせる必要があります
- info
- モジュールモードで
go get
を実行すると$GOPATH/bin
に実行ファイルが作られなくなるパッケージが一部存在するようです。 (go install が失敗してるっぽい?) - 少し変な例ですがモジュールモード で dep をインストールしてみます
root@e437ad44b37e:/go/src/test# go get github.com/golang/dep/cmd/dep go: finding github.com/golang/dep v0.5.4 go: downloading github.com/golang/dep v0.5.4 go: extracting github.com/golang/dep v0.5.4 go: downloading golang.org/x/sync v0.0.0-20190423024810-112230192c58 go: finding github.com/pelletier/go-toml v1.6.0 go: finding github.com/armon/go-radix v1.0.0 go: finding github.com/pkg/errors v0.8.1 go: finding github.com/Masterminds/vcs v1.13.1 go: finding gopkg.in/yaml.v2 v2.2.7 go: downloading gopkg.in/yaml.v2 v2.2.7 go: downloading github.com/pelletier/go-toml v1.6.0 go: downloading github.com/armon/go-radix v1.0.0 go: downloading github.com/pkg/errors v0.8.1 go: downloading github.com/Masterminds/vcs v1.13.1 go: finding github.com/golang/protobuf v1.3.2 go: finding github.com/Masterminds/semver v1.5.0 go: finding github.com/sdboyer/constext latest go: finding github.com/boltdb/bolt v1.3.1 go: downloading github.com/boltdb/bolt v1.3.1 go: extracting golang.org/x/sync v0.0.0-20190423024810-112230192c58 go: downloading github.com/Masterminds/semver v1.5.0 go: downloading github.com/golang/protobuf v1.3.2 go: downloading github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 go: extracting gopkg.in/yaml.v2 v2.2.7 go: extracting github.com/armon/go-radix v1.0.0 go: extracting github.com/pkg/errors v0.8.1 go: downloading golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a go: extracting github.com/pelletier/go-toml v1.6.0 go: extracting github.com/Masterminds/vcs v1.13.1 go: finding github.com/jmank88/nuts v0.4.0 go: finding github.com/nightlyone/lockfile latest go: extracting github.com/boltdb/bolt v1.3.1 go: downloading github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 go: extracting github.com/Masterminds/semver v1.5.0 go: downloading github.com/jmank88/nuts v0.4.0 go: extracting github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 go: extracting github.com/golang/protobuf v1.3.2 go: extracting github.com/jmank88/nuts v0.4.0 go: extracting github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 go: extracting golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a go: downloading golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 go: extracting golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 go: finding golang.org/x/sync v0.0.0-20190423024810-112230192c58 go: finding golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 # github.com/golang/dep/gps /go/pkg/mod/github.com/golang/[email protected]/gps/constraint.go:149:4: undefined: semver.Constraint # 実行ファイルが見つからない root@e437ad44b37e:/go/src/test# dep bash: dep: command not found # bin にもない root@e437ad44b37e:/go/src/test# ls /go/bin # 適当にgo.mod が無いディレクトリに移動して root@e437ad44b37e:/go/src/test# cd /root/ root@e437ad44b37e:~# go get github.com/golang/dep/cmd/dep # できた root@e437ad44b37e:~# dep Dep is a tool for managing dependencies for Go projects Usage: "dep [command]" Commands: init Set up a new Go project, or migrate an existing one status Report the status of the project's dependencies ensure Ensure a dependency is safely vendored in the project version Show the dep version information check Check if imports, Gopkg.toml, and Gopkg.lock are in sync Examples: dep init set up a new project dep ensure install the project's dependencies dep ensure -update update the locked versions of all dependencies dep ensure -add github.com/pkg/errors add a dependency to the project Use "dep help [command]" for more information about a command. # 当然 bin にもある root@e437ad44b37e:~# ls /go/bin dep
- このような場合にはGOPATHモードで
go get
するとうまくいきます。 - つまり
go.mod
がない(モジュールモード が有効になっていない)ディレクトリで、 もしくは 環境変数をGO111MODULE=off
で実行すればOKです。 (他に良い対応あれば教えて下さい)
- モジュールモードで
そして Go modules
がさらに賢いのは、ビルドに必要なパッケージが不足していると自動的に取得してくれて、
go.mod
に追加してくれるのです。神かな?
root@13c6011d163e:/go/src/test# go run main.go
main.go:6:2: cannot find package "golang.org/x/text/width" in any of:
/usr/local/go/src/golang.org/x/text/width (from $GOROOT)
/go/src/golang.org/x/text/width (from $GOPATH)
# go.mod を作る
root@13c6011d163e:/go/src/test# go mod init
go: creating new go.mod: module test
# ビルドする
root@13c6011d163e:/go/src/test# go run main.go
go: finding golang.org/x/text v0.3.2
go: downloading golang.org/x/text v0.3.2
go: extracting golang.org/x/text v0.3.2
24歳,学生です
# 追記までされてる!
root@13c6011d163e:/go/src/test# cat go.mod
module test
go 1.13
require golang.org/x/text v0.3.2
main.goの内容
package main
import (
"fmt"
"golang.org/x/text/width"
)
func main() {
zen := width.Widen.String("24歳,学生です")
fmt.Println(zen)
}
利用パッケージをアップグレードしたければ go.mod
のバージョンを書き換えればビルド時に自動的に記載されたパッケージがダウンロードされます。
Go modules には他にもたくさんの特徴や機能がありますが、とてもじゃないですがこの記事内ですべてを伝えることはできません。 また得るものがあったら記事にするかもしれません。
これからは積極的に Go modules
を使っていきましょう。
明日も書きます。よろしくー。
参考
https://github.com/golang/go/wiki/Modules https://text.baldanders.info/golang/go-module-aware-mode/ https://qiita.com/yoshinori_hisakawa/items/268ba201611401ca7935 https://tech.opst.co.jp/2019/07/09/go-modules%E3%82%82%E8%A7%A6%E3%82%8C%E3%81%A6%E3%81%BF%E3%82%8Bgo%E5%85%A5%E9%96%80/