Go Wiki: Go Modules
この Wiki ページは、使用方法とトラブルシューティングガイドとして機能します。
- チュートリアルブログ投稿については、Go Modules の使用を参照してください。
- 技術的なリファレンスについては、Go Modules リファレンス(開発中)を参照してください。
Go は、ここで提案されているバージョン管理されたモジュールに対するサポートを 1.11 以降に含んでいます。最初のプロトタイプである vgo
は、2018 年 2 月に発表されました。2018 年 7 月には、バージョン管理されたモジュールが Go のメインリポジトリに取り込まれました。
Go 1.14 以降、モジュールサポートは本番環境で使用できる状態と見なされており、すべてのユーザーは他の依存関係管理システムからモジュールに移行することが推奨されています。Go ツールチェーンの問題のために移行できない場合は、その問題に未解決の課題が登録されていることを確認してください。(その問題が Go1.16 マイルストーンにない場合は、移行できない理由をコメントして、適切に優先順位を付けることができます)。より詳細なフィードバックについては、エクスペリエンスレポートを提供することもできます。
最近の変更
Go 1.16
詳細については、Go 1.16 リリースノートを参照してください。
- モジュールモード(
GO111MODULE=on
)は、すべての場合でデフォルトになっています。 - コマンドはデフォルトで
go.mod
/go.sum
を変更しなくなりました(-mod=readonly
)。 go install pkg@version
は、パッケージ/実行ファイルをグローバルにインストールするための推奨方法です。retract
はgo.mod
で使用できます。
Go 1.15
詳細については、Go 1.15 リリースノートを参照してください。
- モジュールキャッシュの場所は、
GOMODCACHE
環境変数で設定できるようになりました。GOMODCACHE
のデフォルト値はGOPATH[0]/pkg/mod
であり、この変更前のモジュールキャッシュの場所です。 - モジュールキャッシュにアクセスする go コマンドで発生する Windows の「アクセス拒否」エラー(外部プログラムがファイルシステムを同時にスキャンすることによって発生する、#36568を参照)に対する回避策が利用できるようになりました。この回避策は、1.14.2 および 1.13.10 より前の Go バージョンが同じモジュールキャッシュと同時に実行されている場合に安全ではないため、デフォルトでは有効になっていません。環境変数
GODEBUG=modcacheunzipinplace=1
を明示的に設定することで有効にできます。
Go 1.14
詳細については、Go 1.14 リリースノートを参照してください。
- メインモジュールにトップレベルの vendor ディレクトリが含まれており、その
go.mod ファイル
がgo 1.14
以上を指定している場合、go コマンドは、そのフラグを受け入れる操作に対してデフォルトで-mod=vendor
を使用するようになりました。 go.mod
ファイルが読み取り専用で、トップレベルの vendor ディレクトリが存在しない場合、-mod=readonly
がデフォルトで設定されるようになりました。-modcacherw
は、新しく作成されたディレクトリをモジュールキャッシュにデフォルトの権限で残すように go コマンドに指示する新しいフラグです。読み取り専用にはしません。-modfile=file
は、モジュールルートディレクトリにあるものとは別のgo.mod
ファイルを読み取り(そして場合によっては書き込み)するように go コマンドに指示する新しいフラグです。- モジュール認識モードが明示的に有効になっている場合(
GO111MODULE=on
を設定することによって)、go.mod
ファイルが存在しない場合、ほとんどのモジュールコマンドの機能は制限されます。 - go コマンドは、モジュールモードで Subversion リポジトリをサポートするようになりました。
Go 1.13
詳細については、Go 1.13 リリースノートを参照してください。
go
ツールは、デフォルトでhttps://proxy.golang.orgにあるパブリック Go モジュールミラーからモジュールをダウンロードするようになり、また、ダウンロードされたモジュール(ソースに関係なく)をhttps://sum.golang.orgにあるパブリック Go チェックサムデータベースに対して検証するようになりました。- プライベートコードがある場合は、
GOPRIVATE
設定(例:go env -w GOPRIVATE=*.corp.com,github.com/secret/repo
)または、頻度の低いユースケースに対応するより詳細なバリアントであるGONOPROXY
やGONOSUMDB
を設定する必要がある可能性が高いです。詳細については、ドキュメントを参照してください。
- プライベートコードがある場合は、
GO111MODULE=auto
は、go.mod
が GOPATH 内に見つかった場合でも、モジュールモードを有効にします。(Go 1.13 より前では、GO111MODULE=auto
は GOPATH 内ではモジュールモードを有効にしませんでした)。go get
の引数が変更されました。go get -u
(引数なし)は、現在のパッケージの直接および間接の依存関係のみをアップグレードするようになり、モジュール全体を検査しなくなりました。- モジュールのルートから実行される
go get -u ./...
は、モジュールの直接および間接の依存関係をすべてアップグレードしますが、テスト依存関係は除外するようになりました。 go get -u -t ./...
は同様ですが、テスト依存関係もアップグレードします。go get
は-m
をサポートしなくなりました(他の変更によりgo get -d
とほぼ重複するようになったためです。通常、go get -m foo
はgo get -d foo
で置き換えることができます)。
目次
「クイックスタート」セクションと「新しい概念」セクションは、モジュールを使い始める人にとって特に重要です。「使用方法」セクションでは、メカニズムに関する詳細を説明しています。このページで最も多くのコンテンツは、より具体的な質問に答える FAQ にあります。ここにリストされている FAQ のワンライナーをざっと目を通すだけでも価値があります。
- クイックスタート
- 新しい概念
- モジュールの使用方法
- モジュールへの移行
- 追加のリソース
- 最初の Vgo 提案以降の変更
- GitHub の課題
- FAQ
- FAQ — 追加の制御
- モジュールの操作に使用できるコミュニティツールは何ですか?
- 'replace' ディレクティブはいつ使用する必要がありますか?
- ローカルファイルシステム上の VCS の外部で完全に作業できますか?
- モジュールでベンダーを使用するにはどうすればよいですか?ベンダーは廃止されますか?
- 「常にオン」のモジュールリポジトリとエンタープライズプロキシはありますか?
- go.mod の更新時期と、go ツールがネットワークを使用して依存関係を満たす時期を制御できますか?
- Travis や CircleCI などの CI システムでモジュールを使用するにはどうすればよいですか?
- 特定のパッケージやテストをビルドするために必要なモジュールをダウンロードするにはどうすればよいですか?
- FAQ — go.mod と go.sum
- FAQ — セマンティックインポートバージョン管理
- なぜメジャーバージョン番号はインポートパスに表示される必要があるのですか?
- なぜメジャーバージョン v0、v1 はインポートパスから省略されるのですか?
- プロジェクトにメジャーバージョン v0、v1 でタグを付けたり、v2 以降で破壊的な変更を加えることの含意は何ですか?
- モジュールは、モジュールにオプトインしていないパッケージを使用できますか?
- モジュールは、モジュールにオプトインしていない v2 以降のパッケージを使用できますか?'+incompatible' とは何を意味しますか?
- モジュールサポートが有効になっていない場合、ビルドで v2 以降のモジュールはどのように処理されますか?1.9.7 以降、1.10.3 以降、および 1.11 での「最小モジュール互換性」はどのように機能しますか?
- go.mod を作成しても、リポジトリに semver タグを適用しなかった場合はどうなるでしょうか?
- モジュールは、それ自体の異なるバージョンに依存できますか?
- FAQ — マルチモジュールリポジトリ
- よくある質問 — 最小バージョン選択
- よくある質問 — 発生する可能性のある問題
- 問題が発生した場合、スポットチェックできる一般的な事項は何ですか?
- 依存関係の期待されるバージョンが表示されない場合、何をチェックできますか?
- 「パッケージfooを提供するモジュールが見つかりません」というエラーが表示されるのはなぜですか?
- 「go mod init」で「ソースディレクトリのモジュールパスを決定できません」というエラーが表示されるのはなぜですか?
- モジュールに対応していない複雑な依存関係に問題があります。その現在の依存関係マネージャーの情報を使用できますか?
- インポートパスと宣言されたモジュールIDの不一致によって発生する「go.modの解析エラー:予期しないモジュールパス」と「モジュール要件の読み込みエラー」を解決するにはどうすればよいですか?
- 「go build」でgccが必要なのはなぜですか?また、net/httpなどのプリビルドパッケージが使用されないのはなぜですか?
- モジュールは、`import "./subdir"`のような相対インポートで動作しますか?
- 移入されたvendorディレクトリに必要なファイルが存在しない場合があります。
クイックスタート
例
詳細は、このページの残りの部分で説明しますが、ここでは、モジュールを最初から作成する簡単な例を示します。
GOPATHの外部にディレクトリを作成し、必要に応じてVCSを初期化します。
$ mkdir -p /tmp/scratchpad/repo
$ cd /tmp/scratchpad/repo
$ git init -q
$ git remote add origin https://github.com/my/repo
新しいモジュールを初期化します。
$ go mod init github.com/my/repo
go: creating new go.mod: module github.com/my/repo
コードを記述します。
$ cat <<EOF > hello.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
EOF
ビルドして実行します。
$ go mod tidy
go: finding module for package rsc.io/quote
go: found rsc.io/quote in rsc.io/quote v1.5.2
$ go build -o hello
$ ./hello
Hello, world.
go.mod
ファイルは、依存関係に明示的なバージョンを含めるように更新されました。ここで`v1.5.2`はsemverタグです。
$ cat go.mod
module github.com/my/repo
go 1.16
require rsc.io/quote v1.5.2
日々のワークフロー
1.16より前では、`go build -o hello`を実行する前に`go get`または`go mod tidy`は必要ありませんでした。go.mod
とgo.sum
ファイルの暗黙的な変更は、1.16でデフォルトで無効化されました。
一般的な日々のワークフローは次のとおりです。
- 必要に応じて、
.go
コードにインポートステートメントを追加します。 go build
やgo test
などの標準コマンドは、インポートを満たすために必要な新しい依存関係を自動的に追加します(go.mod
を更新し、新しい依存関係をダウンロードします)。- 必要に応じて、
go get foo@v1.2.3
、go get foo@master
(Mercurialではfoo@default
)、go get foo@e3702bed2
などのコマンド、またはgo.mod
を直接編集することで、依存関係のより具体的なバージョンを選択できます。
使用できる可能性のあるその他の一般的な機能の概要
go list -m all
— すべての直接および間接依存関係のビルドで使用される最終バージョンを表示します(詳細)go list -u -m all
— すべての直接および間接依存関係で使用可能なマイナーおよびパッチアップグレードを表示します(詳細)go get -u ./...
またはgo get -u=patch ./...
(モジュールルートディレクトリから)— すべての直接および間接依存関係を最新のマイナーまたはパッチアップグレードに更新します(プレリリースは無視されます)(詳細)go build ./...
またはgo test ./...
(モジュールルートディレクトリから)— モジュール内のすべてのパッケージをビルドまたはテストします(詳細)go mod tidy
— 不要になった依存関係をgo.mod
から削除し、OS、アーキテクチャ、ビルドタグの他の組み合わせに必要な依存関係を追加します(詳細)replace
ディレクティブまたはgohack
— フォーク、ローカルコピー、または依存関係の正確なバージョンを使用します(詳細)go mod vendor
—vendor
ディレクトリを作成するためのオプションの手順です(詳細)
次の4つの「新しい概念」に関するセクションを読んだ後、ほとんどのプロジェクトでモジュールを使い始めるのに十分な情報が得られます。また、上記の目次(そこに記載されているFAQのワンライナーを含む)を確認して、より詳細なトピックのリストに慣れておくことも役立ちます。
新しい概念
これらのセクションでは、主な新しい概念の概要を簡単に説明します。詳細と理由については、設計の背後にある哲学について説明するRuss Coxによる40分の紹介ビデオをご覧ください、公式提案文書、またはより詳細な最初のvgoブログシリーズを参照してください。
モジュール
モジュールとは、単一のユニットとしてバージョン管理された、関連するGoパッケージのコレクションです。
モジュールは、正確な依存関係の要件を記録し、再現可能なビルドを作成します。
ほとんどの場合、バージョン管理リポジトリには、リポジトリルートに定義されたモジュールが1つだけ含まれています。(単一のリポジトリに複数のモジュールをサポートしていますが、通常、リポジトリごとに1つのモジュールよりも継続的な作業が増えることになります)。
リポジトリ、モジュール、パッケージ間の関係をまとめます。
- リポジトリには、1つ以上のGoモジュールが含まれています。
- 各モジュールには、1つ以上のGoパッケージが含まれています。
- 各パッケージは、単一ディレクトリ内の1つ以上のGoソースファイルで構成されています。
モジュールは、semverに従ってセマンティックバージョン管理を行う必要があります。通常、v(major).(minor).(patch)
形式(例:v0.1.0
、v1.2.3
、v1.5.0-rc.1
)です。先頭のv
が必要です。Gitを使用する場合は、タグを使用して、リリースされたコミットにバージョンを付けます。公開および非公開のモジュールリポジトリとプロキシが利用可能になりつつあります(下のFAQを参照)。
go.mod
モジュールは、ツリーのルートディレクトリにgo.mod
ファイルを持つGoソースファイルのツリーによって定義されます。モジュールのソースコードはGOPATHの外部に配置できます。4つのディレクティブがあります:module
、require
、replace
、exclude
。
モジュールgithub.com/my/thing
を定義するgo.mod
ファイルの例を次に示します。
module github.com/my/thing
require (
github.com/some/dependency v1.2.3
github.com/another/dependency/v4 v4.0.0
)
モジュールは、モジュールパスを提供するmodule
ディレクティブを介して、そのgo.mod
でそのIDを宣言します。モジュール内のすべてのパッケージのインポートパスは、モジュールパスを共通のプレフィックスとして共有します。モジュールパスと、go.mod
からパッケージのディレクトリまでの相対パスを組み合わせることで、パッケージのインポートパスが決まります。
たとえば、インポートパスがgithub.com/user/mymod/foo
とgithub.com/user/mymod/bar
の2つのパッケージを含むリポジトリgithub.com/user/mymod
のモジュールを作成する場合、go.mod
ファイルの最初の行には通常、module github.com/user/mymod
としてモジュールパスが宣言され、対応するディスク上の構造は次のようになります。
mymod
|-- bar
| `-- bar.go
|-- foo
| `-- foo.go
`-- go.mod
Goソースコードでは、モジュールパスを含む完全パスを使用してパッケージがインポートされます。たとえば、上記の例で、go.mod
にmodule github.com/user/mymod
としてモジュールIDを宣言した場合、コンシューマーは次のように実行できます。
import "github.com/user/mymod/bar"
これにより、モジュールgithub.com/user/mymod
からパッケージbar
がインポートされます。
exclude
およびreplace
ディレクティブは、現在の(「メイン」)モジュールでのみ機能します。メインモジュール以外のモジュール内のexclude
およびreplace
ディレクティブは、メインモジュールをビルドする際に無視されます。したがって、replace
およびexclude
ステートメントにより、メインモジュールは独自のビルドを完全に制御できますが、依存関係によって完全に制御されることはありません。(replace
ディレクティブを使用する時期については、下のFAQを参照してください)。
バージョン選択
go.mod
のrequire
でまだカバーされていない新しいインポートをソースコードに追加すると、「go build」や「go test」などのほとんどのgoコマンドは、適切なモジュールを自動的に検索し、新しい直接依存関係の最高のバージョンをモジュールのgo.mod
にrequire
ディレクティブとして追加します。たとえば、新しいインポートが、最新のタグ付きリリースバージョンがv1.2.3
である依存関係Mに対応する場合、モジュールのgo.mod
にはrequire M v1.2.3
が追加されます。これは、モジュールMがバージョン>= v1.2.3(v2はv1と互換性がないと見なされるため、< v2)が許可された依存関係であることを示しています。
最小バージョン選択アルゴリズムは、ビルドで使用されるすべてのモジュールのバージョンを選択するために使用されます。ビルド内の各モジュールについて、最小バージョン選択によって選択されたバージョンは、常に、メインモジュールまたはその依存関係のいずれかでrequire
ディレクティブによって明示的にリストされているバージョンのセマンティックに最も高いバージョンです。
例として、モジュールがrequire D v1.0.0
を持つモジュールAに依存し、モジュールがrequire D v1.1.1
を持つモジュールBにも依存する場合、最小バージョン選択では、ビルドにDのv1.1.1
が選択されます(リストされているrequire
バージョンの中で最も高いため)。このv1.1.1
の選択は、後でDのv1.2.0
が利用可能になっても変わりません。これは、モジュールシステムが100%再現可能なビルドを提供する方法の一例です。準備ができたら、モジュール作成者またはユーザーは、Dの最新バージョンにアップグレードするか、Dの明示的なバージョンを選択できます。
最小バージョン選択アルゴリズムの簡単な理由と概要については、公式提案の「High Fidelity Builds」セクション、またはより詳細なvgo
ブログシリーズを参照してください。
選択されたモジュールバージョン(間接依存関係を含む)のリストを表示するには、go list -m all
を使用します。
下記の「依存関係のアップグレードとダウングレードの方法」セクションと、「バージョンが非互換としてマークされるのはどのようにしてですか?」のFAQも参照してください。
セマンティックインポートバージョン管理
長年にわたり、公式のGo FAQには、パッケージのバージョニングに関する次のアドバイスが含まれていました。
「公開用に意図されたパッケージは、進化する際に後方互換性を維持しようとします。Go 1互換性ガイドラインはここで良い参照となります。エクスポートされた名前を削除しないでください。タグ付き複合リテラルを推奨し、その他も同様です。異なる機能が必要な場合は、古い名前を変更する代わりに新しい名前を追加します。完全なブレークが必要な場合は、新しいインポートパスを持つ新しいパッケージを作成します。」
最後の文は特に重要です。互換性を壊す場合は、パッケージのインポートパスを変更する必要があります。Go 1.11モジュールでは、そのアドバイスがインポート互換性ルールに公式化されています。
古いパッケージと新しいパッケージが同じインポートパスを持つ場合、新しいパッケージは古いパッケージとの後方互換性を持たなければなりません。
semver を思い出してください。v1以上のバージョンで後方互換性のない変更を加えた場合は、メジャーバージョンの変更が必要になります。インポート互換性ルールとsemverの両方を遵守した結果を、セマンティックインポートバージョン管理と呼びます。メジャーバージョンはインポートパスに含まれ、互換性の破損によってメジャーバージョンが増加するたびにインポートパスが変更されることを保証します。
セマンティックインポートバージョン管理の結果として、Goモジュールを採用するコードは**これらのルールに従わなければなりません**
- semverに従ってください。(例としてVCSタグは
v1.2.3
です)。 - モジュールがv2以上のバージョンである場合、モジュールのメジャーバージョンは、
go.mod
ファイルで使用されるモジュールパスの最後に/vN
として含める必要があります(例:module github.com/my/mod/v2
、require github.com/my/mod/v2 v2.0.1
)。これは、go get
コマンドで使用されるパスにも含まれます(例:go get github.com/my/mod/v2@v2.0.1
。この例には/v2
と@v2.0.1
の両方が含まれています。ある意味、モジュール名に/v2
が含まれるようになったため、モジュール名を使用する場合は常に/v2
を含める必要があります)。 - モジュールがv0またはv1である場合、モジュールパスとインポートパスのいずれにもメジャーバージョンを含めないでください。
一般的に、インポートパスが異なるパッケージは異なるパッケージです。例えば、math/rand
はcrypto/rand
とは異なるパッケージです。これは、インポートパスが異なるメジャーバージョンによる場合にも当てはまります。したがって、example.com/my/mod/mypkg
はexample.com/my/mod/v2/mypkg
とは異なるパッケージであり、どちらも単一のビルドでインポートできます。これには、ダイヤモンド依存性の問題に対処するなどの利点があり、v1モジュールをv2の代替物で実装したり、その逆を行うこともできます。
セマンティックインポートバージョン管理の詳細については、go
コマンドのドキュメントの「モジュールの互換性とセマンティックバージョン管理」セクションを参照してください。セマンティックバージョン管理の詳細についてはhttps://semver.orgを参照してください。
これまでのセクションでは、モジュールを採用し、他のモジュールをインポートするコードに焦点を当ててきました。しかし、v2以上のモジュールのインポートパスにメジャーバージョンを入れることで、古いバージョンのGoや、まだモジュールを採用していないコードとの間に互換性の問題が発生する可能性があります。これを解決するために、上記の動作とルールの3つの重要な移行時の特別なケースまたは例外があります。これらの移行時の例外は、より多くのパッケージがモジュールを採用するにつれて、時間の経過とともに重要性が低くなります。
3つの移行時の例外
-
gopkg.in
gopkg.in
で始まるインポートパス(例:gopkg.in/yaml.v1
とgopkg.in/yaml.v2
)を使用している既存のコードは、モジュールを採用した後も、それらの形式をモジュールパスとインポートパスに使用し続けることができます。 -
非モジュールv2+パッケージをインポートする場合の'+incompatible'
モジュールは、それ自体がモジュールを採用していないv2+パッケージをインポートできます。有効なv2+ semver タグを持つ非モジュールv2+パッケージは、インポートするモジュールの
go.mod
ファイルに+incompatible
サフィックスが付加されて記録されます。+incompatible
サフィックスは、v2+パッケージが有効なv2+ semver タグ(例:v2.0.0
)を持っている場合でも、v2+パッケージが積極的にモジュールを採用していないことを示しています。したがって、v2+パッケージはセマンティックインポートバージョン管理の意味と、インポートパスでメジャーバージョンを使用する方法を理解せずに作成されたと想定されます。モジュールモードで動作している場合、go
ツールは非モジュールv2+パッケージをパッケージのv1バージョンシリーズの(非互換な)拡張として扱い、パッケージがセマンティックインポートバージョン管理を認識していないと仮定します。+incompatible
サフィックスは、go
ツールがそうしていることを示す指標です。 -
モジュールモードが無効になっている場合の「最小限のモジュール互換性」
後方互換性を向上させるために、Goバージョン1.9.7以降、1.10.3以降、および1.11は、これらのリリースでビルドされたコードが、既存のコードを変更せずにv2+モジュールを適切に利用できるようにするために更新されています。この動作は「最小限のモジュール互換性」と呼ばれ、
go
ツールの完全なモジュールモードが無効になっている場合にのみ有効になります(例:Go 1.11でGO111MODULE=off
を設定した場合、またはGoバージョン1.9.7以降または1.10.3以降を使用している場合)。Go 1.9.7以降、1.10.3以降、および1.11でこの「最小限のモジュール互換性」メカニズムに依存している場合、モジュールを採用していないパッケージは、インポートされたv2+モジュールのインポートパスにメジャーバージョンを含めません。対照的に、モジュールを採用しているパッケージは、セマンティックインポートバージョン管理を完全に認識した状態でgo
ツールが完全なモジュールモードで動作している場合に、v2+モジュールを正しくインポートするために、v2+モジュールをインポートするインポートパスにメジャーバージョンを含める必要があります。
v2以上のモジュールをリリースするために必要な正確なメカニズムについては、以下の「モジュールのリリース(v2以上)」セクションを参照してください。
モジュールの使用方法
モジュールサポートのインストールと有効化の方法
モジュールを使用するには、2つのインストールオプションがあります。
- 最新のGo 1.11リリースをインストールします。.
master
ブランチからソースからGoツールチェーンをインストールします。
インストール後、次の2つの方法のいずれかでモジュールサポートを有効にできます。
$GOPATH/src
ツリーの外側のディレクトリで、現在のディレクトリまたはその親ディレクトリに有効なgo.mod
ファイルがあり、環境変数GO111MODULE
が設定されていない(または明示的にauto
に設定されている)状態でgo
コマンドを呼び出します。- 環境変数
GO111MODULE=on
を設定してgo
コマンドを呼び出します。
モジュールの定義方法
既存のプロジェクトにgo.mod
を作成するには
-
GOPATHの外側のモジュールのソースツリーのルートに移動します。
$ cd <project path outside $GOPATH/src> # e.g., cd ~/projects/hello
GOPATHの外側では、モジュールモードを有効にするために
GO111MODULE
を設定する必要はありません。または、GOPATHで作業する場合
$ export GO111MODULE=on # manually active module mode $ cd $GOPATH/src/<project path> # e.g., cd $GOPATH/src/you/hello
-
最初のモジュール定義を作成し、
go.mod
ファイルに書き込みます。$ go mod init
この手順では、既存の
dep
Gopkg.lock
ファイルまたはその他の合計9種類のサポートされている依存関係形式から変換され、既存の設定に一致するrequireステートメントが追加されます。go mod init
は、多くの場合、補助データ(VCSメタデータなど)を使用して適切なモジュールパスを自動的に決定できますが、go mod init
がモジュールパスを自動的に決定できないと表示された場合、またはパスを上書きする必要がある場合は、オプションの引数としてモジュールパスをgo mod init
に提供できます。例:$ go mod init github.com/my/repo
依存関係にv2以上のモジュールが含まれている場合、またはv2以上のモジュールを初期化している場合、
go mod init
を実行した後、上記の「セマンティックインポートバージョン管理」セクションで説明されているように、インポートパスとモジュールパスに/vN
を追加するために、go.mod
と.go
コードを編集する必要がある場合があります。これは、go mod init
がdep
またはその他の依存関係マネージャーから依存関係情報を自動的に変換した場合でも適用されます。(このため、go mod init
を実行した後、通常はgo mod tidy
を、go build ./...
などを正常に実行するまで実行しないでください。これはこのセクションで示されているシーケンスです)。 -
モジュールをビルドします。モジュールのルートディレクトリから実行された場合、
./...
パターンは現在のモジュール内のすべてのパッケージに一致します。go build
は、この特定のビルド呼び出しのインポートを満たすために必要な、欠落している依存関係または変換されていない依存関係を自動的に追加します。$ go build ./...
-
選択したバージョンで機能することを確認するために、モジュールをテストします。
$ go test ./...
-
(オプション) 非互換性を確認するために、モジュールとそのすべての直接的および間接的依存関係のテストを実行します。
$ go test all
リリースにタグ付けする前に、以下の「リリースの準備方法」セクションを参照してください。
これらのトピックの詳細については、公式モジュールドキュメントの主要なエントリポイントはgolang.orgで利用可能です。
依存関係のアップグレードとダウングレードの方法
依存関係の日々のアップグレードとダウングレードは、「go get」を使用して行う必要があります。これにより、go.mod
ファイルが自動的に更新されます。または、go.mod
を直接編集することもできます。
さらに、「go build」、「go test」、または「go list」などのgoコマンドは、インポートを満たすために必要な新しい依存関係を自動的に追加します(go.mod
を更新し、新しい依存関係をダウンロードします)。
依存関係を最新バージョンにアップグレードするには
go get example.com/package
依存関係とそのすべての依存関係を最新バージョンにアップグレードするには
go get -u example.com/package
すべての直接的および間接的依存関係で使用可能なマイナーおよびパッチアップグレードを表示するには
go list -u -m all
直接依存関係のみのマイナーおよびパッチアップグレードを表示するには、次を実行します。
go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}: {{.Version}} -> {{.Update.Version}}{{end}}' -m all 2> /dev/null
現在のモジュールのすべての直接的および間接的依存関係の最新バージョンにアップグレードするには、モジュールルートディレクトリから次を実行できます。
- 最新のマイナーまたはパッチリリースを使用するには(テスト依存関係もアップグレードするには
-t
を追加します)go get -u ./...
- 最新のパッチリリースを使用するには(テスト依存関係もアップグレードするには
-t
を追加します)go get -u=patch ./...
go get foo
はfoo
の最新バージョンに更新します。go get foo
はgo get foo@latest
と同等です。つまり、@
バージョンが指定されていない場合は@latest
がデフォルトになります。
このセクションでは、「latest」はsemverタグを持つ最新バージョン、またはsemverタグがない場合は最新の既知のコミットです。プレリリースタグは、リポジトリに他のsemverタグがない場合を除き、「latest」として選択されません(詳細)。
よくある間違いは、go get -u foo
がfoo
の最新バージョンのみを取得すると考えることです。実際には、go get -u foo
またはgo get -u foo@latest
の-u
は、foo
のすべての直接的および間接的依存関係の最新バージョンも取得することを意味します。foo
をアップグレードする場合の一般的な出発点は、-u
なしでgo get foo
またはgo get foo@latest
を実行することです(動作したら、go get -u=patch foo
、go get -u=patch
、go get -u foo
、またはgo get -u
を検討してください)。
より具体的なバージョンにアップグレードまたはダウングレードするには、「go get」を使用すると、@version
サフィックスまたは「モジュールクエリ」をパッケージ引数に追加することでバージョン選択を上書きできます。例:go get foo@v1.6.2
、go get foo@e3702bed2
、またはgo get foo@'<v1.6.2'
。
go get foo@master
(Mercurialの場合はfoo@default
)のようなブランチ名を使用すると、semverタグの有無にかかわらず、最新のコミットを取得できます。
一般的に、semverタグに解決されないモジュールクエリは、go.mod
ファイルに疑似バージョンとして記録されます。
詳細については、go
コマンドのドキュメントの「モジュール対応go get」セクションと「モジュールクエリ」セクションを参照してください。
モジュールは、モジュールをまだ採用していないパッケージを使用できます。これには、利用可能なsemverタグをgo.mod
に記録し、それらのsemverタグを使用してアップグレードまたはダウングレードすることも含まれます。モジュールは、適切なsemverタグを持っていないパッケージも使用できます(その場合、疑似バージョンを使用してgo.mod
に記録されます)。
依存関係のアップグレードまたはダウングレード後、ビルド内のすべてのパッケージ(直接的および間接的な依存関係を含む)のテストを再度実行して、互換性の問題がないか確認することをお勧めします。
$ go test all
リリースの準備方法
モジュールのリリース(すべてのバージョン)
モジュールのリリースを作成するためのベストプラクティスは、初期のモジュール実験の一部として確立される予定です。これらの多くは、将来の「go release」ツールによって自動化される可能性があります。
タグ付けする前に考慮すべき、現在推奨されているベストプラクティスの一部
-
不要な要件を削除するために
go mod tidy
を実行します(こちらで説明)。また、現在のgo.modに、可能なすべてのビルドタグ/OS/アーキテクチャの組み合わせが反映されていることを確認します(こちらで説明)。- これとは対照的に、
go build
やgo test
などの他のコマンドは、不要になった依存関係をgo.mod
から削除せず、現在のビルド呼び出しのタグ/OS/アーキテクチャに基づいてgo.mod
を更新するだけです。
- これとは対照的に、
-
go test all
を実行してモジュールをテストし(直接的および間接的な依存関係のテストも含む)、現在選択されているパッケージバージョンが互換性があることを検証します。- 可能なバージョンの組み合わせの数は、モジュールの数に対して指数関数的に増加するため、一般的に、依存関係が依存関係のすべて可能な組み合わせに対してテストされていると期待することはできません。
- モジュール作業の一環として、
go test all
はより有用なものになるように再定義されました。現在のモジュール内のすべてのパッケージと、1つ以上のインポートを通じて依存するすべてのパッケージを含め、現在のモジュールでは問題にならないパッケージを除外します。
-
go.sum
ファイルは、go.mod
ファイルと一緒にコミットしてください。以下のFAQで、詳細と理由を参照してください。
モジュールのリリース(v2以降)
v2以上のモジュールをリリースする場合は、まず上記「セマンティックインポートバージョン管理」セクションの議論を確認してください。そこには、v2以上のモジュールでメジャーバージョンがモジュールパスとインポートパスに含まれる理由、およびGoバージョン1.9.7以上と1.10.3以上がその移行を簡素化するために更新された方法が記載されています。
モジュールを初めて採用する前に、既存のリポジトリまたはパッケージのセットに対してv2.0.0
以上がすでにタグ付けされている場合、推奨されるベストプラクティスは、モジュールを初めて採用する際にメジャーバージョンを増分することです。たとえば、foo
の作成者であり、foo
リポジトリの最新のタグがv2.2.2
であり、foo
がまだモジュールを採用していない場合、モジュールを採用するfoo
の最初のリリース(つまり、go.mod
ファイルを含むfoo
の最初のリリース)にはv3.0.0
を使用するのがベストプラクティスです。この場合、メジャーバージョンを増分すると、foo
の利用者にとってより明確になり、必要に応じてv2シリーズのfoo
で追加の非モジュールパッチまたはマイナーリリースが可能になり、モジュールベースのfoo
の利用者に対して、import "foo"
と対応するrequire foo v2.2.2+incompatible
、およびimport "foo/v3"
と対応するrequire foo/v3 v3.0.0
で異なるメジャーバージョンが生成されるという強力なシグナルが提供されます。(モジュールを初めて採用する際にメジャーバージョンを増分するというこのアドバイスは、最新のバージョンがv0.x.xまたはv1.x.xである既存のリポジトリまたはパッケージには適用されません)。
v2以上のモジュールをリリースする代替メカニズムが2つあります。どちらの方法でも、モジュール作成者が新しいタグをプッシュすると、新しいモジュールリリースが利用者にとって利用可能になります。v3.0.0
リリースの作成例では、2つのオプションがあります。
-
メジャーブランチ:
go.mod
ファイルを更新して、module
ディレクティブのモジュールパスの最後に/v3
を含めます(例:module github.com/my/module/v3
)。モジュール内のインポート文も/v3
を使用するように更新します(例:import "github.com/my/module/v3/mypkg"
)。リリースにv3.0.0
というタグを付けます。- Goバージョン1.9.7以上、1.10.3以上、および1.11は、モジュールをまだ採用していない利用者側のコードを更新することなく、このアプローチを使用して作成されたv2以上のモジュールを適切に消費してビルドできます(上記「セマンティックインポートバージョン管理」セクションで説明)。
- コミュニティツールgithub.com/marwan-at-work/modは、この手順の自動化に役立ちます。概要については、リポジトリまたは以下のコミュニティツールに関するFAQを参照してください。
- このアプローチによる混乱を避けるために、モジュールの
v3.*.*
コミットを別のv3ブランチに配置することを検討してください。 - 注記:新しいブランチを作成する必要はありません。以前にmasterでリリースしており、masterに
v3.0.0
というタグを付ける方が良い場合は、それも有効なオプションです。(ただし、go
ツールがGo 1.11以前のsemverを認識していない場合、またはGo 1.11以降でモジュールモードが有効になっていない場合、master
で非互換なAPI変更を導入すると、非モジュールユーザーがgo get -u
を実行した場合に問題が発生する可能性があることに注意してください)。 dep
などの既存の依存関係管理ソリューションは、この方法で作成されたv2以上のモジュールを消費する際に問題が発生することがあります。dep#1962を参照してください。
-
メジャーサブディレクトリ:新しい
v3
サブディレクトリ(例:my/module/v3
)を作成し、そのサブディレクトリに新しいgo.mod
ファイルを作成します。モジュールパスは/v3
で終わる必要があります。コードをv3
サブディレクトリにコピーまたは移動します。モジュール内のインポート文も/v3
を使用するように更新します(例:import "github.com/my/module/v3/mypkg"
)。リリースにv3.0.0
というタグを付けます。- これにより、より高い下位互換性が実現します。特に、1.9.7および1.10.3より古いGoバージョンでも、このアプローチを使用して作成されたv2以上のモジュールを適切に消費してビルドできます。
- ここでは、より洗練されたアプローチとして、型エイリアス(Go 1.9で導入)と、異なるサブディレクトリにあるメジャーバージョン間の転送シムを利用することができます。これにより、追加の互換性を提供し、あるメジャーバージョンを別のメジャーバージョンで実装できますが、モジュール作成者にとってより多くの作業が必要になります。これを自動化する進行中のツールは
goforward
です。詳細と理由、および機能するgoforward
の初期バージョンについては、こちらを参照してください。 dep
などの既存の依存関係管理ソリューションは、この方法で作成されたv2以上のモジュールを消費できるはずです。
これらの代替策の詳細については、https://research.swtch.com/vgo-moduleを参照してください。
リリースの公開
新しいモジュールバージョンは、モジュールソースコードを含むリポジトリにタグをプッシュすることで公開できます。タグは、プレフィックスとバージョンという2つの文字列を連結して形成されます。
バージョンは、リリースのセマンティックインポートバージョンです。セマンティックインポートバージョン管理のルールに従って選択する必要があります。
プレフィックスは、リポジトリ内でモジュールが定義されている場所を示します。モジュールがリポジトリのルートで定義されている場合、プレフィックスは空であり、タグはバージョンだけです。ただし、マルチモジュールリポジトリでは、プレフィックスによって異なるモジュールのバージョンが区別されます。プレフィックスは、リポジトリ内でモジュールが定義されているディレクトリです。リポジトリが上記で説明したメジャーサブディレクトリパターンに従っている場合、プレフィックスにはメジャーバージョンのサフィックスは含まれません。
たとえば、モジュールexample.com/repo/sub/v2
があり、バージョンv2.1.6
を公開するとします。リポジトリルートはexample.com/repo
に対応し、モジュールはリポジトリ内のsub/v2/go.mod
で定義されています。このモジュールのプレフィックスはsub/
です。このリリースの完全なタグはsub/v2.1.6
にする必要があります。
モジュールへの移行
このセクションでは、モジュールに移行する際に決定する必要がある主要な事項を簡単に列挙し、その他の移行関連のトピックをリストしようとします。詳細については、一般的に他のセクションへの参照が提供されています。
この資料は、主にモジュール実験の一部としてコミュニティから得られたベストプラクティスに基づいています。したがって、これは進行中のセクションであり、コミュニティがより多くの経験を積むにつれて改善されるでしょう。
概要
- モジュールシステムは、全体的なGoエコシステム内の異なるパッケージが異なる速度でオプトインできるように設計されています。
- すでにv2以上のバージョンにあるパッケージは、主にセマンティックインポートバージョン管理の影響のために、より多くの移行に関する考慮事項があります。
- 新しいパッケージとv0またはv1のパッケージは、モジュールを採用する際の考慮事項が大幅に少なくなります。
- Go 1.11で定義されたモジュールは、古いGoバージョンでも使用できます(ただし、正確なGoバージョンは、メインモジュールとその依存関係で使用される戦略によって異なります。以下で説明)。
移行トピック
以前の依存関係マネージャーからの自動移行
go mod init
は、dep、glide、govendor、godep、およびその他5つの既存の依存関係管理ツール から必要な情報を自動的に変換し、同等のビルドを行うgo.mod
ファイルを作成します。- v2以降のモジュールを作成する場合は、変換された
go.mod
ファイルのmodule
ディレクティブに適切な/vN
(例:module foo/v3
)を含めるようにしてください。 - v2以降のモジュールをインポートする場合、既存の依存関係管理ツールからの変換後、
go mod init
によって生成されたrequire
ステートメントに/vN
を追加するために、手動で調整が必要になる場合があります。詳細については、上記の“モジュールの定義方法”セクションを参照してください。 - さらに、
go mod init
は、インポート文に必要な/vN
を追加するために.go
コードを編集しません。必要な手順については、上記の“セマンティックインポートバージョン管理”および“モジュールのリリース(v2以降)”セクションを参照してください。変換を自動化するコミュニティツールに関するオプションも含まれています。
旧バージョンのGoおよびモジュールを使用しないコンシューマーへの依存関係情報の提供
- 旧バージョンのGoは、
go mod vendor
によって作成されたvendorディレクトリを使用する方法を理解しており、モジュールモードが無効になっている場合、Go 1.11および1.12以降でも同様です。したがって、ベンダー化は、モジュールを完全に理解していない旧バージョンのGo、およびモジュール自体を有効にしていないコンシューマーに、モジュールが依存関係を提供する1つの方法です。詳細については、ベンダー化に関するFAQとgo
コマンドのドキュメントを参照してください。
既存のインストール手順の更新
- モジュール以前は、インストール手順に
go get -u foo
が含まれることが一般的でした。モジュールfoo
を公開する場合は、モジュールベースのコンシューマーに対する手順から-u
を削除することを検討してください。-u
は、go
ツールにfoo
の直接的および間接的な依存関係をすべてアップグレードするように指示します。- モジュールコンシューマーは後で
go get -u foo
を実行することもできますが、-u
を最初のインストール手順に含めない場合、「高忠実度ビルド」により多くの利点があります。詳細については、“依存関係のアップグレードとダウングレードの方法”を参照してください。 go get -u foo
は依然として機能し、インストール手順として有効な選択肢です。
- さらに、モジュールベースのコンシューマーにとって
go get foo
は厳密には必要ありません。import "foo"
というインポート文を追加するだけで十分です。(go build
やgo test
などの後続のコマンドは、必要に応じてfoo
を自動的にダウンロードし、go.mod
を更新します)。
- モジュールベースのコンシューマーは、デフォルトで
vendor
ディレクトリを使用しません。go
ツールでモジュールモードが有効になっている場合、go.mod
に含まれる情報とgo.sum
の暗号化チェックサムを考慮すると、モジュールを使用する際にvendor
は厳密には必要ありませんが、既存のインストール手順の中には、go
ツールがデフォルトでvendor
を使用すると想定しているものもあります。ベンダー化に関するFAQで詳細を確認してください。
go get foo/...
を含むインストール手順には、場合によっては問題が発生する可能性があります(#27215の議論を参照)。
既存のインポートパスの破壊を回避する
モジュールは、module
ディレクティブ(例:module github.com/my/module
)を使用してgo.mod
でその識別情報を宣言します。モジュール内のすべてのパッケージは、モジュールの宣言されたモジュールパスと一致するインポートパスを使用して、モジュール認識コンシューマーによってインポートする必要があります(ルートパッケージの場合は完全に一致するか、インポートパスのプレフィックスとしてモジュールパスを使用します)。インポートパスと対応するモジュールの宣言されたモジュールパスが一致しない場合、go
コマンドはunexpected module path
エラーを報告します。
既存のパッケージセットにモジュールを採用する際には、メジャーバージョンの増分を行わない限り、既存のコンシューマーによって使用されている既存のインポートパスを壊さないように注意する必要があります。
たとえば、既存のREADMEでコンシューマーにimport "gopkg.in/foo.v1"
を使用するように指示していて、v1リリースでモジュールを採用する場合、最初のgo.mod
はほぼ確実にmodule gopkg.in/foo.v1
になります。gopkg.in
の使用をやめたい場合、それは現在のコンシューマーにとって破壊的な変更になります。1つのアプローチは、後でv2に移行する場合、module github.com/repo/foo/v2
のようなものに変更することです。
モジュールパスとインポートパスは大文字と小文字が区別されます。たとえば、モジュールをgithub.com/Sirupsen/logrus
からgithub.com/sirupsen/logrus
に変更することは、GitHubがリポジトリ名を新しいリポジトリ名に自動的に転送する場合でも、コンシューマーにとって破壊的な変更です。
モジュールを採用した後、go.mod
でモジュールパスを変更することは破壊的な変更です。
全体として、これは、“インポートパスコメント”(「インポートプラグマ」または「インポートパスエンフォースメント」と呼ばれることもあります)による標準的なインポートパスのモジュール以前の適用と似ています。例として、パッケージgo.uber.org/zap
は現在github.com/uber-go/zap
でホストされていますが、パッケージ宣言の横にインポートパスコメントを使用して、間違ったgithubベースのインポートパスを使用するモジュール以前のコンシューマーに対してエラーをトリガーします。
package zap // import "go.uber.org/zap"
インポートパスコメントは、go.modファイルのモジュールステートメントによって廃止されました。
v2以降のパッケージを含むモジュールを最初に採用する際のメジャーバージョンの増分
- モジュールを採用する前にすでにv2.0.0以上でタグ付けされているパッケージがある場合、モジュールを最初に採用する際にはメジャーバージョンを増分することをお勧めします。たとえば、まだモジュールを採用しておらず
v2.0.1
の場合、モジュールを採用する最初のリリースにはv3.0.0
を使用します。詳細については、上記の“モジュールのリリース(v2以降)”セクションを参照してください。
v2以降のモジュールでは、単一のビルド内に複数のメジャーバージョンを使用できます
- モジュールがv2以上の場合、単一のビルドに複数のメジャーバージョンを含めることができることが意味されます(例:
foo
とfoo/v3
が単一のビルドに含まれる可能性があります)。- これは、「異なるインポートパスを持つパッケージは異なるパッケージである」というルールから自然に派生します。
- このような場合、パッケージレベルの状態(例:
foo
のパッケージレベルの状態とfoo/v3
のパッケージレベルの状態)が複数コピー存在し、各メジャーバージョンは独自のinit
関数を実行します。 - このアプローチは、ダイヤモンド依存関係の問題、大規模コードベース内での新しいバージョンへの段階的な移行、および異なるメジャーバージョンをラップするシムとしてメジャーバージョンを実装することを含め、モジュールシステムの複数の側面に役立ちます。
- https://research.swtch.com/vgo-importの「シングルトン問題の回避」セクションまたは#27514で、関連する議論を参照してください。
非モジュールコードを使用するモジュール
- モジュールは、モジュールにまだ参加していないパッケージを、インポートするモジュールの
go.mod
に適切なパッケージバージョン情報が記録されている場合に、使用できます。モジュールは、まだ適切なsemverタグを持っていないパッケージを使用できます。詳細については、以下のFAQ を参照してください。 - モジュールは、モジュールに参加していないv2以降のパッケージもインポートできます。インポートされたv2以降のパッケージに有効なsemverタグがある場合、
+incompatible
サフィックスで記録されます。詳細については、以下のFAQ を参照してください。
モジュールを使用する非モジュールコード
-
v0およびv1モジュールを使用する非モジュールコード:
- まだモジュールに参加していないコードは、使用されるGoバージョンに関係なく、v0およびv1モジュールを使用およびビルドできます。
-
v2以降のモジュールを使用する非モジュールコード:
-
Goバージョン1.9.7以降、1.10.3以降、および1.11は、これらのリリースでビルドされたコードが、上記の“セマンティックインポートバージョン管理”および“モジュールのリリース(v2以降)”セクションで説明されているように、既存のコードを変更せずにv2以降のモジュールを適切に使用できるように更新されています。
-
1.9.7および1.10.3より前のGoバージョンは、v2以降のモジュールが上記の“モジュールのリリース(v2以降)”セクションで概説されている「メジャーサブディレクトリ」アプローチに従って作成されている場合、v2以降のモジュールを使用できます。
-
既存のv2以降のパッケージの作者向けの戦略
モジュールへの参加を検討している既存のv2以降のパッケージの作者にとって、代替アプローチを要約する1つの方法は、3つの最上位戦略のいずれかを選択することです。各選択肢には、その後、決定事項とバリエーションが続きます(上記で概説されています)。これらの代替最上位戦略は次のとおりです。
-
Goバージョン1.9.7以降、1.10.3以降、または1.11以降を使用するようクライアントに要求する.
このアプローチは「メジャーブランチ」アプローチを使用し、1.9.7および1.10.3にバックポートされた「最小限のモジュール認識」に依存しています。詳細については、上記の“セマンティックインポートバージョン管理”および“モジュールのリリース(v2以降)”セクションを参照してください。
-
Go 1.8などの古いGoバージョンを使用できるようにする.
このアプローチは「メジャーサブディレクトリ」アプローチを使用し、
/v2
または/v3
などのサブディレクトリを作成することを含みます。詳細については、上記の“セマンティックインポートバージョン管理”および“モジュールのリリース(v2以降)”セクションを参照してください。 -
モジュールへの参加を待つ.
この戦略では、モジュールをオプトインしたクライアントコードと、オプトインしていないクライアントコードの両方で動作し続けます。時間が経つにつれて、Goバージョン1.9.7+、1.10.3+、1.11+はますます長い期間利用可能になり、将来のある時点で、Goバージョン1.9.7+/1.10.3+/1.11+を必須とする方が自然で、クライアントにとって使いやすいものになります。その時点で、上記の戦略1(Goバージョン1.9.7+、1.10.3+、または1.11+を必須とする)または上記の戦略2(ただし、1.8のような古いGoバージョンをサポートするために最終的に上記の戦略2を使用する場合は、今すぐ実行できます)を実装できます。
追加のリソース
ドキュメントと提案
- 公式ドキュメント
- golang.orgのモジュールに関する最新のHTMLドキュメント
- モジュールに関する詳細については、
go help modules
を実行してください。(これはgo help
によるモジュールトピックの主要なエントリポイントです) go mod
コマンドの詳細については、go help mod
を実行してください。- モジュール認識モードでの
go get
の動作の詳細については、go help module-get
を実行してください。 file:///
URLによる純粋なファイルベースのオプションを含む、モジュールプロキシの詳細については、go help goproxy
を実行してください。
- Russ Coxによる
vgo
に関するブログ投稿シリーズの最初のもの「Go & Versioning」(2018年2月20日初投稿) - 提案を紹介する公式golang.orgブログ投稿(2018年3月26日)
- これは、完全な
vgo
ブログシリーズよりも簡潔な提案の概要を提供し、提案の背景にある歴史とプロセスの一部も説明しています。
- これは、完全な
- 公式バージョン管理されたGoモジュール提案(最終更新日:2018年3月20日)
入門資料
- 40分の入門ビデオ「Goにおけるバージョンの原則」、Russ CoxによるGopherConシンガポール(2018年5月2日)
- 「互換性」、「再現性」、「協調」という3つのコア原則を含む、バージョン管理されたGoモジュールの設計の背後にある哲学を簡潔に説明しています。
- 例に基づいた35分の入門ビデオ「Goモジュールとは何か、そしてどのように使用するのか?」(スライド)、Paul Jollyによる(2018年8月15日)
- 入門ブログ投稿「Taking Go Modules for a Spin」、Dave Cheneyによる(2018年7月14日)
- モジュールに関するGo Meetupのスライド、Chris Hinesによる(2018年7月16日)
- 30分の入門ビデオ「GoモジュールとSemVer入門」、Francesc Campoyによる(2018年11月15日)
追加資料
- ブログ投稿「Using Go modules with vendor support on Travis CI」、Fatih Arslanによる(2018年8月26日)
- ブログ投稿「Go Modules and CircleCI」、Todd Keechによる(2018年7月30日)
- ブログ投稿「The vgo proposal is accepted. Now what?」、Russ Coxによる(2018年5月29日)
- バージョン管理されたモジュールが現在実験的なオプトイン機能であることを意味することをまとめたものです。
- 最新のGoからビルドし、Goモジュールを使い始める方法に関するブログ投稿「how to build go from tip and start using go modules」、Carolyn Van Slyckによる(2018年7月16日)
最初の Vgo 提案以降の変更
提案、プロトタイプ、ベータ版のプロセスの過程で、全体的なコミュニティによって400以上の課題が作成されました。引き続きフィードバックを提供してください。
以下は、より大きな変更と改善の一部を部分的にリストしたものです。ほとんどすべてが主にコミュニティからのフィードバックに基づいていました。
- トップレベルのベンダーサポートは、vgoベースのビルドがベンダーディレクトリを完全に無視するのではなく、保持されました(ディスカッション、CL)
- 古いGoバージョン1.9.7+および1.10.3+がv2+プロジェクトのモジュールをより簡単に使用できるように、最小限のモジュール認識をバックポートしました(ディスカッション、CL)
- まだgo.modを持っていない既存のパッケージについて、vgoがデフォルトでv2+タグを使用できるようにしました(関連する動作の最近のアップデートについてはこちらで説明されています)
- コマンド
go get -u=patch
を使用して、すべての推移的な依存関係を同じマイナーバージョンの最新の利用可能なパッチレベルバージョンに更新するサポートを追加しました(ディスカッション、ドキュメント) - 環境変数によるより細かい制御(例:#26585のGOFLAGS、CL)
- go.modの更新を許可するかどうか、ベンダーディレクトリを使用する方法、ネットワークアクセスを許可するかどうかについて、より細かい制御を追加しました(例:-mod=readonly、-mod=vendor、GOPROXY=off;最近の変更に関する関連CL)
- より柔軟な置換ディレクティブを追加しました(CL)
- モジュールを問い合わせる追加の方法を追加しました(人間が消費するため、そしてより良いエディター/IDE統合のため)
- go CLIのUXは、これまでの経験に基づいて継続的に改良されてきました(例:#26581、CL)
- CIやDockerビルドなどのユースケースでキャッシュをウォームアップするためのサポートを追加しました(
go mod download
経由)(#26610) - 最も可能性が高い:GOBINへのプログラムの特定のバージョンのインストールのサポートの改善(#24250)
GitHub の課題
- 現在公開されているモジュールの課題
- 解決済みのモジュールの課題
- 解決済みのvgoの課題
- 接頭辞として「cmd/go:」を使用して新しいモジュールの課題を送信してください。
FAQ
バージョンが非互換としてマークされるのはどのような場合ですか?
require
ディレクティブを使用すると、任意のモジュールが、依存関係Dのバージョン>= x.y.zでビルドする必要があることを宣言できます(モジュールDのバージョンdep
とcargo
で使用されている制約の支配的な形式であることが示唆されています。さらに、ビルドのトップレベルモジュールは、依存関係の特定のバージョンをexclude
したり、他のモジュールを異なるコードでreplace
したりできます。詳細と根拠については、提案全体を参照してください。
バージョン管理されたモジュール提案の主要な目標の1つは、ツールと開発者の両方にとって、Goコードのバージョンの共通の語彙とセマンティクスを追加することです。これにより、将来、追加の非互換性の形式を宣言するための基盤が築かれます。たとえば、
- 初期の
vgo
ブログシリーズで説明されているように、非推奨バージョンを宣言する - たとえば、提案プロセスの際にここで議論されているように、外部システムでモジュール間のペアワイズの非互換性を宣言する
- リリースが公開された後に、モジュールのペアワイズの非互換なバージョンまたは安全でないバージョンを宣言します。たとえば、#24031と#26829で進行中の議論を参照してください。
古い動作と新しいモジュールベースの動作はいつ取得されますか?
一般的に、モジュールはGo 1.11ではオプトインであるため、設計上、デフォルトで古い動作が保持されます。
古い1.10の現状の動作と、新しいオプトインのモジュールベースの動作がいつ得られるかをまとめます。
- GOPATH内 — デフォルトで古い1.10の動作(モジュールを無視)
go.mod
のあるファイルツリー内にあるGOPATH外 — デフォルトでモジュールの動作- GO111MODULE環境変数
- 設定されていないか
auto
— 上記のデフォルトの動作 on
— ディレクトリの場所に関わらず、モジュールサポートを強制的に有効にするoff
— ディレクトリの場所に関わらず、モジュールサポートを強制的に無効にする
- 設定されていないか
go get
を使用してツールをインストールすると、エラーcannot find main module
が発生するのはなぜですか?
これは、GO111MODULE=on
を設定しているが、go get
を実行したときにgo.mod
のあるファイルツリー内にいない場合に発生します。
最も簡単な解決策は、GO111MODULE
を設定しない(または同等にGO111MODULE=auto
を明示的に設定する)ことです。これにより、このエラーを回避できます。
モジュールが存在する主な理由の1つは、正確な依存関係情報を記録することです。この依存関係情報は、現在のgo.mod
に書き込まれます。go.mod
のあるファイルツリー内にいないにもかかわらず、GO111MODULE=on
を設定することでgo get
コマンドにモジュールモードで動作するように指示した場合、go get
を実行すると、依存関係情報を記録できるgo.mod
がないため、エラーcannot find main module
が発生します。
代替ソリューションには以下が含まれます。
-
GO111MODULE
を設定しない(デフォルト、または明示的にGO111MODULE=auto
を設定する)ことで、よりフレンドリーな動作になります。モジュール外にいる場合はGo 1.10の動作になり、go get
がcannot find main module
を報告することを回避します。 -
GO111MODULE=on
のままにし、必要に応じて一時的にモジュールを無効にして、go get
中にGo 1.10の動作を有効にします(例:GO111MODULE=off go get example.com/cmd
)。これは、alias oldget='GO111MODULE=off go get'
などの簡単なスクリプトまたはシェルエイリアスに変換できます。 -
@rogpeppeによる簡単なシェルスクリプトによって自動化されている一時的な
go.mod
ファイルを作成し、後で破棄します。このスクリプトを使用すると、vgoget example.com/cmd[@version]
を使用してバージョン情報をオプションで提供できます。(これは、エラーcannot use path@version syntax in GOPATH mode
を回避するための解決策になる可能性があります)。 -
gobin
は、メインパッケージをインストールして実行するためのモジュール認識コマンドです。デフォルトでは、gobin
は最初にモジュールを手動で作成する必要なくメインパッケージをインストール/実行しますが、-m
フラグを使用すると、既存のモジュールを使用して依存関係を解決するように指示できます。詳細と追加のユースケースについては、gobin
のREADMEとFAQを参照してください。 -
グローバルにインストールされたツールをトラッキングするために使用する
go.mod
(例:~/global-tools/go.mod
)を作成し、グローバルにインストールされたツールのgo get
またはgo install
を実行する前に、そのディレクトリにcd
します。 -
~/tools/gorename/go.mod
や~/tools/goimports/go.mod
など、個別のディレクトリにツールごとにgo.mod
を作成し、ツールのgo get
またはgo install
を実行する前に、適切なディレクトリにcd
します。
現在のこの制限は解消されます。ただし、主な問題は、モジュールが現在オプトインであることであり、完全な解決策は、GO111MODULE=onがデフォルトの動作になるまで待つ可能性が高いです。詳細な議論、およびこのコメントについては、#24250 を参照してください。
これは最終的に確実に動作する必要があります。私が確信していないのは、バージョンに関してこれが正確に何をするかということです。一時的なモジュールルートとgo.modを作成し、インストールしてから破棄するのでしょうか?おそらくそうです。しかし、私は完全に確信しておらず、今のところ、go.modツリーの外でvgoに動作させることで人々を混乱させたくありませんでした。確かに、最終的なgoコマンドの統合はこれをサポートする必要があります。
このFAQでは、グローバルにインストールされたツールの追跡について説明してきました。
代わりに、特定のモジュールに必要なツールを追跡する場合は、次のFAQを参照してください。
モジュールのツール依存関係をどのように追跡できますか?
もしあなたが
- モジュールに取り組んでいる間にgoベースのツール(例:
stringer
)を使用したい場合、かつ - モジュールの
go.mod
ファイルにツールのバージョンを追跡しながら、誰もが同じバージョンのツールを使用していることを確認したい場合
現在推奨されるアプローチの1つは、関心のあるツールのインポートステートメント(例:import _ "golang.org/x/tools/cmd/stringer"
)と//go:build tools
ビルド制約を含むtools.go
ファイルをモジュールに追加することです。インポートステートメントにより、go
コマンドはモジュールのgo.mod
にツールのバージョン情報を正確に記録できます。一方、//go:build tools
ビルド制約により、通常のビルドでツールを実際にインポートすることはなくなります。
これを行う方法の具体的な例については、この「Go Modules by Example」のチュートリアルを参照してください。
このアプローチに関する議論と、これを行う方法の以前の具体的な例は、#25922のこのコメントにあります。
簡単な理由(#25922からも)
私は、
tools.go
ファイルが、実際にはツール依存関係のベストプラクティスであり、Go 1.11では特にそうであると考えています。新しいメカニズムを導入しないため、気に入っています。
既存のものを再利用するだけです。
また(Go 1.16以降)、go install tool@version
を使用すると特定のバージョンをインストールできます。また(Go 1.17以降)、#42088および#40276で実装されているように、go run tool@version
を使用すると、インストールせずにツールを実行できます。これにより、tools.go
の必要性がなくなります。
IDE、エディター、goimports、gorenameなどの標準ツールにおけるモジュールサポートの状況は?
エディターやIDEへのモジュールサポートが導入され始めています。
例えば
- GoLand:現在、GOPATHの内外にあるモジュールを完全にサポートしており、こちらで説明されているように、補完、構文解析、リファクタリング、ナビゲーションなどが含まれます。
- VS Code:作業は完了しており、MSはGOPATHよりもモジュールを推奨しています。以前の追跡の問題(#1532)は閉じられました。VS Codeモジュールリポジトリでドキュメントを入手できます。
- Atom with go-plus:追跡の問題は#761です。
- vim with vim-go:
go.mod
の構文ハイライトとフォーマットの初期サポートは実装済みです。より広範なサポートは#1906で追跡されています。 - emacs with go-mode.el:追跡の問題は#237です。
goimports、guru、gorenameなどの他のツールの状況は、包括的な問題#24661で追跡されています。最新の状況については、この包括的な問題を参照してください。
特定のツールの追跡の問題には以下が含まれます。
- gocode:追跡の問題はmdempsky/gocode/#46です。
nsf/gocode
は、ユーザーにnsf/gocode
からmdempsky/gocode
への移行を推奨しています。 - go-tools(dominikhによるツール、例:staticcheck、megacheck、gosimple):サンプルの追跡の問題はdominikh/go-tools#328です。
一般的に、エディター、IDE、その他のツールがまだモジュールに対応していなくても、GOPATH内でモジュールを使用し、go mod vendor
を実行する場合(適切な依存関係がGOPATHを介して取得されるため)、その機能の多くはモジュールで動作するはずです。
完全な修正は、パッケージをロードするプログラムをgo/build
からgolang.org/x/tools/go/packages
に移行することです。これは、モジュールを認識する方法でパッケージの場所を特定する方法を理解しています。これは最終的にgo/packages
になる可能性があります。
FAQ — 追加の制御
モジュールの操作に使用できるコミュニティツールは何ですか?
コミュニティは、モジュールの上にツールを構築し始めています。例えば
- github.com/rogpeppe/gohack
replace
とマルチモジュールワークフローを自動化し、大幅に簡素化する新しいコミュニティツール。依存関係の1つを簡単に変更することもできます。- たとえば、
gohack example.com/some/dependency
は、適切なリポジトリを自動的にクローンし、必要なreplace
ディレクティブをgo.mod
に追加します。 gohack undo
を使用して、すべてのgohack replaceステートメントを削除します。- 他のモジュール関連のワークフローを容易にするために、プロジェクトは拡大を続けています。
- github.com/marwan-at-work/mod
- モジュールのメジャーバージョンのアップグレード/ダウングレードを自動化するコマンドラインツール
go.mod
ファイルとgoソースコード内の関連するインポートステートメントを自動的に調整します。- アップグレード時、またはv2以上のパッケージでモジュールを初めてオプトインする場合に役立ちます。
- github.com/akyoto/mgit
- ローカルプロジェクトのすべてのsemverタグを表示および制御できます。
- タグ付けされていないコミットを表示し、一度にすべてタグ付けできます(
mgit -tag +0.0.1
)
- github.com/goware/modvendor
- シェルスクリプト、.cppファイル、.protoファイルなど、追加のファイルを
vendor/
フォルダにコピーするのに役立ちます。
- シェルスクリプト、.cppファイル、.protoファイルなど、追加のファイルを
- github.com/psampaz/go-mod-outdated
- 時代遅れの依存関係を人間が理解しやすい方法で表示します。
- 更新のない間接的な依存関係と依存関係をフィルタリングする方法を提供します。
- 時代遅れの依存関係がある場合にCIパイプラインを中断する方法を提供します。
- github.com/oligot/go-mod-upgrade
- 時代遅れのGo依存関係を対話的に更新します。
いつreplace
ディレクティブを使用する必要がありますか?
上記の「go.mod」の概念に関するセクションで説明されているように、replace
ディレクティブは、Goソースまたはgo.modファイルで見つかった依存関係を満たすために実際に使用されるものを、トップレベルのgo.mod
で追加的に制御します。一方、メインモジュール以外のモジュール内のreplace
ディレクティブは、メインモジュールをビルドする際には無視されます。
replace
ディレクティブを使用すると、VCS(GitHubなど)にある別のモジュール、または相対パスまたは絶対パスを持つローカルファイルシステムにある別のインポートパスを指定できます。実際のソースコードのインポートパスを更新する必要なく、replace
ディレクティブからの新しいインポートパスが使用されます。
replace
を使用すると、トップレベルのモジュールで依存関係に使用される正確なバージョンを制御できます。例えば、
replace example.com/some/dependency => example.com/some/dependency v1.2.3
replace
を使用すると、フォークされた依存関係を使用することもできます。例えば、
replace example.com/some/dependency => example.com/some/dependency-fork v1.2.3
ブランチを参照することもできます。例えば、
replace example.com/some/dependency => example.com/some/dependency-fork master
1つの使用例は、依存関係で何かを修正または調査する必要がある場合です。ローカルフォークを作成し、トップレベルのgo.mod
に次のようなものを追加できます。
replace example.com/original/import/path => /your/forked/import/path
replace
は、マルチモジュールプロジェクトのモジュールのディスク上の相対パスまたは絶対パスをgoツールに知らせるためにも使用できます。例えば、
replace example.com/project/foo => ../foo
注記:replace
ディレクティブの右辺がファイルシステムパスである場合、その場所にgo.mod
ファイルが存在する必要があります。go.mod
ファイルが存在しない場合は、go mod init
を使用して作成できます。
一般的に、replace
ディレクティブの=>
の左側にバージョンを指定できますが、通常、省略した場合の方が変更の影響を受けにくいです(上記のすべてのreplace
の例のように)。
直接的な依存関係の各replace
ディレクティブには、require
ディレクティブが必要です。ファイルシステムパスから依存関係を置き換える場合、対応するrequire
ディレクティブのバージョンは本質的に無視されます。この場合、疑似バージョンv0.0.0
はこれを明確にするために良い選択です。例:require example.com/module v0.0.0
。
go list -m all
を実行すると、replace
ステートメントを考慮してビルドで使用される実際の最終バージョンが表示されるため、期待どおりのバージョンを取得していることを確認できます。
詳細については、「go mod edit」のドキュメントを参照してください。
github.com/rogpeppe/gohackは、このようなワークフローをはるかに容易にします。特に、モジュールの依存関係の変更可能なチェックアウトを行うことを目的としている場合です。リポジトリまたはすぐ前のFAQを参照して概要を確認してください。
VCSの外部で完全に動作するreplace
の使用の詳細については、次のFAQを参照してください。
ローカルファイルシステム上の VCS の外部で完全に作業できますか?
はい。VCSは必須ではありません。
一度に編集する単一モジュールがVCSの外部にある場合(モジュールが合計で1つしかないか、他のモジュールがVCSにある場合)、これは非常に簡単です。この場合、単一のgo.mod
を含むファイルツリーを便利な場所に配置できます。単一のモジュールがVCSの外部にあっても(go.mod
にreplace
を使用する必要なく)、go build
、go test
などコマンドは機能します。
ローカルディスク上に複数の相互関連するモジュールを同時に編集したい場合、`replace`ディレクティブが1つのアプローチです。 バージョン管理システム(VCS)に依存せずに、`hello`モジュールを`goodbye`モジュールのディスク上の場所を指す相対パスで置き換える`go.mod`のサンプルを以下に示します。
module example.com/me/hello
require (
example.com/me/goodbye v0.0.0
)
replace example.com/me/goodbye => ../goodbye
実行可能な小さな例は、このスレッドに示されています。
モジュールでベンダーを使用するにはどうすればよいですか?ベンダーは廃止されますか?
初期の`vgo`ブログ記事シリーズでは、ベンダーディレクトリを完全に廃止することを提案していましたが、コミュニティからのフィードバックにより、ベンダーディレクトリのサポートは維持されました。
簡単に言うと、モジュールでベンダーディレクトリを使用するには
- `go mod vendor`は、`go.mod`ファイルとGoソースコードの状態に基づいて、メインモジュールのベンダーディレクトリをリセットし、すべてのモジュールのパッケージのビルドとテストに必要なすべてのパッケージを含めます。
- デフォルトでは、モジュールモードでは`go build`などのgoコマンドはベンダーディレクトリを無視します。
- `-mod=vendor`フラグ(例:`go build -mod=vendor`)は、goコマンドに、依存関係を満たすためにメインモジュールの最上位のベンダーディレクトリを使用するように指示します。このモードのgoコマンドは、`go.mod`内の依存関係の説明を無視し、ベンダーディレクトリに依存関係の正しいコピーが含まれていると想定します。 他の場所にあるベンダーディレクトリは引き続き無視されることに注意してください。 メインモジュールの最上位のベンダーディレクトリのみが使用されます。
- 一部の人は、`GOFLAGS=-mod=vendor`環境変数を設定することで、ルーチン的にベンダーディレクトリを使用することを選択したいと考えています。
Go 1.10などの古いバージョンは、`go mod vendor`によって作成されたベンダーディレクトリを使用する方法を理解しており、モジュールモードが無効になっている場合、Go 1.11と1.12以降でも同様です。したがって、ベンダーディレクトリは、モジュールを完全に理解していない古いバージョンのGo、およびモジュール自体を有効にしていないコンシューマーに依存関係を提供する1つの方法です。
ベンダーディレクトリを使用することを検討している場合は、tipドキュメントの「モジュールとベンダーディレクトリ」セクションと「依存関係のベンダーコピーを作成する」セクションを読むことをお勧めします。
「常にオン」のモジュールリポジトリとエンタープライズプロキシはありますか?
公開ホストされている「常時稼働」の不変モジュールリポジトリと、オプションのプライベートホストプロキシおよびリポジトリが利用可能になりつつあります。
例えば
- proxy.golang.org - 公式プロジェクト - Googleが運営 - Goチームによって構築されたデフォルトのGoモジュールプロキシ。
- proxy.golang.com.cn - 中国のプロキシプロジェクト - China Golang Contributor Clubが運営 - 中国のGoモジュールプロキシ。
- mirrors.tencent.com/go - 商用プロジェクト - Tencent Cloudが運営 - Goモジュールプロキシの代替。
- mirrors.aliyun.com/goproxy - 商用プロジェクト - Alibaba Cloudが運営 - Goモジュールプロキシの代替。
- goproxy.cn - オープンソースプロジェクト - Qiniu Cloudが運営 - 中国で最も信頼されているGoモジュールプロキシ。
- goproxy.io - オープンソースプロジェクト - China Golang Contributor Clubが運営 - Goモジュールのグローバルプロキシ。
- Athens - オープンソースプロジェクト - 自己ホスト型 - Goモジュールデータストアとプロキシ。
- athens.azurefd.net - オープンソースプロジェクト - Athensを実行しているホスト型モジュールプロキシ。
- Goproxy - オープンソースプロジェクト - 自己ホスト型 - ミニマリストなGoモジュールプロキシハンドラー。
- THUMBAI - オープンソースプロジェクト - 自己ホスト型 - Go modプロキシサーバーとGo vanityインポートパスサーバー。
プロキシを実行する必要はないことに注意してください。 むしろ、1.11のgoツールは、GOPROXYを介してオプションのプロキシサポートを追加し、より多くのエンタープライズユースケース(より高度な制御など)を可能にし、「GitHubがダウンしている」などの状況や、ユーザーがGitHubリポジトリを削除する状況をより適切に処理できるようにしました。
go.mod の更新時期と、go ツールがネットワークを使用して依存関係を満たす時期を制御できますか?
デフォルトでは、`go build`などのコマンドは、必要に応じてネットワークにアクセスしてインポートを満たします。
一部のチームは、特定の時点でgoツールがネットワークに触れることを許可しない、またはgoツールが`go.mod`を更新するタイミング、依存関係の取得方法、ベンダーディレクトリの使い方についてより詳細な制御を望む場合があります。
goツールは、`-mod=readonly`、`-mod=vendor`、`GOFLAGS`、`GOPROXY=off`、`GOPROXY=file:///filesystem/path`、`go mod vendor`、`go mod download`などを使用して、これらのデフォルトの動作を調整または無効にするためのかなりの柔軟性を提供します。
これらのオプションの詳細については、公式ドキュメント全体に分散されています。 これらの動作に関連するノブの統合された概要に関するコミュニティによる試みはこちらにあり、詳細については公式ドキュメントへのリンクが含まれています。
Travis や CircleCI などの CI システムでモジュールを使用するにはどうすればよいですか?
最も簡単なアプローチはおそらく、ほとんどのCIシステムで動作する環境変数`GO111MODULE=on`を設定することです。
ただし、一部のユーザーはまだモジュールを有効にしていないことを考えると、モジュールを有効にした状態と無効にした状態でGo 1.11でCIでテストを実行することは有益です。ベンダーディレクトリも考慮すべき点です。
次の2つのブログ記事では、これらのトピックについて具体的に説明しています。
特定のパッケージやテストをビルドするために必要なモジュールをダウンロードするにはどうすればよいですか?
`go mod download`コマンド(または同等の`go mod download all`)は、ビルドリスト(`go list -m all`によって報告される)内のすべてのモジュールをダウンロードします。これらのモジュールの多くは、メインモジュールのパッケージをビルドするのに必要ありません。完全なビルドリストには、テスト依存関係や他のモジュールのツール依存関係など、さまざまなものが含まれているためです。 その結果、`go mod download`で準備されたDockerイメージは、必要以上に大きくなる可能性があります。
代わりに、`go list`を使用することを検討してください。たとえば、`go list ./...`は、`./...`(モジュールルートディレクトリから実行した場合のメインモジュールのパッケージのセット)をビルドするのに必要なモジュールをダウンロードします。
テスト依存関係もダウンロードするには、`go list -test ./...`を使用します。
デフォルトでは、`go list`は現在のプラットフォームに必要な依存関係のみを考慮します。 `GOOS`と`GOARCH`を設定して、`go list`に別のプラットフォームを考慮させることができます(例:`GOOS=linux GOARCH=amd64 go list ./...`)。`-tags`フラグを使用して、特定のビルドタグを持つパッケージを選択することもできます。
遅延モジュール読み込みが実装された将来(#36460を参照)には、モジュールパターン`all`に含まれるモジュールが少なくなるため、このテクニックはそれほど必要なくなる可能性があります。
FAQ — go.mod と go.sum
なぜ 'go mod tidy' は間接依存関係とテスト依存関係を 'go.mod' に記録するのですか?
モジュールシステムは、`go.mod`に正確な依存関係の要件を記録します。(詳細については、上記のgo.modの概念セクション、またはgo.mod tipドキュメントを参照してください)。
`go mod tidy`は、モジュールのテストに必要な依存関係を含めるように現在の`go.mod`を更新します。テストが失敗した場合、失敗を再現するために使用された依存関係を知る必要があります。
`go mod tidy`はまた、現在の`go.mod`が、OS、アーキテクチャ、ビルドタグのすべての可能な組み合わせの依存関係の要件を反映していることを保証します(こちらで説明されているとおり)。一方、`go build`や`go test`などの他のコマンドは、現在の`GOOS`、`GOARCH`、ビルドタグの下で要求されたパッケージによってインポートされたパッケージを提供するように`go.mod`を更新するだけです(これは、`go mod tidy`が`go build`などでは追加されなかった要件を追加する可能性のある理由の1つです)。
モジュールの依存関係自体に`go.mod`がない場合(たとえば、依存関係自体がまだモジュールを有効にしていないため)、またはその`go.mod`ファイルに1つ以上の依存関係がない場合(たとえば、モジュール作成者が`go mod tidy`を実行しなかったため)、不足している推移的な依存関係は、あなたのモジュールの要件に追加され、依存関係があなたのモジュール内の直接のインポートからではないことを示す`// indirect`コメントが追加されます。
これは、直接または間接的な依存関係から不足しているテスト依存関係も`go.mod`に記録されることを意味します。(これが重要な場合の例:`go test all`は、モジュールのすべての直接および間接的な依存関係のテストを実行し、現在のバージョンの組み合わせが連携して動作することを検証する1つの方法です。`go test all`を実行するときに依存関係の1つでテストが失敗した場合、再現可能な`go test all`動作を確保するために、完全なテスト依存関係情報セットを記録することが重要です)。
`go.mod`ファイルに`// indirect`依存関係があるもう1つの理由は、`go get -u`または`go get foo@1.2.3`を実行した場合など、直接的な依存関係で必要なものを超えて間接的な依存関係の1つをアップグレード(またはダウングレード)した場合です。goツールは、これらの新しいバージョンを記録する場所が必要であり、`go.mod`ファイルで行います(依存関係に到達してそれらの`go.mod`ファイルを修正することはありません)。
一般的に、上記で説明した動作は、正確な依存関係情報を記録することで、モジュールが100%再現可能なビルドとテストを提供する方法の一部です。
特定のモジュールが`go.mod`に表示されている理由を知りたい場合は、`go mod why -m <module>`を実行して、その質問に答えることができます。要件とバージョンを検査するための他の便利なツールには、`go mod graph`と`go list -m all`があります。
'go.sum' はロックファイルですか?なぜ 'go.sum' には、もはや使用していないモジュールバージョンの情報が含まれているのですか?
いいえ、`go.sum`はロックファイルではありません。ビルド内の`go.mod`ファイルには、100%再現可能なビルドに必要な情報が十分に含まれています。
検証目的で、`go.sum`には、特定のモジュールバージョンのコンテンツの期待される暗号化チェックサムが含まれています。`go.sum`の詳細(通常は`go.sum`をチェックインする理由など)については、下記のFAQ、およびtipドキュメントの「モジュールのダウンロードと検証」セクションを参照してください。
さらに、モジュールの`go.sum`には、ビルドで使用されるすべての直接的および間接的な依存関係のチェックサムが記録されます(したがって、`go.sum`には、`go.mod`よりも多くのモジュールがリストされていることがよくあります)。
'go.mod' ファイルと同様に 'go.sum' ファイルもコミットする必要がありますか?
通常、モジュールの`go.sum`ファイルは`go.mod`ファイルと一緒にコミットする必要があります。
- `go.sum`には、特定のモジュールバージョンのコンテンツの期待される暗号化チェックサムが含まれています。
- 誰かがあなたのリポジトリをクローンしてgoコマンドを使用して依存関係をダウンロードした場合、ダウンロードされた依存関係のコピーと`go.sum`の対応するエントリとの間に不一致がある場合、エラーが発生します。
- さらに、`go mod verify`は、モジュールのダウンロードのディスク上のキャッシュされたコピーが`go.sum`のエントリとまだ一致していることを確認します。
go.sum
は、他の依存関係管理システムで使用されているようなロックファイルではありません。(go.mod
は再現可能なビルドに必要な十分な情報を提供します。)go.sum
をチェックインする理由については、Filippo Valsordaによる非常に短い解説はこちらを参照してください。詳細については、tipドキュメントの「モジュールのダウンロードと検証」セクションを参照してください。将来的な拡張については、例えば#24117と#25530で議論されている内容を参照してください。
依存関係がない場合でも 'go.mod' ファイルを追加する必要がありますか?
はい。これにより、GOPATHの外で作業することがサポートされ、モジュールをオプトインしていることをエコシステムに伝えるのに役立ちます。さらに、go.mod
内のmodule
ディレクティブは、コードのアイデンティティを明確に宣言する役割を果たします(これが、インポートコメントが最終的に非推奨となる可能性のある理由の1つです)。もちろん、モジュールはGo 1.11では純粋にオプトイン機能です。
FAQ — セマンティックインポートバージョン管理
なぜメジャーバージョン番号はインポートパスに表示される必要があるのですか?
上記の“セマンティックインポートバージョン管理”概念セクションで、セマンティックインポートバージョン管理とインポート互換性ルールに関する議論を参照してください。提案を発表したブログ記事も参照してください。そこでは、インポート互換性ルールの動機と正当性について詳しく説明されています。
メジャーバージョンv0、v1がインポートパスから省略されるのはなぜですか?
公式の提案に関する議論からの以前のFAQで、「メジャーバージョンv0、v1がインポートパスから省略されるのはなぜですか?」という質問を参照してください。
プロジェクトにメジャーバージョン v0、v1 でタグを付けたり、v2 以降で破壊的な変更を加えることの含意は何ですか?
「k8sはマイナーリリースを行うが、各マイナーリリースでGo APIを変更する」というコメントへの回答として、Russ Coxは、プロジェクトでv0、v1を選択すること、またはv2、v3、v4などで頻繁に破壊的変更を行うことの影響を強調した以下の回答を行いました。
k8sの開発サイクルなどは完全に理解していませんが、一般的にk8sチームは、安定性についてユーザーに何を保証するつもりなのかを決定/確認し、それに応じてバージョン番号を適用する必要があると思います。
- API互換性に関する約束をする(これは最高のユーザーエクスペリエンスのように思えます!)のであれば、それを始め、1.X.Yを使用してください。
- すべてのリリースで下位互換性のない変更を行う柔軟性を持ちながら、大規模プログラムの異なる部分が異なるスケジュールでコードをアップグレードできるようにする(つまり、異なる部分が1つのプログラムでAPIの異なるメジャーバージョンを使用できる)場合は、X.Y.0と、
k8s.io/client/vX/foo
のようなインポートパスを使用してください。- API互換性に関する約束をせず、すべてのビルドでk8sライブラリの1つのコピーのみを必要とし(すべての部分が準備ができていなくても、すべての部分が同じバージョンを使用することを暗黙的に強制する)、その場合は0.X.Yを使用してください。
関連事項として、Kubernetesにはいくつかの非標準的なビルドアプローチがあります(現在、godepの上にカスタムラッパースクリプトを含んでいます)。そのため、Kubernetesは他の多くのプロジェクトにとって完璧な例ではありませんが、KubernetesがGo 1.11モジュールを採用するにつれて、興味深い例になる可能性があります。
モジュールは、モジュールにオプトインしていないパッケージを使用できますか?
はい。
リポジトリがモジュールをオプトインしていないが、有効なsemverタグ(必須の先頭のv
を含む)でタグ付けされている場合、これらのsemverタグはgo get
で使用でき、対応するsemverバージョンはインポートするモジュールのgo.mod
ファイルに記録されます。リポジトリに有効なsemverタグがない場合、リポジトリのバージョンはv0.0.0-20171006230638-a6e239ea1c69
など、「擬似バージョン」で記録されます(タイムスタンプとコミットハッシュが含まれ、go.mod
に記録されたバージョン全体での全順序付けを容易にし、どの記録されたバージョンが別の記録されたバージョンよりも「後」であるかを判断しやすくするように設計されています)。
例えば、パッケージfoo
の最新バージョンがv1.2.3
でタグ付けされているが、foo
自体がモジュールをオプトインしていない場合、モジュールMの内側からgo get foo
またはgo get foo@v1.2.3
を実行すると、モジュールMのgo.mod
ファイルに以下のように記録されます。
require foo v1.2.3
go
ツールは、非モジュールパッケージの追加ワークフロー(モジュールの依存関係をパッチリリースで利用可能なものへアップグレードするgo list -u=patch
、利用可能なアップグレードを表示するgo list -u -m all
など)でも、利用可能なsemverタグを使用します。
v2以降のパッケージでモジュールをオプトインしていないものに関する追加の詳細については、次のFAQを参照してください。
モジュールは、モジュールにオプトインしていない v2 以降のパッケージを使用できますか?'+incompatible' とは何を意味しますか?
はい、モジュールはモジュールをオプトインしていないv2以降のパッケージをインポートできます。インポートされたv2以降のパッケージに有効なsemverタグがある場合、+incompatible
サフィックス付きで記録されます。
追加の詳細
上記の“セマンティックインポートバージョン管理”セクションの資料をよく理解しておいてください。
一般的に役立つが、このFAQで説明されている動作について考える際に特に心に留めておくことが重要な、いくつかの基本原則を最初に確認することをお勧めします。
go
ツールがモジュールモード(例:GO111MODULE=on
)で動作している場合、次の基本原則は常に当てはまります。
- パッケージのインポートパスは、パッケージのアイデンティティを定義します。
- 異なるインポートパスを持つパッケージは、異なるパッケージとして扱われます。
- 同じインポートパスを持つパッケージは、同じパッケージとして扱われます(VCSタグがパッケージのメジャーバージョンが異なることを示していても、これは当てはまります)。
/vN
を含まないインポートパスは、v1またはv0モジュールとして扱われます(インポートされたパッケージがモジュールをオプトインしておらず、VCSタグがメジャーバージョンが1より大きいことを示していても、これは当てはまります)。- モジュールの
go.mod
ファイルの先頭で宣言されたモジュールパス(例:module foo/v2
)は、両方とも- そのモジュールのアイデンティティの明確な宣言
- そのモジュールが消費コードによってインポートされる方法の明確な宣言
次のFAQでわかるように、これらの原則は、go
ツールがモジュールモードになっていない場合は必ずしも当てはまりませんが、go
ツールがモジュールモードになっている場合は常に当てはまります。
簡単に言うと、+incompatible
サフィックスは、次の場合に原則2が有効であることを示しています。
- インポートされたパッケージがモジュールをオプトインしておらず、
- そのVCSタグがメジャーバージョンが1より大きいことを示しており、
- 原則2がVCSタグを上書きしている(
/vN
を含まないインポートパスは、VCSタグが異なることを示していても、v1またはv0モジュールとして扱われます)。
go
ツールがモジュールモードの場合、モジュールではないv2以降のパッケージは、セマンティックインポートバージョン管理を認識しておらず、パッケージのv1バージョンシリーズの(非互換な)拡張として扱われると仮定します(+incompatible
サフィックスは、go
ツールがそうしていることを示しています)。
例
以下を仮定します。
oldpackage
は、モジュールの導入前に存在していたパッケージです。oldpackage
はモジュールをオプトインしたことがなく(したがって、go.mod
自体を持っていません)。oldpackage
には有効なsemverタグv3.0.1
があり、これが最新のタグです。
この場合、例えばモジュールMの内側からgo get oldpackage@latest
を実行すると、モジュールMのgo.mod
ファイルに以下が記録されます。
require oldpackage v3.0.1+incompatible
上記のgo get
コマンドや記録されたrequire
ディレクティブのoldpackage
の末尾に/v3
は使用されていません。モジュールパスとインポートパスで/vN
を使用することはセマンティックインポートバージョン管理の機能であり、oldpackage
は、oldpackage
自体内にgo.mod
ファイルを持つことでモジュールをオプトインしていないため、セマンティックインポートバージョン管理の権利と責任を認められていません。言い換えれば、oldpackage
がv3.0.1
のsemverタグを持っている場合でも、oldpackage
はセマンティックインポートバージョン管理(インポートパスに/vN
を使用するなど)の権利と責任を付与されていません。なぜなら、oldpackage
はまだそうする意思を表明していないからです。
+incompatible
サフィックスは、oldpackage
のv3.0.1
バージョンがモジュールを積極的にオプトインしておらず、したがってoldpackage
のv3.0.1
バージョンはセマンティックインポートバージョン管理やインポートパスでメジャーバージョンを使用する方法を理解していないと仮定されていることを示しています。モジュールモードで動作している場合、go
ツールは、モジュールではないoldpackage
のv3.0.1
バージョンをoldpackage
のv1バージョンシリーズの(非互換な)拡張として扱い、oldpackage
のv3.0.1
バージョンはセマンティックインポートバージョン管理を認識していないと仮定します。+incompatible
サフィックスは、go
ツールがそうしていることを示しています。
セマンティックインポートバージョン管理によると、oldpackage
のv3.0.1
バージョンがv1リリースシリーズの一部と見なされるということは、例えばバージョンv1.0.0
、v2.0.0
、v3.0.1
はすべて同じインポートパスを使用して常にインポートされることを意味します。
import "oldpackage"
繰り返しますが、oldpackage
の末尾に/v3
は使用されていません。
一般的に、インポートパスが異なるパッケージは異なるパッケージです。この例では、oldpackage
のバージョンv1.0.0
、v2.0.0
、v3.0.1
はすべて同じインポートパスを使用してインポートされるため、ビルドによって同じパッケージとして扱われます(oldpackage
はまだセマンティックインポートバージョン管理をオプトインしていないため)。1つのoldpackage
のコピーが、特定のビルドに含まれます。(使用されるバージョンは、任意のrequire
ディレクティブにリストされているバージョンのうち、セマンティック的に最も高いバージョンです。“バージョン選択”を参照してください)。
その後、モジュールを採用したoldpackage
の新しいv4.0.0
リリースが作成されたと仮定すると(したがって、go.mod
ファイルが含まれます)、これはoldpackage
がセマンティックインポートバージョン管理の権利と責任を理解していることを示す信号であり、したがってモジュールベースのコンシューマは、インポートパスで/v4
を使用してインポートするようになります。
import "oldpackage/v4"
そして、バージョンは以下のように記録されます。
require oldpackage/v4 v4.0.0
oldpackage/v4
は、oldpackage
とは異なるインポートパスであるため、異なるパッケージです。ビルド内のコンシューマの一部がimport "oldpackage/v4"
を使用し、同じビルド内の他のコンシューマがimport "oldpackage"
を使用している場合、2つのコピー(インポートパスごとに1つ)がモジュール認識ビルドに含まれることになります。これは、モジュールの段階的な採用を可能にする戦略の一部として望ましいものです。さらに、モジュールが現在の移行段階を終了した後でも、この動作は、大規模なビルド内の異なるコンシューマが異なる速度で新しいバージョンにアップグレードすることを可能にする(例えば、大規模なビルド内の異なるコンシューマがoldpackage/v4
から将来のoldpackage/v5
へのアップグレードを異なる速度で選択できるようにする)という点で望ましいものです。
モジュールサポートが有効になっていない場合、ビルドで v2 以降のモジュールはどのように処理されますか?1.9.7 以降、1.10.3 以降、および 1.11 での「最小モジュール互換性」はどのように機能しますか?
以前のGoバージョンやモジュールをまだオプトインしていないGoコードを検討する場合、セマンティックインポートバージョン管理は、v2以降のモジュールに関連して重要な下位互換性の影響を及ぼします。
上記の“セマンティックインポートバージョン管理”セクションで説明されているように
- バージョンがv2以上のモジュールには、独自の
go.mod
で宣言されたモジュールパスに/vN
を含める必要があります。 - モジュールベースのコンシューマ(つまり、モジュールをオプトインしたコード)は、v2以上のモジュールをインポートするために、インポートパスに
/vN
を含める必要があります。
ただし、エコシステムは、モジュールとセマンティックインポートバージョン管理の採用ペースが異なることが予想されます。
「v2+ モジュールのリリース方法」セクション(“v2+ モジュールのリリース方法”)でより詳細に説明されているように、「メジャーサブディレクトリ」アプローチでは、v2+ モジュールの作成者は、mymodule/v2
や mymodule/v3
などのサブディレクトリを作成し、適切なパッケージをこれらのサブディレクトリの下に移動またはコピーします。これは、従来のインポートパスロジック(Go 1.8 や 1.7 などの古い Go リリースでも)が、import "mymodule/v2/mypkg"
などのインポート文を見ると適切なパッケージを見つけることを意味します。したがって、「メジャーサブディレクトリ」v2+ モジュールにあるパッケージは、モジュールサポートが有効になっていない場合でも(Go 1.11 を実行していてモジュールを有効にしていない場合、または完全なモジュールサポートがない Go 1.7、1.8、1.9、1.10 などの古いバージョンを実行している場合)検出され、使用されます。「メジャーサブディレクトリ」アプローチの詳細については、“v2+ モジュールのリリース方法”セクションを参照してください。
この FAQ の残りの部分は、「v2+ モジュールのリリース方法」セクション(“v2+ モジュールのリリース方法”)で説明されている「メジャーブランチ」アプローチに焦点を当てています。「メジャーブランチ」アプローチでは、/vN
サブディレクトリは作成されず、代わりにモジュールバージョン情報は go.mod
ファイルによって伝えられ、コミットに semver タグを適用することで伝達されます(多くの場合 master
ブランチ上で行われますが、異なるブランチ上で行われることもあります)。
現在の移行期間中に支援するために、「最小限のモジュール互換性」が 導入 され、Go 1.11 により Go コードとの互換性が向上しました。この「最小限のモジュール互換性」は、Go 1.9.7 と 1.10.3 にもバックポートされました(これらのバージョンでは、古い Go バージョンには完全なモジュールサポートがないため、事実上常に完全なモジュールモードが無効になっています)。
「最小限のモジュール互換性」の主な目標は次のとおりです。
-
古い Go バージョン(1.9.7 以降および 1.10.3 以降)が、インポートパスに
/vN
を使用しているセマンティックインポートバージョンを使用するモジュールをより簡単にコンパイルできるようにし、モジュールモード が Go 1.11 で無効になっている場合にも同じ動作を提供します。 -
古いコードが、v2+ モジュールを使用する際に、古いコンシューマーコードが新しい
/vN
インポートパスを使用するようにすぐに変更する必要なく、v2+ モジュールを使用できるようにします。 -
モジュール作成者が
/vN
サブディレクトリを作成することに依存せずに、これを実現します。
追加の詳細 – 「最小限のモジュール互換性」
「最小限のモジュール互換性」は、go
ツールの完全なモジュールモードが無効になっている場合にのみ有効になります。たとえば、Go 1.11 で GO111MODULE=off
を設定した場合、または Go 1.9.7 以降または 1.10.3 以降を使用している場合などです。
v2+ モジュールの作成者が /v2
または /vN
サブディレクトリを作成しておらず、「最小限のモジュール互換性」メカニズムを Go 1.9.7 以降、1.10.3 以降、および 1.11 で使用している場合
- モジュールを有効にしていないパッケージは、インポートされた v2+ モジュールのインポートパスにメジャーバージョンを含めません。
- 逆に、モジュールを有効にしているパッケージは、v2+ モジュールをインポートするには、インポートパスにメジャーバージョンを含める必要があります。
- パッケージがモジュールを有効にしているが、v2+ モジュールをインポートする際にインポートパスにメジャーバージョンを含めていない場合、
go
ツールが完全なモジュールモードで動作しているときは、そのモジュールの v2+ バージョンはインポートされません。(モジュールを有効にしているパッケージは、セマンティックインポートバージョンを「話す」と想定されています。foo
が v2+ バージョンを持つモジュールである場合、セマンティックインポートバージョンではimport "foo"
はfoo
の v1 セマンティックインポートバージョンのシリーズをインポートすることを意味します)。
- パッケージがモジュールを有効にしているが、v2+ モジュールをインポートする際にインポートパスにメジャーバージョンを含めていない場合、
- 「最小限のモジュール互換性」を実装するために使用されるメカニズムは、意図的に非常に狭くなっています。
- ロジック全体は、GOPATH モードで動作している場合、
/vN
を含む解決できないインポート文は、インポート文がモジュールを有効にしているコード内にある場合(つまり、有効なgo.mod
ファイルのあるツリー内の.go
ファイル内のインポート文)、/vN
を削除した後に再度試行されます。 - その結果、モジュール内に存在するコード内の
import "foo/v2"
などのインポート文は、1.9.7 以降、1.10.3 以降、および 1.11 の GOPATH モードでも正しくコンパイルされ、import "foo"
(/v2
なし)のように解決されます。つまり、追加の/v2
に混乱することなく、GOPATH に存在するfoo
のバージョンが使用されます。 - 「最小限のモジュール互換性」は、
go
コマンドラインで使用されるパス(go get
やgo list
への引数など)を含む、他のものには影響しません。
- ロジック全体は、GOPATH モードで動作している場合、
- この移行期の「最小限のモジュール認識」メカニズムは、「異なるインポートパスを持つパッケージは異なるパッケージとして扱われる」というルールを意図的に破り、非常に具体的な後方互換性の目標、つまり古いコードが v2+ モジュールを使用する場合に修正なしでコンパイルできるようにするという目標を追求しています。もう少し詳しく説明すると
- 古いコードが v2+ モジュールを使用するための唯一の方法が、最初に古いコードを変更することだけであるとすると、全体的なエコシステムにとってより負担が大きくなります。
- 古いコードを変更しない場合、その古いコードは v2+ モジュールのモジュール以前のインポートパスで動作する必要があります。
- 一方、モジュールを有効にする新しいコードまたは更新されたコードは、v2+ モジュールには新しい
/vN
インポートを使用する必要があります。 - 新しいインポートパスは古いインポートパスと等しくありませんが、どちらも単一のビルドで動作することが許可されているため、同じパッケージに解決される2つの異なる機能を持つインポートパスが存在します。
- たとえば、GOPATH モードで動作している場合、モジュールベースのコードに表示される
import "foo/v2"
は、GOPATH に存在するコードと同じコードに解決され、ビルドはfoo
の1つのコピー、特に GOPATH のディスクにあるバージョンを使用します。これにより、import "foo/v2"
を持つモジュールベースのコードは、1.9.7 以降、1.10.3 以降、および 1.11 の GOPATH モードでもコンパイルできます。
- 逆に、
go
ツールが完全なモジュールモードで動作している場合- 「異なるインポートパスを持つパッケージは異なるパッケージである」というルールには例外はありません(ベンダリングも完全なモジュールモードで洗練され、このルールにも準拠しています)。
- たとえば、
go
ツールが完全なモジュールモードであり、foo
が v2+ モジュールである場合、import "foo"
はfoo
の v1 バージョンを要求しており、import "foo/v2"
はfoo
の v2 バージョンを要求しています。
go.mod を作成しても、リポジトリに semver タグを適用しなかった場合はどうなるでしょうか?
semver はモジュールシステムの基礎です。コンシューマーにとって最高のエクスペリエンスを提供するために、モジュール作成者は semver VCS タグ(例:v0.1.0
または v1.2.3-rc.1
)を適用することを推奨していますが、semver VCS タグは厳密には必須ではありません。
-
go
コマンドがドキュメントどおりに動作するには、モジュールがsemver 仕様に従う必要があります。これには、破壊的変更が許可される方法とタイミングに関する semver 仕様に従うことが含まれます。 -
semver VCS タグを持たないモジュールは、擬似バージョン形式の semver バージョンを使用してコンシューマーによって記録されます。通常、モジュール作成者が“メジャーサブディレクトリ”アプローチに従って v2+ モジュールを作成しない限り、これは v0 メジャーバージョンになります。
-
したがって、semver VCS タグを適用しておらず、「メジャーサブディレクトリ」を作成していないモジュールは、事実上、semver v0 メジャーバージョンのシリーズにあると宣言しており、モジュールベースのコンシューマーはそれらを semver v0 メジャーバージョンとして扱います。
モジュールは、それ自体の異なるバージョンに依存できますか?
モジュールは、それ自体の異なるメジャーバージョンに依存できます。概して、これは異なるモジュールに依存することと比較できます。これは、モジュールのメジャーバージョンを別のメジャーバージョンをラップするシムとして実装するために役立ちます。
さらに、まったく異なる2つのモジュールがお互いに循環的に依存できるのと同じように、モジュールは循環的にそれ自体の異なるメジャーバージョンに依存できます。
ただし、モジュールがそれ自体の異なるバージョンに依存することを期待していない場合は、間違いの兆候である可能性があります。たとえば、v3 モジュールのパッケージをインポートすることを意図した .go コードは、インポート文に必要な /v3
が不足している可能性があります。その間違いは、v3 モジュールがそれ自体の v1 バージョンに依存しているという形で現れる可能性があります。
モジュールがそれ自体の異なるバージョンに依存していることに驚いた場合は、上記の“セマンティックインポートバージョン”セクションとFAQの“依存関係の期待されるバージョンが表示されない場合に確認できること”を確認する価値があります。
2つのパッケージがお互いに循環的に依存することはできないという制約は依然として残っています。
FAQ — 複数モジュールリポジトリ
マルチモジュールリポジトリとは何ですか?
複数モジュールリポジトリとは、それぞれ独自の go.mod ファイルを持つ複数のモジュールを含むリポジトリのことです。各モジュールは、その go.mod ファイルを含むディレクトリから始まり、そのディレクトリとそのサブディレクトリから再帰的にすべてのパッケージを含み、別の go.mod ファイルを含むサブツリーは除外します。
各モジュールには独自のバージョン情報があります。リポジトリのルートの下にあるモジュールのバージョンタグには、相対ディレクトリをプレフィックスとして含める必要があります。たとえば、次のリポジトリを考えてみてください。
my-repo
`-- foo
`-- rop
`-- go.mod
モジュール「my-repo/foo/rop」のバージョン1.2.3のタグは「foo/rop/v1.2.3」です。
通常、リポジトリ内の1つのモジュールのパスは、他のモジュールのパスへのプレフィックスになります。たとえば、次のリポジトリを考えてみてください。
my-repo
|-- bar
|-- foo
| |-- rop
| `-- yut
|-- go.mod
`-- mig
|-- go.mod
`-- vub
図 A トップレベルモジュールのパスは、別のモジュールのパスのプレフィックスです。
このリポジトリには2つのモジュールが含まれています。ただし、モジュール「my-repo」はモジュール「my-repo/mig」のパスへのプレフィックスです。
単一のリポジトリに複数のモジュールを含める必要がありますか?
このような構成でモジュールを追加、削除、およびバージョン管理するには、相当な注意と熟考が必要です。そのため、既存のリポジトリに複数のモジュールを管理するよりも、単一モジュールリポジトリを管理する方がほぼ常に簡単でシンプルです。
Russ Cox は #26664 でコメントしました。
パワーユーザーを除くほとんどのユーザーにとって、1つのリポジトリを1つのモジュールとする通常の規約を採用することをお勧めします。リポジトリが複数のモジュールを含むことが可能であることは、コード保存オプションの長期的な進化にとって重要ですが、デフォルトでそれを行うことはほとんど間違いなく望ましいことではありません。
マルチモジュールがより多くの作業になる2つの例
- リポジトリルートからの
go test ./...
は、もはやリポジトリ内のすべてをテストしません。 replace
ディレクティブを使用して、モジュール間の関係を日常的に管理する必要があるかもしれません。
しかし、これらの2つの例を超えた追加のニュアンスがあります。単一のリポジトリに複数のモジュールを含めることを検討している場合は、このサブセクションのFAQを注意深くお読みください。
リポジトリに複数のgo.mod
ファイルがあることが理にかなう2つのシナリオの例
-
例自体が複雑な依存関係セットを持つ使用例がある場合(たとえば、小さなパッケージを持っているが、Kubernetesを使用してパッケージを使用する例を含める場合など)。その場合、こちらに示すように、独自の
go.mod
を持つexample
または_example
ディレクトリにリポジトリを持たせることが理にかなう場合があります。 -
複雑な依存関係セットを持つリポジトリがあり、依存関係のセットがより小さいクライアントAPIがある場合。場合によっては、独自の
go.mod
を持つapi
またはclientapi
などのディレクトリを作成するか、そのclientapi
を別々のリポジトリに分離することが理にかなう場合があります。
ただし、これらの両方のケースにおいて、多数の非直接的な依存関係のパフォーマンスまたはダウンロードサイズのためにマルチモジュールリポジトリの作成を検討している場合は、まずGOPROXYを試すことを強くお勧めします。GOPROXYはGo 1.13でデフォルトで有効になります。GOPROXYを使用すると、マルチモジュールリポジトリを作成することによって得られるパフォーマンス上の利点や依存関係のダウンロードサイズ上の利点の大部分が得られます。
マルチモジュールリポジトリにモジュールを追加することはできますか?
はい。ただし、この問題には2つのクラスがあります。
最初のクラス:モジュールが追加されるパッケージはまだバージョン管理されていません(新しいパッケージ)。このケースは簡単です。パッケージとgo.modを同じコミットに追加し、コミットにタグを付け、プッシュします。
2番目のクラス:モジュールが追加されるパスはバージョン管理されており、1つ以上の既存のパッケージが含まれています。このケースでは、かなりの注意が必要です。例として、次のリポジトリ(現実の世界をよりよくシミュレートするためにgithub.comの場所に配置)をもう一度検討してください。
github.com/my-repo
|-- bar
|-- foo
| |-- rop
| `-- yut
|-- go.mod
`-- mig
`-- vub
モジュール「github.com/my-repo/mig」を追加することを検討してください。上記と同じアプローチに従うと、パッケージ/my-repo/migは2つの異なるモジュールによって提供される可能性があります。「github.com/my-repo」の古いバージョンと、新しいスタンドアロンモジュール「github.com/my-repo/mig」です。両方のモジュールがアクティブな場合、「github.com/my-repo/mig」のインポートは、コンパイル時に「あいまいなインポート」エラーが発生します。
これを回避する方法は、新しく追加されたモジュールが、「切り出された」モジュールに、切り出された後のバージョンで依存するようにすることです。
「github.com/my-repo」が現在v1.2.3であると仮定して、上記のリポジトリでこの手順を実行してみましょう。
-
github.com/my-repo/mig/go.modを追加
cd path-to/github.com/my-repo/mig go mod init github.com/my-repo/mig # Note: if "my-repo/mig" does not actually depend on "my-repo", add a blank # import. # Note: version must be at or after the carve-out. go mod edit -require github.com/myrepo@v1.3
-
git commit
-
git tag v1.3.0
-
git tag mig/v1.0.0
-
次に、これらをテストしましょう。goコマンドは各依存モジュールをモジュールキャッシュからフェッチしようと試みるため、
go build
またはgo test
を単純に実行することはできません。そのため、replace
ルールを使用して、go
コマンドがローカルコピーを使用するようにする必要があります。cd path-to/github.com/my-repo/mig go mod edit -replace github.com/my-repo@v1.3.0=../ go test ./... go mod edit -dropreplace github.com/my-repo@v1.3.0
-
git push origin master v1.3.0 mig/v1.0.0
コミットと両方のタグをプッシュ
将来、golang.org/issue/28835により、テスト手順がより簡単なエクスペリエンスになるはずです。
マイナーバージョンの間でモジュール「github.com/my-repo」からコードが削除されたことにも注意してください。これをメジャーな変更と見なさないと奇妙に思えるかもしれませんが、このインスタンスでは、推移的な依存関係は、削除されたパッケージの互換性のある実装を元のインポートパスで引き続き提供します。
マルチモジュールリポジトリからモジュールを削除することはできますか?
はい、上記と同じ2つのケースと同様の手順です。
モジュールは、別のモジュールの内部/にある`internal`パッケージに依存できますか?
はい。1つのモジュールのパッケージは、内部/パスコンポーネントまで同じパスプレフィックスを共有している限り、別のモジュールから内部パッケージをインポートできます。たとえば、次のリポジトリを考えてみましょう。
my-repo
|-- foo
| `-- go.mod
|-- go.mod
`-- internal
ここでは、モジュール「my-repo/foo」がモジュール「my-repo」に依存している限り、パッケージfooは/my-repo/internalをインポートできます。同様に、次のリポジトリでは
my-repo
|-- foo
| `-- go.mod
`-- internal
`-- go.mod
ここでは、モジュール「my-repo/foo」がモジュール「my-repo/internal」に依存している限り、パッケージfooはmy-repo/internalをインポートできます。セマンティクスは両方とも同じです。my-repoはmy-repo/internalとmy-repo/fooの間の共有パスプレフィックスであるため、パッケージfooはパッケージinternalをインポートできます。
追加のgo.modで不要なコンテンツを除外できますか?モジュールには、.gitignoreファイルに相当するものがありますか?
単一のリポジトリに複数のgo.mod
ファイルを持つもう1つのユースケースは、リポジトリにモジュールから削除する必要があるファイルが含まれている場合です。たとえば、リポジトリには、Goモジュールに必要のない非常に大きなファイルが含まれている場合や、複数言語のリポジトリには多くの非Goファイルが含まれている場合があります。
ディレクトリ内の空のgo.mod
は、そのディレクトリとそのすべてのサブディレクトリを最上位のGoモジュールから除外します。
除外されたディレクトリに.go
ファイルが含まれていない場合は、空のgo.mod
ファイルを作成する以外に追加の手順は必要ありません。除外されたディレクトリに.go
ファイルが含まれている場合は、最初にこのマルチモジュールリポジトリセクションの他のFAQを注意深く確認してください。
よくある質問 — 最小バージョン選択
最小バージョン選択では、開発者が重要なアップデートを受け取ることができなくなりますか?
以前の公式提案の議論からのFAQにある「最小バージョン選択により、開発者は重要なアップデートを取得できなくなりますか?」という質問を参照してください。
よくある質問 — 発生する可能性のある問題
問題が発生した場合、スポットチェックできる一般的な事項は何ですか?
go env
を実行して、読み取り専用のGOMOD
変数に空の値が表示されないことを確認することにより、モジュールが有効になっていることを二重チェックしてください。- 注:
GOMOD
は事実上読み取り専用のデバッグ出力であるため、変数として設定することはありません。go env
が出力します。 - モジュールを有効にするために
GO111MODULE=on
を設定している場合は、誤って複数形のGO111MODULES=on
になっていないことを二重チェックしてください。(この機能は多くの場合「モジュール」と呼ばれているため、人々は自然にS
を含めることがよくあります)。
- 注:
- ベンダーを使用する必要がある場合は、
-mod=vendor
フラグがgo build
または同様のフラグに渡されているか、GOFLAGS=-mod=vendor
が設定されていることを確認してください。- モジュールは、
go
ツールにvendor
を使用するように指示しない限り、デフォルトでvendor
ディレクトリを無視します。
- モジュールは、
- ビルド用に選択された実際のバージョンのリストを確認するには、
go list -m all
をチェックすると頻繁に役立ちます。go list -m all
は、go.mod
ファイルを見るだけの場合と比較して、通常はより詳細な情報を提供します。
go get foo
が何らかの理由で失敗した場合、または特定のパッケージfoo
でgo build
が失敗した場合、go get -v foo
またはgo get -v -x foo
の出力を確認すると役立つことがよくあります。- 一般的に、
go get
はgo build
よりも詳細なエラーメッセージを提供することがよくあります。 go get
への-v
フラグは、より詳細な情報を印刷するように要求しますが、リモートリポジトリの構成方法に基づいて、404エラーなどの特定の「エラー」が予想される可能性があることに注意してください。- 問題の本質がまだ不明な場合は、より詳細な
go get -v -x foo
を試すこともできます。これにより、発行されているgitまたはその他のVCSコマンドも表示されます。(正当な理由がある場合は、トラブルシューティングのためにgo
ツールのコンテキストの外で同じgitコマンドを実行できます)。
- 一般的に、
- 特に古いgitバージョンを使用しているかどうかを確認できます。
- 古いバージョンのgitは、
vgo
プロトタイプとGo 1.11ベータ版の一般的な問題の原因でしたが、GA 1.11でははるかに少ない頻度です。
- 古いバージョンのgitは、
- Go 1.11のモジュールキャッシュは、以前にネットワークの問題が発生した場合や、複数の
go
コマンドが並行して実行された場合に、さまざまなエラーを引き起こす可能性があります(#26794を参照してください。これはGo 1.12で解決されています)。トラブルシューティング手順として、$GOPATH/pkg/modをバックアップディレクトリにコピーし(後で調査が必要な場合に備えて)、go clean -modcache
を実行し、元の問題が解決するかどうかを確認できます。 - Dockerを使用している場合は、Dockerの外で動作を再現できるかどうかを確認すると役立ちます(動作がDockerでのみ発生する場合、上記の箇条書きのリストを、Docker内と外の結果を比較するための出発点として使用できます)。
現在調べているエラーは、ビルドに特定のモジュールまたはパッケージの予想バージョンがないことが原因で発生する二次的な問題である可能性があります。したがって、特定のエラーの原因が明らかでない場合は、次のFAQで説明されているように、バージョンをスポットチェックすると役立つ場合があります。
依存関係の期待されるバージョンが表示されない場合、何をチェックできますか?
-
最初のステップとして、
go mod tidy
を実行することをお勧めします。これにより問題が解決する可能性がありますが、.go
ソースコードに関してgo.mod
ファイルを整合性のある状態にするのに役立ち、その後の調査を容易にします。(go mod tidy
自体が予期しない方法で依存関係のバージョンを変更する場合、最初に「go mod tidy」に関するこのFAQを読んでください。それで説明されない場合は、go.mod
をリセットしてからgo list -mod=readonly all
を実行してみてください。これにより、バージョンを変更する必要があるものに関するより具体的なメッセージが表示される可能性があります)。 -
2番目のステップは通常、
go list -m all
をチェックして、ビルド用に選択された実際のバージョンのリストを確認することです。go list -m all
は、間接的な依存関係についても、共有依存関係のバージョンを解決した後についても、最終的に選択されたバージョンを表示します。また、replace
およびexclude
ディレクティブの結果も表示されます。 -
次のステップとして、
go mod graph
またはgo mod graph | grep <module-of-interest>
の出力を調べることができます。go mod graph
は、(置換を考慮して)モジュールの要件グラフを出力します。出力の各行には2つのフィールドがあります。最初の列は消費モジュールであり、2番目の列はそのモジュールの要件の1つです(その消費モジュールによって要求されるバージョンを含む)。これは、ビルドに特定の依存関係を要求しているモジュールをすばやく確認する方法です。これは、ビルドに異なる消費者が異なる必要なバージョンを持つ依存関係がある場合(その場合、「バージョン選択」セクションで説明されている動作に精通していることが重要です)。
go mod why -m <module>
もここで役立つ可能性がありますが、これは通常、依存関係がなぜ含まれているのか(依存関係が特定のバージョンになる理由ではなく)を確認する場合に、より役立ちます。
go list
は、必要に応じてモジュールを調査するために役立つクエリのさらに多くのバリエーションを提供します。1 つの例を以下に示します。これにより、テスト専用の依存関係を除外したビルドで使用されている正確なバージョンが表示されます。
go list -deps -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' ./... | sort -u
モジュールの調査に関するコマンドと例のより詳細なセットは、実行可能な「Go Modules by Example」のウォークスルーで確認できます。
予期しないバージョンの原因の1つは、意図されていなかった無効または予期しないgo.mod
ファイルが作成されたこと、または関連するミス(例:モジュールのv2.0.1
バージョンが、必要な/v2
なしでmodule foo
として誤って宣言されている可能性があります。v3モジュールをインポートすることを目的とした.go
コードのインポートステートメントに必要な/v3
が不足している可能性があります。v4モジュールのgo.mod
のrequire
ステートメントに必要な/v4
が不足している可能性があります)です。したがって、表示されている特定の問題の原因が明らかでない場合は、まず上記の“go.mod”セクションと“セマンティックインポートバージョニング”セクションの資料を再読し(これらにはモジュールが従うべき重要なルールが含まれているため)、数分かけて最も関連性の高いgo.mod
ファイルとインポートステートメントをスポットチェックすることをお勧めします。
「パッケージfooを提供するモジュールが見つかりません」というエラーが表示されるのはなぜですか?
これは、いくつかの異なる根本的な原因で発生する可能性のある一般的なエラーメッセージです。
場合によっては、このエラーは単にタイプミスされたパスが原因であるため、最初のステップはおそらく、エラーメッセージに記載されている詳細に基づいて、パスが間違っていないかどうかを二重に確認することです。
まだ行っていない場合は、go get -v foo
またはgo get -v -x foo
を試すことが次のステップとして有効です。
- 一般的に、
go get
はgo build
よりも詳細なエラーメッセージを提供することがよくあります。 - 詳細については、このセクションの最初のトラブルシューティングFAQを参照してください。上部
その他の考えられる原因
-
現在のディレクトリに
.go
ソースファイルがないのにgo build
またはgo build .
を実行した場合、「パッケージfooを提供するモジュールが見つかりません」というエラーが表示される場合があります。これが発生している場合は、go build ./...
(ここで./...
は現在のモジュール内のすべてのパッケージに一致するように展開されます)などの代替呼び出しが解決策となる可能性があります。#27122を参照してください。 -
Go 1.11のモジュールキャッシュは、ネットワークの問題や複数の
go
コマンドが並行して実行される場合など、このエラーを引き起こす可能性があります。これはGo 1.12で解決されています。詳細と修正手順については、このセクションの最初のトラブルシューティングFAQを参照してください。上部
「go mod init」で「ソースディレクトリのモジュールパスを決定できません」というエラーが表示されるのはなぜですか?
引数なしでgo mod init
を実行すると、VCSメタデータなどのさまざまなヒントに基づいて、適切なモジュールパスを推測しようとします。ただし、go mod init
が常に適切なモジュールパスを推測できるとは限りません。
go mod init
でこのエラーが発生した場合は、ヒューリスティックが推測できず、モジュールパスを自分で指定する必要があります(例:go mod init github.com/you/hello
)。
モジュールに対応していない複雑な依存関係に問題があります。その現在の依存関係マネージャーの情報を使用できますか?
はい。これにはいくつかの手動手順が必要ですが、より複雑な場合に役立つ場合があります。
独自のモジュールを初期化するときにgo mod init
を実行すると、Gopkg.lock
、glide.lock
、vendor.json
などの構成ファイルを、対応するrequire
ディレクティブを含むgo.mod
ファイルに変換することによって、以前の依存関係マネージャーから自動的に変換されます。たとえば、既存のGopkg.lock
ファイルの情報は、通常、直接および間接の依存関係すべてのバージョン情報を記述しています。
ただし、まだモジュールにオプトインしていない新しい依存関係を追加する場合は、新しい依存関係が使用していた可能性のある以前の依存関係マネージャーからの同様の自動変換プロセスはありません。その新しい依存関係自体が破壊的な変更があった非モジュール依存関係を持っている場合、場合によっては互換性の問題が発生する可能性があります。言い換えれば、新しい依存関係の以前の依存関係マネージャーは自動的に使用されず、場合によっては間接的な依存関係に問題が発生する可能性があります。
1つの方法は、問題のある非モジュール直接依存関係でgo mod init
を実行して、現在の依存関係マネージャーから変換し、結果として得られる一時的なgo.mod
からのrequire
ディレクティブを使用して、モジュールのgo.mod
に情報を追加または更新することです。
たとえば、github.com/some/nonmodule
が現在別の依存関係マネージャーを使用しているモジュールの問題のある直接依存関係である場合、次のような操作を行うことができます。
$ git clone -b v1.2.3 https://github.com/some/nonmodule /tmp/scratchpad/nonmodule
$ cd /tmp/scratchpad/nonmodule
$ go mod init
$ cat go.mod
一時的なgo.mod
からの結果のrequire
情報は、モジュールの実際のgo.mod
に手動で移動するか、このユースケースを対象としたコミュニティツールであるhttps://github.com/rogpeppe/gomodmergeの使用を検討できます。さらに、手動でクローンしたバージョンと一致するように、実際のgo.mod
にrequire github.com/some/nonmodule v1.2.3
を追加する必要があります。
Dockerに対してこのテクニックに従った具体的な例は、この#28489コメントにあり、github.com/sirupsen/logrus
とgithub.com/Sirupsen/logrus
の間の大文字と小文字を区別する問題を回避するためのDocker依存関係の一貫したバージョンのセットの取得を示しています。
インポートパスと宣言されたモジュールIDの不一致によって発生する「go.modの解析エラー:予期しないモジュールパス」と「モジュール要件の読み込みエラー」を解決するにはどうすればよいですか?
なぜこのエラーが発生するのですか?
一般的に、モジュールはmodule
ディレクティブ(例:module example.com/m
)を使用して、go.mod
でそのアイデンティティを宣言します。これはそのモジュールの「モジュールパス」であり、go
ツールは、宣言されたモジュールパスと、コンシューマーによって使用されるインポートパスの間の整合性を強制します。モジュールのgo.mod
ファイルにmodule example.com/m
が記述されている場合、コンシューマーはそのモジュールから、そのモジュールパスで始まるインポートパス(例:import "example.com/m"
またはimport "example.com/m/sub/pkg"
)を使用してパッケージをインポートする必要があります。
コンシューマーによって使用されるインポートパスと対応する宣言されたモジュールパスに不一致がある場合、go
コマンドはparsing go.mod: unexpected module path
という致命的なエラーを報告します。さらに、場合によっては、go
コマンドはその後、より一般的なerror loading module requirements
エラーを報告します。
このエラーの最も一般的な原因は、名前の変更(例:github.com/Sirupsen/logrus
からgithub.com/sirupsen/logrus
)があった場合、またはバニティインポートパスが原因でモジュールがモジュール化前に2つの異なる名前で時々使用されていた場合(例:github.com/golang/sync
と推奨されるgolang.org/x/sync
)です。
依存関係が古い名前(例:github.com/Sirupsen/logrus
)または非標準名(例:github.com/golang/sync
)でまだインポートされているが、その依存関係がその後モジュールを採用し、その標準名をgo.mod
で宣言している場合、問題が発生する可能性があります。このエラーは、モジュールのアップグレードされたバージョンが見つかり、標準モジュールパスが古いインポートパスと一致しなくなった場合のアップグレード中に発生する可能性があります。
問題のあるシナリオの例
- あなたは間接的に
github.com/Quasilyte/go-consistent
に依存しています。 - プロジェクトはモジュールを採用し、その後、名前を
github.com/quasilyte/go-consistent
に変更します(Q
を小文字のq
に変更します)。これは破壊的な変更です。GitHubは古い名前から新しい名前に転送します。 go get -u
を実行すると、直接および間接の依存関係すべてをアップグレードしようとします。github.com/Quasilyte/go-consistent
のアップグレードが試行されますが、見つかった最新のgo.mod
にはmodule github.com/quasilyte/go-consistent
と記述されています。- 全体的なアップグレード操作が完了せず、エラーが発生します。
go: github.com/Quasilyte/go-consistent@v0.0.0-20190521200055-c6f3937de18c: parsing go.mod: unexpected module path “github.com/quasilyte/go-consistent” go get: error loading module requirements
解決策
エラーの最も一般的な形式は次のとおりです。
go: example.com/some/OLD/name@vX.Y.Z: parsing go.mod: unexpected module path “example.com/some/NEW/name”
example.com/some/NEW/name
(エラーの右側)のリポジトリにアクセスして、最新のリリースまたはmaster
のgo.mod
ファイルを確認し、最初の行にmodule example.com/some/NEW/name
として宣言されているかどうかを確認できます。その場合、「古いモジュール名」と「新しいモジュール名」の問題が発生しているというヒントになります。
このセクションの残りの部分では、次の手順を順に実行することで、このエラーの「古い名前」と「新しい名前」の形式を解決することに重点を置いています。
-
独自のコードを確認して、
example.com/some/OLD/name
を使用してインポートしているかどうかを確認します。その場合は、コードを更新してexample.com/some/NEW/name
を使用してインポートします。 -
アップグレード中にこのエラーが発生した場合は、よりターゲットを絞ったアップグレードロジック(#26902) を備え、この問題を回避し、この状況に対するより良いエラーメッセージを提供することが多い、最新のGoバージョンを使用してアップグレードを試みる必要があります。最新のGoバージョンの
go get
引数は、1.12のものとは異なります。最新のバージョンを取得して依存関係をアップグレードする例を以下に示します。
go get golang.org/dl/gotip && gotip download
gotip get -u all
gotip mod tidy
問題のある古いインポートは多くの場合間接的な依存関係にあるため、最新のバージョンでアップグレードしてからgo mod tidy
を実行すると、多くの場合、問題のあるバージョンを超えてアップグレードし、go.mod
から不要になった問題のあるバージョンを削除できるため、日常的にGo 1.12または1.11を使用する場合に機能状態になります。たとえば、この方法でgithub.com/golang/lint
とgolang.org/x/lint
の問題を超えてアップグレードする方法をこちらで確認できます。
-
go get -u foo
またはgo get -u foo@latest
の実行中にこのエラーが発生した場合は、-u
を削除してみてください。これにより、foo
の作者がfoo
のリリース時に機能していると検証した可能性のあるバージョンを超えてfoo
の依存関係をアップグレードすることなく、foo@latest
で使用されている依存関係のセットが得られます。これは、foo
の直接および間接の依存関係の一部がまだsemverまたはモジュールを採用していない可能性のあるこの移行期間中に特に重要です。(よくある間違いは、go get -u foo
によってfoo
の最新バージョンのみを取得すると考えていることです。実際には、go get -u foo
またはgo get -u foo@latest
の-u
は、foo
の直接および間接の依存関係のすべての最新バージョンも取得することを意味します。それはあなたが望むものかもしれませんが、深い間接的な依存関係のために失敗している場合はそうではない可能性があります)。 -
上記の手順でエラーが解決しない場合は、次のアプローチはやや複雑ですが、多くの場合、「古い名前」と「新しい名前」の形式のこのエラーを解決するはずです。これは、エラーメッセージ自体からの情報のみと、いくつかのVCS履歴を簡単に確認したものを使用します。
4.1.
example.com/some/NEW/name
リポジトリに移動します。4.2. そこで
go.mod
ファイルがいつ導入されたかを判断します(例:go.mod
のブレイムビューまたは履歴ビューを確認します)。4.3. そこで
go.mod
ファイルが導入される直前のリリースまたはコミットを選択します。4.4. `go.mod`ファイルに、`replace`ステートメントを追加します。`replace`ステートメントの両側に古い名前を使用します。`replace example.com/some/OLD/name => example.com/some/OLD/name
` 以前の例では、`github.com/Quasilyte/go-consistent`が古い名前で、`github.com/quasilyte/go-consistent`が新しい名前です。`go.mod`が最初に導入されたのは、コミット00c5b0cf371aです。このリポジトリはsemverタグを使用していないため、直前のコミット00dd7fb039eを使用し、大文字の古いQuasilyte名を使用して両側に追加します。
replace github.com/Quasilyte/go-consistent => github.com/Quasilyte/go-consistent 00dd7fb039e
この`replace`ステートメントにより、「古い名前」と「新しい名前」の不一致という問題を回避できます。`go.mod`の存在下で古い名前が新しい名前にアップグレードされるのを効果的に防止します。通常、`go get -u`などによるアップグレードで、エラーを回避できるようになります。アップグレードが完了したら、古い名前をインポートしているものがまだあるかどうかを確認できます(例:`go mod graph | grep github.com/Quasilyte/go-consistent`)。なければ、`replace`を削除できます。(多くの場合、これが機能するのは、アップグレード自体が、古い問題のあるインポートパスが使用されている場合に失敗する可能性があるためです。最終結果では使用されていない場合でも、アップグレードが完了していれば、これは#30831で追跡されています)。
- 上記の手順で問題が解決しない場合は、問題のある古いインポートパスが、1つ以上の依存関係の最新バージョンでまだ使用されている可能性があります。この場合、問題のある古いインポートパスをまだ使用しているものを特定し、問題のあるインポーターが現在標準的なインポートパスを使用するように変更するよう依頼する問題を特定するか、開くことが重要です。ステップ2.で`gotip`を使用すると、問題のあるインポーターを特定できる可能性がありますが、すべての場合、特にアップグレードの場合にそうなるわけではありません(#30661)。問題のある古いインポートパスを使用しているインポーターが不明な場合は、クリーンなモジュールキャッシュを作成し、エラーをトリガーする操作を実行し、モジュールキャッシュ内で古い問題のあるインポートパスをgrepすることで、通常は確認できます。例えば
export GOPATH=$(mktemp -d)
go get -u foo # perform operation that generates the error of interest
cd $GOPATH/pkg/mod
grep -R --include="*.go" github.com/Quasilyte/go-consistent
- これらの手順で問題が解決しない場合、または循環参照のために古い問題のあるインポートパスへの参照を削除できないプロジェクトの保守担当者である場合は、別のwikiページで、この問題についてより詳細な説明を参照してください。
最後に、上記の手順は、「古い名前」と「新しい名前」という根本的な問題の解決方法に焦点を当てています。ただし、同じエラーメッセージは、`go.mod`が間違った場所に配置されているか、単にモジュールパスが間違っている場合にも表示されることがあります。その場合は、そのモジュールのインポートは常に失敗します。新しく作成したモジュールをインポートしていて、これまで成功したことがない場合は、`go.mod`ファイルが正しく配置されていること、およびその場所に対応する適切なモジュールパスが設定されていることを確認する必要があります。(最も一般的なアプローチは、リポジトリごとに1つの`go.mod`を使用し、リポジトリのルートに1つの`go.mod`ファイルを配置し、`module`ディレクティブで宣言されたモジュールパスとしてリポジトリ名を使用することです)。詳細については、“go.mod”セクションを参照してください。
「go build」でgccが必要なのはなぜですか?また、net/httpなどのプリビルドパッケージが使用されないのはなぜですか?
要約すると
プリビルドパッケージはモジュールではないビルドであり、再利用できません。申し訳ありません。今のところcgoを無効にするか、gccをインストールしてください。
これは、モジュールを有効にしている場合(例:`GO111MODULE=on`)にのみ問題になります。#26988で、追加の議論を参照してください。
モジュールは、`import "./subdir"`のような相対インポートで動作しますか?
いいえ。#26645を参照してください。これには以下が含まれています。
モジュールでは、サブディレクトリの名前がついに付けられました。親ディレクトリが「module m」と言っている場合、サブディレクトリは「m/subdir」としてインポートされ、「./subdir」とはなりません。
移入されたvendorディレクトリに必要なファイルが存在しない場合があります。
`.go`ファイルのないディレクトリは、`go mod vendor`によって`vendor`ディレクトリ内にコピーされません。これは設計によるものです。
要約すると、特定のベンダーの動作を別にすれば、goビルドの全体的なモデルは、パッケージをビルドするために必要なファイルは`.go`ファイルのあるディレクトリに存在する必要があるということです。
cgoの例を使用すると、他のディレクトリにあるCソースコードを変更しても再ビルドはトリガーされず、代わりにビルドは古いキャッシュエントリを使用します。cgoのドキュメントには、現在含まれています
他のディレクトリのファイルの変更によってパッケージが再コンパイルされるわけではないため、パッケージのすべての非Goソースコードは、パッケージディレクトリに格納する必要があり、サブディレクトリに格納しないでください。
コミュニティツールhttps://github.com/goware/modvendorを使用すると、モジュールから`.c`、`.h`、`.s`、`.proto`、その他のファイルを`vendor`ディレクトリに簡単にコピーできます。これは役立ちますが、パッケージをビルドするために必要なファイルが`.go`ファイルのあるディレクトリの外部にある場合、(ベンダーの有無にかかわらず)goビルドが適切に処理されていることを確認する必要があります。
#26366で追加の議論を参照してください。
従来のベンダーリングとは異なるアプローチとして、モジュールキャッシュをチェックインする方法があります。従来のベンダーリングと同様の利点を提供し、いくつかの点でより忠実度の高いコピーになります。このアプローチは、「Go Modules by Example」のチュートリアルとして説明されています。
このコンテンツは、Go Wikiの一部です。