モジュール
すべてのモダンなプログラミング言語には、それぞれ独自の成熟した依存関係管理ツールがあります。例えば 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 が有効になっていることを確認します。以下のコマンドを使用して有効になっているかどうかを確認します。
$ go env GO111MODULE有効になっていない場合は、以下のコマンドで Go Module を有効にします。
$ go env -w GO111MODULE=on作成
まず、公開アクセス可能なソースコードリポジトリが必要です。これには多くの選択肢がありますが、Github をお勧めします。そこに新しいプロジェクトを作成し、hello という名前にします。リポジトリ名には特別な制限はありませんが、モジュール名に影響を与える可能性があるため、特殊文字を使用しないことをお勧めします。

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

次に、これをローカルにクローンし、go mod init コマンドでモジュールを初期化します。
$ 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 つの関数のみです。
// 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)
}ついでに、ユニットテスト用のテストファイルも作成します。
// 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/ ディレクトリに保存され、そこで関連コードを記述します。
// 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())
}
}テスト
開発が完了したら、ソースコードをフォーマットしてテストします。
$ go fmt && go vet ./...
$ go test -v .
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok github.com/246859/hello 0.023sコマンドラインプログラムを実行します。
$ go run ./cmd/hello -name jack
hello jack!ドキュメント
最後に、このライブラリの簡潔で分かりやすい README を作成し、他の開発者が一目で使用方法を理解できるようにします。
# 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 ドキュメントです。必要に応じて内容を充実させることもできます。
アップロード
すべてのコードの作成とテストが完了したら、変更をコミットしてリモートリポジトリにプッシュできます。
$ 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 つのコミットは多くありません。コミットが完了したら、最新のコミットにタグを作成します。
$ 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最後にリモートリポジトリにプッシュします。
$ 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 を使用してライブラリを導入します。
$ go get github.com/246859/hello@latest
go: downloading github.com/246859/hello v1.0.0
go: added github.com/246859/hello v1.0.0go install を使用してコマンドラインプログラムをインストールします。
$ go install github.com/246859/hello/cmd/hello@latest && hello -name jack
hello jack!または go run を使用して直接実行します。
$ 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 はプロキシダウンロードが失敗した場合、プロキシキャッシュをバイパスして直接ソースコードリポジトリにアクセスすることを示します。
$ go env -w GOPROXY=https://goproxy.cn,directプロキシの変更が成功すると、今後の依存関係のダウンロードが非常に迅速になります。
依存関係のダウンロード
プロキシを変更した後、サードパーティの依存関係をインストールしてみましょう。Go 公式には専用の依存関係照会サイトがあります:Go Packages。
コードの導入
その中で有名な Web フレームワーク Gin を検索します。

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

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

ここではアドレスをコピーして、以前に作成したプロジェクトの下で go get コマンドを使用するだけです。コマンドは以下の通りです。
$ go get github.com/gin-gonic/ginプロセス中に多くの依存関係がダウンロードされます。エラーが発生しなければダウンロード成功です。
$ 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 ファイルを確認します。
$ 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 という名前のファイルがディレクトリに追加されていることもわかります。
$ ls
go.mod go.sum main.goここではまず詳しく説明せず、main.go ファイルを以下のコードに修正します。
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
gin.Default().Run()
}プロジェクトを再度実行します。
$ 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 を削除する例を示します。
$ go get github.com/gin-gonic/gin@none
go: removed github.com/gin-gonic/gin v1.9.0依存関係アドレスの後に @none を追加するだけで、その依存関係を削除できます。結果も削除成功を示しています。この時点で go.mod ファイルを再度確認すると、Gin 依存関係がなくなっていることがわかります。
$ cat go.mod | grep github.com/gin-gonic/gin最新バージョンにアップグレードする場合は、@latest サフィックスを追加するか、使用可能なリリースバージョン番号を自分で照会できます。
$ go get -u github.com/gin-gonic/gin@latestコマンドラインのインストール
go install コマンドは、サードパーティの依存関係をローカルにダウンロードし、バイナリファイルにコンパイルします。Go のコンパイル速度のおかげで、このプロセスは通常それほど時間がかかりません。その後、Go はこれを $GOPATH/bin または $GOBIN ディレクトリに保存し、グローバルでバイナリファイルを実行できるようにします(これらのパスを環境変数に追加していることが前提です)。
TIP
install コマンドを使用する際は、バージョン番号を指定する必要があります。
例えば、Go 言語で書かれたデバッガー delve をダウンロードします。
$ 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:メジャーバージョンが変更された場合、プロジェクトに互換性のない変更があったことを示します。旧バージョンのプロジェクトを新バージョンにアップグレードすると、おそらく正常に実行されません。minor:minorバージョンが変更された場合、プロジェクトに新機能が追加されたことを示します。以前のバージョンの機能に新機能が追加されただけです。patch:patchバージョンが変更された場合、バグが修正されただけで、新機能は追加されていないことを示します。
よく使用されるコマンド
| コマンド | 説明 |
|---|---|
go mod download | 現在のプロジェクトの依存パッケージをダウンロード |
go mod edit | go.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 を変更して、別の場所に保存することもできます。
$ go env -w GOMODCACHE=あなたのモジュールキャッシュパス同じマシン上のすべての Go Module プロジェクトは、このディレクトリ下のキャッシュを共有します。キャッシュにはサイズの制限がなく、自動的に削除されることもありません。キャッシュ内で解凍された依存関係のソースファイルはすべて読み取り専用です。キャッシュをクリアしたい場合は、以下のコマンドを実行できます。
$ go clean -modcache$GOMODCACHE/cache/download ディレクトリには、依存関係の元のファイル(ハッシュファイル、元の圧縮パッケージなど)が保存されています。例は以下の通りです。
$ 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解凍された依存関係の組織形式は以下の通りで、指定されたモジュールのソースコードです。
$ 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/protobuf、golang.org/x/crypto などです。通常、この一連の URL は同時に Go プロジェクトのモジュール名でもあります。これにより問題が発生します。URL は大文字小文字を区別しませんが、依存関係を保存するフォルダは大文字小文字を区別します。したがって、go get github.com/gin-gonic/gin と go 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/ginGin のモジュール名は依存関係をダウンロードする際に使用されるアドレスです。这也是通常推奨されるモジュール名フォーマットで、ドメイン/ユーザー/リポジトリ名 です。
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/modgo
go キーワードは現在のプロジェクトの作成に使用された Go バージョンを示します。バージョン番号はセマンティックバージョニング規則に従う必要があります。Go のバージョンが異なると、Go Mod は異なる動作を示します。以下は簡単な例です。Go の利用可能なバージョンについては、公式で照会してください。
go 1.20require
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@fe3a3abad311exclude
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 は指定されたバージョンの依存関係を置き換えます。モジュールパスとバージョン、または他のプラットフォームで指定されたファイルパスで置き換えることができます。例
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 指示を使用できます。
一部のバージョンを取り消す
retract (
v1.0.0 // Published accidentally.
v1.0.1 // Contains retractions only.
)バージョン範囲を取り消す
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.0go get コマンドはまず依存パッケージをローカルのキャッシュディレクトリにダウンロードします。通常、このディレクトリは $GOMODCACHE/cache/download/ です。このディレクトリはドメイン名に基づいて異なるウェブサイトの依存パッケージを区別するため、以下のようなディレクトリ構造が表示される可能性があります。
$ 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/可能性のあるディレクトリ構造は以下の通りで、いくつかのバージョン命名のファイルがあります。
$ 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 モジュールがあります。それぞれ auth、user です。
$ ls -1
LICENSE
README.md
auth
go.work
userauth モジュールは user モジュールの構造体 User に依存しており、内容は以下の通りです。
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 モジュールの内容は以下の通りです。
package user
type User struct {
Name string
Password string
Age int
}このプロジェクトでは、以下のように go.work ファイルを記述できます。
go 1.22
use (
./auth
./user
)その内容は非常に理解しやすく、use 指示を使用して、どのモジュールがコンパイルに参加するかを指定します。次に、auth モジュールのコードを実行します。
// 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)
}以下のコマンドを実行し、結果からモジュールが正常にインポートされたことがわかります。
$ go run ./auth/example
true以前のバージョンでは、これら 2 つの独立したモジュールについて、auth モジュールが user モジュールのコードを使用したい場合、2 つの方法しかありませんでした。
- user モジュールの変更をコミットしてリモートリポジトリにプッシュし、新バージョンをリリースしてから、
go.modファイルを指定されたバージョンに修正する go.modファイルを修正して、依存関係をローカルファイルにリダイレクトする
2 つの方法どちらも go.mod ファイルを修正する必要があります。ワークスペースの存在は、go.mod ファイルを修正せずに他のモジュールをインポートできるようにするためです。ただし、理解すべき点は、go.work ファイルは開発プロセス中のみ使用され、その存在はより便利なローカル開発を行うためだけであり、依存関係管理を行うためではないということです。これは一時的にリリースまでのプロセスを省略し、user モジュールの新しい修正を待たずにすぐに使用できるようにするためです。user モジュールのテストが完了した後、最終的に新バージョンをリリースし、auth モジュールも go.mod ファイルを修正して最新バージョンを参照する必要があります(このプロセスは go work sync コマンドで完了できます)。したがって、通常の go 開発プロセスにおいて、go.work は VCS にコミットされるべきではありません(サンプルリポジトリの go.work はデモンストレーション用のみ)。その内容はすべてローカルファイルに依存しており、その機能もローカル開発のみに限定されるためです。
コマンド
以下はワークスペースのコマンドです。
| コマンド | 説明 |
|---|---|
| edit | go.work を編集 |
| init | 新しいワークスペースを初期化 |
| sync | ワークスペースのモジュール依存関係を同期 |
| use | go.work に新しいモジュールを追加 |
| vendor | 依存関係を vendor フォーマットでコピー |
go work cmd でコマンドの詳細情報をご覧ください。
指示
go.work ファイルの内容は非常にシンプルで、3 つの指示のみがあります。
go:go バージョンを指定use:使用するモジュールを指定replace:置き換えるモジュールを指定
use 指示を除き、他の 2 つは基本的に go.mod の指示と同等ですが、go.work の replace 指示はすべてのモジュールに作用します。完全な go.work は以下の通りです。
go 1.22
use(
./auth
./user
)
repalce github.com/246859/hello v1.0.0 => /home/jack/code/hello