ethan

ethan

新知,热爱生活,码农,读书
twitter
email
github

go modパッケージ管理ツール

最早の頃、Go 言語が依存していたすべてのサードパーティライブラリは GOPATH というディレクトリの下に置かれていました。これにより、同じライブラリは 1 つのバージョンのコードしか保存できなくなりました。異なるプロジェクトが同じサードパーティライブラリの異なるバージョンに依存している場合、どのように解決すればよいのでしょうか?
go module は Go 言語が 1.11 バージョン以降に公式に導入したバージョン管理ツールであり、Go1.13 バージョンからは go module が Go 言語のデフォルトの依存管理ツールとなりました。

一般的なgo modコマンドは以下の表の通りです:

コマンド作用
go mod download依存パッケージをローカルにダウンロード (デフォルトは GOPATH/pkg/mod ディレクトリ)
go mod editgo.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】新しいプロジェクトを作成:

  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 getgo buildgo modなどの各種コマンド実行時に go.mod ファイルを変更および維持します。

go.mod は module、require、replace、exclude の 4 つのコマンドを提供します:

  • module 文はパッケージの名前(パス)を指定します;
  • require 文は依存項目モジュールを指定します;
  • replace 文は依存項目モジュールを置き換えることができます;
  • exclude 文は依存項目モジュールを無視することができます。

初期生成された go.mod ファイルは以下の通りです:

module hello

go 1.13

  1. 依存を追加します。

新しい 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.  }
  1.  go mod init *** で go.mod を初期化します。

go mod init hello
go: creating new go.mod: module hello

  1. 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は無効になりました。

  1. 古いパッケージのインポート方法を更新します。

したがって、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 バージョン以上にアップグレードすれば解決します。

  1. ここまで来ると、新しいプロジェクトを作成するのと何ら変わりありません。

goimports は何ができるか#

Goimports は依存を標準ライブラリと非標準ライブラリの 2 つのグループに自動的に分け、グループ内の依存をソートします。

goimports の欠点#

Goimports の最大の問題は空行を処理できないことです。一度依存間に空行が挿入されると、goimports は 2 つの空行間のコードを独立したグループとして扱い、そのためインポートコードブロック内の全体的なグループとソートが保証されなくなります。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。