最早の頃、Go 言語が依存していたすべてのサードパーティライブラリは GOPATH というディレクトリの下に置かれていました。これにより、同じライブラリは 1 つのバージョンのコードしか保存できなくなりました。異なるプロジェクトが同じサードパーティライブラリの異なるバージョンに依存している場合、どのように解決すればよいのでしょうか?
go module は Go 言語が 1.11 バージョン以降に公式に導入したバージョン管理ツールであり、Go1.13 バージョンからは go module が Go 言語のデフォルトの依存管理ツールとなりました。
一般的なgo mod
コマンドは以下の表の通りです:
コマンド | 作用 |
---|---|
go mod download | 依存パッケージをローカルにダウンロード (デフォルトは GOPATH/pkg/mod ディレクトリ) |
go mod edit | go.mod ファイルを編集 |
go mod graph | モジュール依存グラフを印刷 |
go mod init | 現在のフォルダを初期化し、go.mod ファイルを作成 |
go mod tidy | 欠けているパッケージを追加し、無駄なパッケージを削除 |
go mod vendor | 依存を vendor ディレクトリにコピー |
go mod verify | 依存の検証 |
go mod why | なぜ依存が必要なのかを説明 |
GOPROXY#
proxy はその名の通り、代理サーバーを意味します。皆さんご存知のように、国内のネットワークにはファイアウォールが存在し、これにより一部の Go 言語のサードパーティパッケージを直接go get
コマンドで取得することができません。GOPROXY は Go 言語公式が提供する、中間代理業者を通じてユーザーにパッケージダウンロードサービスを提供する方法です。GOPROXY を使用するには、環境変数 GOPROXY を設定するだけで済みます。
現在公開されている代理サーバーのアドレスは以下の通りです:
- goproxy.io;
- goproxy.cn:(推奨)国内の七牛クラウドが提供。
MacOS または Linux で GOPROXY を設定するコマンドは次の通りです:
export GOPROXY=https://goproxy.cn
Go 言語は 1.13 バージョン以降、GOPROXY のデフォルト値はhttps://proxy.golang.org ですが、国内ではダウンロードが遅いまたはアクセスできない場合があるため、GOPROXY を国内の goproxy.cn に設定することを強くお勧めします。
go get コマンドを使用して指定バージョンの依存パッケージをダウンロードする#
go get
コマンドを実行すると、依存パッケージをダウンロードする際にそのバージョンを指定することもできます。
go get -u
コマンドを実行すると、プロジェクト内のパッケージが最新のマイナーバージョンまたは修正バージョンにアップグレードされます;go get -u=patch
コマンドを実行すると、プロジェクト内のパッケージが最新の修正バージョンにアップグレードされます;go get [パッケージ名]@[バージョン番号]
コマンドを実行すると、対応するパッケージの指定バージョンをダウンロードするか、対応するパッケージを指定のバージョンにアップグレードします。
ヒント:go get [パッケージ名]@[バージョン番号]
コマンドのバージョン番号は x.y.z の形式で、例えば go get foo@v1.2.3 のように指定できます。また、git 上のブランチやタグ、例えば go get foo@master、git コミット時のハッシュ値、例えば go get foo@e3702bed2 も指定可能です。
プロジェクトでの使用方法#
【例 1】新しいプロジェクトを作成:
- GOPATH ディレクトリの外に新しいディレクトリを作成し、
go mod init
で go.mod ファイルを初期化します。
go mod init hello
go: creating new go.mod: module hello
go.mod ファイルが作成されると、その内容は go toolchain によって完全に管理され、go toolchain はgo get
、go build
、go mod
などの各種コマンド実行時に go.mod ファイルを変更および維持します。
go.mod は module、require、replace、exclude の 4 つのコマンドを提供します:
- module 文はパッケージの名前(パス)を指定します;
- require 文は依存項目モジュールを指定します;
- replace 文は依存項目モジュールを置き換えることができます;
- exclude 文は依存項目モジュールを無視することができます。
初期生成された go.mod ファイルは以下の通りです:
module hello
go 1.13
- 依存を追加します。
新しい main.go ファイルを作成し、以下のコードを書き込みます:
1. package main
3. import (
4. "net/http"
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", func(c echo.Context) error {
11. return c.String(http.StatusOK, "Hello, World!")
12. })
13. e.Logger.Fatal(e.Start(":1323"))
14. }
go run main.go
を実行すると、go mod が自動的に依存を探してダウンロードすることがわかります:
go run main.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon v0.3.0
......
go: finding golang.org/x/text v0.3.0
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
高性能でミニマリストなGoウェブフレームワーク
https://echo.labstack.com
____________________________________O/_______
O\
⇨ httpサーバーが[::]:1323で開始されました
現在、go.mod の内容を確認します:
module hello
go 1.13
require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)
go module がパッケージをインストールする原則は、まず最新のリリースタグを取得し、タグがない場合は最新のコミットを取得することです。詳細はModules 公式の紹介を参照してください。
go は依存ツリーを記録するために自動的に go.sum ファイルを生成します:
github.com/davecgh/go-spew v1.1.0/go.mod h1/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/labstack/echo v3.3.10+incompatible h1=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.3.0 h1=
github.com/labstack/gommon v0.3.0/go.mod h1+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
... 省略多数行
再度go run main.go
スクリプトを実行すると、依存のチェックとインストールのステップがスキップされることがわかります。
go list -m -u all
コマンドを使用してアップグレード可能なパッケージを確認し、go get -u need-upgrade-package
でアップグレードすると、新しい依存バージョンが go.mod に更新されます。また、go get -u
を使用してすべての依存をアップグレードすることもできます。
【例 2】既存プロジェクトの改造。
プロジェクトのディレクトリ構造は以下の通りです:
├─ main.go
│
└─ api
└─ apis.go
main.go のソースコードは以下の通りです:
1. package main
3. import (
4. api "./api" // ここでは相対パスを使用
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }
api/apis.go のソースコードは以下の通りです:
1. package api
3. import (
4. "net/http"
6. "github.com/labstack/echo"
7. )
9. func HelloWorld(c echo.Context) error {
10. return c.JSON(http.StatusOK, "hello world")
11. }
-
go mod init ***
で go.mod を初期化します。
go mod init hello
go: creating new go.mod: module hello
go run main.go
を実行します。
go run main.go
go: finding golang.org/x/crypto latest
build _/D_/code/src/api: cannot find module for path _/D_/code/src/api
最初に依存を探してダウンロードし、次に main.go スクリプトを実行しますが、ここでエラーが発生します:
build /D/code/src/api: cannot find module for path /D/code/src/api
しかし、go.mod はすでに更新されています:
module hello
go 1.13
require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)
なぜこのエラーが発生するのでしょうか?
これは、main.go で内部パッケージのメソッドを使用する方法が以前とは異なり、go.mod が作業ディレクトリ内のすべてのパッケージをスキャンし、インポート方法を変更するためです。hello をパスのプレフィックスとして扱う必要があり、つまりimport hello/api
と書く必要があります。以前の GOPATH/dep モードで許可されていたimport ./api
は無効になりました。
- 古いパッケージのインポート方法を更新します。
したがって、main.go は次のように書き換える必要があります:
1. package main
3. import (
4. api "hello/api" // ここでは相対パスを使用
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }
ヒント:Go 言語 1.11 バージョンで go mod を使用する際に、
go build github.com/valyala/fasttemplate: module requires go 1.12
のようなエラーが発生することがあります。このような問題に直面した場合は、直接 Go 言語 1.12 バージョン以上にアップグレードすれば解決します。
- ここまで来ると、新しいプロジェクトを作成するのと何ら変わりありません。
goimports は何ができるか#
Goimports は依存を標準ライブラリと非標準ライブラリの 2 つのグループに自動的に分け、グループ内の依存をソートします。
goimports の欠点#
Goimports の最大の問題は空行を処理できないことです。一度依存間に空行が挿入されると、goimports は 2 つの空行間のコードを独立したグループとして扱い、そのためインポートコードブロック内の全体的なグループとソートが保証されなくなります。