Skip to content

モジュール

すべてのモダンなプログラミング言語には、それぞれ独自の成熟した依存関係管理ツールがあります。例えば Java の Gradle、Python の Pip、NodeJs の Npm などです。優れた依存関係管理ツールは、開発者の時間を大幅に節約し、開発効率を向上させることができます。しかし、Go は初期には成熟した依存関係管理ソリューションを提供していませんでした。当時はすべてのコードが GOPATH ディレクトリに保存されており、プロジェクトにとって非常に不便でした。バージョンの混乱、依存関係の管理困難などの問題がありました。この問題を解決するために、コミュニティの開発者たちが様々な解決策を提案し、一時期混乱した状況が続きました。その中で Vendor などの優れたソリューションも現れましたが、Go1.11 で公式に Go Mod という公式の依存関係管理ツールがリリースされ、それまでの混乱に終止符を打ちました。その後のアップデートで不断完善され、古いツールは淘汰されました。現在、この記事を書いている時点で Go のバージョンは 1.20 になっており、ほぼすべての Go プロジェクトが Go Mod を採用しています。そのため、この記事では Go Mod についてのみ解説します。公式は Go モジュールについて非常に詳細なドキュメントを作成しています:Go Modules Reference

モジュールの作成

Go Module は本質的に VCS(バージョン管理システム)に基づいています。依存関係をダウンロードする際、実際には VCS コマンド(例えば git)を実行しています。したがって、作成したライブラリを共有したい場合は、以下の 3 点を実行するだけです。

  • ソースコードリポジトリが公開アクセス可能で、VCS が以下のいずれかであること
    • git
    • hg (Mercurial)
    • bzr (Bazaar)
    • svn
    • fossil
  • 規格に準拠した go mod プロジェクトであること
  • セマンティックバージョニング規格に準拠していること

したがって、通常通り VCS を使用して開発し、特定のバージョンに標準に準拠した Tag を付けるだけで、他の人がモジュール名を通じてライブラリをダウンロードできます。以下では、例を通じてモジュール開発のいくつかのステップを説明します。

サンプルリポジトリ:246859/hello: say hello (github.com)

準備

開始前に、バージョンが go mod を完全にサポートしていること(go >= 1.17)、および Go Module が有効になっていることを確認します。以下のコマンドを使用して有効になっているかどうかを確認します。

bash
$ go env GO111MODULE

有効になっていない場合は、以下のコマンドで Go Module を有効にします。

bash
$ go env -w GO111MODULE=on

作成

まず、公開アクセス可能なソースコードリポジトリが必要です。これには多くの選択肢がありますが、Github をお勧めします。そこに新しいプロジェクトを作成し、hello という名前にします。リポジトリ名には特別な制限はありませんが、モジュール名に影響を与える可能性があるため、特殊文字を使用しないことをお勧めします。

作成が完了すると、リポジトリの URL は https://github.com/246859/hello になり、対応する go モジュール名は github.com/246859/hello になります。

次に、これをローカルにクローンし、go mod init コマンドでモジュールを初期化します。

bash
$ git clone git@github.com:246859/hello.git
Cloning into 'hello'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (5/5), done.

$ cd hello && go mod init github.com/246859/hello
go: creating new go.mod: module github.com/246859/hello

開発

その後、開発作業を行うことができます。その機能は非常にシンプルで、1 つの関数のみです。

go
// hello.go
package hello

import "fmt"

// Hello returns hello message
func Hello(name string) string {
        if name == "" {
                name = "world"
        }
        return fmt.Sprintf("hello %s!", name)
}

ついでに、ユニットテスト用のテストファイルも作成します。

go
// hello_test.go
package hello_test

import (
        "testing"
        "fmt"
        "github.com/246859/hello"
)

func TestHello(t *testing.T) {
        data := "jack"
        expected := fmt.Sprintf("hello %s!", data)
        result := hello.Hello(data)

        if result != expected {
                t.Fatalf("expected result %s, but got %s", expected, result)
        }

}

次に、hello を出力するためのコマンドラインプログラムを作成します。その機能も非常にシンプルです。コマンドラインプログラムについては、規格に従ってプロジェクトの cmd/app_name/ に作成するため、hello コマンドラインプログラムのファイルは cmd/hello/ ディレクトリに保存され、そこで関連コードを記述します。

go
// cmd/hello/main.go
package main

import (
  "flag"
  "github.com/246859/hello"
  "os"
)

var name string

func init() {
  flag.StringVar(&name, "name", "world", "name to say hello")
}

func main() {
  flag.Parse()
  msg := hello.Hello(name)
  _, err := os.Stdout.WriteString(msg)
  if err != nil {
    os.Stderr.WriteString(err.Error())
  }
}

テスト

開発が完了したら、ソースコードをフォーマットしてテストします。

bash
$ go fmt && go vet ./...

$ go test -v .
=== RUN   TestHello
--- PASS: TestHello (0.00s)
PASS
ok      github.com/246859/hello 0.023s

コマンドラインプログラムを実行します。

bash
$ go run ./cmd/hello -name jack
hello jack!

ドキュメント

最後に、このライブラリの簡潔で分かりやすい README を作成し、他の開発者が一目で使用方法を理解できるようにします。

markdown
# hello

just say hello

## Install

import code

```bash
go get github.com/246859/hello@latest
```

install cmd

```bash
go install github.com/246859/hello/cmd/hello@latest
```

## Example

Here's a simple example as follows:

```go
package main

import (
  "fmt"
  "github.com/246859/hello"
)

func main() {
  result := hello.Hello("jack")
  fmt.Println(result)
}
```

これは非常にシンプルな README ドキュメントです。必要に応じて内容を充実させることもできます。

アップロード

すべてのコードの作成とテストが完了したら、変更をコミットしてリモートリポジトリにプッシュできます。

bash
$ git add go.mod hello.go hello_test.go cmd/ example/ README.md

$ git commit -m "chore(mod): mod init" go.mod
[main 5087fa2] chore(mod): mod init
 1 file changed, 3 insertions(+)
 create mode 100644 go.mod

$ git commit -m "feat(hello): complete Hello func" hello.go
[main 099a8bf] feat(hello): complete Hello func
 1 file changed, 11 insertions(+)
 create mode 100644 hello.go

$ git commit -m "test(hello): complete hello testcase" hello_test.go
[main 76e8c1e] test(hello): complete hello testcase
 1 file changed, 17 insertions(+)
 create mode 100644 hello_test.go

$ git commit -m "feat(hello): complete hello cmd" cmd/hello/
[main a62a605] feat(hello): complete hello cmd
 1 file changed, 22 insertions(+)
 create mode 100644 cmd/hello/main.go

$ git commit -m "docs(example): add hello example" example/
[main 5c51ce4] docs(example): add hello example
 1 file changed, 11 insertions(+)
 create mode 100644 example/main.go

$ git commit -m "docs(README): update README" README.md
[main e6fbc62] docs(README): update README
 1 file changed, 27 insertions(+), 1 deletion(-)

合計 6 つのコミットは多くありません。コミットが完了したら、最新のコミットにタグを作成します。

bash
$ git tag v1.0.0

$ git tag -l
v1.0.0

$ git log --oneline
e6fbc62 (HEAD -> main, tag: v1.0.0, origin/main, origin/HEAD) docs(README): update README
5c51ce4 docs(example): add hello example
a62a605 feat(hello): complete hello cmd
76e8c1e test(hello): complete hello testcase
099a8bf feat(hello): complete Hello func
5087fa2 chore(mod): mod init
1f422d1 Initial commit

最後にリモートリポジトリにプッシュします。

bash
$ git push --tags
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (21/21), 2.43 KiB | 1.22 MiB/s, done.
Total 21 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), done.
To github.com:246859/hello.git
   1f422d1..e6fbc62    main -> main
  * [new tag]         v1.0.0 -> v1.0.0

プッシュが完了したら、release を作成します(タグが 1 つあれば十分で、release は github の規格に準拠しているだけです)。

これでモジュールの作成が完了しました。以上がモジュール開発の基本的なフローです。他の開発者はモジュール名を通じてコードを導入したり、コマンドラインツールをインストールしたりできます。

導入

go get を使用してライブラリを導入します。

bash
$ go get github.com/246859/hello@latest
go: downloading github.com/246859/hello v1.0.0
go: added github.com/246859/hello v1.0.0

go install を使用してコマンドラインプログラムをインストールします。

bash
$ go install github.com/246859/hello/cmd/hello@latest && hello -name jack
hello jack!

または go run を使用して直接実行します。

bash
$ go run -mod=mod github.com/246859/hello/cmd/hello -name jack
hello jack!

ライブラリが導入されると、Go Package が自動的にページを作成します。このプロセスは自動的に実行され、開発者が何か作業を行う必要はありません。例えば、hello ライブラリには専用のドキュメントページがあります。下図に示す通りです。

モジュールのアップロードに関する詳細情報については、Add a package をご覧ください。

モジュール情報の削除については、Removing a package をご覧ください。

プロキシの設定

Go には Maven Repo、PyPi、NPM のような中央リポジトリはありませんが、公式のプロキシリポジトリがあります:Go modules services (golang.org)。これはバージョンとモジュール名に基づいて、開発者がダウンロードしたモジュールをキャッシュします。ただし、サーバーが海外にデプロイされているため、国内ユーザーのアクセス速度はあまり良くありません。そのため、デフォルトのモジュールプロキシアドレスを変更する必要があります。現在、国内で優れているプロキシは以下のいくつかがあります。

ここでは七牛云のプロキシを選択し、以下のコマンドを実行して Go プロキシを変更します。direct はプロキシダウンロードが失敗した場合、プロキシキャッシュをバイパスして直接ソースコードリポジトリにアクセスすることを示します。

sh
$ go env -w GOPROXY=https://goproxy.cn,direct

プロキシの変更が成功すると、今後の依存関係のダウンロードが非常に迅速になります。

依存関係のダウンロード

プロキシを変更した後、サードパーティの依存関係をインストールしてみましょう。Go 公式には専用の依存関係照会サイトがあります:Go Packages

コードの導入

その中で有名な Web フレームワーク Gin を検索します。

ここには多くの検索結果が表示されます。サードパーティの依存関係を使用する際は、引用回数と更新時間に基づいて採用するかどうかを決定する必要があります。ここでは最初のものを選択します。

対応するページに入ると、これが依存関係のドキュメントページであることがわかります。非常に多くの詳細情報が記載されており、後でドキュメントを参照する際にもここを利用できます。

ここではアドレスをコピーして、以前に作成したプロジェクトの下で go get コマンドを使用するだけです。コマンドは以下の通りです。

sh
$ go get github.com/gin-gonic/gin

プロセス中に多くの依存関係がダウンロードされます。エラーが発生しなければダウンロード成功です。

sh
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.8.0
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.0
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.11.2
go: added github.com/goccy/go-json v0.10.0
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.0.9
go: added github.com/leodido/go-urn v1.2.1
go: added github.com/mattn/go-isatty v0.0.17
go: added github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.6
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.9
go: added golang.org/x/arch v0.0.0-20210923205945-b76863e36670
go: added golang.org/x/crypto v0.5.0
go: added golang.org/x/net v0.7.0
go: added golang.org/x/sys v0.5.0
go: added golang.org/x/text v0.7.0
go: added google.golang.org/protobuf v1.28.1
go: added gopkg.in/yaml.v3 v3.0.1

完了後、go.mod ファイルを確認します。

sh
$ cat go.mod
module golearn

go 1.20

require github.com/gin-gonic/gin v1.9.0

require (
  github.com/bytedance/sonic v1.8.0 // indirect
  github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
  github.com/gin-contrib/sse v0.1.0 // indirect
  github.com/go-playground/locales v0.14.1 // indirect
  github.com/go-playground/universal-translator v0.18.1 // indirect
  github.com/go-playground/validator/v10 v10.11.2 // indirect
  github.com/goccy/go-json v0.10.0 // indirect
  github.com/json-iterator/go v1.1.12 // indirect
  github.com/klauspost/cpuid/v2 v2.0.9 // indirect
  github.com/leodido/go-urn v1.2.1 // indirect
  github.com/mattn/go-isatty v0.0.17 // indirect
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
  github.com/modern-go/reflect2 v1.0.2 // indirect
  github.com/pelletier/go-toml/v2 v2.0.6 // indirect
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
  github.com/ugorji/go/codec v1.2.9 // indirect
  golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
  golang.org/x/crypto v0.5.0 // indirect
  golang.org/x/net v0.7.0 // indirect
  golang.org/x/sys v0.5.0 // indirect
  golang.org/x/text v0.7.0 // indirect
  google.golang.org/protobuf v1.28.1 // indirect
  gopkg.in/yaml.v3 v3.0.1 // indirect
)

以前と比べて多くのものが追加されていることがわかります。同時に、go.sum という名前のファイルがディレクトリに追加されていることもわかります。

sh
$ ls
go.mod  go.sum  main.go

ここではまず詳しく説明せず、main.go ファイルを以下のコードに修正します。

go
package main

import (
  "github.com/gin-gonic/gin"
)

func main() {
  gin.Default().Run()
}

プロジェクトを再度実行します。

sh
$ go run golearn
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

これで、1 行のコードで最もシンプルな Web サーバーを実行できるようになりました。ある依存関係が不要になった場合も、go get コマンドを使用して削除できます。ここでは Gin を削除する例を示します。

sh
$ go get github.com/gin-gonic/gin@none
go: removed github.com/gin-gonic/gin v1.9.0

依存関係アドレスの後に @none を追加するだけで、その依存関係を削除できます。結果も削除成功を示しています。この時点で go.mod ファイルを再度確認すると、Gin 依存関係がなくなっていることがわかります。

sh
$ cat go.mod | grep github.com/gin-gonic/gin

最新バージョンにアップグレードする場合は、@latest サフィックスを追加するか、使用可能なリリースバージョン番号を自分で照会できます。

sh
$ go get -u github.com/gin-gonic/gin@latest

コマンドラインのインストール

go install コマンドは、サードパーティの依存関係をローカルにダウンロードし、バイナリファイルにコンパイルします。Go のコンパイル速度のおかげで、このプロセスは通常それほど時間がかかりません。その後、Go はこれを $GOPATH/bin または $GOBIN ディレクトリに保存し、グローバルでバイナリファイルを実行できるようにします(これらのパスを環境変数に追加していることが前提です)。

TIP

install コマンドを使用する際は、バージョン番号を指定する必要があります。

例えば、Go 言語で書かれたデバッガー delve をダウンロードします。

bash
$ go install github.com/go-delve/delve/cmd/dlv@latest
go: downloading github.com/go-delve/delve v1.22.1
go: downloading github.com/cosiner/argv v0.1.0
go: downloading github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d
go: downloading github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62
go: downloading github.com/google/go-dap v0.11.0
go: downloading github.com/hashicorp/golang-lru v1.0.2
go: downloading golang.org/x/arch v0.6.0
go: downloading github.com/cpuguy83/go-md2man/v2 v2.0.2
go: downloading go.starlark.net v0.0.0-20231101134539-556fd59b42f6
go: downloading github.com/cilium/ebpf v0.11.0
go: downloading github.com/mattn/go-runewidth v0.0.13
go: downloading github.com/russross/blackfriday/v2 v2.1.0
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2

$ dlv -v
Error: unknown shorthand flag: 'v' in -v
Usage:
  dlv [command]

Available Commands:
  attach      Attach to running process and begin debugging.
  completion  Generate the autocompletion script for the specified shell
  connect     Connect to a headless debug server with a terminal client.
  core        Examine a core dump.
  dap         Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
  debug       Compile and begin debugging main package in current directory, or the package specified.
  exec        Execute a precompiled binary, and begin a debug session.
  help        Help about any command
  test        Compile test binary and begin debugging program.
  trace       Compile and begin tracing program.
  version     Prints version.

Additional help topics:
  dlv backend    Help about the --backend flag.
  dlv log        Help about logging flags.
  dlv redirect   Help about file redirection.

Use "dlv [command] --help" for more information about a command.

モジュール管理

上記のすべての内容は Go Mod の基本的な使用方法についてのみ説明していますが、実際には Go Mod を習得するにはこれだけでは全く十分ではありません。公式はモジュールを「バージョンタグ付けされたパッケージのセット」と定義しています。上記の定義において、パッケージは非常に馴染み深い概念であり、バージョンはセマンティックバージョニングに準拠する必要があります。v(major).(minor).(patch) の形式で定義されます。例えば Go のバージョン v1.20.1、メジャーバージョンは 1、マイナーバージョンは 20、パッチバージョンは 1 で、合わせて v1.20.1 になります。以下はより詳細な説明です。

  • major:メジャーバージョンが変更された場合、プロジェクトに互換性のない変更があったことを示します。旧バージョンのプロジェクトを新バージョンにアップグレードすると、おそらく正常に実行されません。
  • minorminor バージョンが変更された場合、プロジェクトに新機能が追加されたことを示します。以前のバージョンの機能に新機能が追加されただけです。
  • patchpatch バージョンが変更された場合、バグが修正されただけで、新機能は追加されていないことを示します。

よく使用されるコマンド

コマンド説明
go mod download現在のプロジェクトの依存パッケージをダウンロード
go mod editgo.mod ファイルを編集
go mod graphモジュール依存グラフを出力
go mod init現在のディレクトリで go mod を初期化
go mod tidyプロジェクトモジュールをクリーンアップ
go mod verifyプロジェクトの依存関係の合法性を検証
go mod whyプロジェクトのどの場所で依存関係が使用されているかを説明
go clean -modcacheプロジェクトモジュール依存キャッシュを削除
go list -mモジュールを一覧表示

go mod cmd でコマンドの詳細情報をご覧ください。

モジュールストレージ

Go Mod を使用してプロジェクトを管理する場合、モジュールキャッシュはデフォルトで $GOPATH/pkg/mod ディレクトリに保存されます。$GOMODCACHE を変更して、別の場所に保存することもできます。

sh
$ go env -w GOMODCACHE=あなたのモジュールキャッシュパス

同じマシン上のすべての Go Module プロジェクトは、このディレクトリ下のキャッシュを共有します。キャッシュにはサイズの制限がなく、自動的に削除されることもありません。キャッシュ内で解凍された依存関係のソースファイルはすべて読み取り専用です。キャッシュをクリアしたい場合は、以下のコマンドを実行できます。

sh
$ go clean -modcache

$GOMODCACHE/cache/download ディレクトリには、依存関係の元のファイル(ハッシュファイル、元の圧縮パッケージなど)が保存されています。例は以下の通りです。

bash
$ ls $(go env GOMODCACHE)/cache/download/github.com/246859/hello/@v -1
list
v1.0.0.info
v1.0.0.lock
v1.0.0.mod
v1.0.0.zip
v1.0.0.ziphash

解凍された依存関係の組織形式は以下の通りで、指定されたモジュールのソースコードです。

bash
$ ls $(go env GOMODCACHE)/github.com/246859/hello@v1.0.0 -1
LICENSE
README.md
cmd/
example/
go.mod
hello.go
hello_test.go

バージョン選択

Go は依存関係のバージョン選択において、最小バージョン選択原則に従います。以下は公式サイトの例です。主モジュールはモジュール A の 1.2 バージョンとモジュール B の 1.2 バージョンを参照しています。同時に、モジュール A の 1.2 バージョンはモジュール C の 1.3 バージョンを参照し、モジュール B の 1.2 バージョンはモジュール C の 1.4 バージョンを参照しています。また、モジュール C の 1.3 と 1.4 バージョンは両方ともモジュール D の 1.2 バージョンを参照しています。最小利用可能バージョン原則に基づき、Go が最終的に選択するバージョンは A1.2、B1.2、C1.4、D1.2 です。その中で水色は go.mod ファイルが読み込んだもので、枠で囲まれたのが最終的に選択されたバージョンです。

公式サイトには他のいくつかの例 も掲載されており、大体同じ意味です。

go.mod

Go Mod プロジェクトを作成するたびに go.mod ファイルが生成されます。したがって、go.mod ファイルに精通することは非常に必要ですが、ほとんどの場合、go.mod ファイルを手動で修正する必要はありません。

module golearn

go 1.20

require github.com/gin-gonic/gin v1.9.0

require (
   github.com/bytedance/sonic v1.8.0 // indirect
   github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
   github.com/gin-contrib/sse v0.1.0 // indirect
   github.com/go-playground/locales v0.14.1 // indirect
   github.com/go-playground/universal-translator v0.18.1 // indirect
   github.com/go-playground/validator/v10 v10.11.2 // indirect
   github.com/goccy/go-json v0.10.0 // indirect
   github.com/json-iterator/go v1.1.12 // indirect
   github.com/klauspost/cpuid/v2 v2.0.9 // indirect
   github.com/leodido/go-urn v1.2.1 // indirect
   github.com/mattn/go-isatty v0.0.17 // indirect
   github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
   github.com/modern-go/reflect2 v1.0.2 // indirect
   github.com/pelletier/go-toml/v2 v2.0.6 // indirect
   github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
   github.com/ugorji/go/codec v1.2.9 // indirect
   golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
   golang.org/x/crypto v0.5.0 // indirect
   golang.org/x/net v0.7.0 // indirect
   golang.org/x/sys v0.5.0 // indirect
   golang.org/x/text v0.7.0 // indirect
   google.golang.org/protobuf v1.28.1 // indirect
   gopkg.in/yaml.v3 v3.0.1 // indirect
)

ファイル内で、ほとんどの依存関係アドレスに github などの文字が含まれていることがわかります。これは Go には共通の依存関係リポジトリがなく、ほとんどのオープンソースプロジェクトが Github にホストされているためです。一部は独自にリポジトリを構築しています。例えば google.golang.org/protobufgolang.org/x/crypto などです。通常、この一連の URL は同時に Go プロジェクトのモジュール名でもあります。これにより問題が発生します。URL は大文字小文字を区別しませんが、依存関係を保存するフォルダは大文字小文字を区別します。したがって、go get github.com/gin-gonic/gingo get github.com/gin-gonic/Gin は同じ依存関係を参照しますが、ローカルに保存されるパスが異なります。この状況が発生した場合、Go は大文字を直接保存パスとして使用せず、! 小文字 にエスケープします。例えば github.com\BurntSushi は最終的に github.com\!burnt!sushi にエスケープされます。

module

module キーワードは現在のプロジェクトのモジュール名を宣言します。go.mod ファイルには module キーワードが 1 つしか存在できません。例の

module golearn

は現在のモジュール名が golearn であることを示します。例えば Gin 依存関係の go.mod ファイルを開くと、その module 名がわかります。

module github.com/gin-gonic/gin

Gin のモジュール名は依存関係をダウンロードする際に使用されるアドレスです。这也是通常推奨されるモジュール名フォーマットで、ドメイン/ユーザー/リポジトリ名 です。

TIP

注意すべき点は、メジャーバージョンが 1 より大きい場合、メジャーバージョン番号をモジュール名に反映する必要があることです。例えば

github.com/my/example

バージョンが v2.0.0 にアップグレードされた場合、モジュール名を以下のように変更する必要があります。

github.com/my/example/v2

既存のプロジェクトが旧バージョンを参照しており、新バージョンが区別しない場合、依存関係を参照する際にパスが一致するため、使用者はメジャーバージョンの変更による互換性のない変更を区別できません。これによりプログラムのエラーが発生する可能性があります。

Deprecated

module の上一行のコメントで Deprecated を使用して、そのモジュールが非推奨であることを示します。例えば

// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

go

go キーワードは現在のプロジェクトの作成に使用された Go バージョンを示します。バージョン番号はセマンティックバージョニング規則に従う必要があります。Go のバージョンが異なると、Go Mod は異なる動作を示します。以下は簡単な例です。Go の利用可能なバージョンについては、公式で照会してください。

go 1.20

require

require キーワードは外部依存関係を参照したことを示します。例えば

require github.com/gin-gonic/gin v1.9.0

フォーマットは require モジュール名 バージョン で、複数の参照がある場合は括弧で囲みます。

require (
   github.com/bytedance/sonic v1.8.0 // indirect
)

// indirect コメントが付いている依存関係は、現在のプロジェクトによって直接参照されていないことを示します。プロジェクトが直接参照している依存関係がその依存関係を参照している可能性があります。そのため、現在のプロジェクトにとっては間接参照となります。前述したように、メジャーバージョンが変更された場合はモジュール名に反映する必要があります。この規則に従わないモジュールは非規格モジュールと呼ばれ、require 時に incompatible コメントが追加されます。

require example.com/m v4.1.2+incompatible

疑似バージョン

上記の go.mod ファイルで、一部の依存パッケージのバージョンがセマンティックバージョニング番号ではなく、意味不明な文字列であることがわかります。これは実際に対応するバージョンの CommitID です。セマンティックバージョニングは通常、特定の Release を指します。疑似バージョン番号は特定の Commit に細分化できます。通常のフォーマットは vx.y.z-yyyyMMddHHmmss-CommitId です。vx.y.z は必ずしも実際に存在するとは限らないため、疑似バージョンと呼ばれます。例えば、以下の例の v0.0.0 は存在せず、実際に有効なのはその後の 12 桁の CommitID です。

// CommitID は通常前 12 桁を取得
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect

同様に、依存関係をダウンロードする際も、セマンティックバージョニング番号の代わりに CommitID を指定できます。

go get github.com/chenzhuoyu/base64x@fe3a3abad311

exclude

exclude キーワードは、指定されたバージョンの依存関係を読み込まないことを示します。同時に require が同じバージョンの依存関係を参照している場合も、無視されます。このキーワードは主モジュールでのみ有効です。例えば

exclude golang.org/x/net v1.2.3

exclude (
    golang.org/x/crypto v1.4.5
    golang.org/x/text v1.6.7
)

replace

replace は指定されたバージョンの依存関係を置き換えます。モジュールパスとバージョン、または他のプラットフォームで指定されたファイルパスで置き換えることができます。例

text
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net
)

=> 左側のバージョンのみが置き換えられ、他のバージョンの同じ依存関係は通常通りアクセスできます。ローカルパスまたはモジュールパスを使用して置き換えを指定する場合、置き換えモジュールが go.mod ファイルを持っている場合、その module 指示は置き換えられるモジュールパスと一致する必要があります。

retract

retract 指示は、retract で指定された依存関係のバージョンまたはバージョン範囲に依存すべきでないことを示します。例えば、新しいバージョンがリリースされた後に重大な問題が発見された場合、この時点で retract 指示を使用できます。

一部のバージョンを取り消す

text
retract (
    v1.0.0 // Published accidentally.
    v1.0.1 // Contains retractions only.
)

バージョン範囲を取り消す

text
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)

go.sum

go.sum ファイルはプロジェクト作成初期には存在せず、外部依存関係を正式に参照した後にのみ生成されます。go.sum ファイルは人間が読むのには適しておらず、このファイルを手動で修正することもお勧めしません。その主な役割は一貫性のあるビルド問題を解決することです。つまり、異なる人が異なる環境で同じプロジェクトをビルドする際に、参照される依存パッケージが完全に同じであることを保証します。これは go.mod ファイル 1 つだけでは保証できません。

次に、依存関係をダウンロードする際に Go が最初から最後まで何を行ったかを見てみましょう。まず以下のコマンドを使用して依存関係をダウンロードします。

go get github.com/bytedance/sonic v1.8.0

go get コマンドはまず依存パッケージをローカルのキャッシュディレクトリにダウンロードします。通常、このディレクトリは $GOMODCACHE/cache/download/ です。このディレクトリはドメイン名に基づいて異なるウェブサイトの依存パッケージを区別するため、以下のようなディレクトリ構造が表示される可能性があります。

sh
$ ls
cloud.google.com/      go.opencensus.io/     gopkg.in/          nhooyr.io/
dmitri.shuralyov.com/  go.opentelemetry.io/  gorm.io/           rsc.io/
github.com/            go.uber.org/          honnef.co/         sumdb/
go.etcd.io/            golang.org/           lukechampine.com/
go.mongodb.org/        google.golang.org/    modernc.org/

上記の例でダウンロードされた依存パッケージが保存されるパスは以下の通りです。

$GOMODCACHE/cache/download/github.com/bytedance/sonic/@v/

可能性のあるディレクトリ構造は以下の通りで、いくつかのバージョン命名のファイルがあります。

sh
$ ls
list         v1.8.0.lock  v1.8.0.ziphash  v1.8.3.mod
v1.5.0.mod   v1.8.0.mod   v1.8.3.info     v1.8.3.zip
v1.8.0.info  v1.8.0.zip   v1.8.3.lock     v1.8.3.ziphash

通常、このディレクトリには list ファイルが 1 つあり、依存関係の既知のバージョン番号を記録します。各バージョンについては、以下のファイルがあります。

  • zip:依存関係のソースコード圧縮パッケージ
  • ziphash:依存関係圧縮パッケージに基づいて計算されたハッシュ値
  • info:json フォーマットのバージョンメタデータ
  • mod:このバージョンの go.mod ファイル
  • lock:一時ファイル(公式も用途を明言していません)

通常、Go は圧縮パッケージと go.mod ファイルの 2 つのファイルのハッシュ値を計算し、GOSUMDB で指定されたサーバー(デフォルトは sum.golang.org)で依存関係のハッシュ値を照会します。ローカルで計算されたハッシュ値が照会された結果と一致しない場合、以降の実行は行われません。一致する場合、go.mod ファイルを更新し、go.sum ファイルに 2 つのレコードを挿入します。概ね以下の通りです。

github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=

TIP

GOSUMDB を無効にした場合、Go はローカルで計算されたハッシュ値を直接 go.sum ファイルに書き込みます。通常、これはお勧めしません。

通常、すべての依存関係には 2 つのレコードがあります。1 つ目は圧縮パッケージのハッシュ値、2 つ目は依存関係パッケージの go.mod ファイルのハッシュ値です。レコードフォーマットは モジュール名 バージョン アルゴリズム名:ハッシュ値 です。一部の古い依存関係パッケージには go.mod ファイルがない可能性があるため、2 つ目のハッシュレコードがない場合があります。このプロジェクトが別の人の環境でビルドされる場合、Go は go.mod で指定されたローカル依存関係のハッシュ値を計算し、go.sum に記録されたハッシュ値と比較します。ハッシュ値が一致しない場合、依存関係のバージョンが異なることを示し、ビルドを拒否します。この状況が発生した場合、ローカル依存関係と go.sum ファイルの両方が変更された可能性がありますが、go.sum は GOSUMDB 照会レコードを経ているため、go.sum ファイルをより信頼します。

プライベートモジュール

Go Mod のほとんどのツールはオープンソースプロジェクト向けですが、Go はプライベートモジュールもサポートしています。プライベートプロジェクトの場合、通常、以下の環境設定を設定してモジュールのプライベート処理を行う必要があります。

  • GOPROXY:依存関係のプロキシサーバー集合
  • GOPRIVATE:プライベートモジュールのモジュールパスプレフィックスの共通モードリスト。モジュール名が規則に適合する場合、そのモジュールはプライベートモジュールであることを示します。具体的な動作は GONOPROXY および GONOSUMDB と一致します。
  • GONOPROXY:プロキシからダウンロードしないモジュールパスプレフィックスの共通モードリスト。規則に適合する場合、モジュールをダウンロードする際に GOPROXY を使用せず、直接バージョン管理システムからダウンロードしようとします。
  • GONOSUMDB:GOSUMDB 公共検証を行わないモジュールパスプレフィックスの共通モードリスト。規則に適合する場合、モジュールをダウンロードする際にチェックサムの公共データベースを使用しません。
  • GOINSECURE:HTTP および他の安全でないプロトコルを通じて取得できるモジュールパスプレフィックスの共通モードリスト。

ワークスペース

前述したように、go.mod ファイルは replace 指示をサポートしており、これによりローカルでリリースできない一時的な修正を使用できます。以下の通りです。

replace (
  github.com/246859/hello v1.0.1 => ./hello
)

コンパイル時、go はローカルの hello モジュールを使用します。後日新バージョンをリリースした後に削除します。

ただし、replace 指示を使用すると go.mod ファイルの内容が変更され、その変更が誤ってリモートリポジトリにコミットされる可能性があります。これは私たちが望まないことです。replace 指示で指定されたターゲットはネットワーク URL ではなくファイルパスであるため、このマシンで使用できるパスが別のマシンでは使用できない可能性があります。ファイルパスはクロスプラットフォームにおいても大きな問題となります。このような問題を解決するために、ワークスペースが生まれました。

ワークスペース(workspace)は、Go が 1.18 で導入したマルチモジュール管理に関する新しいソリューションで、ローカルでのマルチモジュール開発作業をより良く行うことを目的としています。以下では、例を通じて説明します。

サンプルリポジトリ:246859/work: go work example (github.com)

まず、プロジェクトには 2 つの独立した go モジュールがあります。それぞれ authuser です。

bash
$ ls -1
LICENSE
README.md
auth
go.work
user

auth モジュールは user モジュールの構造体 User に依存しており、内容は以下の通りです。

go
package auth

import (
  "errors"
  "github.com/246859/work/user"
)

// Verify user credentials if is ok
func Verify(user user.User) (bool, error) {
  password, err := query(user.Name)
  if err != nil {
    return false, err
  }
  if password != user.Password {
    return false, errors.New("authentication failed")
  }
  return true, nil
}

func query(username string) (string, error) {
  if username == "jack" {
    return "jack123456", nil
  }
  return "", errors.New("user not found")
}

user モジュールの内容は以下の通りです。

go
package user

type User struct {
  Name     string
  Password string
  Age      int
}

このプロジェクトでは、以下のように go.work ファイルを記述できます。

go 1.22

use (
  ./auth
  ./user
)

その内容は非常に理解しやすく、use 指示を使用して、どのモジュールがコンパイルに参加するかを指定します。次に、auth モジュールのコードを実行します。

go
// auth/example/main.go
package main

import (
  "fmt"
  "github.com/246859/work/auth"
  "github.com/246859/work/user"
)

func main() {
  ok, err := auth.Verify(user.User{Name: "jack", Password: "jack123456"})
  if err != nil {
    panic(err)
  }
  fmt.Printf("%v", ok)
}

以下のコマンドを実行し、結果からモジュールが正常にインポートされたことがわかります。

bash
$ go run ./auth/example
true

以前のバージョンでは、これら 2 つの独立したモジュールについて、auth モジュールが user モジュールのコードを使用したい場合、2 つの方法しかありませんでした。

  1. user モジュールの変更をコミットしてリモートリポジトリにプッシュし、新バージョンをリリースしてから、go.mod ファイルを指定されたバージョンに修正する
  2. go.mod ファイルを修正して、依存関係をローカルファイルにリダイレクトする

2 つの方法どちらも go.mod ファイルを修正する必要があります。ワークスペースの存在は、go.mod ファイルを修正せずに他のモジュールをインポートできるようにするためです。ただし、理解すべき点は、go.work ファイルは開発プロセス中のみ使用され、その存在はより便利なローカル開発を行うためだけであり、依存関係管理を行うためではないということです。これは一時的にリリースまでのプロセスを省略し、user モジュールの新しい修正を待たずにすぐに使用できるようにするためです。user モジュールのテストが完了した後、最終的に新バージョンをリリースし、auth モジュールも go.mod ファイルを修正して最新バージョンを参照する必要があります(このプロセスは go work sync コマンドで完了できます)。したがって、通常の go 開発プロセスにおいて、go.work は VCS にコミットされるべきではありません(サンプルリポジトリの go.work はデモンストレーション用のみ)。その内容はすべてローカルファイルに依存しており、その機能もローカル開発のみに限定されるためです。

コマンド

以下はワークスペースのコマンドです。

コマンド説明
editgo.work を編集
init新しいワークスペースを初期化
syncワークスペースのモジュール依存関係を同期
usego.work に新しいモジュールを追加
vendor依存関係を vendor フォーマットでコピー

go work cmd でコマンドの詳細情報をご覧ください。

指示

go.work ファイルの内容は非常にシンプルで、3 つの指示のみがあります。

  • go:go バージョンを指定
  • use:使用するモジュールを指定
  • replace:置き換えるモジュールを指定

use 指示を除き、他の 2 つは基本的に go.mod の指示と同等ですが、go.workreplace 指示はすべてのモジュールに作用します。完全な go.work は以下の通りです。

tex
go 1.22

use(
  ./auth
  ./user
)

repalce github.com/246859/hello v1.0.0 => /home/jack/code/hello

Golang学习网由www.golangdev.cn整理维护