Golang runtime MemStats翻訳

pkg.go.dev

Allocは、割り当てられたヒープオブジェクトのバイト数である。 これは HeapAlloc (後述) と同じである。 Alloc uint64

TotalAllocは、ヒープオブジェクトに割り当てられた累積バイト数です。

TotalAllocはヒープオブジェクトが割り当てられると増加しますが、AllocやHeapAllocとは異なり、オブジェクトが解放されても減少することはありません。 TotalAlloc uint64

Sysは、OSから取得したメモリの総バイト数です。 Sysは、以下のXSysフィールドの合計です。Sys は、ヒープ、スタック、その他の内部データ構造用に Go ランタイムが予約した仮想アドレス空間を測定します。一般的には、ある時点ですべての仮想アドレス空間が物理メモリにバックアップされているわけではありませんが、ある時点ですべての仮想アドレス空間が物理メモリにバックアップされている可能性があります。 Sys uint64

Lookupsはランタイムが実行したポインタのルックアップの数である。 これは主にランタイム内部をデバッグするのに便利である。 Lookups uint64

Mallocsは、割り当てられたヒープオブジェクトの累積数である。 ライブオブジェクトの数はMallocs - Freesである。 Mallocs uint64

Freesは、ヒープオブジェクトが解放された累積数です。 Frees uint64

HeapAllocは、割り当てられたヒープオブジェクトのバイト数です。 "割り当てられた "ヒープオブジェクトは、すべての到達可能なオブジェクトと、ガベージコレクタがまだ解放していない到達不可能なオブジェクトを含みます。具体的には、ヒープオブジェクトが割り当てられるとHeapAllocは増加し、ヒープが掃引され到達不可能なオブジェクトが解放されると減少します。掃引はGCサイクルの間にインクリメンタルに行われるので、この2つのプロセスは同時に発生し、その結果HeapAllocは滑らかに変化する傾向があります(Stop-the-Worldガベージコレクタに典型的に見られる鋸歯状とは対照的です)。 HeapAlloc uint64

HeapSysはOSから取得したヒープメモリのバイト数です。 // HeapSys は、ヒープ用に予約された仮想アドレス空間の量を測定します。これには、物理メモリは消費しないが小さくなりがちな、予約済みで未使用の仮想アドレス空間と、未使用になって物理メモリがOSに戻された仮想アドレス空間(後者の指標は HeapReleased を参照のこと)が含まれます。 // HeapSys は、ヒープが持つ最大のサイズを推定します。 HeapSys uint64

HeapIdle はアイドル(未使用)スパンでのバイト数です。 // アイドルスパンにはオブジェクトがありません。これらのスパンはOSに戻されるか(すでに戻されているかもしれない)、ヒープ割り当てに再利用されるか、スタックメモリとして再利用される。 // HeapIdleからHeapReleasedを引いた値は、OSに返せるがランタイムが保持しているメモリ量を推定し、OSにメモリを要求せずにヒープを成長させることができるようにする。この差がヒープサイズより著しく大きい場合、ライブヒープサイズに最近一時的なスパイクがあったことを示す。 HeapIdle uint64

HeapInuseは、使用中のスパンでのバイト数です。 // 使用中のスパンには、少なくとも1つのオブジェクトがあります。これらのスパンは、ほぼ同じサイズの他のオブジェクトにのみ使用することができます。 // HeapInuse から HeapAlloc を引いた値は、特定のサイズクラスに割り当てられているが、現在使用されていないメモリの量を推定する。これは断片化に対する上限であるが、一般にこのメモリは効率的に再利用できる。 HeapInuse uint64

HeapReleasedは、OSに返された物理メモリのバイト数です。 // これは、OSに戻され、まだヒープに再取得されていないアイドルスパンからのヒープメモリを数えます。 HeapReleased uint64

HeapObjects は、割り当てられたヒープオブジェクトの数です。 // HeapAlloc と同様、オブジェクトが割り当てられると増加し、ヒープが掃除され、到達できないオブジェクトが解放されると減少します。 HeapObjects uint64

StackInuse は、スタックスパンでのバイト数です。 // 使用中のスタックスパンには、少なくとも 1 つのスタックがあります。これらのスパンは、同じサイズの他のスタックにのみ使用することができます。 // 未使用のスタックスパンはヒープに戻されるため、StackIdle は存在しない(したがって HeapIdle にカウントされる)。 StackInuse uint64

StackSys は OS から取得したスタックメモリバイトである. // StackSys は StackInuse に、OS スレッドスタック用に OS から直接取得したメモリ(最小であるべき)を加えたものです。 StackSys uint64

MSpanInuseは,割り当てられたmspan構造体のバイト数. MSpanInuse uint64

MSpanSysはOSから取得したmspan構造体のメモリ量(バイト)です。 MSpanSys uint64

MCacheInuseは、割り当て済みmcache構造体のバイト数です。 MCacheInuse uint64

MCacheSysは、mcache構造体用にOSから取得したメモリのバイト数です。 MCacheSys uint64

BuckHashSysは、プロファイリングのバケットハッシュテーブルのメモリのバイト数です。 BuckHashSys uint64

GCSysは、ガベージコレクションメタデータに含まれるメモリのバイト数です。 GCSys uint64

OtherSysは、雑多なオフヒープランタイムアロケーションのメモリのバイト数です。 OtherSys uint64

NextGCは、次のGCサイクルのターゲットヒープサイズです。 // ガベージコレクタの目標は、HeapAlloc ≤ NextGC を維持することです。 各GCサイクルの終了時に、到達可能なデータ量とGOGCの値に基づいて、次のサイクルのターゲットが計算されます。 NextGC uint64

LastGCは、1970年(UNIXエポック)以降のナノ秒として、最後のガベージコレクションが終了した時間です。 LastGC uint64

PauseTotalNsは、プログラム開始以来、GCストップザワールドのポーズにおける累積ナノ秒です。 // 世界停止の間、すべてのゴルーチンは一時停止され、ガベージコレクタのみが実行できます。 PauseTotalNs uint64

PauseNsは、最近のGCのStop-the-World一時停止時間のナノ秒単位の円形バッファです。 一時停止時間をナノ秒単位で表したものです。 // 最新の一時停止は PauseNs[(NumGC+255)%256] にあります。一般的に、PauseNs[N%256]は、直近のN%256番目のGCサイクルで一時停止した時間を記録します。GCサイクルごとに複数の一時停止がある場合があります。これは、サイクル中のすべての一時停止の合計です。 PauseNs [256]uint64

PauseEndは、1970年(UNIXエポック)以降のナノ秒として、最近のGC一時停止終了時刻の循環バッファです。 // このバッファは、PauseNs と同じ方法で満たされます。GCサイクルごとに複数の一時停止があるかもしれません; これはサイクル内の最後の一時停止の終了を記録します。 PauseEnd [256]uint64

NumGCは、完了したGCサイクルの数です。 NumGC uint32

NumForcedGCは、GC関数を呼び出すアプリケーションによって強制されたGCサイクルの数です。 NumForcedGC uint32

GCCPUFractionは、プログラムが開始されてからGCによって使用されるこのプログラムの利用可能なCPU時間の割合です。 // GCCPUFraction は 0 と 1 の間の数値で表され、0 は GC がこのプログラムの CPU を全く消費していないことを意味します。プログラムの利用可能な CPU 時間は、プログラム開始以来の GOMAXPROCS の積分として定義されます。つまり、GOMAXPROCS が 2 で、プログラムが 10 秒間実行されている場合、その「利用可能な CPU」は 20 秒です。GCCPUFraction には、書き込みバリアアクティビティに使用される CPU 時間は含まれません。 // これはGODEBUG=gctrace=1によって報告されるCPUの割合と同じである。 GCCPUFraction float64

EnableGCは、GCが有効であることを示す。GOGC=offの場合でも、常にtrueである。 EnableGC bool

DebugGC は現在未使用。 DebugGC bool

BySize は、サイズごとのクラス割り当て統計情報を報告します。 // BySize[N] は、 BySize[N-1].Size < S ≤ BySize[N].Size となるサイズ S の割り当てに関する統計情報を提供します。 // BySize[60].Sizeより大きなアロケーションは報告されません。 BySize [61]struct { (構造体) Sizeは、このサイズクラスにおけるオブジェクトの最大バイトサイズです。 Size uint32

Mallocsは、このサイズクラスで割り当てられたヒープオブジェクトの累積数です。割り当ての累積バイト数は、Size*Mallocsです。このサイズクラスで生きているオブジェクトの数は、Mallocs - Freesです。
Mallocs uint64

Freesは、このサイズ・クラスで解放されたヒープ・オブジェクトの累積数です。
Frees uint64

}

Go Modules Reference 翻訳

go.dev

DeepLありがとう

導入

モジュールは、Go が依存関係を管理する方法です。

このドキュメントは Go のモジュールシステムに関する詳細なリファレンスマニュアルです。Go プロジェクトの作成方法については、Go コードの書き方 をご覧ください。モジュールの使用、プロジェクトのモジュールへの移行、その他のトピックについては、Go モジュールの使用で始まるブログシリーズをご覧ください。

モジュール、パッケージ、バージョン

モジュールはパッケージの集まりで、一緒にリリースされ、バージョン管理され、配布されます。モジュールはバージョン管理リポジトリから直接ダウンロードすることもできますし、モジュールプロキシサーバーからダウンロードすることもできます。

モジュールは、go.modファイルで宣言されたモジュールパスと、モジュールの依存関係の情報によって識別されます。モジュールルートディレクトリは、go.mod ファイルを含むディレクトリです。メインモジュールは、goコマンドが呼び出されるディレクトリを含むモジュールです。

モジュール内の各パッケージは、同じディレクトリにあるソースファイルをコンパイルしたものです。パッケージのパスは、モジュールのパスに、パッケージを含むサブディレクトリを加えたものです(モジュールルートからの相対パス)。例えば、"golang.org/x/net "というモジュールは、"html "というディレクトリにパッケージを含んでいます。そのパッケージのパスは "golang.org/x/net/html" です。

モジュールのパス

モジュールパスはモジュールの正規名で、モジュールのgo.modファイルのmoduleディレクティブで宣言されます。モジュールのパスは、そのモジュール内のパッケージのパスのプレフィックスになります。

モジュールパスは、そのモジュールが何をするのか、どこにあるのかを記述する必要があります。一般的に、モジュールパスはリポジトリルートパス、リポジトリ内のディレクトリ(通常は空)、メジャーバージョンサフィックス(メジャーバージョン2以上の場合のみ)で構成されています。

  • リポジトリルートパスは、モジュールが開発されたバージョン管理リポジトリのルートディレクトリに対応するモジュールパスの部分です。ほとんどのモジュールはそのリポジトリのルートディレクトリで定義されているので、これは通常パス全体となります。例えば、 golang.org/x/net は同名のモジュールのリポジトリルートパスになります。モジュールパスから派生した HTTP リクエストを使用して go コマンドがどのようにリポジトリを見つけるかについての情報は Finding a repository for a module path を参照してください。
  • モジュールがリポジトリのルートディレクトリに定義されていない場合、モジュールパスのうち、メジャーバージョンサフィックスを含まないディレクトリ名の部分がモジュールサブディレクトリになります。これは意味的なバージョンタグの接頭辞としても機能します。たとえば、モジュール golang.org/x/tools/gopls はルートパス golang.org/x/tools のリポジトリの gopls サブディレクトリにあるので、モジュールサブディレクトリ gopls を持っています。バージョンとコミットの対応付けと、リポジトリ内のモジュールディレクトリを参照してください。
  • もしモジュールがメジャーバージョン2以上でリリースされているなら、モジュールのパスは /v2 のようなメジャーバージョンのサフィックスで終わらなければなりません。これはサブディレクトリの名前の一部であってもなくてもかまいません。例えば、パスが golang.org/x/repo/sub/v2 のモジュールは、リポジトリ golang.org/x/repo の /sub または /sub/v2 サブディレクトリにある可能性があります。

もしあるモジュールが他のモジュールに依存する可能性がある場合、go コマンドがモジュールを見つけてダウンロードできるように、これらのルールに従わなければなりません。また、モジュールパスで許可される文字には、いくつかの字句の制限があります。

バージョン

バージョンは、モジュールの不変のスナップショットを識別し、リリースまたはプレリリースのいずれかになります。各バージョンは v という文字で始まり、その後にセマンティックバージョンというものが続きます。バージョンのフォーマット、解釈、比較の詳細については、セマンティックバージョン管理 2.0.0 を参照してください。

要約すると、セマンティックバージョンは3つの非負の整数(左からメジャー、マイナー、パッチバージョン)で構成され、ドットで区切られています。パッチバージョンの後には、オプションでハイフンで始まるプレリリース文字列を付けることができる。プレリリース文字列またはパッチバージョンの後に、プラスで始まるビルドメタデータ文字列を続けることができます。例えば、v0.0.0, v1.12.134, v8.0.5-pre, v2.0.9+meta は、有効なバージョンです。

バージョンの各部分は、そのバージョンが安定しているかどうか、以前のバージョンと互換性があるかどうかを示しています。

  • 例えば、パッケージが削除された後など、モジュールの公開インターフェースや文書化された機能に後方互換性のない変更が行われた場合、メジャーバージョンをインクリメントし、マイナーバージョンとパッチバージョンをゼロに設定しなければなりません。
  • マイナーバージョンは、後方互換性のある変更が行われた後、例えば新しい関数が追加された後、インクリメントされ、パッチバージョンはゼロに設定されなければならない。
  • バグフィックスや最適化など、モジュールのパブリックインターフェースに影響を与えない変更の後は、パッチバージョンをインクリメントしなければなりません。
  • pre-release サフィックスは、あるバージョンがプリリリースであることを示します。プレリリースバージョンは対応するリリースバージョンの前にソートされます。例えば、v1.2.3-pre は v1.2.3 よりも前のバージョンです。
  • ビルドメターデータの接尾辞は、バージョンを比較する目的では無視されます。ビルドメタデータを含むタグはバージョン管理リポジトリでは無視されますが、go.mod ファイルで指定されたバージョンではビルドメタデータが保持されます。接尾辞 +incompatible は、モジュールバージョンのメジャーバージョン 2 以降に移行する前にリリースされたバージョンを表します (「モジュール以外のリポジトリとの互換性」参照)。

メジャーバージョンが 0 であるか、サフィックスが pre-release である場合、そのバージョンは不安定であるとみなされます。不安定なバージョンは互換性要求の対象にはなりません。たとえば、v0.2.0 は v0.1.0 と互換性がなく、v1.5.0-beta は v1.5.0 と互換性がない可能性があります。

Goはバージョン管理システムにおいて、これらの規則に従わないタグ、ブランチ、リビジョンを使ってモジュールにアクセスすることがあります。しかし、メインモジュール内では、go コマンドがこの標準に従わないリビジョン名を自動的に正規のバージョンに変換してくれます。go コマンドは、この処理の一部として、ビルドのメタデータ接尾辞(+incompatible を除く)も削除します。この結果、バージョン管理システムからのリビジョン識別子(Git コミットハッシュなど)とタイムスタンプをエンコードしたプレリリースバージョンである擬似バージョンが生成されることがあります。例えば、 go get -d golang.org/x/net@daa7c041 コマンドはコミットハッシュ daa7c041 を擬似バージョン v0.0.0-20191109021931-daa7c04131f5 に変換します。 メインモジュール以外では正規バージョンが必要であり、もし go.mod ファイルに master など非正規バージョンがあれば、 go コマンドはエラーを報告することになります。

擬似バージョン

擬似バージョンは、バージョン管理リポジトリ内の特定のリビジョンに関する情報をエンコードした、特別な書式のプレリリースバージョンです。例えば、 v0.0.0-20191109021931-daa7c04131f5 は擬似バージョンです。

疑似バージョンは、意味的なバージョンタグが利用できないリビジョンを参照することができます。開発ブランチなどで、バージョンタグを作成する前にコミットをテストするために使用されるかもしれません。

各擬似バージョンは、3つの部分から成ります。

  • 基本バージョンプレフィックス (vX.0.0 または vX.Y.Z-0) は、そのリビジョンの前にある意味でのバージョンタグから取得するか、そのようなタグがない場合は vX.0.0 から取得します。
  • タイムスタンプ (yyyymmddhhmmss): リビジョンが作成されたUTC時間です。Git では、これはコミット時刻であり、作者時刻ではありません。
  • リビジョン識別子 (abcdefabcdef) は、コミットハッシュの12文字のプレフィックス、または Subversion では、ゼロパッドのリビジョン番号です。

各仮想バージョンは、ベースバージョンによって、3つの形式のうちの1つになります。これらの形式は、擬似バージョンは基本バージョンより高く、次のタグ付きバージョンより低く比較されることを保証します。

  • vX.0.0-yyyymmddhhmmss-abcdefabcdef は、既知のベースバージョンがない場合に使用されます。すべてのバージョンと同様に、メジャーバージョンXは、モジュールのメジャーバージョンサフィックスと一致しなければなりません。
  • vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef は、ベースバージョンが vX.Y.Z-pre のようなプレリリースバージョンであるときに使用されます。
  • vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef は、ベースバージョンが vX.Y.Z のようなリリースバージョンである場合に使用します。例えば、ベースバージョンが v1.2.3 である場合、疑似バージョンは v1.2.4-0.20191109021931-daa7c04131f5 となる可能性があります。

複数の擬似バージョンが、異なるベースバージョンを使用して同じコミットを参照することがあります。これは、擬似バージョンが書かれた後に下位バージョンがタグ付けされたときに自然に発生します。

これらの形式は、擬似バージョンに2つの有用な性質を与えています。

  • ベースバージョンがわかっている擬似バージョンは、そのバージョンよりも高く、他のプレリリースバージョンやそれ以降のバージョンよりも低くソートされます。
  • 同じベースバージョンのプレフィックスを持つ擬似バージョンは、時系列にソートされます。

go コマンドは、擬似バージョンと他のバージョンを比較する方法をモジュール作成者が制御できるように、また、擬似バージョンが実際にモジュールのコミット履歴の一部であるリビジョンを参照していることを確認するために、いくつかのチェックを行います。

  • 基本バージョンを指定する場合、疑似バージョンで記述されたリビジョンの先祖に当たる、対応するセマンティックバージョンタグが存在する必要があります。これにより、開発者が v1.999.999-99999999-daa7c04131f5 のように、すべてのタグ付きバージョンより高いバージョンを比較する疑似バージョンを使用して、最小限のバージョン選択を回避することができなくなります。
  • タイムスタンプはリビジョンのタイムスタンプと一致しなければなりません。これにより、攻撃者がモジュールプロキシに無制限に同一の擬似バージョンを流し込むことを防ぎます。また、モジュール消費者がバージョンの相対的な順序を変更することも防げます。
  • リビジョンはモジュールリポジトリのブランチまたはタグのいずれかの先祖でなければなりません。これにより、攻撃者が未承認の変更やプルリクエストを参照するのを防ぐことができます。

擬似バージョンは決して手で入力する必要はありません。多くのコマンドはコミットハッシュやブランチ名を受け取り、それを自動的に擬似バージョン(可能であればタグ付きバージョン)に変換します。たとえば

go get -d example.com/mod@master
go list -m -json example.com/mod@abcd1234

メジャーバージョンサフィックス

メジャーバージョン 2 以降、モジュールのパスにはメジャーバージョンに対応した /v2 のようなサフィックスが必要です。たとえば、あるモジュールが v1.0.0 のときに example.com/mod というパスを持っていた場合、バージョン v2.0.0 のときには example.com/mod/v2 というパスを持っていなければなりません。

メジャーバージョンのサフィックスは、インポート互換性ルールを実装しています。

古いパッケージと新しいパッケージが同じインポートパスを持っている場合、新しいパッケージは古いパッケージと後方互換性がなければなりません。

定義上、あるモジュールの新しいメジャーバージョンのパッケージは、前のメジャーバージョンの対応するパッケージと後方互換性がありません。その結果、v2以降では、パッケージは新しいインポートパスを必要とします。これは、モジュールパスにメジャーバージョンのサフィックスを追加することで実現されます。モジュールパスはモジュール内の各パッケージのインポートパスのプレフィックスなので、メジャーバージョンサフィックスをモジュールパスに追加することで、互換性のない各バージョンのインポートパスを明確にすることができます。

メジャーバージョンサフィックスはメジャーバージョンv0やv1では使用できません。v0は不安定で互換性の保証がないため、v0とv1の間でモジュールパスを変更する必要はありません。さらに、ほとんどのモジュールでは、v1 は最後の v0 バージョンと後方互換性があります。v1 のバージョンは、v0 と比較して互換性のない変更を示すのではなく、互換性を約束するものとして機能します。

特殊なケースとして、gopkg.in/ で始まるモジュールのパスには、v0 や v1 であっても必ずメジャーバージョンのサフィックスが必要です。 サフィックスはスラッシュではなく、ドットで始まる必要があります(たとえば gopkg.in/yaml.v2 など)。

メジャーバージョンサフィックスは、同じビルドに複数のメジャーバージョンのモジュールを共存させることができます。これは、ダイヤモンドの依存関係の問題で必要になることがあります。通常、あるモジュールが推移的依存関係によって2つの異なるバージョンで必要とされる場合、より高いバージョンが使用されます。しかし、2つのバージョンに互換性がない場合、どちらのバージョンもすべてのクライアントを満足させることはできません。非互換なバージョンはメジャーバージョン番号が異なるはずなので、メジャーバージョンのサフィックスによってモジュールパスも異なるはずである。接尾辞が異なるモジュールは別のモジュールとして扱われ、そのパッケージは、たとえモジュールルートから相対的に同じサブディレクトリにあるパッケージであっても、別個のものになるのです。

多くのGoプロジェクトはモジュールに移行する前に(おそらくモジュールが導入される前に)メジャーバージョンの接尾辞を使わずにv2以上のバージョンをリリースしています。これらのバージョンには +incompatible ビルドタグが付けられています (例: v2.0.0+incompatible). 詳しくは モジュール以外のリポジトリとの互換性 をご覧ください。

パッケージとモジュールの対応付け

go コマンドがパッケージパスを使用してパッケージをロードするとき、どのモジュールがそのパッケージを提供するかを決定する必要があります。

goコマンドはまず、パッケージパスのプレフィックスであるパスを持つモジュールをビルドリストから探します。例えば、パッケージ example.com/a/b がインポートされ、モジュール example.com/a がビルドリストにある場合、go コマンドは example.com/a がディレクトリ b にパッケージを含んでいるかどうかをチェックします。ビルドの制約は、この目的には適用されません。ビルドリストの中でちょうど1つのモジュールがパッケージを提供する場合、そのモジュールが使用されます。パッケージを提供するモジュールがない場合、または 2 つ以上のモジュールがパッケージを提供する場合、go コマンドはエラーを報告します。mod=mod フラグは、go コマンドに、足りないパッケージを提供する 新しいモジュールを見つけようと試み、go.mod と go.sum を更新するように指示します。go get と go mod tidy コマンドは自動的にこれを行ないます。

go コマンドがパッケージパスの新しいモジュールを探すとき、環境変数 GOPROXY を調べます。これはカンマで区切られたプロキシ URL のリストか、キーワード direct か off を指定します。proxy URL は go コマンドが GOPROXY プロトコルを使ってモジュールのプロキシに問い合わせることを意味します。GOPRIVATE と GONOPROXY 環境変数もこの動作を制御するために使用することができます。

GOPROXY リストの各エントリに対して、go コマンドはパッケージを提供する可能性のある各モジュールパスの最新バージョン (つまり、パッケージパスの各プレフィックス) を要求します。リクエストに成功した各モジュールパスについて、go コマンドは最新バージョンのモジュールをダウンロードし、そのモジュールにリクエストされたパッケージが含まれているかどうかをチェックします。一つ以上のモジュールが要求されたパッケージを含んでいる場合、最も長いパスを持つモジュールが使用されます。一つ以上のモジュールが見つかったが、要求されたパッケージを含んでいない場合、エラーが報告されます。モジュールが見つからない場合、go コマンドは GOPROXY リストの次のエントリを試行します。エントリが残っていない場合は、エラーが報告されます。

例えば、go コマンドが golang.org/x/net/html パッケージを提供するモジュールを探していて、GOPROXY が https://corp.example.com,https://proxy.golang.org に設定されているとします。goコマンドは以下のようなリクエストをすることがあります。

https://corp.example.com/ へ(並列で)。
    golang.org/x/net/htmlの最新版のリクエスト。
    golang.org/x/netの最新版のリクエスト。
    golang.org/xの最新版のリクエスト
    golang.orgの最新バージョンのリクエスト
https://corp.example.com/ へのすべてのリクエストが 404 か 410 で失敗した場合、 https://proxy.golang.org/ へ。
    golang.orgの最新バージョンのリクエスト/x/net/html
    最新版の golang.org/x/net をリクエストしてください。
    golang.org/xの最新バージョンのリクエスト
    golang.orgの最新版のリクエスト

適切なモジュールが見つかったら、go コマンドは新しいモジュールのパスとバージョンを新しい要件として、メインモジュールの go.mod ファイルに追加します。これにより、将来同じパッケージがロードされたときに、同じモジュールが同じバージョンで使用されることが保証されます。解決されたパッケージがメインモジュールのパッケージによってインポートされない場合、新しい要件は // 間接的なコメントを持つことになります。

go.mod ファイル

モジュールは、ルートディレクトリにあるgo.modというUTF-8エンコードされたテキストファイルで定義されます。go.mod ファイルは行指向です。各行は、キーワードと引数で構成される一つのディレクティブを保持します。例えば

module example.com/my/thing

go 1.12

require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]

先頭のキーワードは、囲碁のインポートのように、隣接する行から因数分解してブロックを作ることができる。

require (
    example.com/new/thing/v2 v2.3.4
    example.com/old/thing v1.2.3
)

go.modファイルは、人間が読むことができ、機械が書き込めるように設計されています。go コマンドは go.mod ファイルを変更するいくつかのサブコマンドを提供します。例えば、go get は特定の依存関係をアップグレードまたはダウングレードすることができます。モジュールグラフを読み込むコマンドは、必要なときに自動的にgo.modを更新します。golang.org/x/mod/modfile パッケージは Go プログラムがプログラム的に同じ変更をするために使うことができます。

go.mod ファイルはメインモジュールと、ローカルファイルパスで指定されたすべての代替モジュールに必要です。しかし、明示的な go.mod ファイルがないモジュールでも、依存関係として要求されたり、モジュールパスとバージョンで指定された代替モジュールとして使用されることがあります。

字句の要素

go.modファイルが解析されるとき、その内容は一連のトークンに分割されます。トークンには、ホワイトスペース、コメント、句読点、キーワード、識別子、文字列のいくつかの種類があります。

ホワイトスペースは、スペース(U+0020)、タブ(U+0009)、キャリッジリターン(U+000D)、ニューライン(U+000A)で構成されています。改行以外の空白文字は、結合されるはずのトークンを分離する以外の効果はない。改行は重要なトークンである。

コメントは // で始まり、行末まで続きます。/ /コメントは使用できません。

句読点には、(, ), =>があります。

キーワードは、go.mod ファイル内の異なる種類のディレクティブを区別します。使用できるキーワードは、module、go、require、replace、exclude、retractです。

識別子とは、モジュールパスやセマンティックバージョンなど、空白でない文字の並びのことです。

文字列は、引用符で囲まれた文字列です。文字列には、引用符("、U+0022)で始まり、引用符で終わる解釈文字列と、グレイヴ・アクセント(`、U+0060)で始まり、グレイヴ・アクセントで終わる生の文字列の2種類があります。解釈文字列は、バックスラッシュ( \, U+005C)の後に別の文字が続くエスケープシーケンスを含むことができま す。エスケープされた引用符( \") は、解釈される文字列を終了させない。解釈文字列の引用符なしの値は、引用符の間の文字列で、各エスケープシーケンスはバックス ラッシュに続く文字に置き換えられます(例えば、" \" は " に置き換えられ、" \n" は n に置き換えられます)。これに対して、生の文字列の引用符なしの値は、単にグレイヴ・アクセント間の文字列である。

go.modの文法では識別子と文字列は交換可能です。

モジュールのパスとバージョン

go.modファイル内のほとんどの識別子と文字列は、モジュールパスかバージョンです。

モジュールパスは、以下の要件を満たす必要があります。

  • パスはスラッシュ (/, U+002F) で区切られた1つ以上のパス要素で構成されていなければなりません。スラッシュで始まったり終わったりしてはいけません。
  • 各パスの要素は、ASCII文字、ASCII数字、およびASCII句読点(-、.、_、~)からなる空でない文字列であること。
  • パス要素はドット(.,U+002E)で始まったり終わったりしてはならない。
  • Windows では、最初のドットまでの要素は、大文字小文字に関係なく予約されたファイル名であってはなりません(CON、com1、NuL など)。
  • 最初のドットまでの要素は、チルダに続く1桁以上の数字で終わってはいけません(EXAMPL~1.COMのように)。

モジュールパスが require 命令の中に現れて置換されない場合、またはモジュールパスが replace 命令の右側に現れる場合、go コマンドはそのパスでモジュールをダウンロードする必要があり、いくつかの追加の要件を満たす必要があります。

  • 最初のパス要素 (最初のスラッシュまで) は、慣習的にドメイン名で、小文字の ASCII 文字、ASCII 数字、ドット (., U+002E) およびダッシュ (-, U+002D) のみでなければなりません。
  • 最終的なパス要素が /vN という形式で、 N が数字 (ASCII 数字とドット) の場合、 N は先頭が 0 であってはならず、 /v1 であってはならず、 またドットを含んではなりません。
    • gopkg.in/ で始まるパスの場合、この要件は gopkg.in サービスの規約に従うという要件に置き換わります。

go.mod ファイル内のバージョンは、正規版と非正規版があります。

正規のバージョンは v という文字で始まり、その後に Semantic Versioning 2.0.0 仕様に従ったバージョンが続きます。詳しくはバージョンをご覧ください。

他のほとんどの識別子や文字列は非正規バージョンとして使用できますが、ファイルシステムリポジトリ、モジュールプロキシでの問題を避けるためにいくつかの制約があります。非正規バージョンはメインモジュールの go.mod ファイルでのみ許可されています。go コマンドは go.mod ファイルを自動的に更新する際に、 非正規バージョンを同等の正規バージョンに置き換えようとします。

モジュールのパスがバージョンと関連付けられている場所 (require, replace, exclude ディレクティブなど) では、最後のパス要素がバージョンと一致していなければなりません。メジャーバージョン接尾辞 を参照してください。

文法

go.modの文法は、EBNF(Extended Backus-Naur Form)を使って以下に指定されています。EBNFの詳細については、Go言語仕様の記法セクションを参照してください。

GoMod = { Directive } .
Directive = ModuleDirective |
            GoDirective |
            RequireDirective |
            ExcludeDirective |
            ReplaceDirective |
            RetractDirective .

改行、識別子、文字列は、それぞれ newline、ident、string で表す。

モジュールのパスとバージョンは、ModulePath と Version で表す。

ModulePath = ident | string . /* see restrictions above */
Version = ident | string .    /* see restrictions above */

モジュールディレクティブ

module ディレクティブは、メインモジュールのパスを定義します。go.modファイルには、正確に1つのmoduleディレクティブが含まれていなければなりません。

ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .

module golang.org/x/net

非推奨

Deprecated: (大文字小文字を区別します) という文字列を段落の最初に含むコメントブロックの中で、あるモジュールを非推奨としてマークすることができます。非推奨のメッセージはコロンの後ろから始まり、段落の終わりまで続きます。コメントは module ディレクティブの直前でも、同じ行の直後でもかまいません。

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

Go 1.17 以降、go list -m -u はビルドリストにあるすべての deprecated モジュールに関する情報をチェックします。go get はコマンドラインから指定したパッケージをビルドするのに必要な deprecated モジュールをチェックします。

go コマンドがモジュールの deprecation 情報を取得するとき、retraction や exclusion を考慮せずに @latest version クエリにマッチするバージョンから go.mod ファイルをロードします。go コマンドは同じ go.mod ファイルから非推奨情報をロードします。

モジュールを非推奨にするために、作者は // Deprecated: というコメントを追加し、新しいリリースにタグを付けることができます。作者は、より高いリリースで deprecation メッセージを変更または削除することができます。

非推奨は、そのモジュールのすべてのマイナーバージョンに適用されます。v2 よりも高いメジャーバージョンは、メジャーバージョンの接尾辞がモジュールのパスを区別するため、この目的では別のモジュールとみなされます。

Deprecation メッセージは、モジュールがサポートされなくなったことをユーザーに知らせ、最新のメジャーバージョンへの移行手順などを提供するためのものです。個々のマイナーバージョンやパッチバージョンを非推奨にすることはできません。そのような場合は retract の方がより適切かもしれません。

go directive

go ディレクティブは、モジュールがあるバージョンの Go のセマンティクスを想定して書かれていることを示します。バージョンは有効な Go のリリースバージョンでなければなりません。正の整数の後にドットと 負でない整数が続きます (例えば 1.9, 1.14 など)。

goディレクティブは、もともとGo言語の後方互換性のない変更(Go 2 transitionを参照)をサポートするためのものでした。モジュールが導入されて以来、互換性のない言語の変更はありませんでしたが、 go ディレクティブは新しい言語機能の使用に影響を与えます。

  • モジュール内のパッケージでは、コンパイラは go ディレクティブで指定されたバージョン以降に導入された言語機能の使用を拒否します。たとえば、モジュールが go 1.12 というディレクティブを持っている場合、そのパッケージは 1_000_000 のような数値リテラルを使用することができません。
  • 古いバージョンの Go でビルドしてコンパイルエラーが発生した場合、そのモジュールが新しい Go バージョン用に書かれたものであることがエラーとして表示されます。たとえば、あるモジュールが go 1.13 で、あるパッケージが数値リテラル 1_000_000 を使っているとします。そのパッケージが Go 1.12 でビルドされた場合、コンパイラはコードが Go 1.13 用に書かれたものであることを注意します。

さらに、go コマンドは go ディレクティブで指定されたバージョンに基づいて動作を変えます。これには次のような効果があります。

  • go 1.14 以降では、自動ベンダリングが有効になる場合があります。vendor/modules.txt ファイルが存在し、go.mod と整合性が取れていれば、 -mod=vendor フラグを明示的に使用する必要はありません。
  • go 1.16 以降では、all package パターンはメインモジュール内の packages および test によって transitively import されたパッケージのみにマッチします。これはモジュールが導入されて以来、go mod vendor によって保持されているパッケージのセットと同じものです。より低いバージョンでは、all はメインモジュールのパッケージによってインポートされたパッケージのテスト、それらのパッケージのテスト、なども含みます。
  • go 1.17 以降で。
    • go.mod ファイルは、メインモジュールのパッケージやテストによって過渡的にインポートされるパッケージを提供する、各モジュールに対する明示的な require ディレクティブを含んでいます。(go 1.16 以下では、間接的な依存関係は、最小限のバージョン選択で他のバージョンが選択される場合にのみ含まれます)。この追加情報により、モジュールグラフの刈り込みと遅延モジュールの読み込みが可能になります。
    • 以前のバージョンの go よりも多くの // 間接依存関係が存在する可能性があるため、間接依存関係は go.mod ファイル内の別のブロックに記録されます。
    • go mod ベンダーは、ベンダーの依存関係のための go.mod と go.sum ファイルを省略します。(これにより、 vendor のサブディレクトリ内で go コマンドを実行したときに、 正しいメインモジュールを識別することができます)。
    • go mod vendor は vendor/modules.txt に各依存関係の go.mod ファイルから go バージョンを記録します。

go.mod ファイルは、最大でひとつの go ディレクティブを含むことができます。ほとんどのコマンドは、go ディレクティブが存在しない場合、現在の Go バージョンで go ディレクティブを追加します。

Go 1.17 リリースでは、go ディレクティブがない場合、go 1.16 と見なされます。

GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

go 1.14

要求ディレクティブ

require ディレクティブは、与えられたモジュールの依存関係の中で、 必要最低限のバージョンを宣言します。各要求モジュールのバージョンについて、go コマンドはそのバージョンの go.mod ファイルをロードし、そのファイルから要件を取り込みます。すべての要件がロードされると、go コマンドは最小バージョン選択 (MVS) を使用してそれらを解決し、ビルドリストを生成します。

go コマンドは、いくつかの要件に対して // 間接的なコメントを自動的に追加します。間接コメントは、要求されたモジュールのどのパッケージもメインモジュールのどのパッケージからも直接インポートされていないことを示す。

go ディレクティブが go 1.16 以下を指定している場合、選択されたモジュールの バージョンが、メインモジュールの他の依存関係によって既に暗示されているもの よりも高いとき、go コマンドは間接的な要件を追加します。これは、明示的なアップグレード (go get -u ./...) や、以前に要件を課していた他の依存関係の削除 (go mod tidy) 、あるいは、それ自身の go.mod ファイルに対応する要件のないパッケージをインポートする依存関係 (go.mod ファイルがまったくない依存関係など) によって発生する可能性があります。

go 1.17 以降では、go コマンドは、メインモジュールのパッケージまたはテストによって (間接的にでも) インポートされたパッケージ、あるいは go get への引数として渡されたパッケージを提供する各モジュールに対して間接的な要件を追加しています。これらの包括的な要件により、モジュールグラフの刈り込みと遅延モジュールのロードが可能になります。

RequireDirective = "require" ( RequireSpec | "(" newline { RequireSpec } ")" newline ) .
RequireSpec = ModulePath Version newline .

require golang.org/x/net v1.2.3

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

排除ディレクティブ

exclude ディレクティブは、モジュールのバージョンが go コマンドによってロードされるのを防ぎます。

Go 1.16 以降、go.mod ファイルの require ディレクティブで参照されるバージョンが、メインモジュールの go.mod ファイルの exclude ディレクティブで除外されている場合、その要件は無視されます。これにより、go get や go mod tidy などのコマンドが、適切であれば // 間接的なコメントとともに、より高いバージョンの新しい要件を go.mod に追加することがあります。

Go 1.16 以前では、除外されたバージョンが require ディレクティブによって参照された場合、go コマンドはモジュールの利用可能なバージョンをリストアップし (go list -m -versions で表示)、除外されていない次の高いバージョンを代わりにロードしていました。これは、次の上位バージョンが時間の経過とともに変化する可能性があるため、非決定的なバージョン選択となる可能性があります。この目的のために、リリースバージョンとプレリリースバージョンの両方が考慮されましたが、擬似バージョンは考慮されませんでした。より高いバージョンがない場合、go コマンドはエラーを報告しました。

exclude ディレクティブはメインモジュールの go.mod ファイルにのみ適用され、他のモジュールでは無視されます。詳しくは 最小限のバージョン選択 をご覧ください。

ExcludeDirective = "exclude" ( ExcludeSpec | "(" newline { ExcludeSpec } ")" newline ) .
ExcludeSpec = ModulePath Version newline .
exclude golang.org/x/net v1.2.3

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

置換ディレクティブ

置換ディレクティブは、あるモジュールの特定のバージョン、またはすべての バージョンの内容を、他の場所にある内容と置き換えます。置き換えには、別のモジュールのパスとバージョン、あるいは プラットフォーム固有のファイルパスのいずれかを指定することができます。

矢印の左側にバージョンがある場合(=>)、その特定のバージョンのみが置き換えられ、他のバージョンは通常通りアクセスされます。左側のバージョンが省略された場合、モジュールのすべてのバージョンが置き換えられます。

矢印の右側のパスが絶対パスまたは相対パス (先頭が ./ または ../) の場合、置換後のモジュールのルートディレクトリへのローカルファイルパスとして解釈され、このディレクトリには go.mod ファイルが含まれていなければなりません。この場合、置換後のバージョンは省略しなければなりません。

右辺のパスがローカルパスでない場合、有効なモジュールパスでなければなりません。この場合、バージョンは必須です。同じモジュールのバージョンがビルドリストにも表示されてはならない。

置き換えがローカルパスかモジュールパスのどちらで指定されたかに関わらず、 置き換えモジュールが go.mod ファイルを持っている場合、そのモジュールディレクティブは 置換するモジュールパスと一致しなければなりません。

置換ディレクティブはメインモジュールの go.mod ファイルにのみ適用され、他のモジュールでは無視されます。詳しくは 最小限のバージョン選択 を参照してください。

複数のメインモジュールがある場合、すべてのメインモジュールの go.mod ファイルが適用されます。メインモジュール間で競合するreplace指示は禁止されており、go.workファイルのreplaceで削除するか上書きする必要があります。

置換ディレクティブだけでは、モジュールはモジュールグラフに追加されないことに 注意してください。メインモジュールの go.mod ファイルか依存モジュールの go.mod ファイルに、置き換えられたモジュールのバージョンを参照する require ディレクティブも必要です。replace ディレクティブは、左側のモジュールのバージョンが要求されていない場合は 効果を持ちません。

ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */

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
)

retract directive

retract ディレクティブは、go.mod で定義されたモジュールのバージョンや範囲に 依存してはいけないということを示します。retract ディレクティブは、あるバージョンが時期尚早に公開された場合や、 そのバージョンが公開された後に深刻な問題が発見された場合などに有用です。retract されたバージョンは、それに依存するビルドが壊れないように、バージョン管理リポジトリやモジュールプロキシで利用可能なままであるべきです。retract という言葉は学術文献から借用したもので、撤回された研究論文はまだ利用可能ですが、問題があり、将来の研究の基礎となるべきものではありません。

あるモジュールのバージョンが撤回されると、ユーザーは go get や go mod tidy などのコマンドを使って自動的にそのバージョンにアップグレードすることはありません。撤回されたバージョンに依存するビルドは引き続き動作しますが、ユーザが go list -m -u で更新をチェックしたり、go get で関連モジュールを更新したりすると、撤回されたことが通知されるでしょう。

バージョンを撤回するには、モジュールの作者が go.mod に retract ディレクティブを追加し、そのディレクティブを含む新しいバージョンを公開する必要があります。新しいバージョンは他のリリースやプレリリースバージョンより上位でなければなりません。つまり、retraction が考慮される前に @latest version クエリが新しいバージョンに解決されなければなりません。go コマンドは、go list -m -retracted $modpath@latest ($modpath はモジュールパス) で示されるバージョンから、retraction をロードして適用します。

引かれたバージョンは、-retracted フラグが使われていない限り、 go list -m -versions によって表示されるバージョンリストからは隠されます。撤回されたバージョンは @>=v1.2.3 や @latest のようなバージョン問い合わせを解決する際に除外されます。

retract を含むバージョンは、それ自体が後退する可能性があります。もし、あるモジュールの最も高いリリースやプレリリースバージョンがそれ自身を撤回した場合、@latest クエリは撤回されたバージョンを除外した後、より低いバージョンに解決されます。

例として、モジュール example.com/m の作者が誤ってバージョン v1.0.0 を公開した場合を考えてみましょう。ユーザが v1.0.0 にアップグレードするのを防ぐために、作者は go.mod に retract ディレクティブを二つ追加し、 v1.0.1 に retractions のタグを付けることができます。

retract (
    v1.0.0 // 誤って公開されました。
    v1.0.1 // 取り消しのみを含む。
)

ユーザーが go get example.com/m@latest を実行すると、go コマンドは v1.0.1 から retractions を読み込み、これが現在の最高バージョンとなります。v1.0.0 と v1.0.1 の両方が retract されたので、go コマンドは次の最も高いバージョン、おそらく v0.9.5 にアップグレード (またはダウングレード!) するでしょう。

retract ディレクティブは、(v1.0.0 のような) 単一のバージョンか、[ と ] で区切られた 上限と下限のある閉じたバージョン間隔 ([v1.1.0, v1.2.0] など) で記述することができます。単一のバージョンは、上限と下限が同じである区間と同じです。他のディレクティブと同様に、複数の retract ディレクティブは、行末を( )で区切られたブロックにまとめることができます。

各 retract ディレクティブは、retract の根拠を説明するコメントを持つべきですが、これは必須ではありません。go コマンドは、retract されたバージョンに関する警告や go リストの出力に、根拠となるコメントを表示することがあります。理由付けのコメントは、retract ディレクティブのすぐ上 (間に空白行を入れずに) か、同じ行の後に書くことができます。コメントがブロックの上に表示された場合、ブロック内の、それ自身のコメントを持っていないすべてのretractディレクティブに適用されます。引込みコメントは、複数行に渡ることもあります。

RetractDirective = "retract" ( RetractSpec | "(" newline { RetractSpec } ")" newline ) .
RetractSpec = ( Version | "[" Version "," Version "]" ) newline .

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

retract指示文は、Go 1.16で追加されました。Go 1.15 以前のバージョンでは、メインモジュールの go.mod ファイルに retract ディレクティブが書かれているとエラーになり、依存関係にある go.mod ファイルにある retract ディレクティブは無視されます。

自動更新

ほとんどのコマンドは、go.modに情報が欠けていたり、現実を正確に反映していない場合、エラーを報告します。go get と go mod tidy コマンドはこれらの問題のほとんどを修正するために使用されます。さらに、ほとんどのモジュール認識コマンド (go build, go test など) で -mod=mod フラグを使うと、go.mod と go.sum の問題を自動的に修正するよう go コマンドに指示することができます。

たとえば、次のような go.mod ファイルを考えてみましょう。

module example.com/M

go 1.16

require (
    example.com/A v1
    example.com/B v1.0.0
    example.com/C v1.0.0
    example.com/D v1.2.3
    example.com/E dev
)

exclude example.com/D v1.2.3

mod=mod で起動される更新は、正規でないバージョン識別子を正規の semver 形式に書き換えるため、example.com/A の v1 は v1.0.0 になり、example.com/E の dev は dev ブランチの最新コミットの疑似バージョン (v0.0.0-20180523231146-b3f5c0f6e5f1 など) になる。

この更新では、除外された要件が修正され、除外された example.com/D v1.2.3 の要件は、次の利用可能なバージョンの example.com/D (v1.2.4 または v1.3.0) を使用するように更新されています。

この更新により、冗長な要件や誤解を招くような要件が削除されます。たとえば、example.com/A v1.0.0 自体が example.com/B v1.2.0 と example.com/C v1.0.0 を必要とする場合、go.mod の example.com/B v1.0.0 の要件は (example.com/A の v1.2.0 の必要性に取って代わられた) 誤解を招く恐れがあり、また example.com/C v1.0.0 の要件は (example.com/A の同じバージョンの必要性に示唆されて) 冗長になるのでどちらも削除されることになるでしょう。メインモジュールに example.com/B や example.com/C のパッケージを直接インポートするパッケージがある場合、要件は維持されますが、実際に使用されているバージョンに更新されます。

最後に、アップデートは go.mod を正規の書式で再フォーマットし、将来の機械的な変更が最小限の差分となるようにします。フォーマットの変更のみが必要な場合、goコマンドはgo.modを更新しません。

モジュールグラフは import 文の意味を定義するので、パッケージを読み込むコマンドはすべて go.mod も使用し、したがって go build, go get, go install, go list, go test, go mod tidy を含めて更新することができます。

Go 1.15 以下では、デフォルトで -mod=mod フラグが有効で、アップデートは自動的に実行されました。Go 1.16 以降、go コマンドは -mod=readonly が代わりに設定されているかのように動作します: go.mod への変更が必要な場合、go コマンドはエラーを報告し、修正を提案します。

最小限のバージョン選択 (MVS)

Go は最小バージョン選択 (MVS) と呼ばれるアルゴリズムを使って、パッケージ構築時に使用するモジュールのバージョン セットを選択します。MVSについては、Russ Cox著のMinimal Version Selectionで詳しく説明されています。

概念的には、MVS は go.mod ファイルで指定されたモジュールの有向グラフ上で動作します。グラフの各頂点は、モジュールのバージョンを表します。グラフの各頂点はモジュールのバージョンを表し、各辺は依存関係にあるモジュールの必要最小限のバージョンを表し、require指示文により指定されます。このグラフは、メインモジュールの go.mod ファイル中の exclude および replace ディレクティブ、および go.work ファイル中の replace ディレクティブによって変更することができます。

MVSは、ビルドに使用されたモジュールのバージョン一覧を出力します。

MVSはメインモジュール(グラフの特別な頂点で、バージョンを持たない)から開始し、各モジュールの最も必要なバージョンを追跡しながらグラフを横断する。巡回が終わった時点で、最も要求の高いバージョンがビルドリストを構成する。これは、すべての要求を満たす最小のバージョンである。

ビルドリストは go list -m all コマンドで確認することができます。他の依存関係管理システムとは異なり、ビルドリストは「ロック」ファイルには保存されません。MVS は決定論的であり、依存関係の新しいバージョンがリリースされてもビルドリストは変更されないので、すべてのモジュールを認識するコマンドの最初に計算するために MVS が使用されます。

下図の例で考えてみましょう。メインモジュールはモジュールAのバージョン1.2以上、モジュールBのバージョン1.2以上を必要とします。A 1.2 と B 1.2 は、それぞれ C 1.3 と C 1.4 を必要とします。C 1.3 と C 1.4 はともに D 1.2 を必要とする。

https://go.dev/doc/mvs/buildlist.svg

MVSは青色で表示されているモジュールのバージョンごとにgo.modファイルを訪問し、ロードします。グラフのトラバースの最後に、MVSは太字のバージョンを含むビルドリストを返します。A 1.2、B 1.2、C 1.4、そしてD 1.2です。BとDにはより高いバージョンがありますが、MVSはそれらを必要としないので、選択しません。

置き換え

メインモジュールのgo.modファイルやワークスペースのgo.workファイルのreplace指示により、モジュールの内容(go.modファイルを含む)の置き換えが可能です。replace ディレクティブは、特定のバージョンのモジュールに適用することもできますし、すべてのバージョンのモジュールに適用することもできます。

置換されたモジュールは、置換されたバージョンとは異なる依存関係を持つ可能性があるため、置換によってモジュールグラフが変化します。

R は D 1.2 ではなく D 1.3 に依存しているので、MVS は A 1.2, B 1.2, C 1.4 (R に置換), および D 1.3 を含むビルドリストを返します。

https://go.dev/doc/mvs/replace.svg

除外

モジュールは、メインモジュールの go.mod ファイルにある exclude ディレクティブを使用して、特定のバージョンで除外することもできます。

除外はモジュールグラフも変更します。あるバージョンが除外されると、モジュールグラフから削除され、そのバージョンに対する要求は、次の上位バージョンにリダイレクトされます。

以下の例を見てください。C 1.3が除外されています。MVSは、A 1.2がC 1.3ではなく、C 1.4(次の上位バージョン)を要求しているかのように動作します。

https://go.dev/doc/mvs/exclude.svg

アップグレード

go get コマンドは、モジュール群のアップグレードに使用することができます。アップグレードを行うには、MVS を実行する前に go コマンドでモジュールグラフを変更し、訪問したバージョンからアップグレードされたバージョンへのエッジを追加します。

以下の例で考えてみましょう。モジュール B は 1.2 から 1.3 へ、C は 1.3 から 1.4 へ、D は 1.2 から 1.3 へとアップグレードされるかもしれません。

https://go.dev/doc/mvs/upgrade.svg

アップグレード (およびダウングレード) は、間接的な依存関係を追加または削除することがあります。この場合、E 1.1 と F 1.1 は B 1.3 に必要とされるため、アップグレード後のビルドリストに表示されます。

アップグレードを維持するために、go コマンドは go.mod の requirements を更新します。これは、B の要件をバージョン 1.3 に変更します。また、C 1.4 と D 1.3 の要件を // 間接的なコメント付きで追加します。

ダウングレード

go get コマンドは、モジュール群をダウングレードするのにも使われます。ダウングレードを行うために、go コマンドはダウングレードされたバージョンより上のバージョンを削除して、モジュールグラフを変更します。また、削除されたバージョンに依存している他のモジュールのバージョンも削除されます。なぜなら、そのモジュールの依存関係がダウングレードされたバージョンと互換性がない可能性があるからです。もし、メインモジュールがダウングレードによって削除されたバージョンのモジュールを必要とするなら、その要求は削除されていない以前のバージョンに変更されます。以前のバージョンがない場合、要件は削除されます。

以下の例で考えてみよう。C 1.4 で問題が見つかったので、C 1.3 にダウングレードしたとします。C 1.4 はモジュールグラフから削除されます。B 1.2 も、C 1.4 以上を要求しているので、削除されます。B のメインモジュールの要件は 1.1 に変更されます。

https://go.dev/doc/mvs/downgrade.svg

go get は、引数の後に @none というサフィックスを付けて、依存関係を完全に取り除くこともできます。これはダウングレードと同じように動作します。指定されたモジュールのすべてのバージョンが、モジュールグラフから削除されます。

モジュールグラフの刈り込み

メインモジュールが go 1.17 以上の場合、最小バージョンの選択に使われるモジュールグラフは、そのモジュールのバージョンが go 1.16 以下の他の依存関係によって (transitively) も必要とされない限り、独自の go.mod ファイルで go 1.17 以上と指定された各モジュール依存関係の即時要件のみが含まれます (go 1.16 と go 1.17 の推移的依存関係は、モジュールグラフに含まれません)。(go 1.17 の依存関係の推移的な依存関係は、モジュールグラフから切り捨てられます)。

go 1.17 の go.mod ファイルは、そのモジュールのパッケージやテストをビルドするために必要なすべての依存関係のための require ディレクティブを含んでいるので、刈り込まれたモジュールグラフは、メインモジュールによって明示的に要求される依存関係のパッケージをビルドしたりテストしたりするために必要なすべての依存関係を含んでいます。あるモジュールのパッケージのビルドやテストに必要でないモジュールは、そのパッケージのランタイム動作に影響を与えることができないので、モジュールグラフから刈り込まれた依存関係は、それ以外の無関係なモジュール間の干渉を引き起こすだけでしょう。

選択されたバージョンは既知で明確に定義されており、パッケージはそれらのモジュールからロードできます (たとえば、他のモジュールからロードされたテストの推移的依存関係として)。しかし、go コマンドはこれらのモジュールのどの依存性が満たされているかを簡単に特定できないため、 go build と go test への引数に、要件が刈り込まれたモジュールからのパッケージを含めることができません。

Go 1.16 以前のバージョンではモジュールグラフのプルーニングがサポートされていないため、go 1.16 以下を指定する各モジュールには、依存関係の完全な推移的クロージャ (go 1.17 の推移的依存関係を含む) がまだ含まれています (go 1.16 以下では、go.mod ファイルには直接的な依存関係のみが含まれるため、はるかに大きなグラフを読み込む必要があります)。(go 1.16 以下では、go.mod ファイルには直接依存関係のみが含まれるため、すべての間接依存関係が含まれるように、より大きなグラフがロードされなければなりません)。

デフォルトでモジュールのために go mod tidy によって記録される go.sum ファイルは、その go ディレクティブで指定されたバージョンの一つ下のバージョンで必要なチェックサムを含みます。つまり、go 1.17 モジュールは、Go 1.16 によってロードされた完全なモジュールグラフに必要なチェックサムを含みますが、go 1.18 モジュールは、Go 1.17 によってロードされた刈り込まれたモジュールグラフに必要なチェックサムのみを含むことになります。compat フラグを使用すると、デフォルトのバージョンをオーバーライドできます (たとえば、go 1.17 モジュールで go.sum ファイルをより積極的にプルーンする場合など)。

詳しくは設計ドキュメントをご覧ください。

遅延モジュールロード

モジュールグラフのプルーニングのために追加されたより包括的な要件は、モジュール内で作業する際の別の最適化も可能にします。メインモジュールが go 1.17 以上の場合、go コマンドはモジュールグラフ全体の読み込みを必要なときまで (そして必要でないときも) 避けるようになりました。その代わり、メインモジュールの go.mod ファイルのみをロードし、 その後、それらの要件のみを使用してビルドするパッケージのロードを 試みます。インポートするパッケージ (たとえば、メインモジュール外のパッケージのテストの依存関係) がこれらの要件の中で見つからない場合、残りのモジュールグラフがオンデマンドでロードされます。

モジュールグラフをロードせずにインポートされたパッケージがすべて見つかった場合、go コマンドはそれらのパッケージを含むモジュールだけの go.mod ファイルをロードし、それらの要件がメインモジュールの要件とチェックされて、それらがローカルに一貫していることが保証されます (矛盾は、メインモジュールの要件のために発生します。(バージョン管理によるマージ、手作業による編集、ローカルファイルシステムのパスを使って置換されたモジュールの変更などにより、不整合が生じる可能性があります)。

ワークスペース

ワークスペースとは、最小版数選択(MVS)を実行する際にメインモジュールとして使用するディスク上のモジュールの集合体です。

ワークスペースは、ワークスペース内の各モジュールのモジュール・ディレクトリへの相対パスを指定した go.work ファイルで宣言することができる。go.work ファイルが存在しない場合、ワークスペースはカレントディレクトリを含む単一のモジュールで構成されます。

go mod init, go mod why, go mod edit, go mod tidy, go mod vendor, and go get は常に一つのメインモジュールに対して動作します。

コマンドは、最初に環境変数GOWORKを調べることによって、それがワークスペースのコンテキストにあるかどうかを決定します。GOWORK が off に設定されている場合、コマンドは単一モジュールのコンテキストにあります。GOWORK が空であるか、提供されていない場合、コマンドは現在の作業ディレクトリと、それに続く親ディレクトリを検索して、ファイル go.work を探します。もしファイルが見つかれば、コマンドはそれが定義するワークスペース内で動作します。GOWORK が .work で終わる既存のファイルへのパスを指定した場合、ワークスペース・モードが有効になります。それ以外の値はエラーになります。go env GOWORK コマンドは、go コマンドがどの go.work ファイルを使用しているかを判断することができます。

go.workファイル

ワークスペースは go.work という名前の UTF-8エンコードされたテキストファイルで定義されます。go.workファイルは行指向です。各行は、キーワードと引数で構成される1つのディレクティブを保持します。例えば

go 1.18

use ./my/first/thing
use ./my/second/thing

replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5

go.modファイルと同様に、先頭のキーワードを隣接する行から因数分解してブロックを作成することができます。

use (
    ./my/first/thing
    ./my/second/thing

go コマンドは go.work ファイルを操作するためのいくつかのサブコマンドを提供します。 go work init は新しい go.work ファイルを作成します。go work use は go.work ファイルにモジュールディレクトリを追加します。golang.org/x/mod/modfile パッケージは、Go プログラムがプログラム的に同じ変更を行うために使用することができます。

字句の要素

go.work ファイルの語彙要素は go.mod ファイルと全く同じように定義されます。

文法

go.workの文法は、拡張バッカスナウル形式(EBNF)を使用して、以下のように指定されます。EBNFの構文の詳細については、Go言語仕様の記法セクションを参照してください。

GoWork = { Directive } .
Directive = GoDirective |
            UseDirective |
            ReplaceDirective .

改行、識別子、文字列は、それぞれ newline、ident、string で表す。

モジュールのパスとバージョンは、ModulePathとVersionで表します。モジュールのパスとバージョンは、go.modファイルと全く同じ方法で指定します。

ModulePath = ident | string . /* see restrictions above */
Version = ident | string .    /* see restrictions above */

goディレクティブ

goディレクティブは、有効なgo.workファイルの中で必要とされます。version は有効な Go のリリースバージョンでなければなりません。正の整数の後にドットと 負でない整数が続きます (たとえば 1.18, 1.19 など)。

go ディレクティブは、go.work ファイルが動作するように意図されている go ツールチェインバージョンを指定します。go.workファイルの形式に変更が加えられた場合、ツールチェーンの将来のバージョンは、その指示されたバージョンに従ってファイルを解釈します。

go.workファイルには、最大で1つのgoディレクティブを含めることができます。

GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

go 1.18

use ディレクティブ

useは、ディスク上のモジュールをワークスペースのメインモジュールのセットに追加します。引数は、モジュールの go.mod ファイルを含むディレクトリへの相対パスです。use ディレクティブは、引数ディレクトリのサブディレクトリに含まれる モジュールを追加するわけではありません。これらのモジュールは、その go.mod ファイルを含むディレクトリに、別の use ディレクティブで追加されるかもしれません。

UseDirective = "use" ( UseSpec | "(" newline { UseSpec } ")" newline ) .
UseSpec = FilePath newline .
FilePath = /* platform-specific relative or absolute file path */

use ./mymod  // example.com/mymod

use (
    ../othermod
    ./subdir/thirdmod
)

置換ディレクティブ

go.mod ファイルの replace ディレクティブと同様に、go.work ファイルの replace ディレクティブは、特定のバージョンのモジュール、またはすべてのバージョンの モジュールの内容を、他の場所にある内容と置き換えます。go.work のワイルドカードによる置換は、go.mod ファイルの バージョンによる置換を上書きします。

go.work ファイルの replace ディレクティブは、ワークスペースモジュールの同じモジュールやモジュールの バージョンの replacement を上書きします。

ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */

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
)

非モジュールリポジトリとの互換性

GOPATH からモジュールへのスムーズな移行を保証するために、go コマンドは go.mod ファイルを追加することで、モジュールに移行していないリポジトリからモジュールを意識したモードでパッケージをダウンロードしビルドすることができます。

go コマンドがあるバージョンのモジュールをリポジトリから直接ダウンロードするとき、モジュールのパスに対してリポジトリの URL を検索し、バージョンをリポジトリ内のリビジョンにマップし、そのリビジョンでリポジトリアーカイブを抽出します。モジュールのパスがリポジトリのルートパスと同じで、リポジトリのルートディレクトリに go.mod ファイルがない場合、go コマンドはモジュールディレクティブとそれ以外のものを含む go.mod ファイルをモジュールキャッシュに合成します。合成された go.mod ファイルは依存関係のための require ディレクティブを含まないので、依存する他のモジュールは、それぞれの依存関係がすべてのビルドで同じバージョンで取得されるように、追加の require ディレクティブ (//間接コメント付き) を必要とするかもしれません。

go コマンドがプロキシからモジュールをダウンロードするとき、モジュールの残りの内容とは別に go.mod ファイルをダウンロードします。プロキシは、元のモジュールが合成 go.mod ファイルを持っていない場合、合成 go.mod ファイルを提供することが期待されています。

+互換性のないバージョン

メジャーバージョン 2 以上でリリースされたモジュールは、 そのモジュールのパスにメジャーバージョンに対応したサフィックスを 付けなければなりません。例えば、あるモジュールが v2.0.0 でリリースされた場合、そのパスには /v2 というサフィックスが必要です。これにより、たとえ同じリポジトリで開発されたとしても、go コマンドは複数のメジャーバージョンを別々のモジュールとして扱えるようになります。

メジャーバージョンサフィックス要件は go コマンドにモジュールサポートが追加されたときに導入されましたが、多くのリポジトリはそれ以前にすでにメジャーバージョン 2 以上のリリースにタグを付けていました。これらのリポジトリとの互換性を維持するために、go コマンドは go.mod ファイルがないメジャーバージョン 2 以上のバージョンに +incompatible というサフィックスを追加します。+その結果、ビルドが壊れる可能性があっても、go コマンドは自動的に互換性のない高いバージョンにアップグレードすることがあります。

以下の要件例で考えてみましょう。

require example.com/m v4.1.2+incompatible

彼はバージョン v4.1.2+incompatible を、モジュール example.com/m を提供するリポジトリのセマンティックバージョンタグ v4.1.2 を参照しています。モジュールはリポジトリのルートディレクトリになければならず(つまり、リポジトリのルートパスも example.com/m でなければなりません)、go.mod ファイルは存在してはいけません。モジュールには v1.5.2 のようなメジャーバージョン番号の低いバージョンがあり、go コマンドはそれらのバージョンと互換性のない v4.1.2+ に自動的にアップグレードするかもしれません(アップグレードの動作については最小バージョン選択 (MVS) を参照してください)。

バージョン v2.0.0 がタグ付けされた後にモジュールに移行するリポジトリは、通常、新しいメジャーバージョンをリリースする必要があります。上記の例では、作者は example.com/m/v5 というパスでモジュールを作成し、バージョン v5.0.0 をリリースする必要があります。また、作者はモジュール内のパッケージの import を更新し、example.com/m ではなく example.com/m/v5 という接頭語を使用する必要があります。より詳細な例については Go Modules: v2 and Beyond を参照してください。

リポジトリのタグに +incompatible という接尾辞を付けてはいけないことに注意してください。 v4.1.2+incompatible のようなタグは無視されます。このサフィックスは go コマンドで使用されるバージョンにのみ表示されます。バージョンとタグの区別の詳細については、バージョンとコミットのマッピングをご覧ください。

また、+incompatible というサフィックスが疑似バージョンに現れることがあることに注意してください。例えば、 v2.0.1-20200722182040-012345abcdef+incompatible は有効な擬似バージョンかもしれません。

最小限のモジュール互換性

メジャーバージョン2以上でリリースされたモジュールは、そのモジュールパスにメジャーバージョンのサフィックスを付ける必要があります。そのモジュールは、リポジトリ内のメジャーバージョンサブディレクトリで開発されていてもいなくてもかまいません。これは GOPATH モードを構築する際に、モジュール内のパッケージをインポートするパッケージに影響を与えます。

通常、GOPATH モードでは、パッケージはそのリポジトリのルートパスとリポジトリ内のディレクトリを結合したディレクトリに格納されます。例えば、ルートパスが example.com/repo でサブディレクトリが sub のパッケージは $GOPATH/src/example.com/repo/sub に格納され、 example.com/repo/sub としてインポートされることになるでしょう。

メジャーバージョンのサフィックスを持つモジュールでは、ディレクトリ $GOPATH/src/example.com/repo/v2/sub に example.com/repo/v2/sub というパッケージがあると思うかも知れません。この場合、モジュールはそのリポジトリの v2 サブディレクトリで開発される必要があります。go コマンドはこれをサポートしますが、必須ではありません(バージョンとコミットの対応付けを参照)。

もしモジュールがメジャーバージョンのサブディレクトリで開発されていなければ、GOPATH のそのディレクトリはメジャーバージョンのサフィックスを含まず、そのパッケージはメジャーバージョンのサフィックスを付けずにインポートされるかもしれません。上記の例では、パッケージは $GOPATH/src/example.com/repo/sub というディレクトリにあり、example.com/repo/sub としてインポートされるでしょう。

これは、モジュールモードとGOPATHモードの両方でビルドされることを意図したパッケージでは問題が生じます。モジュールモードではサフィックスが必要ですが、GOPATHモードではサフィックスが不要です。

この問題を解決するために、Go 1.11 で最小限のモジュール互換性が追加され、Go 1.9.7 と 1.10.3 にバックポートされました。GOPATH モードでインポートパスがディレクトリに解決されたとき。

GOPATH モードでインポートパスをディレクトリに解決する場合: $modpath/$vn/$dir 形式のインポートを解決するとき、次のようになります。
    modpathは有効なモジュールパスです。
    vn はメジャーバージョンのサフィックスです。
    dirは空の可能性のあるサブディレクトリーです。
以下のすべてが真である場合。
    パッケージ $modpath/$vn/$dir は、関連するどのベンダーディレクトリにも存在しません。
    go.mod ファイルがインポートファイルと同じディレクトリ、または $GOPATH/src ルートまでの任意の親ディレクトリに存在する。
    GOPATH[i]/src/$modpath/$vn/$suffix ディレクトリが存在しない (任意のルート $GOPATH[i] の場合)。
    ファイル $GOPATH[d]/src/$modpath/go.mod が存在し(あるルート $GOPATH[d] に対して)、モジュールパスを $modpath/$vn として宣言しています。
すると、$modpath/$vn/$dirのインポートは$GOPATH[d]/src/$modpath/$dirというディレクトリに解決されます。

このルールにより、モジュールに移行されたパッケージは、メジャーバージョンのサブディレクトリが使用されなかった場合でも、GOPATHモードでビルドされたときにモジュールに移行された他のパッケージをインポートすることができます。

モジュールを意識したコマンド

ほとんどの go コマンドはモジュールを意識したモードか GOPATH モードで実行されます。モジュールを意識したモードでは、go コマンドは go.mod ファイルを使ってバージョン管理された依存関係を見つけ、通常はモジュールキャッシュからパッケージをロードし、モジュールが見つからない場合はダウンロードします。GOPATH モードでは、go コマンドはモジュールを無視し、ベンダーのディレクトリや GOPATH 内を検索して依存関係を見つけます。

Go 1.16 では、go.mod ファイルがあるかどうかに関係なく、モジュールを考慮したモードがデフォルトで有効になっています。それ以前のバージョンでは、現在のディレクトリや親ディレクトリに go.mod ファイルが存在すると、モジュール認識モードが有効になっていました。

モジュール認識モードは、GO111MODULE環境変数で制御することができ、on、off、autoに設定することができます。

GO111MODULE=off の場合、go コマンドは go.mod ファイルを無視し、GOPATH モードで実行されます。
GO111MODULE=onまたは設定されていない場合、go.modファイルが存在しなくても、goコマンドはモジュールを意識したモードで実行されます。すべてのコマンドが go.mod ファイルなしで動作するわけではありません:モジュール外のモジュール・コマンドを参照してください。
GO111MODULE=auto の場合、現在のディレクトリまたは任意の親ディレクトリに go.mod ファイルが存在すると、go コマンドはモジュールを意識したモードで実行されます。go mod サブコマンドと go install with a version query は、go.mod ファイルが存在しなくても、モジュールを意識したモードで実行されます。

モジュール対応モードでは、GOPATH はビルド中に import の意味を定義しなくなりますが、ダウンロードされた依存関係(GOPATH/pkg/mod; モジュールキャッシュ参照)とインストールされたコマンド(GOBIN が設定されていなければ GOPATH/bin に)は保存されます。 ビルドコマンド

パッケージに関する情報を読み込むすべてのコマンドは、モジュールを認識します。これには

go build
go build 修正
go generate
取得する
インストールする
リストアップ
実行する
テストする
go vet

モジュール認識モードで実行すると、これらのコマンドは go.mod ファイルを使用して、コマンドラインにリストされた、または Go ソースファイルに書かれた import パスを解釈します。これらのコマンドは、すべてのモジュールコマンドに共通する、次のフラグを受け付けます。

mod フラグは、go.mod が自動的に更新されるかどうか、また vendor ディレクトリが使用されるかどうかを制御します。
    -mod=mod は go コマンドに vendor ディレクトリを無視させ、例えばインポートされたパッケージが既知のモジュールによって提供されていない場合、自動的に go.mod を更新させます。
    -mod=readonly は、go コマンドに vendor ディレクトリを無視し、 go.mod の更新が必要な場合にエラーを報告するように指示します。
    -mod=vendor は、go コマンドに vendor ディレクトリを使用するように指示します。このモードでは、go コマンドはネットワークやモジュールキャッシュを使用しません。
    デフォルトでは、go.mod の go バージョンが 1.14 以上で vendor ディレクトリが存在する場合、go コマンドは -mod=vendor が使用されたように動作します。そうでない場合は、-mod=readonly が使用されたかのように動作します。
modcacherw フラグは、モジュールキャッシュに新しいディレクトリを 作成する際に、読み取り専用ではなく、読み書き可能なパーミッションで作成する ように go コマンドに指示します。このフラグが一貫して使われている場合 (通常は環境で GOFLAGS=-modcacherw を設定するか、 go env -w GOFLAGS=-modcacherw を実行します)、最初にパーミッションを変更せずに rm -r などのコマンドでモジュールキャッシュを削除することができます。go clean -modcache コマンドは -modcacherw の有無にかかわらず、モジュールキャッシュを削除するために使われます。
-modfile=file.mod フラグは、go コマンドにモジュールルートディレクトリの go.mod の代わりに代替ファイルを読む (そしておそらくは書き込む) ように指示します。ファイル名は .mod で終わらなければなりません。go.mod という名前のファイルは、モジュールルートディレクトリを決定するためにまだ存在しなければなりませんが、アクセスされることはありません。modfile が指定された場合、代替の go.sum ファイルも使用されます。そのパスは、-modfile フラグから .mod 拡張子を削除して .sum を追加することで得られます。

ベンダリング

モジュールを使用する場合、go コマンドは通常、モジュールをソースからモジュールキャッシュにダウンロードし、 ダウンロードしたコピーからパッケージをロードすることで依存性を満たします。古いバージョンの Go との相互運用を可能にしたり、ビルドに使用するすべてのファイルが単一のファイルツリーに格納されるようにするために、ベンダリングが使用されることがあります。

go mod vendor コマンドは、メインモジュールのルートディレクトリに vendor という名前のディレクトリを作成し、そこにメインモジュールのパッケージのビルドとテストに必要なすべてのパッケージのコピーを格納します。メインモジュール外のパッケージのテストによってのみインポートされるパッケージは含まれません。go mod tidy や他のモジュールコマンドと同様に、 vendor ディレクトリを構築するときに ignore 以外のビルドの制約は考慮されません。

go mod vendor は vendor/modules.txt ファイルも作成します。このファイルには vendor されたパッケージのリストと、それらがコピーされたモジュールのバージョンが含まれています。vendoring が有効な場合、このマニフェストは、 go list -m や go version -m が報告するモジュールのバージョン情報のソースとして使用されます。go コマンドが vendor/modules.txt を読むとき、 モジュールのバージョンが go.mod と一致するかどうかをチェックします。vendor/modules.txt が生成された後に go.mod が変更された場合、go コマンドはエラーを報告します。

vendor ディレクトリがメインモジュールのルートディレクトリにある場合、 メインモジュールの go.mod ファイルにある go バージョンが 1.14 以上であれば、 自動的に使用されます。明示的に vendoring を有効にするには、go コマンドに -mod=vendor というフラグを付けて起動します。ベンダリングを無効にするには、フラグ -mod=readonly または -mod=mod を使用します。

vendoring が有効な場合、go build や go test などのビルドコマンドは、 ネットワークやローカルのモジュールキャッシュにアクセスする代わりに、 vendor ディレクトリからパッケージをロードします。go list -m コマンドは go.mod にリストされているモジュールに関する情報のみを表示します。 go mod download や go mod tidy のような go mod コマンドは vendoring が有効な場合でも変わらずにモジュールをダウンロードし、 モジュールキャッシュにアクセスします。

go get

使い方

go get [-d] [-t] [-u] [build flags] [packages]

# 特定のモジュールをアップグレードする。
$ go get -d golang.org/x/net

# メインモジュールのパッケージがインポートするパッケージを提供するモジュールをアップグレードする。
$ go get -d -u ./...

# モジュールの特定のバージョンをアップグレードまたはダウングレードする。
$ go get -d golang.org/x/text@v0.3.2

# モジュールの master ブランチのコミットに更新する。
$ go get -d golang.org/x/text@master

# あるモジュールの依存関係を取り除き、それを必要とするモジュールをダウングレードする。
# モジュールの依存関係を取り除き、それを必要とするモジュールをそれを必要としないバージョンにダウングレードする。
$ go get -d golang.org/x/text@none

go get コマンドは、メインモジュールの go.mod ファイルにあるモジュールの依存関係を更新し、コマンドラインにリストされたパッケージをビルドしてインストールします。

go get はパッケージ、パッケージパターン、モジュールパスのリストを引数として 受け付けます。package 引数が指定された場合、go get はそのパッケージを提供するモジュールを更新します。パッケージパターンが指定された場合 (例えば all や ... ワイルドカードを含むパス)、 go get はそのパターンをパッケージの集合に展開し、そのパッケージを提供する モジュールを更新します。もし引数がパッケージではなくモジュールの名前だった場合(例えば、モジュール golang.org/x/net はそのルートディレクトリにパッケージを持っていません)、 go get はモジュールを更新しますが、パッケージはビルドしません。引数が指定されない場合、go get は .が指定された場合(カレントディレクトリのパッケージ)のように動作します。

各引数には、go get golang.org/x/text@v0.3.0 のように、希望するバージョンを示すバージョン問い合わせ接尾辞を含めることができます。このサフィックスには、特定のバージョン (v0.3.0)、バージョンプレフィックス (v0.3)、ブランチまたはタグ名 (master)、リビジョン (1234abcd)、または latest, upgrade, patch, none のうちの特別なクエリが含まれます。バージョンが指定されない場合、go get は @upgrade クエリを使用します。

go get が引数を特定のモジュールとバージョンに解決したら、go get はメインモジュールの go.mod ファイルに require 命令を追加、変更、削除し、モジュールが将来も希望のバージョンに保たれるようにします。go.mod ファイルの required バージョンは最小バージョンであり、新しい依存関係が追加されると 自動的に増加する可能性があることに注意してください。モジュールを意識したコマンドでどのようにバージョンが選択され、競合が解決されるかの詳細は Minimal version selection (MVS) を参照してください。

コマンドラインで指定されたモジュールが追加、アップグレード、またはダウングレードされたとき、指定されたモジュールの新しいバージョンがより高いバージョンで他のモジュールを必要とする場合、他のモジュールがアップグレードされることがあります。例えば、モジュール example.com/a がバージョン v1.5.0 にアップグレードされ、そのバージョンはバージョン v1.2.0 のモジュール example.com/b を必要とするとします。モジュール example.com/b が現在バージョン v1.1.0 で必要とされているなら、 go get example.com/a@v1.5.0 は example.com/b を v1.2.0 にまでアップグレードします。

https://go.dev/doc/mvs/get-upgrade.svg

コマンドラインで指定されたモジュールがダウングレードされたり削除されたりすると、他のモジュールもダウングレードされることがあります。上記の例を続けるために、モジュール example.com/b が v1.1.0 にダウングレードされたとします。モジュール example.com/a もまた、バージョン v1.1.0 以下の example.com/b を必要とするバージョンにダウングレードされるでしょう。

https://go.dev/doc/mvs/get-downgrade.svg

モジュール要件は、バージョンサフィックス @none を使って削除することができます。これは特別な種類のダウングレードです。削除されたモジュールに依存しているモジュールは、必要に応じてダウングレードされるか、削除されます。モジュール要件は、そのパッケージの1つ以上がメインモジュールのパッケージによってインポートされている場合でも削除されることがあります。この場合、次のビルドコマンドで新しいモジュール要件が追加される可能性があります。

あるモジュールが (コマンドライン引数で明示的に指定されたり、 アップグレードやダウングレードを満たすために) 二つの異なるバージョンで必要になった場合、 go get はエラーを報告します。

go get が新しいバージョンのセットを選択した後、新しく選択されたモジュールの バージョンや、コマンドラインで指定されたパッケージを提供するモジュールが、 撤回されたか非推奨になったかを調べます。go get は撤回されたバージョンや非推奨のモジュールを 見つけるごとに警告を表示します。

go get は go.mod ファイルを更新した後、コマンドラインで指定された パッケージをビルドします。実行可能ファイルは環境変数 GOBIN で指定されたディレクトリにインストールされます。環境変数 GOPATH が設定されていない場合、デフォルトは $GOPATH/bin または $HOME/go/bin になります。

go get は以下のフラグをサポートしています。

  • d フラグは、go get にパッケージのビルドやインストールをしないように指示します。d が使われると、go get は go.mod で依存関係を管理するだけになります。d を使わずに go get を使ってパッケージをビルドしたりインストールしたりするのは非推奨です (Go 1.17 現在)。Go 1.18 では、-d は常に有効になります。
  • u フラグは、コマンドラインで指定されたパッケージから直接または間接的にインポートされたパッケージを提供するモジュールをアップグレードするように go get に指示します。u で選択された各モジュールは、より高いバージョン (プリリリース) で既に必要とされていない限り、最新バージョンにアップグレードされます。
  • u=patch フラグ (-u patch ではありません) も go get に依存関係をアップグレードさせますが、go get はそれぞれの依存関係を最新のパッチバージョンにアップグレードします (@patch バージョン問い合わせに似ています)。
  • t フラグは、go get にコマンドラインから指定されたパッケージのテストを構築するために必要なモジュールを考慮するように指示します。t と -u が一緒に使われた場合、go get はテストの依存関係も更新します。
  • insecure フラグは、もはや使うべきではありません。このフラグは go get がカスタム import パスを解決し、リポジトリやモジュールプロキシから HTTP のような安全でない方式で取得することを許可します。GOINSECURE 環境変数はよりきめ細かい制御を提供しますので、代わりに使用してください。

Go 1.16 以降、プログラムのビルドとインストールには go install が推奨されるコマンドです。バージョンサフィックス (@latest や @v1.4.6 など) とともに使用すると、go install はモジュールを意識したモードでパッケージをビルドし、現在のディレクトリや親ディレクトリに go.mod ファイルがある場合は、それを無視します。

go get は、go.mod の要件を管理することに重点を置いています。d フラグは非推奨で、Go 1.18 では常に有効になっています。

go install

使い方

go install [build flags] [packages]

# 最新バージョンのプログラムをインストールします。
# もしあれば、カレントディレクトリの go.mod は無視する。
$ go install golang.org/x/tools/gopls@latest

# あるプログラムの特定のバージョンをインストールする。
$ go install golang.org/x/tools/gopls@v0.6.4

# モジュールが選択したバージョンのプログラムをカレントディレクトリにインストールします。
$ go install golang.org/x/tools/gopls # ディレクトリ内の全てのプログラムをインストールする。

# ディレクトリにある全てのプログラムをインストールする。
$ go install ./cmd/...

go installコマンドは、コマンドライン上のパスで指定されたパッケージをビルドしてインストールします。実行可能ファイル(メイン・パッケージ)は、環境変数GOBINで指定されたディレクトリにインストールされます(環境変数GOPATHが設定されていない場合、デフォルトは$GOPATH/binまたは$HOME/go/binです)。GOROOTにある実行可能ファイルは、$GOBINではなく、$GOROOT/binまたは$GOTOOLDIRにインストールされます。非実行可能なパッケージはビルドされ、キャッシュされますが、インストールされません。

Go 1.16 以降、引数にバージョンのサフィックス (@latest や @v1.0.0 など) がある場合、go install はモジュールを考慮したモードでパッケージをビルドします。これは、メインモジュールの依存関係に影響を与えずに実行可能ファイルをインストールするのに便利です。

ビルドに使用されるモジュールのバージョンに関するあいまいさをなくすために、引数は以下の制約を満たす必要があります。

引数はパッケージパスまたはパッケージパターン (「...」というワイルドカードを使用) でなければなりません。標準パッケージ(fmt など)、メタパターン(std、cmd、all)、相対パスや絶対ファイルパスであってはなりません。
すべての引数は、同じバージョンのサフィックスを持つ必要があります。同じバージョンを参照していても、異なるクエリーは許可されません。
すべての引数は、同じモジュール内の同じバージョンのパッケージを参照する必要があります。
パッケージパスの引数は、メインパッケージを参照する必要があります。パターン引数は、メイン・パッケージのみにマッチします。
どのモジュールもメインモジュールとはみなされません。
    コマンドラインで指定されたパッケージを含むモジュールが go.mod ファイルを持っている場合、それがメインモジュールであった場合に異なって解釈されるようなディレクティブ (replace と exclude) を含んでいてはいけません。
    そのモジュールは、それ自身の上位バージョンを要求してはいけません。
    ベンダーディレクトリはどのモジュールでも使用されていないこと。(ベンダーディレクトリはモジュールの zip ファイルに含まれないため、go install はそれらをダウンロードしません)。

サポートされているバージョンの問い合わせ構文については、バージョンの問い合わせを参照してください。Go 1.15 とそれ以下では、go install でバージョンクエリを使用することはサポートされていません。

引数にバージョンサフィックスがない場合、go install は GO111MODULE 環境変数と go.mod ファイルの存在によって、モジュールを認識するモードまたは GOPATH モードで実行されることがあります。詳しくは、モジュール対応コマンドを参照してください。モジュール認識モードが有効な場合、go install はメインモジュールのコンテキストで実行され、インストールされるパッケージを含むモジュールとは異なる場合があります。

go list -m

使い方

go list -m [-u] [-retracted] [-versions] [list flags] [modules]

$ go list -m all
$ go list -m -versions example.com/m
$ go list -m -json example.com/m@latest

m フラグを指定すると、go list はパッケージの代わりにモジュールをリストアップします。このモードでは、go list への引数はモジュール、モジュールパターン (ワイルドカード ... を含む)、バージョンクエリー、またはビルドリストのすべてのモジュールにマッチする特別なパターン all のいずれかになります。引数が指定されない場合、メインモジュールがリストアップされます。

モジュールをリストする場合、-f フラグは Go 構造体に適用されるフォーマット テンプレートを指定しますが、モジュール構造体に適用されるようになりました。

type Module struct { (タイプモジュール構造体)
    Path string // モジュールのパス
    Version string // モジュールのバージョン
    Versions []string // 利用可能なモジュールバージョン (-versions と共に)
    Replace *Module // このモジュールに置き換わる
    Time *time.Time // バージョンが作成された時間
    Update *Module // 更新があれば、利用可能 (-u 付き)
    Main bool // このモジュールがメインか?
    Indirect bool // このモジュールはメインモジュールの間接的な依存関係にすぎないか?
    Dir string // このモジュールのファイルを格納するディレクトリ(もしあれば)。
    GoMod string // このモジュールの go.mod ファイルへのパス (もしあれば)
    GoVersion string // モジュールで使用される go のバージョン。
    Deprecated string // 非推奨のメッセージがある場合 (-u 付き)
    Error *ModuleError // モジュールの読み込みエラー


type ModuleError struct {
    Err string // エラーそのもの
}

デフォルトの出力は、モジュールのパス、そしてバージョンと置換に関する情報(あれば)です。例えば、go list -m all は次のように出力します。

example.com/main/module
golang.org/x/net v0.1.0
golang.org/x/text v0.3.0 => /tmp/text
rsc.io/pdf v0.1.1

モジュール構造体には、この出力の行をフォーマットする String メソッドがあり、デフォルトのフォーマットは -f '{{.String}}' と等しくなっています。

モジュールが置換された場合、そのReplaceフィールドには置換後のモジュールが記述され、Dirフィールドには置換後のモジュールのソースコードが設定されます(存在する場合)。(つまり、Replace が非 nil の場合、Dir は Replace.Dir に設定され、置き換えられたソースコードにはアクセスできません)。

u フラグは、利用可能なアップグレードに関する情報を追加します。モジュールの最新バージョンが現在のモジュールより新しい場合、 list -u はモジュールの Update フィールドに新しいモジュールの情報を設定します。 list -u は、現在選択されているバージョンが後退しているかどうか、 モジュールが非推奨かどうかも表示します。モジュールの String メソッドは、現在のバージョンの後に新しいバージョンを括弧で囲んで表示することで、利用可能なアップグレードを示します。たとえば、go list -m -u all は次のように表示されます。

example.com/main/module
golang.org/x/old v1.9.9 (deprecated)
golang.org/x/net v0.1.0 (retracted) [v0.2.0]
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 [v0.1.2]

(ツールの場合、go list -m -u -json all の方がより便利に解析できるかもしれません)。

バージョンフラグを指定すると、モジュールのバージョンフィールドに、 モジュールのすべての既知のバージョンを、セマンティックバージョン付けに従って、 低いものから高いものへと並べたリストをセットします。このフラグはまた、デフォルトの出力形式を変更し、モジュールのパスの後に空白で区切られたバージョンのリストを表示します。retracted フラグが指定されていない限り、リトラクトされたバージョンはこの一覧から省かれます。

retracted フラグは、-versions フラグで表示されるリストに撤回されたバージョンを表示し、バージョン問い合わせを解決する際に撤回されたバージョンを考慮するようにリストに指示します。例えば、go list -m -retracted example.com/m@latest はモジュール example.com/m の最高リリースまたはプレリリースバージョンを表示しますが、そのバージョンが後退している場合も同様です。retracted フラグは、Go 1.16 で追加されました。

テンプレート関数 module は、モジュールパスかクエリでなければならない1つの文字列引数を取り、指定されたモジュールを Module 構造体として返します。エラーが発生した場合は、ErrorフィールドがnilでないModule構造体が返されます。

go mod download

使い方

go mod download [-json] [-x] [modules]

$ go mod download
$ go mod download golang.org/x/mod@v0.2.0

go mod download コマンドは指定されたモジュールをモジュールキャッシュにダウンロードします。引数には、メインモジュールの依存関係を選択するモジュールパスまたはモジュールパターン、あるいはpath@version形式のバージョンクエリを指定できます。引数なしの場合、ダウンロードはメインモジュールのすべての依存関係に適用されます。

go コマンドは、通常の実行中に必要に応じて自動的にモジュールをダウンロードします。go mod download コマンドは主にモジュールキャッシュの事前充填や、モジュールプロキシが提供するデータのロードに便利です。

デフォルトでは、download は標準出力に何も書きません。進捗状況やエラーは標準エラーに出力されます。

json フラグを指定すると、ダウンロードされた各モジュール(または失敗)について、この Go 構造体に対応する一連の JSON オブジェクトを標準出力に出力します。

type Module struct { (タイプモジュール構造体)
    Path string // モジュールのパス
    Version string // モジュールのバージョン
    Error string // モジュールの読み込みエラー
    Info string // キャッシュされた .info ファイルへの絶対パス
    GoMod string // キャッシュされた .mod ファイルへの絶対パス
    Zip string // キャッシュされた .zip ファイルへの絶対パス
    Dir string // キャッシュされたソースルートディレクトリへの絶対パス
    Sum string // パス、バージョン (go.sum のような) のチェックサム
    GoModSum string // go.mod のチェックサム (go.sum と同様)
}

x フラグは、download が実行するコマンドを標準エラーに出力します。

go mod edit

使い方

go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

# replace ディレクティブを追加します。
$ go mod edit -replace example.com/a@v1.0.0=./a

# replace ディレクティブを削除する。
$ go mod edit -dropreplace example.com/a@v1.0.0

# go のバージョンを設定し、要件を追加し、ファイルを表示する。
# ディスクに書き込むのではなく、ファイルを出力します。
$ go mod edit -go=1.14 -require=example.com/m@v1.0.0 -print

# go.mod ファイルをフォーマットします。
$ go mod edit -fmt

# 別の.modファイルをフォーマットして表示する。
$ go mod edit -print tools.mod

# go.modファイルをJSONで表示する。
$ go mod edit -json

go mod edit コマンドは、主にツールやスクリプトによって使用される go.mod ファイルの編集とフォーマットのためのコマンドラインインターフェイスを提供します。go mod edit は一つの go.mod ファイルだけを読み込み、他のモジュールに関する情報は調べません。デフォルトでは、go mod edit はメインモジュールの go.mod ファイルを読み書きしますが、編集フラグの後に別のターゲットファイルを指定することができます。

編集フラグは、一連の編集操作を指定します。

-module フラグはモジュールのパス (go.mod ファイルのモジュール行) を変更します。
go=versionフラグは、予想されるGo言語のバージョンを設定します。
require=path@version と -droprequire=path フラグは、与えられたモジュールパスとバージョンに要件を追加し、削除します。ただし、-require は path に存在するすべての要件を上書きします。これらのフラグは、主にモジュールグラフを理解するツールのためのものです。ユーザは、他のモジュールによって課せられた制約を満たすために、 必要に応じて他の go.mod の調整を行う go get path@version や go get path@none の方を好むべきです。go get を参照してください。
exclude=path@version と -dropexclude=path@version フラグは、与えられたモジュールのパスとバージョンに対して除外を追加したり削除したりします。exclude=path@version は、その除外がすでに存在する場合は、ノー・オプションであることに注意してください。
-replace=old[@v]=new[@v] フラグは、与えられたモジュールパスと バージョンのペアの置換を追加します。old@v の @v が省略された場合、左側にバージョンを伴わない置換が追加され、古いモジュールパスのすべてのバージョンに適用されます。new@v の @v が省略された場合、新しいパスはモジュールパスではなく、ローカルモジュールのルートディレクトリであるべきです。なお、-replace は old[@v] の冗長な置換を上書きしますので、@v を省略すると特定のバージョンの置換を削除します。
dropreplace=old[@v] フラグは、指定されたモジュールパスとバージョンのペアの置換を落とします。v が指定された場合、指定されたバージョンの置き換えを落とします。左辺にバージョンのない既存の置き換えは、まだモジュールを置き換えることができます。v が省略された場合、バージョンを持たない置換は削除されます。
retract=version と -dropretract=version フラグは、与えられたバージョンに 対して、retract を追加、削除します。このフラグは、単一のバージョン (v1.2.3 など) であっても、ある間隔 ([v1.1.0,v1.2.0] など) であったとしてもかまいません。retract フラグは、retract ディレクティブの根拠となるコメントを追加することができないことに注意してください。理由付けコメントは推奨されており、go list -m -u や他のコマンドで表示されるかもしれません。

編集フラグは繰り返すことができます。変更は与えられた順番に適用されます。

go mod edit には、その出力を制御する追加のフラグがあります。

fmt フラグは、他の変更を加えることなく go.mod ファイルを再フォーマットします。この再フォーマットは、go.modファイルを使用したり書き換えたりする他のすべての変更によって暗示されます。このフラグが必要なのは、go mod edit -fmtのように、他のフラグが指定されていない場合だけです。
printフラグは、最終的なgo.modをディスクに書き戻す代わりに、テキスト形式で表示します。
jsonフラグは、最終的なgo.modをテキスト形式でディスクに書き戻す代わりに、JSON形式で出力します。JSON出力は、これらのGoタイプに対応します。 
type Module struct {
    Path    string
    Version string
}

type GoMod struct {
    Module  Module
    Go      string
    Require []Require
    Exclude []Module
    Replace []Replace
}

type Require struct {
    Path     string
    Version  string
    Indirect bool
}

type Replace struct {
    Old Module
    New Module
}

type Retract struct {
    Low       string
    High      string
    Rationale string
}

これはgo.modファイルそのものを記述しているだけで、間接的に参照される他のモジュールを記述しているわけではないことに注意してください。ビルドに利用可能なモジュールの完全なセットについては、go list -m -json all を使ってください。go list -m を参照してください。

例えば、ツールは go mod edit -json の出力をパースしてデータ構造として go.mod ファイルを取得し、その後 go mod edit を -require, -exclude などで呼び出して変更することができます。

ツールは golang.org/x/mod/modfile パッケージを使って go.mod ファイルを解析、編集、フォーマットすることもできます。

go mod graph

使い方

go mod graph [-go=version]

go mod graphコマンドは、モジュール要求グラフを(置換を適用して)テキスト形式で表示します。たとえば、以下のようになります。

example.com/main example.com/a@v1.1.0
example.com/main example.com/b@v1.2.0
example.com/a@v1.1.0 example.com/b@v1.1.1
example.com/a@v1.1.0 example.com/c@v1.3.0
example.com/b@v1.1.0 example.com/c@v1.1.0
example.com/b@v1.2.0 example.com/c@v1.2.0

モジュールグラフの各頂点は、あるモジュールの特定のバージョンを表します。グラフの各エッジは、依存関係の最小バージョンに対する要求を表します。

go mod graph はグラフのエッジを1行に1つずつ表示します。各行はスペースで区切られた2つのフィールドを持ちます: モジュールのバージョンと依存関係の1つです。各モジュールのバージョンはpath@versionという形式の文字列として識別されます。メインモジュールはバージョンを持たないので、@versionという接尾辞はありません。

go フラグは、go.mod ファイル中の go ディレクティブによって示されるバージョンではなく、指定された Go バージョンによってロードされたモジュールグラフを報告するようにします。

バージョンがどのように選択されるかについてのより詳しい情報は Minimal version selection (MVS) を参照してください。選択されたバージョンを印刷するための go list -m と、なぜそのモジュールが必要なのかを理解するための go mod why も参照してください。

go mod init

使い方

go mod init [module-path]

go mod init
go mod init example.com/m

go mod initコマンドは、カレントディレクトリに新しいgo.modファイルを初期化して書き込み、事実上、カレントディレクトリをルートとする新しいモジュールを作成します。go.mod ファイルはまだ存在してはいけません。

init はオプションの引数として、新しいモジュールのモジュールパスをひとつ受け取ります。モジュールパスの選択方法については、モジュールパスを参照してください。モジュールパス引数が省略された場合、initは.goファイル中のimportコメント、vendoringツール設定ファイル、カレントディレクトリ(GOPATHにある場合)を使用してモジュールパスを推測しようとします。

ベンダリングツールの設定ファイルが存在する場合、initはそこからモジュール要件をインポートしようとします。initは以下の設定ファイルをサポートしています。

GLOCKFILE (Glock)
Godeps/Godeps.json (Godeps)
Gopkg.lock (dep)
dependencies.tsv (godeps)
glide.lock (glide)
vendor.conf (trash)
vendor.yml (govend)
vendor/manifest (gvt)
vendor/vendor.json (govendor)

ベンダリングツールの設定ファイルは常に完璧に忠実に翻訳されるわけではありません。例えば、同じリポジトリ内の複数のパッケージが異なるバージョンでインポートされ、リポジトリには1つのモジュールしか含まれていない場合、インポートされた go.mod は1つのバージョンのモジュールしか要求することができません。ビルドリストのすべてのバージョンをチェックするために go list -m all を実行し、足りない要件を追加したり、使われていない要件を削除するために go mod tidy を実行するとよいかもしれません。

go mod tidy

使い方

go mod tidy [-e] [-v] [-go=version] [-compat=version]

go mod tidy は go.mod ファイルがモジュール内のソースコードと一致することを確認します。現在のモジュールのパッケージと依存関係をビルドするために必要なモジュールの要件を追加し、関連するパッケージを提供しないモジュールの要件を削除します。また、go.sum に不足しているエントリを追加し、不要なエントリを削除します。

e フラグ (Go 1.16 で追加) は、パッケージのロード中にエラーが発生しても go mod tidy が処理を続行するようにします。

v フラグは、削除されたモジュールについての情報を標準エラーに出力するように go mod tidy をします。

go mod tidy は、メインモジュール内のすべてのパッケージと、それらがインポートするすべてのパッケージを再帰的にロードすることで動作します。go mod tidy はすべての build タグが有効であるかのように動作するので、プラットフォーム固有のソースファイルやカスタム build タグを必要とするファイルを、たとえそれらのソースファイルが通常ビルドされないとしても、考慮します。1つの例外があります: ignore build タグは有効になっていませんので、ビルド制約 // +build ignore を持つファイルは考慮されません。go mod tidy は、testdata という名前のディレクトリや、.や_で始まる名前のメインモジュールのパッケージは、それらのパッケージが他のパッケージによって明示的にインポートされていない限り、考慮しないことに注意してください。

一旦、go mod tidy がパッケージのこのセットをロードすると、1つ以上のパッケージを提供する各モジュールは、メインモジュールの go.mod ファイルで require ディレクティブを持つか、メインモジュールが go 1.16 以下の場合、他の必須モジュールによって必須であることを確認します。go mod tidy は、足りないそれぞれのモジュールの最新バージョンに requirement を加えます (最新バージョンの定義は Version queries 参照) 。go mod tidy は上記のセットでパッケージを提供しないモジュールに対する require ディレクティブを除去します。

go mod tidy はまた、require ディレクティブに // 間接的なコメントを追加または削除するかもしれません。間接的なコメントは、メインモジュールのパッケージによってインポートされたパッケージを提供しないモジュールを表します。(間接的な依存関係とコメントがいつ追加されるかの詳細については require ディレクティブを参照してください)。

go フラグが設定されている場合、go mod tidy は go ディレクティブを指定されたバージョンに更新し、そのバージョンに従ってモジュールグラフの刈り込みと遅延モジュール読み込み (と必要に応じて間接要件の追加と削除) を有効にしたり無効にしたりしています。

デフォルトでは、go mod tidy は、go ディレクティブで示されたバージョンの直前の Go バージョンによってモジュールグラフがロードされるとき、モジュールの選択されたバージョンが変更されないことを確認します。互換性のためにチェックされるバージョンは、 -compat フラグによって明示的に指定することもできます。

go mod vendor

使い方

go mod vendor [-e] [-v] [-o]

go mod vendor コマンドは、メインモジュールのルートディレクトリに vendor というディレクトリを作成し、そこにメインモジュール内のパッケージのビルドとテストをサポートするために必要なすべてのパッケージのコピーを格納します。メインモジュール外のパッケージのテストによってのみインポートされるようなパッケージは含まれません。go mod tidy や他のモジュールコマンドと同様、 vendor ディレクトリを構築する際に ignore 以外のビルドの制約は考慮されません。

vendoring が有効な場合、go コマンドはモジュールをソースからモジュールキャッシュにダウンロードし、それらのダウンロードしたコピーを使う代わりに、 vendor ディレクトリからパッケージをロードします。詳しくは ベンダリング を参照してください。

go mod vendor は vendor/modules.txt ファイルも作成します。このファイルには vendor されたパッケージと、そのパッケージがコピーされたモジュールのバージョンのリストが含まれています。vendoring が有効な場合、このマニフェストはモジュールのバージョン情報のソースとして使用され、go list -m と go version -m によって報告されます。go コマンドが vendor/modules.txt を読むとき、 モジュールのバージョンが go.mod と一致するかどうかをチェックします。vendor/modules.txt が生成された後に go.mod が変更された場合、go mod vendor を再度実行する必要があります。

go mod vendor は vendor ディレクトリが存在する場合、再構築する前に削除することに注意してください。ローカルな変更は vendor パッケージに対して行われるべきではありません。go コマンドは vendor ディレクトリのパッケージが変更されていないことを確認しませんが、 go mod vendor を実行して、変更が加えられていないことを確認することで vendor ディレクトリの整合性を確認することができます。

e フラグ (Go 1.16 で追加) は、go mod vendor がパッケージのロード中にエラーが発生しても処理を続行するようにします。

v フラグは、go mod vendor がベンダーのモジュールとパッケージの名前を標準エラーに出力するようにします。

o フラグ (Go 1.18 で追加) は、go mod vendor に vendor の代わりに指定したディレクトリにある vendor ツリーを出力させます。引数には絶対パスか、モジュールルートからの相対パスを指定します。

go mod verify

使い方

go mod verify

go mod verify は、モジュールキャッシュに保存されているメインモジュールの依存関係が、ダウンロードされた後に変更されていないことをチェックします。このチェックを行うために、go mod verify はダウンロードされた各モジュールの .zip ファイルと展開されたディレクトリをハッシュし、それらのハッシュをモジュールが最初にダウンロードされたときに記録されたハッシュと比較します。 go mod verify は構築リスト (go list -m all で表示可能) の中の各モジュールをチェックします。

すべてのモジュールが変更されていなければ、go mod verify は "all modules verified" と表示します。そうでない場合は、どのモジュールが変更されたかを報告し、0 以外のステータスで終了します。

すべてのモジュールを認識するコマンドは、メインモジュールの go.sum ファイル中のハッシュが、モジュールキャッシュにダウンロードされた モジュールについて記録されたハッシュと一致するかどうかを検証することに 注意してください。ハッシュが go.sum にない場合(例えば、モジュールが初めて使われる場合)、go コマンドはチェックサムデータベースを使ってハッシュを検証します(モジュールのパスが GOPRIVATE または GONOSUMDB でマッチする場合を除きます)。詳しくはモジュールの認証を見てください。

対照的に、go mod verify はモジュールの .zip ファイルと展開されたディレクトリのハッシュが、最初にダウンロードされたときにモジュールキャッシュに記録されたハッシュと一致するかどうかをチェックします。これはモジュールがダウンロードされ、検証された後にモジュールキャッシュのファイルへの変更を検出するのに便利です。go mod verify はキャッシュにないモジュールのコンテンツをダウンロードしませんし、モジュールのコンテンツを検証するために go.sum ファイルを使用しません。しかし、go mod verify は最小限のバージョン選択を行うために go.mod ファイルをダウンロードすることがあります。これらのファイルを検証するために go.sum を使用し、足りないハッシュのために go.sum のエントリを追加することがあります。

go mod why

使い方

go mod why [-m] [-vendor] packages...

go mod why は、メインモジュールからリストされた各パッケージへの import グラフの最短経路を表示します。

出力は一連のスタンザで、コマンドラインで指定されたパッケージやモジュールごとに、空白行で区切られています。各スタンザは、ターゲットパッケージまたはモジュールを示す#で始まるコメント行で始まります。それ以降の行は、1行に1パッケージずつ、importグラフを通るパスを与えます。パッケージやモジュールがメインモジュールから参照されていない場合、スタンザにはその事実を示す括弧付きの注記が1つ表示されます。 例

$ go mod why golang.org/x/text/language golang.org/x/text/encoding
# golang.org/x/text/language
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language

# golang.org/x/text/encoding
(メインモジュールは golang.org/x/text/encoding パッケージを必要としません)

mフラグはgo mod whyに引数をモジュールのリストとして扱わせます。go mod whyはそれぞれのモジュールに含まれる任意のパッケージへのパスを表示します。m が使用されている場合でも、go mod why は go mod graph によって表示されるモジュールグラフではなく、パッケージグラフを問い合わせることに注意してください。

vendor フラグは go mod why がメインモジュール外のパッケージのテストにおける import を無視するようにします (go mod vendor がそうするように)。デフォルトでは、go mod whyはallパターンにマッチしたパッケージのグラフを考慮します。このフラグは Go 1.16 以降のモジュールで (go.mod 内の go ディレクティブを使って) 宣言した場合は効果がありません。

go mod version -m

使い方

go version [-m] [-v] [file ...]

# goのビルドに使用されたGoのバージョンを表示します。
$ go version

# 特定の実行ファイルをビルドするために使用される Go のバージョンを表示します。
$ go version ~/go/bin/gopls

# 特定の実行ファイルをビルドするために使用される Go のバージョンとモジュールのバージョンを表示します。
$ go version -m ~/go/bin/gopls

# ディレクトリ内の実行ファイルをビルドするために使われたGoのバージョンとモジュールのバージョンを表示します。
$ go version -m ~/go/bin/

go version は、コマンドラインで指定された各実行ファイルをビルドするために使用された Go のバージョンを報告します。

コマンドラインでファイル名が指定されていない場合、go version はそれ自身のバージョン情報を表示します。

ディレクトリが指定された場合、go version はそのディレクトリを再帰的に走査し、認識された Go バイナリを探し、そのバージョンを報告します。デフォルトでは、goバージョンはディレクトリのスキャン中に見つかった認識できないファイルを報告しません。v フラグを使うと、認識できないファイルを報告するようになります。

m フラグを使うと、各実行ファイルの埋め込みモジュールのバージョン情報を表示します (利用可能な場合)。各実行ファイルについて、go version -m は以下のようなタブ区切りの列を持つテーブルを表示します。

$ go version -m ~/go/bin/goimports
/home/jrgopher/go/bin/goimports: go1.14.3
        path    golang.org/x/tools/cmd/goimports
        mod     golang.org/x/tools      v0.0.0-20200518203908-8018eb2c26ba      h1:0Lcy64USfQQL6GAJma8BdHCgeofcchQj+Z7j0SXYAzU=
        dep     golang.org/x/mod        v0.2.0          h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
        dep     golang.org/x/xerrors    v0.0.0-20191204190536-9bdfabe68543      h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

The format of the table may change in the future. The same information may be obtained from runtime/debug.ReadBuildInfo.

The meaning of each row in the table is determined by the word in the first column.

  • path: the path of the main package used to build the executable.
  • mod: the module containing the main package. The columns are the module path, version, and sum, respectively. The main module has the version (devel) and no sum.
  • dep: a module that provided one or more packages linked into the executable. Same format as mod.
  • =>: a replacement for the module on the previous line. If the replacement is a local directory, only the directory path is listed (no version or sum). If the replacement is a module version, the path, version, and sum are listed, as with mod and dep. A replaced module has no sum.

go clean -modcache

使い方

go clean [-modcache]

modcache フラグは、バージョン管理された依存関係の解凍されたソースコードを含むモジュールキャッシュ全体を go clean が削除するようにします。

これは通常、モジュールキャッシュを削除するのに最適な方法です。デフォルトでは、モジュールキャッシュのほとんどのファイルとディレクトリは、テストとエディタが認証された後に意図せずにファイルを変更しないように、読み取り専用になっています。残念ながら、これは rm -r のようなコマンドを失敗させる原因となります。

modcacherw フラグ (go build や他のモジュール対応コマンドで使用可能) を指定すると、モジュールキャッシュ内の新しいディレクトリを書き込み可能にします。すべてのモジュール対応コマンドに -modcacherw を渡すには、 GOFLAGS 変数にこれを追加します。GOFLAGS は環境で設定するか、go env -w で設定します。例えば、以下のコマンドでは恒久的に設定します。

go env -w GOFLAGS=-modcacherw

-modcacherw は注意して使うべきです。開発者はモジュールキャッシュのファイルに変更を加えないように注意すべきです。 go mod verify はキャッシュのファイルがメインモジュールの go.sum ファイルにあるハッシュと一致するかどうかをチェックするために使われます。

バージョンクエリ

コマンドライン上のモジュールやパッケージのパスに続く @ 文字の後に表示されるバージョンクエリを使用して、モジュールのバージョンを指定することができるコマンドがいくつかあります。

go get example.com/m@latest
go mod download example.com/m@master
go list -m -json example.com/m@e3702bed2

バージョンのクエリは、以下のいずれかになります。

v1.2.3 のような完全に指定されたセマンティックバージョンで、特定のバージョンを選択するもの。構文については バージョン を参照してください。
v1 や v1.2 のような意味的なバージョンプレフィックスで、そのプレフィックスを持つ最も高いバージョンを選択する。
<v1.2.3 や >=v1.5.6 などの意味的なバージョン比較で、比較対象に最も近いバージョンを選択します (> と >= では最も低いバージョン、< と <= では最も高いバージョン)。
コミットハッシュのプレフィックス、リビジョンタグ、ブランチ名など、基礎となるソース リポジトリのリビジョン識別子です。リビジョンに意味的なバージョンのタグが付けられている場合、このクエリはそのバージョンを選択します。そうでない場合、このクエリーは基礎となるコミットの擬似バージョンを選択します。他のバージョンクエリでマッチした名前のブランチやタグは、この方法では選択できないことに注意してください。たとえば、v2 というクエリは v2 で始まる最新バージョンを選択しますが、v2 という名前のブランチは選択しません。
latestという文字列は、利用可能な最も高いリリースバージョンを選択します。リリースバージョンがない場合、latest は最も高いプレリリースバージョンを選択します。タグ付けされたバージョンがない場合、latest はリポジトリのデフォルトブランチの先端にあるコミットの疑似バージョンを選択します。
upgrade という文字列は latest と似ていますが、モジュールが latest が選択するバージョンよりも高いバージョンで現在必要とされている場合(たとえば、プレリリース)、upgrade は現在のバージョンを選択します。
patch という文字列は、現在必要とされているバージョンと同じメジャーとマイナーのバージョン番号を持つ、利用可能な最新バージョンを選択します。現在必要とされているバージョンがない場合、patch は latest と同じです。Go 1.16 以降、go get は patch を使用する際に現在のバージョンを要求します (しかし、-u=patch フラグはこの要求を持ちません)。

特定の名前のバージョンやリビジョンに対する問い合わせを除いて、すべての問い合わせは、 go list -m -versions (go list -m 参照) によって報告された利用可能なバージョンを考慮します。このリストにはタグ付けされたバージョンのみが含まれ、擬似バージョンは含まれません。メインモジュールの go.mod ファイルにある exclude ディレクティブで禁止されているモジュールのバージョンは考慮されません。同じモジュールの最新バージョンから go.mod ファイル中の retract ディレクティブによってカバーされているバージョンも、 -retracted フラグが go list -m と一緒に使われているときと retract ディレクティブを読み込むときを除いて、無視されます。

プレリリースバージョンよりもリリースバージョンが優先されます。例えば、v1.2.2 と v1.2.3-pre がある場合、 v1.2.3-pre の方が上位であっても、最新のクエリでは v1.2.2 が選択されます。v1.2.3-preの方がv1.2.4に近いにもかかわらず、<v1.2.4>のクエリでもv1.2.2が選択されるでしょう。リリース版やプレリリース版がない場合、latest、upgrade、patch クエリはリポジトリのデフォルトブランチの先端にあるコミットの擬似バージョンを選択します。その他のクエリーはエラーを報告します。

モジュール外でのモジュールコマンド

モジュールを意識した Go コマンドは通常、作業ディレクトリや親ディレクトリにある go.mod ファイルで定義されたメインモジュールのコンテキストで実行されます。いくつかのコマンドは go.mod ファイルがなくてもモジュール対応モードで実行できますが、ほとんどのコマンドは go.mod ファイルが存在しないと異なる動作をするか、エラーを報告します。

モジュール対応モードの有効化および無効化については、モジュール対応コマンドを参照してください。

  • go build
  • go doc
  • go fix
  • go fmt
  • go generate 標準ライブラリのパッケージと、コマンドラインで .go ファイルとして指定されたパッケージのみ、ロード、インポート、ビルドすることができます。他のモジュールのパッケージはビルドできません。なぜなら、モジュールの要件を記録し、決定論的なビルドを保証する場所がないためです。
  • go install
  • go list
  • go run
  • go test
  • go vet
  • go get パッケージや実行ファイルは通常通りビルドしてインストールすることができます。go.mod ファイルがない状態で go get を実行した場合、メインモジュールは存在しないので、replace ディレクティブや exclude ディレクティブは適用されないことに注意してください。
  • go list -m バージョン問い合わせは、-versionsフラグが使用されている場合を除き、ほとんどの引数で明示的に行う必要があります。
  • go mod download ほとんどの引数で明示的なバージョン問い合わせが必要です。
  • go mod edit 明示的なファイル引数が必要です。
  • go mod graph
  • go mod tidy
  • go mod vendor
  • go mod verify
  • go mod why

go work init

使い方

go work init [moddirs]

Init は、カレントディレクトリに新しい go.work ファイルを初期化して書き込み、事実上、カレントディレクトリに新しいワークスペースを作成します。

go work init はオプションで、ワークスペースモジュールへのパスを引数として受け取ります。引数が省略された場合、モジュールのない空のワークスペースが作成される。

各引数のパスは、go.work ファイル中の use ディレクティブに追加されます。現在のgoのバージョンもgo.workファイルに記載されます。

go work edit

使い方

go work edit [editing flags] [go.work]

go work editコマンドは、主にツールやスクリプトによって使用されるgo.workを編集するためのコマンドラインインターフェイスを提供します。このコマンドは、go.work を読み込むだけで、関連するモジュールの情報を調べることはありません。ファイルが指定されない場合、edit は現在のディレクトリとその親ディレクトリにある go.work ファイルを探します。

編集フラグは、一連の編集作業を指定します。

-fmtフラグは、他の変更を加えることなくgo.workファイルを再フォーマットします。この再フォーマットは、go.work ファイルを使用したり書き換えたりする他の修正によっても 暗示される。このフラグが必要なのは、'go work edit -fmt'のように他のフラグが指定されていない場合だけである。
use=path と -dropuse=path フラグは、go.work ファイルのモジュールディレク トリ群から use ディレクティブを追加したり削除したりします。
replace=old[@v]=new[@v] フラグは、与えられたモジュールのパスと バージョンのペアを置き換える機能を追加します。old@v の @v が省略された場合、左側にバージョンを指定しない置換が追加され、古いモジュールパスのすべてのバージョンに適用されます。new@v の @v が省略された場合、新しいパスはモジュールパスではなく、ローカルモジュールのルートディレクトリであるべきです。なお、-replace は old[@v] の冗長な置換を上書きするので、@v を省略すると特定のバージョンの既存の置換が削除されます。
dropreplace=old[@v] フラグは、指定されたモジュールパスとバージョンのペアの置換を落とします。v を省略すると、左辺にバージョンのない置換を落とします。
go=version フラグは、期待される Go 言語のバージョンを設定します。

編集フラグは繰り返すことができます。変更は、与えられた順番に適用されます。

go work edit は、出力を制御する追加のフラグを持っています。

-print フラグは、最終的な go.work を go.mod に書き戻す代わりに、テキスト形式で出力します。
jsonフラグは、go.modに書き戻す代わりに、最終的なgo.workファイルをJSONフォーマットで出力します。JSON出力は、これらのGoタイプに対応しています。
type Module struct {
    Path    string
    Version string
}

type GoWork struct {
    Go        string
    Directory []Directory
    Replace   []Replace
}

type Use struct {
    Path       string
    ModulePath string
}

type Replace struct {
    Old Module
    New Module
}

go work use

使い方

go work use [-r] [moddirs]

go work use コマンドは、go.work ファイルにディレクトリを追加するための コマンドラインインターフェイスを提供し、オプションで再帰的に追加することが できます。

コマンドライン go.work ファイルにリストされた各引数ディレクトリがディスク上に存在する場合は use ディレクティブが go.work ファイルに追加され、ディスク上に存在しない場合は go.work ファイルから削除されます。

r フラグは引数ディレクトリのモジュールを再帰的に検索し、use コマンドは各ディレクトリが引数として指定されたかのように動作します。つまり、use ディレクティブは存在するディレクトリに対して追加され、存在しないディレクトリに対しては削除されます。

go work sync

使い方

go work sync

go work syncコマンドは、ワークスペースのビルドリストをワークスペースのモジュールにシンクバックします。

ワークスペースのビルドリストは、ワークスペースのビルドに使用されるすべての(推移的)依存モジュールのバージョンのセットです。go work sync は、最小バージョン選択(MVS)アルゴリズムを使用してビルドリストを生成し、それらのバージョンをワークスペースで指定された各モジュールに(使用指令で)同期して返します。

ワークスペースのビルドリストが計算されると、ワークスペースの各モジュールの go.mod ファイルが書き換えられ、そのモジュールに関連する依存関係がワークスペースのビルドリストに一致するようにアップグレードされます。Minimal Version Selection は、ビルドリストの各モジュールのバージョンが、ワークスペースの各モジュールのバージョンと常に同じかそれ以上であることを保証していることに注意してください。

モジュールプロキシ

GOPROXY プロトコル

モジュールプロキシは、以下に指定するパスに対する GET リクエストに応答できる HTTP サーバです。リクエストにはクエリパラメータがなく、特定のヘッダも必要ないため、固定ファイルシステム(file:// URL を含む)からサービスを提供するサイトもモジュールプロキシになることができます。

HTTPレスポンスに成功した場合は、ステータスコード200(OK)を持つ必要があります。リダイレクト(3xx)には従います。ステータスコード4xxと5xxのレスポンスは、エラーとして扱われます。エラーコード 404 (Not Found) と 410 (Gone) は、要求されたモジュールやバージョンはプロキシで利用できないが、他の場所で見つけられるかもしれないことを示します。エラー応答は content type が text/plain で charset が utf-8 か us-ascii のいずれかであるべきです。

go コマンドは GOPROXY 環境変数を使ってプロキシやソースコントロールサーバに 問い合わせるように設定することができます。このリストには direct や off というキーワードを含めることができます (詳細は環境変数を参照してください)。リストの要素はカンマ (,) やパイプ (|) で区切ることができ、これらはエラーのフォールバックの動作を決定します。URL の後にカンマが続く場合、go コマンドは 404 (Not Found) または 410 (Gone) 応答の後にのみ、後続のソースにフォールバックします。URL の後にパイプが続く場合、go コマンドはタイムアウトなどの非 HTTP エラーを含む、すべてのエラーの後に後続のソースにフォールバックします。このエラー処理の動作により、プロキシは未知のモジュールのためのゲートキーパー として動作することができます。例えば、プロキシは承認されたリストにないモジュールに対して 403 (Forbidden) エラーで応答することができます (プライベートなモジュールを扱うプライベートプロキシ参照)。

以下の表は、モジュールプロキシが応答しなければならないクエリを指定します。それぞれのパスに対して、$base はプロキシ URL のパス部分、$module はモジュールパス、$version はバージョンです。たとえば、プロキシ URL が https://example.com/mod で、クライアントがモジュール golang.org/x/text のバージョン v0.3.2 の go.mod ファイルを要求している場合、クライアントは https://example.com/mod/golang.org/x/text/@v/v0.3.2.mod の GET リクエストを送ることになります。

大文字小文字を区別しないファイルシステムから送信するときのあいまいさを避けるために、 $module と $version の要素では大文字をすべてエクスクラメーションマークに置き換え、 その後に対応する小文字を並べるようにしています。これにより、example.com/M と example.com/m の両方のモジュールをディスクに格納することができます。前者は example.com/!m とエンコードされるからです。 無料版のDeepL翻訳(www.DeepL.com/Translator)で翻訳しました。

Path 説明
$base/$module/@v/list 指定されたモジュールの既知のバージョンの一覧をプレーンテキストで一行ごとに返します。このリストには擬似的なバージョンを含んではいけません。モジュールの特定のバージョンに関するメタデータJSON フォーマットで返します。レスポンスは、以下のGoデータ構造に対応するJSONオブジェクトである必要があります。type Info struct { Version string // バージョン文字列\n Time time.Time // コミット時間 }
$base/$module/@v/$version.info Version フィールドは必須で、有効な正規のバージョンを含む必要があります (「バージョン」 を参照ください)。リクエストパスの $version は同じバージョンである必要はなく、有効なバージョンである必要もありません。このエンドポイントは、ブランチ名やリビジョン識別子のバージョンを検索するために使用されるかもしれません。しかし、$version が $module と互換性のあるメジャーバージョンを持つ正規のバージョンである場合、成功したレスポンスの Version フィールドは同じでなければなりません。Timeフィールドはオプションです。存在する場合は、RFC 3339 形式の文字列でなければなりません。これは、そのバージョンが作成された時刻を表します。将来的にはさらに多くのフィールドが追加されるかもしれませんので、他の名前も予約されています。
$base/$module/@v/$version.mod あるモジュールの特定のバージョンの go.mod ファイルを返します。もし、モジュールが要求されたバージョンの go.mod ファイルを持っていなければ、要求されたモジュールのパスを持つ module ステートメントだけを含むファイルが返されなければなりません。そうでなければ、オリジナルの、変更されていない go.mod ファイルが返されなければなりません。
$base/$module/@v/$version.zip 特定のバージョンのモジュールの内容を含む zip ファイルを返します。この zip ファイルがどのようにフォーマットされなければならないかについての詳細は、 モジュールの zip ファイルを参照してください。
$base/$module/@latest モジュールの最新バージョンに関するメタデータを、 $base/$module/@v/$version.info と同じ書式で JSON フォーマットで返します。最新バージョンは、$base/$module/@v/list が空であったり、 適当なバージョンがない場合に go コマンドが使うべきモジュールのバージョンであるべきです。このエンドポイントはオプションであり、モジュールプロキシはこれを実装する必要はありません。

モジュールの最新バージョンを解決するとき、go コマンドは $base/$module/@v/list を要求し、適当なバージョンが見つからなかったら $base/$module/@latest を要求します。go コマンドは、意味的に最も高いリリースバージョン、意味的に最も高いプリリリースバージョン、そして年代的に最も新しい擬似バージョンの順で優先されます。Go 1.12 以前では、go コマンドは $base/$module/@v/list にある擬似バージョンをプレリリースバージョンとみなしていましたが、Go 1.13 以降はそうではありません。

モジュールプロキシは $base/$module/$version.mod と $base/$module/$version.zip のクエリに対する成功した応答に対して、 常に同じコンテンツを提供しなければなりません。このコンテンツは go.sum ファイルと、デフォルトではチェックサムデータベースを使って 暗号的に認証されます。

go コマンドはモジュールプロキシからダウンロードしたコンテンツのほとんどを $GOPATH/pkg/mod/cache/download にあるモジュールキャッシュにキャッシュしています。バージョン管理システムから直接ダウンロードする場合でも、go コマンドは明示的な info、mod、zip ファイルを合成して、プロキシから直接ダウンロードした場合と同じようにこのディレクトリに保存します。キャッシュのレイアウトはプロキシの URL 空間と同じなので、 $GOPATH/pkg/mod/cache/download を https://example.com/proxy で提供する(あるいは https://example.com/proxy にコピーする)と、ユーザは GOPROXY を https://example.com/proxy に設定してキャッシュされたモジュールバージョンにアクセスできるようになります。

プロキシとの通信

go コマンドはモジュールプロキシからモジュールのソースコードメタデータをダウンロードすることができます。環境変数 GOPROXY は go コマンドがどのプロキシに接続し、バージョン管理システムと直接通信するかどうかを設定するために使われます。ダウンロードされたモジュールのデータはモジュールキャッシュに保存されます。go コマンドはキャッシュにない情報が必要なときだけプロキシに連絡します。

GOPROXY プロトコルのセクションでは、GOPROXY サーバーに送ることができるリクエストについて説明します。しかし、go コマンドがどのようなときにこれらのリクエストを行うのかを理解することも有用です。例えば、go build は以下のような手順で実行します。

go.mod ファイルを読んでビルドリストを計算し、最小バージョン選択(MVS)を実行します。
コマンドライン上で指定されたパッケージと、それらがインポートするパッケージを読み込む。
あるパッケージがビルドリストのどのモジュールでも提供されていない場合、そのパッケージを提供するモジュールを見つける。go.modにその最新版のモジュールの要件を追加し、やり直す。
すべてがロードされた後にパッケージをビルドする。

go コマンドがビルドリストを計算するとき、モジュールグラフの各モジュールの go.mod ファイルをロードします。go.mod ファイルがキャッシュにない場合、go コマンドは $module/@v/$version.mod リクエストを使ってプロキシからダウンロードします ($module はモジュールパス、$version はバージョンです)。これらのリクエストはcurlのようなツールでテストすることができます。例えば、以下のコマンドは golang.org/x/mod のバージョン v0.2.0 の go.mod ファイルをダウンロードします。

$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.mod
module golang.org/x/mod

go 1.12

require (
    golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
    golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
    golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
)

パッケージをロードするために、go コマンドはそのパッケージを提供するモジュールのソースコードを必要とします。モジュールのソースコードは .zip ファイルで配布され、モジュールキャッシュに抽出されます。もしモジュールの .zip がキャッシュにない場合、go コマンドは $module/@v/$version.zip リクエストを使用してモジュールをダウンロードします。

$ curl -O https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.zip
$ unzip -l v0.2.0.zip | head
Archive:  v0.2.0.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1479  00-00-1980 00:00   golang.org/x/mod@v0.2.0/LICENSE
     1303  00-00-1980 00:00   golang.org/x/mod@v0.2.0/PATENTS
      559  00-00-1980 00:00   golang.org/x/mod@v0.2.0/README
       21  00-00-1980 00:00   golang.org/x/mod@v0.2.0/codereview.cfg
      214  00-00-1980 00:00   golang.org/x/mod@v0.2.0/go.mod
     1476  00-00-1980 00:00   golang.org/x/mod@v0.2.0/go.sum
     5224  00-00-1980 00:00   golang.org/x/mod@v0.2.0/gosumcheck/main.go

go.mod ファイルは通常 .zip ファイルに含まれていますが、.mod と .zip のリクエストは別々であることに注意してください。go コマンドは多くの異なるモジュールの go.mod ファイルをダウンロードする必要があり、.mod ファイルは .zip ファイルよりずっと小さいです。さらに、Go プロジェクトが go.mod ファイルを持っていない場合、プロキシは module ディレクティブだけを含む合成 go.mod ファイルを提供します。合成 go.mod ファイルは、バージョン管理システムからダウンロードするときに go コマンドによって生成されます。

もし go コマンドがビルドリストのどのモジュールでも提供されていないパッケージをロードする必要がある場合、それを提供する新しいモジュールを見つけようとします。パッケージからモジュールへの解決」のセクションで、この処理について説明します。要約すると、go コマンドはパッケージを含む可能性のある各モジュールパスの最新バージョンに関する情報を要求します。例えば、 golang.org/x/net/html というパッケージの場合、go コマンドは golang.org/x/net/html, golang.org/x/net, golang.org/x/, そして golang.org の最新バージョンを探そうとするでしょう。golang.org/x/netだけが実際に存在し、そのパッケージを提供しているので、goコマンドはそのモジュールの最新バージョンを使用します。もし複数のモジュールがそのパッケージを提供している場合、goコマンドは最も長いパスを持つモジュールを使用します。

go コマンドがモジュールの最新バージョンを要求するとき、まず $module/@v/list にリクエストを送ります。リストが空だったり、返されたバージョンのどれもが使えない場合は、 $module/@latest にリクエストを送ります。バージョンが決まると、go コマンドはメタデータのために $module/@v/$version.info リクエストを送ります。その後、go.mod ファイルとソースコードを読み込むために $module/@v/$version.mod と $module/@v/$version.zip リクエストを送ることがあります。

$ curl https://proxy.golang.org/golang.org/x/mod/@v/list
v0.1.0
v0.2.0

$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.info
{"Version":"v0.2.0","Time":"2020-01-02T17:33:45Z"}

.mod または .zip ファイルをダウンロードした後、go コマンドは暗号ハッシュを計算し、それがメインモジュールの go.sum ファイルにあるハッシュと一致するかどうかをチェックします。ハッシュが go.sum に存在しない場合、デフォルトでは、go コマンドはチェックサムデータベースからハッシュを取得します。計算されたハッシュが一致しない場合、go コマンドはセキュリティエラーを報告し、そのファイルをモジュールキャッシュにインストールしません。GOPRIVATE と GONOSUMDB 環境変数を使用すると、特定のモジュールのチェックサムデータベースへの要求を無効にすることができます。GOSUMDB 環境変数を off に設定すると、チェックサムデータベースへの要求を完全に無効にすることもできます。詳細は モジュールの認証 を参照してください。.info リクエストで返されるバージョンリストとバージョンメタデータは 認証されておらず、時間の経過とともに変化する可能性があることに注意してください。 プロキシから直接モジュールを提供する

ほとんどのモジュールはバージョンコントロールリポジトリから開発され、提供されます。direct モードでは、go コマンドはそのようなモジュールをバージョン管理ツールでダウンロードします (バージョン管理システム参照)。また、モジュールプロキシから直接モジュールを提供することも可能です。これは、バージョンコントロールサーバーを公開せずにモジュールを提供したい組織や、go コマンドがサポートしていないバージョンコントロールツールを使っている組織にとって便利です。

go コマンドが direct モードでモジュールをダウンロードするとき、まずモジュールパスに基づいて HTTP GET リクエストでモジュールサーバーの URL を探します。HTML レスポンスで go-import という名前の タグを探します。タグの内容は、リポジトリのルートパス、バージョン管理システム、URLをスペースで区切って記述する必要があります。詳しくは、モジュールパスのリポジトリ検索をご覧ください。

バージョン管理システムが mod の場合、go コマンドは GOPROXY プロトコルを使って指定された URL からモジュールをダウンロードします。

例えば、go コマンドがモジュール example.com/gopher をバージョン v1.0.0 でダウンロードしようとしているとします。go コマンドは https://example.com/gopher?go-get=1 にリクエストを送ります。サーバーは次のタグを含む HTML ドキュメントで応答します。

この応答に基づいて、go コマンドは https://modproxy.example.com/example.com/gopher/@v/v1.0.0.info, v1.0.0.mod, v1.0.0.zip に対するリクエストを送ることによって、 モジュールをダウンロードします。

プロキシから直接提供されたモジュールは GOPATH モードの go get でダウンロードすることはできないことに注意してください。

バージョン管理システム

go コマンドはモジュールのソースコードメタデータをバージョンコントロールリポジトリから直接ダウンロードすることができます。通常、プロキシからモジュールをダウンロードする方が高速ですが、プロキシが利用できない場合や、モジュールのリポジトリにプロキシがアクセスできない場合(プライベートリポジトリによくあること)には、リポジトリに直接接続する必要があります。Git、SubversionMercurial、Bazaar、Fossil がサポートされています。バージョン管理ツールを go コマンドで使うには、PATH にあるディレクトリにインストールされている必要があります。

プロキシではなくソースリポジトリから特定のモジュールをダウンロードするには、GOPRIVATEまたはGONOPROXY環境変数を設定します。すべてのモジュールをソースリポジトリから直接ダウンロードするように go コマンドを設定するには、GOPROXY を direct に設定します。詳しくは環境変数を参照してください。

モジュールパスのリポジトリ検索

go コマンドが direct モードでモジュールをダウンロードするとき、そのモジュールを含むリポジトリを探すことから始めます。

モジュールパスのパスコンポーネントの最後に VCS qualifier (.bzr, .fossil, .git, .hg, .svn のいずれか)がある場合、go コマンドはそのパス qualifier までのすべてをリポジトリ URL として使用します。たとえば、モジュール example.com/foo.git/bar に対して、go コマンドは example.com/foo.git のリポジトリを git を使ってダウンロードし、そのモジュールが bar サブディレクトリにあることを期待します。go コマンドは、バージョン管理ツールがサポートしているプロトコルをもとに、使用するプロトコルを推測します。

モジュールパスが修飾子を持っていない場合、go コマンドはモジュールパスから派生した URL に HTTP GET リクエストを送り、クエリー文字列に ? 例えば、golang.org/x/mod というモジュールの場合、go コマンドは以下のようなリクエストを送ります。

https://golang.org/x/mod?go-get=1 (推奨)
http://golang.org/x/mod?go-get=1 (フォールバック、GOINSECURE のときのみ)

go コマンドはリダイレクトに従いますが、それ以外のレスポンスステータスコードは無視するので、サーバーは 404 やその他のエラーステータスで応答することがあります。環境変数 GOINSECURE は、特定のモジュールに対して暗号化されていない HTTP へのフォールバックとリダイレクトを許可するように設定されるかもしれません。

サーバーは、文書のにタグを含むHTML文書で応答しなければならない。 タグは、go コマンドの制限付きパーサを混乱させないために、ドキュメントの早い段階で表示されるべきです。特に、生の JavaScriptCSS の前に記述する必要があります。 タグは次のような形式でなければなりません。

<meta name="go-import" content="root-path vcs repo-url"> という形式でなければなりません。

root-path はリポジトリのルートパスで、リポジトリのルートディレクトリに対応するモジュールパスの部分です。要求されたモジュールパスのプレフィックスまたは完全一致である必要があります。完全一致でない場合は、 タグが一致するかどうかを確認するために、プレフィックスに対して別のリクエストを行います。

vcsバージョン管理システムです。下の表にあるツールのどれかか、キーワード mod でなければなりません。mod は go コマンドに GOPROXY プロトコルを使って与えられた URL からモジュールをダウンロードするように指示します。詳しくは、プロキシから直接モジュールを提供するを参照してください。

repo-url はリポジトリの URL です。URL がスキームを含んでいない場合(モジュールパスが VCS qualifier を持っているか、 タグにスキームがないため)、go コマンドはバージョンコントロールシステムでサポートされている各プロトコルを試行します。たとえば、Git の場合、go コマンドは https:// の次に git+ssh:// を試します。安全でないプロトコル(http:// や git:// など)は、モジュールパスが GOINSECURE 環境変数にマッチする場合のみ使用できます。

名前 コマンド GOVCS デフォルト セキュアなスキーム
Bazaar bzr プライベートのみ https, bzr+ssh
Fossil fossil プライベートのみ https
Git git 公開および非公開 https、git+ssh、ssh
Mercurial hg 公開および非公開の https、ssh
Subversion svn プライベートのみ https、svn+ssh

例として、golang.org/x/mod をもう一度考えてみましょう。go コマンドは https://golang.org/x/mod?go-get=1 にリクエストを送ります。サーバーはこのタグを含む HTML ドキュメントで応答します。

<meta name="go-import" content="golang.org/x/mod git https://go.googlesource.com/mod"> このレスポンスから、go コマンドは https://go.googlesource.com/mod にリクエストを送ります。

この応答から、go コマンドはリモート URL https://go.googlesource.com/mod にある Git リポジトリを使用することになります。

GitHub やその他の有名なホスティングサービスは、すべてのリポジトリに対して ? go-get=1 クエリで応答するので、それらのサイトでホストされているモジュールでは通常サーバーの設定は必要ありません。

リポジトリの URL が見つかったら、go コマンドはそのリポジトリをモジュールキャッシュにクローンします。一般的に、go コマンドはリポジトリから必要のないデータを取得することを避けようとします。しかし、実際に使用されるコマンドはバージョン管理システムによって異なり、時間の経過とともに変更される可能性があります。Gitの場合、goコマンドはコミットをダウンロードすることなく、利用可能なほとんどのバージョンをリストアップすることができます。通常、先祖のコミットをダウンロードすることなくコミットを取得しますが、そうすることが必要な場合もあります。

バージョンとコミットの対応付け

go コマンドは、v1.2.3 や v2.4.0-beta, v3.0.0+incompatible など、特定の正規バージョンでリポジトリ内のモジュールをチェックアウトすることがあります。各モジュールのバージョンには、どのリビジョンをチェックアウトすべきかを示す、リポジトリ内のセマンティックなバージョンタグが必要です。

もしモジュールがリポジトリのルートディレクトリや、ルートディレクトリのメジャーバージョンサブディレクトリで定義されている場合、各バージョンタグの名前は対応するバージョンと同じになります。例えば、モジュール golang.org/x/text はそのリポジトリのルートディレクトリで定義されているので、バージョン v0.3.2 はそのリポジトリに v0.3.2 というタグを持っています。これはほとんどのモジュールに当てはまります。

もしモジュールがリポジトリ内のサブディレクトリで定義されている場合、つまり、モジュールパスのモジュールサブディレクトリ部分が空でない場合、各タグ名の前にモジュールサブディレクトリを付け、その後にスラッシュを付けなければなりません。例えば、モジュール golang.org/x/tools/gopls は、ルートパス golang.org/x/tools のリポジトリの gopls サブディレクトリで定義されています。そのモジュールのバージョン v0.4.0 は、そのリポジトリに gopls/v0.4.0 というタグを持たなければなりません。

意味的なバージョンタグのメジャーバージョン番号は、モジュールパスのメジャーバージョンサフィックス(もしあれば)と一致しなければなりません。たとえば、v1.0.0 というタグは、モジュール example.com/mod に属することはできますが、v2.0.0 のようなタグを持つ example.com/mod/v2 には属しません。

メジャーバージョン v2 以上のタグは、go.mod ファイルが存在せず、モジュールがリポジトリのルートディレクトリにある場合、メジャーバージョンサフィックスがないモジュールに属することがあります。このようなバージョンは +incompatible というサフィックスで示されます。バージョンタグ自体にこのサフィックスをつけてはいけません。非モジュールリポジトリとの互換性 をご覧ください。

一度タグを作成したら、削除したり異なるリビジョンに変更したりしてはいけません。バージョンは、安全で再現性のあるビルドを保証するために認証されます。タグが変更された場合、クライアントはそれをダウンロードするときにセキュリティエラーが表示されるかもしれません。タグが削除された後でも、その内容はモジュールプロキシで利用可能なままである可能性があります。

擬似バージョンとコミットの対応付け

go コマンドは、v1.3.2-0.20191109021931-daa7c04131f5 のような疑似バージョンとしてエンコードされた、特定のリビジョンでリポジトリ内のモジュールをチェックアウトすることができます。

擬似バージョンの最後の12文字(上の例ではdaa7c04131f5)は、チェックアウトするリポジトリのリビジョンを示しています。この意味はバージョン管理システムによって異なります。Git や Mercurial では、これはコミットハッシュの接頭辞となります。Subversion では、これはゼロパッドのリビジョン番号です。

コミットをチェックアウトする前に、goコマンドはタイムスタンプ(上記の20191109021931)がコミットの日付と一致するかどうかを確認します。また、ベースバージョン (上の例では v1.3.2 の前のバージョン) がコミットの祖先であるセマンティックバージョンタグに対応しているかどうかも確認します。これらのチェックにより、モジュールの作者は擬似バージョンと他のリリースされたバージョンとの比較を完全に制御できるようになります。

詳しくは 擬似バージョン をご覧ください。

ブランチとコミットのバージョンへのマッピング

あるモジュールを特定のブランチ、タグ、リビジョンでチェックアウトするには、 バージョンのクエリを使用します。

go get example.com/mod@master

go コマンドは、これらの名前を正規のバージョンに変換し、最小バージョン選択 (MVS) で使用できるようにします。MVSは、バージョンを曖昧さなく並べることができるかどうかにかかっています。ブランチ名とリビジョンは、リポジトリの構造に依存し、変更される可能性があるため、時間をかけて確実に比較することはできません。

リビジョンに v1.2.3 のような意味的なバージョンタグが1つ以上付いている場合、最も有効なバージョンのタグが使用されます。例えば、v1.5.2 というタグは example.com/mod/v2 では考慮されず、メジャーバージョンがモジュールパスのサフィックスと一致しないためです。

リビジョンに有効なセマンティックバージョンタグがない場合、go コマンドは擬似バージョンを生成します。リビジョンに有効なセマンティックバージョンタグを持つ祖先がある場合、最も高い祖先のバージョンが擬似バージョンのベースとして使用されます。関連項目: 擬似バージョン。

リポジトリ内のモジュール・ディレクトリー

モジュールのリポジトリが特定のリビジョンでチェックアウトされると、go コマンドはそのモジュールの go.mod ファイルを含むディレクトリ(モジュールのルートディレクトリ)を探す必要があります。

モジュールパスは3つの部分からなることを思い出してください:リポジトリルートパス(リポジトリルートディレクトリに対応)、モジュールサブディレクトリ、メジャーバージョンサフィックス(v2 以上でリリースされたモジュールにのみ対応)です。

ほとんどのモジュールでは、モジュールパスはリポジトリのルートパスと同じなので、モジュールのルートディレクトリはリポジトリのルートディレクトリになります。

モジュールは時々、リポジトリのサブディレクトリで定義されます。これは一般的に、独立してリリースされバージョン管理される必要がある複数のコンポーネントを持つ大規模なリポジトリに対して行われます。このようなモジュールは、リポジトリのルートパスの後にあるモジュールのパスの部分にマッチするサブディレクトリで見つかることが期待されます。例えば、example.com/monorepo/foo/bar というモジュールがルートパス example.com/monorepo のリポジトリにあるとします。その go.mod ファイルは foo/bar サブディレクトリになければなりません。

メジャーバージョン v2 以上でリリースされたモジュールの場合、そのパスにはメジャーバージョンのサフィックスが必要です。メジャーバージョンサフィックスを持つモジュールは、サフィックスを持つサブディレクトリと持たないサブディレクトリのどちらかに定義することができます。たとえば、上記のモジュールの新しいバージョンが example.com/monorepo/foo/bar/v2 というパスでリリースされたとします。その go.mod ファイルは foo/bar または foo/bar/v2 のいずれかにあります。

メジャーバージョンの接尾辞を持つサブディレクトリは、メジャーバージョンサブディレクトリです。これは、ひとつのブランチで複数のメジャーバージョンのモジュールを開発するために使用されます。複数のメジャーバージョンを別々のブランチで開発する場合は不要かもしれません。GOPATH モードでは、パッケージのインポートパスは GOPATH/src 以下のディレクトリと完全に一致します。go コマンドは GOPATH モードで最小限のモジュールの互換性を提供します (非モジュールリポジトリとの互換性参照) ので、メジャーバージョンサブディレクトリは GOPATH モードで構築されたプロジェクトとの互換性のために常に必要というわけではありません。しかし、最小限のモジュールの互換性をサポートしていない古いツールでは問題があるかもしれません。

go コマンドがモジュールのルートディレクトリを見つけたら、そのディレクトリの内容から .zip ファイルを作成し、その .zip ファイルをモジュールキャッシュに展開します。.zip ファイルに含まれるファイルの詳細については、ファイルパスとサイズの制約を参照してください。.zip ファイルのコンテンツは、モジュールキャッシュに抽出する前に、.zip ファイルがプロキシからダウンロードされた場合と同じように、認証されます。

モジュール zip ファイルには、ベンダーディレクトリのコンテンツやネストされたモジュール (go.mod ファイルを含むサブディレクトリ) は含まれません。これは、モジュールがそのディレクトリの外や他のモジュールのファイルを参照しないように注意しなければならないことを意味します。たとえば、//go:embed パターンはネストしたモジュールのファイルにマッチしてはいけません。この動作は、ファイルがモジュールに含まれるべきではない状況での有用な回避策になります。たとえば、リポジトリにある大きなファイルが testdata ディレクトリにチェックインされている場合、モジュールの作者は空の go.mod ファイルを testdata に追加して、ユーザーがそれらのファイルをダウンロードする必要がないようにすることができます。もちろん、これは依存関係をテストするユーザーのカバレッジを低下させるかもしれません。

LICENSE ファイルに関する特別なケース

go コマンドがリポジトリのルートディレクトリにないモジュールの .zip ファイルを作成するとき、そのモジュールのルートディレクトリに LICENSE という名前のファイルがない場合 (go.mod と並んで)、go コマンドはリポジトリのルートディレクトリから LICENSE という名前のファイルが同じリビジョンに存在すればそれをコピーすることになります。

この特別なケースでは、同じ LICENSE ファイルをリポジトリ内のすべてのモジュールに適用することができます。これは、 .txt のような拡張子のない、LICENSE という名前のファイルにのみ適用されます。残念ながら、これは既存のモジュールの暗号和を壊すことなく拡張することはできません。pkg.go.dev のような他のツールやウェブサイトは、他の名前のファイルを認識するかもしれません。

また、go コマンドはモジュールの .zip ファイルを作成するときにシンボリックリンクを含まないことにも注意してください; ファイルパスとサイズの制約を参照してください。その結果、リポジトリがルートディレクトリに LICENSE ファイルを持っていない場合、作者は代わりにサブディレクトリで定義されたモジュールにライセンスファイルのコピーを作成し、それらのファイルがモジュール .zip ファイルに含まれるようにすることができます。

GOVCSによるバージョン管理ツールの制御

git のようなバージョン管理コマンドでモジュールをダウンロードする go コマンドの機能は、どのサーバーからでもコードをインポートできる分散パッケージのエコシステムにとって重要です。また、悪意のあるサーバーが、起動されたバージョン管理コマンドに意図しないコードを実行させる方法を見つけた場合、セキュリティ上の問題となる可能性があります。

機能性とセキュリティのバランスをとるために、goコマンドはデフォルトでgitとhgのみを使用して公開サーバーからコードをダウンロードします。GOPRIVATE環境変数にマッチするパッケージをホストするものとして定義されたプライベートサーバからコードをダウンロードするために、既知のバージョンコントロールシステムを使用します。GitとMercurialだけを許可する根拠は、この2つのシステムが信頼されていないサーバーのクライアントとして実行される問題に最も注意を払ってきたからです。対照的に、Bazaar、Fossil、Subversionは主に信頼され認証された環境で使われており、攻撃対象としてそれほど精査されていません。

バージョン管理コマンドの制限は、コードをダウンロードするためにバージョン管理への直接アクセスを使用する場合にのみ適用されます。プロキシからモジュールをダウンロードする場合、go コマンドは代わりに GOPROXY プロトコルを使用し、これは常に許可されています。デフォルトでは、go コマンドは公開モジュールには Go モジュールミラー (proxy.golang.org) を使い、プライベートモジュールやミラーが公開パッケージの提供を拒否したときのみバージョンコントロールにフォールバックします (通常は法的理由によるものです)。そのため、クライアントはデフォルトで Bazaar、Fossil、または Subversion リポジトリから提供される公開コードにアクセスできます。これらのダウンロードは Go モジュールミラーを使用しており、カスタムサンドボックスを使ってバージョン管理コマンドを実行するというセキュリティリスクを背負っているためです。

GOVCS 変数は、特定のモジュールに対して許可されるバージョンコントロールシステムを変更するために使用できます。GOVCS 変数は、モジュールを使用するモードと GOPATH モードの両方でパッケージをビルドするときに適用されます。モジュールを使うときは、モジュールのパスに対してパターンがマッチします。GOPATH を使用する場合、パターンはバージョンコントロールリポジトリのルートに対応するインポートパスにマッチします。

GOVCS変数の一般的な形式は、pattern:vcslistルールのカンマ区切りのリストである。patternは、モジュールまたはインポートパスの1つ以上の先頭要素に一致する必要があるグロブパターンです。vcslistは許可されたバージョンコントロールコマンドのパイプで区切られたリストで、既知のコマンドの使用を許可する場合はall、何も許可しない場合はoffとなります。モジュールが vcslist off のパターンにマッチしても、オリジンサーバが mod スキームを使用していれば、ダウンロードされるかもしれないことに注意してください。たとえ、それ以降のパターンがマッチしても、リストの中で最も早くマッチしたパターンが適用されます。

たとえば、次のように考えてみましょう。

GOVCS=github.com:git,evil.com:off,*:git |hg

この設定では、モジュールやインポートのパスがgithub.com/で始まるコードはgitだけを使うことができ、 evil.comのパスはどのバージョン管理コマンドも使うことができません。

特殊なパターンである public と private は、モジュールやインポートのパスの公開と非公開にマッチします。パスはGOPRIVATE変数にマッチすればプライベート、そうでなければパブリックとなります。

GOVCS変数にあるルールが特定のモジュールやインポートパスにマッチしない場合、goコマンドはそのデフォルトルールを適用します。これは、GOVCS表記でpublic:git|hg,private:allとまとめることができます。

どのパッケージでも、どのバージョン管理システムでも自由に使えるようにするには、次のようにします。

GOVCS=*:all

バージョン管理の使用をすべて無効にするには、次のようにします。

GOVCS=*:off

go env -w コマンドは、今後の go コマンドの起動時に GOVCS 変数を設定するために使用されます。

GOVCS は Go 1.16 で導入されました。それ以前のバージョンの Go では、どのモジュールでも既知のバージョン管理ツールを使うことができます。

モジュールの zip ファイル

モジュールのバージョンは .zip ファイルとして配布されます。go コマンドはモジュールプロキシとバージョンコントロールリポジトリから自動的にファイルを作成、ダウンロード、抽出するので、これらのファイルと直接やりとりする必要はほとんどありません。しかし、クロスプラットフォーム互換性の制約を理解するため、あるいはモジュールプロクシを実装する際に、これらのファイルについて知っておくことは有用です。

go mod download コマンドは一つまたは複数のモジュールの zip ファイルをダウンロードし、それらのファイルをモジュールキャッシュに抽出します。GOPROXY と他の環境変数に依存して、go コマンドはプロキシから zip ファイルをダウンロードするか、ソースコントロールリポジトリをクローンして、そこから zip ファイルを作成することができます。json フラグを使うと、ダウンロードした zip ファイルとその展開された内容がモジュールキャッシュに格納されている場所を見つけることができます。

golang.org/x/mod/zip パッケージは、zip ファイルの作成、展開、内容の確認をプログラムで行うために使用されるかもしれません。

ファイルパスとサイズの制約

モジュールの zip ファイルの内容には、いくつかの制約があります。これらの制約により、さまざまなプラットフォームで zip ファイルが安全かつ一貫して展開されることが保証されています。

モジュールの zip ファイルのサイズは最大 500 MiB です。go.modファイルは16MiBに制限されています。LICENSE ファイルも 16 MiB に制限されています。これらの制限は、ユーザー、プロキシ、およびモジュール・エコシステムの他の部分に対するサービス拒否攻撃を緩和するために存在します。モジュールのディレクトリツリーに 500 MiB を超えるファイルを含むリポジトリは、モジュールのパッケージをビルドするために必要なファイルのみを含むコミット時に、モジュールのバージョンにタグを付けるべきです。
モジュール zip ファイル内の各ファイルは $module@$version/ というプレフィックスで始まる必要があります。$module はモジュールパス、$version はバージョンで、たとえば golang.org/x/mod@v0.3.0/ のようになります。モジュールパスは有効でなければならず、バージョンも有効かつ正規でなければならず、バージョンもモジュールパスのメジャーバージョンサフィックスと一致していなければなりません。具体的な定義と制限については、「モジュールパスとバージョン」を参照してください。
ファイルのモード、タイムスタンプ、その他のメタデータは無視されます。
空のディレクトリ (パスがスラッシュで終わるエントリ) はモジュール zip ファイルに含まれることがありますが、抽出されることはありません。go コマンドは、作成する zip ファイルに空のディレクトリを含めません。
シンボリックリンクや他の不規則なファイルは、オペレーティングシステムやファイルシステム間で移植性がなく、zip ファイル形式で表現する移植性のある方法がないため、zip ファイルを作成する際には無視されます。
メインモジュール外の vendor ディレクトリは決して使用されないため、 vendor という名前のディレクトリにあるファイルは zip ファイルを作成する際に無視されます。
モジュールのルートディレクトリ以外の go.mod ファイルを含むディレクトリにあるファイルは、モジュールの一部ではないため、zip ファイルを作成する際には無視されます。go コマンドは、zip ファイルを展開するときに、go.mod ファイルを含むサブディレクトリを無視します。
Unicode の大文字小文字変換では、zip ファイル内の 2 つのファイルのパスが同じになることはありません (strings.EqualFold を参照)。これにより、大文字小文字を区別しないファイルシステムで、zip ファイルが衝突することなく展開されることが保証されます。
go.mod ファイルはトップレベルディレクトリ ($module@$version/go.mod) にあってもなくてもかまいません。存在する場合、go.mod という名前 (すべて小文字) でなければなりません。go.modという名前のファイルは、他のディレクトリに置くことはできません。
モジュール内のファイル名とディレクトリ名は、Unicode文字、ASCII数字、ASCIIスペース文字(U+0020)、ASCII句読点文字 !#$%&()+,-.=@[]^_{}~ で構成することができます。パッケージのパスには、これらの文字がすべて含まれていない場合があることに注意してください。違いについては、module.CheckFilePath と module.CheckImportPath を参照してください。
Windows では、最初のドットまでのファイル名やディレクトリ名は、大文字小文字にかかわらず予約ファイル名であってはなりません(CON, com1, NuL など)。

プライベートモジュール

Go モジュールは頻繁に開発され、一般のインターネットでは利用できないバージョン管理サーバーやモジュールプロキシで配布されています。go コマンドはプライベートなソースからモジュールをダウンロードし、ビルドすることができますが、通常はいくつかの設定が必要です。

以下の環境変数を使って、プライベートモジュールへのアクセスを設定することができます。詳しくは 環境変数 を見てください。公開サーバーに送られる情報を制御する方法については プライバシー も参照してください。

GOPROXY - モジュールのプロキシ URL のリストです。go コマンドは順番に各サーバからモジュールをダウンロードしようとします。direct キーワードは、プロキシを使う代わりに、モジュールが開発されているバージョンコントロールリポジトリからモジュールをダウンロードするように、go コマンドに指示します。
GOPRIVATE - プライベートと見なされるモジュール・パス・プレフィックスのグロブ・パターンのリストです。GONOPROXY と GONOSUMDB のデフォルト値として動作します。
GONOPROXY - プロキシからダウンロードされるべきではないモジュール・パスのプレフィックスのグロブ・パターンのリスト。go コマンドは、GOPROXY に関係なく、モジュールが開発されているバージョンコントロールリポジトリからマッチするモジュールをダウンロードします。
GONOSUMDB - 公開チェックサムデータベース sum.golang.org を使ってチェックすべきでない、モジュールパスのプレフィックスのグロブパターンのリスト。
GOINSECURE - HTTP やその他の安全でないプロトコルで取得される可能性がある、モジュールパスのプレフィックスのグロブパターンのリストです。

これらの変数は開発環境で設定することもできますし(例えば .profile ファイル)、go env -w で恒久的に設定することもできます。

このセクションの残りの部分では、プライベートモジュールプロキシとバージョンコントロールリポジトリへのアクセスを提供するための一般的なパターンを説明します。

すべてのモジュールにサービスを提供するプライベートプロキシ

すべてのモジュール (パブリックとプライベート) を扱う中央のプライベートプロキシサーバは、管理者にとって最も制御しやすく、個々の開発者にとって最も設定が少なくてすむものです。

このようなサーバを使うように go コマンドを設定するには、以下の環境変数を設定します。https://proxy.corp.example.com をプロキシの URL に、corp.example.com をモジュールの接頭辞に置き換えてみてください。

GOPROXY=https://proxy.corp.example.com
GONOSUMDB=corp.example.com

GOPROXY 設定は https://proxy.corp.example.com からしかモジュールをダウンロードしないように go コマンドに指示します。go コマンドは他のプロキシやバージョンコントロールリポジトリに接続しません。

GONOSUMDB 設定は corp.example.com で始まるパスのモジュールを認証するために公開チェックサムデータベースを使用しないように go コマンドに指示します。

この設定で動作しているプロキシは、おそらくプライベートなバージョンコントロールサーバへの読み込みアクセスが必要になるでしょう。また、公開モジュールの新しいバージョンをダウンロードするために、公共のインターネットにアクセスする必要があります。

この方法で使用できる GOPROXY サーバーの実装がいくつか存在します。最小限の実装では、モジュールキャッシュディレクトリからファイルを提供し、足りないモジュールを取得するために go mod download を使用します(適切な設定が必要です)。 プライベートプロキシによるプライベートモジュールの提供

プライベートプロキシサーバは一般に利用可能なモジュールを提供することなく、 プライベートなモジュールを提供することができます。go コマンドは、プライベートサーバで利用できないモジュールを公開されているソースにフォールバックするように設定することができます。

go コマンドがこのように動作するように設定するには、以下の環境変数を設定します。https://proxy.corp.example.com をプロキシの URL に、corp.example.com をモジュールの接頭辞に置き換えてください。

GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org,direct GONOSUMDB=corp.example.com

GOPROXY 設定は、go コマンドに、まず https://proxy.corp.example.com からモジュールをダウンロードしようとするように指示します。もしサーバが 404 (Not Found) や 410 (Gone) で応答した場合、go コマンドは https://proxy.golang.org にフォールバックし、その後リポジトリへの直接接続に移行します。

GONOSUMDB 設定は、パスが corp.example.com で始まるモジュールを認証するために、公開チェックサムデータベースを使用しないように go コマンドに指示します。

この設定で使用されるプロキシは、パブリックモジュールにサービスを提供しないにもかかわらず、パブリックモジュールへのアクセスを制御することができることに注意してください。プロキシが 404 や 410 以外のエラーステータスでリクエストに応答した場合、 go コマンドは GOPROXY リストの後のエントリーにフォールバックしません。例えば、不適切なライセンスや既知のセキュリティ脆弱性を持つモジュールに対して、プロキシは 403 (Forbidden) で応答することができます。

プライベートモジュールへの直接アクセス

go コマンドは公開プロキシをバイパスして、バージョンコントロールサーバから直接プライベートモジュールをダウンロードするように設定することができます。これは、プライベートプロキシサーバを実行することが不可能な場合に便利です。

このように go コマンドを設定するには、プライベートモジュールのプレフィックスを corp.example.com に置き換えて、GOPRIVATE を設定します。

GOPRIVATE=corp.example.comとします。

GOPROXY変数は、この状況では変更する必要はありません。デフォルトは https://proxy.golang.org,direct で、go コマンドが最初に https://proxy.golang.org からモジュールをダウンロードしようとし、プロキシが 404 (Not Found) や 410 (Gone) で応答した場合に直接接続にフォールバックするように指示します。

GOPRIVATE 設定は corp.example.com で始まるモジュールについて、プロキシやチェックサムデータベースに接続しないよう go コマンドに指示します。

内部 HTTP サーバーは、モジュールのパスをリポジトリの URL に解決するために必要な場合があります。例えば、go コマンドがモジュール corp.example.com/mod をダウンロードするとき、https://corp.example.com/mod?go-get=1 に GET リクエストを送り、その応答でリポジトリ URL を探します。この要件を回避するには、各プライベートモジュールのパスに、リポジトリのルートプレフィックスを示す VCS サフィックス(.git など)が付いていることを確認します。たとえば、go コマンドで corp.example.com/repo.git/mod というモジュールをダウンロードすると、追加のリクエストをしなくても https://corp.example.com/repo.git あるいは ssh://corp.example.com/repo.git にある Git リポジトリをクローンすることができます。

開発者は、プライベートモジュールを含むリポジトリへの読み取りアクセスが必要になります。これは、.gitconfig のようなグローバルな VCS 設定ファイルで設定することができます。VCS ツールが、対話的な認証プロンプトを必要としないように設定されているとよいでしょう。デフォルトでは、go コマンドは Git を起動する際に GIT_TERMINAL_PROMPT=0 と設定して対話型プロンプトを無効にしますが、これは明示的な設定を尊重します。

プライベートプロキシへの認証情報の受け渡し

go コマンドはプロキシサーバーと通信する際に HTTP 基本認証をサポートします。

認証情報は .netrc ファイルで指定することができます。例えば、以下の行を含む .netrc ファイルは、与えられたユーザー名とパスワードでマシン proxy.corp.example.com に接続するように go コマンドを設定します。

machine proxy.corp.example.com
login jrgopher
password hunter2

ファイルの場所は、NETRC環境変数で設定することができます。NETRCが設定されていない場合、goコマンドはUNIX系プラットフォームでは$HOME/.netrcを、Windowsでは%USERPROFILE%_netrcを読み込みます。

.netrcのフィールドは、スペース、タブ、改行で区切られています。残念ながら、これらの文字はユーザー名やパスワードに使用することはできません。また、マシン名は完全なURLにはできないので、同じマシン上の異なるパスに対して異なるユーザー名とパスワードを指定することはできないことに注意してください。

代わりに、認証情報は GOPROXY の URL で直接指定することもできます。たとえば、以下のようになります。

GOPROXY=https://jrgopher:hunter2@proxy.corp.example.com

この方法をとるときは注意してください:環境変数はシェルの履歴やログに表示されることがあります。

プライベートリポジトリにクレデンシャルを渡す

go コマンドはバージョンコントロールリポジトリから直接モジュールをダウンロードすることができます。これはプライベートプロキシが使われていない場合、プライベートモジュールに必要です。設定方法は Private モジュールへの直接アクセス を見て下さい。

モジュールを直接ダウンロードするとき、go コマンドは git のようなバージョンコントロールツールを実行します。これらのツールは独自の認証を行うので、.gitconfig のようなツール固有の設定ファイルで認証情報を設定する必要があるかもしれません。

これがスムーズに動作するように、go コマンドが正しいリポジトリ URL を使っていることと、バージョン管理ツールが対話的にパスワードの入力を要求しないことを確認してください。go コマンドは、リポジトリの URL を検索するときにスキームを指定しない限り、ssh:// などの他のスキームよりも https:// という URL を優先します。特に GitHub リポジトリの場合は、go コマンドは https:// を想定しています。

ほとんどのサーバーでは、HTTP で認証するようにクライアントを設定することができます。たとえば、GitHub は OAuth パーソナル・アクセストークンを HTTP パスワードとして使用することをサポートしています。プライベートプロキシに認証情報を渡すときのように、HTTP パスワードを .netrc ファイルに保存することができます。

あるいは、https:// の URL を別のスキームに書き換えることもできます。たとえば、.gitconfig の中で

[url "git@github.com:"] 代わりに = https://github.com/

詳しくは、リポジトリのクローンを作成するときに "go get" が HTTPS を使用するのはなぜですか?

プライバシー

go コマンドはモジュールプロキシサーバーやバージョンコントロールシステムからモジュールやメタデータをダウンロードすることがあります。環境変数 GOPROXY は、どのサーバーを使うかを制御します。環境変数 GOPRIVATE と GONOPROXY は、どのモジュールがプロキシから取得されるかを制御します。

GOPROXY のデフォルト値は次のとおりです。

https://proxy.golang.org,direct

この設定では、go コマンドがモジュールやモジュールメタデータをダウンロードするとき、まず Google が運営する公開モジュールプロキシである proxy.golang.org にリクエストを送ります (プライバシーポリシー)。各リクエストで送信される情報の詳細については GOPROXY protocol を参照してください。go コマンドは個人を特定できるような情報を送信しませんが、リクエストされたモジュールのフルパスを送信します。プロキシが 404 (Not Found) や 410 (Gone) で応答した場合、go コマンドはそのモジュールを提供しているバージョン管理システムに直接接続しようとします。詳細は バージョン管理システム を参照してください。

GOPRIVATE や GONOPROXY 環境変数には、プライベートでどのプロキシからも要求されないモジュールの接頭辞にマッチする glob パターンのリストを設定することができます。例えば

GOPRIVATE=.corp.example.com,.research.example.com のようになります。

GOPRIVATE は単に GONOPROXY と GONOSUMDB のデフォルトとして動作するので、GONOSUMDB が異なる値でなければ GONOPROXY を設定する必要はないでしょう。モジュールパスが GONOPROXY と一致する場合、go コマンドはそのモジュールの GOPROXY を無視し、バージョン管理リポジトリから直接モジュールを取得します。これはプライベートモジュールを提供するプロキシがない場合に便利です。プライベートモジュールへの直接アクセス を参照してください。

もし、すべてのモジュールに信頼できるプロキシがあるならば、GONOPROXY は設定されるべきではありません。例えば、GOPROXY が一つのソースに設定されている場合、go コマンドは他のソースからモジュールをダウンロードすることはありません。このような場合でも GONOSUMDB は設定されるべきです。

GOPROXY=https://proxy.corp.example.com GONOSUMDB=.corp.example.com,.research.example.com

プライベートモジュールだけを提供する信頼できるプロキシがある場合、GONOPROXYは設定されるべきではありませんが、プロキシが正しいステータスコードで応答するように注意する必要があります。例えば、次のような設定を考えてみましょう。

GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org GONOSUMDB=.corp.example.com,.research.example.com

タイプミスのために、開発者が存在しないモジュールをダウンロードしようとしたとします。

go mod download corp.example.com/secret-product/typo@latest

go コマンドはまず proxy.corp.example.com にこのモジュールを要求します。もしプロキシが 404 (Not Found) や 410 (Gone) で応答したら、go コマンドは proxy.golang.org にフォールバックして、リクエスト URL に secret-product のパスを送信します。プライベートプロキシが他のエラーコードで応答した場合、goコマンドはそのエラーを表示し、他のソースにフォールバックしません。

プロキシに加えて、go コマンドはチェックサムデータベースに接続し、 go.sum にリストされていないモジュールの暗号ハッシュを検証することができます。環境変数 GOSUMDB は、チェックサムデータベースの名前、URL、公開鍵を設定します。GOSUMDB のデフォルト値は sum.golang.org で、Google が運営する公開チェックサムデータベースです (プライバシーポリシー)。各リクエストで送信される内容についての詳細は チェックサムデータベース を参照ください。プロキシと同様に、go コマンドは個人を特定できる情報を送信しませんが、要求されたモジュールのフルパスを送信し、チェックサムデータベースは公開されていないモジュールのチェックサムを計算することができません。

環境変数 GONOSUMDB は、どのモジュールがプライベートで、チェックサムデータベースから要求すべきではないかを示すパターンに設定されるかもしれません。GOPRIVATE は GONOSUMDB と GONOPROXY のデフォルトとして動作しますので、GONOPROXY が異なる値でない限り、GONOSUMDB を設定する必要はありません。

プロキシはチェックサムデータベースをミラーリングすることができます。GOPROXY のプロキシがこれを行う場合、go コマンドはチェックサムデータベースに直接接続することはありません。

GOSUMDB を off に設定すると、チェックサムデータベースを完全に無効にすることができます。この設定では、go コマンドはダウンロードしたモジュールがすでに go.sum に入っていない限り、認証を行いません。モジュールの認証 を参照してください。

モジュールキャッシュ

モジュールキャッシュは、go コマンドがダウンロードしたモジュールファイルを保存するディレクトリです。モジュールキャッシュは、コンパイル済みのパッケージやその他のビルドアーチファクトを格納するビルドキャッシュとは区別されます。

モジュールキャッシュのデフォルトの場所は$GOPATH/pkg/modです。別の場所を使用するには、GOMODCACHE環境変数を設定します。

モジュール・キャッシュに最大サイズはなく、go コマンドはその内容を自動的に削除しません。

キャッシュは同じマシンで開発された複数のGoプロジェクトで共有されることがあります。go コマンドはメインモジュールの場所に関係なく、同じキャッシュを使用します。go コマンドの複数のインスタンスは、同時に同じモジュールキャッシュに安全にアクセスすることができます。

go コマンドは、ダウンロードした後に誤ってモジュールを変更しないように、キャッシュにモジュールのソースファイルとディレクトリを読み取り専用パーミッションで作成します。このため、キャッシュを rm -rf のようなコマンドで削除するのが難しくなるという不運な副作用があります。キャッシュの削除は go clean -modcache で行えます。また、-modcacherw フラグが使われている場合、go コマンドは read-write パーミッションで新しいディレクトリを作成します。これはエディタ、テスト、その他のプログラムがモジュールキャッシュのファイルを修正するリスクを高めます。go mod verify コマンドは、メインモジュールの依存関係の変更を検出するために使われるかもしれません。これは、各モジュールの依存関係の抽出された内容をスキャンし、それらが go.sum で期待されるハッシュと一致することを確認します。

下の表は、モジュールキャッシュにあるほとんどのファイルの目的を説明しています。いくつかの一時的なファイル(ロックファイル、一時ディレクトリ)は省略されています。各パスについて、$moduleはモジュールのパス、$versionはバージョンです。スラッシュ(/)で終わるパスはディレクトリです。大文字と小文字を区別しないファイルシステムでの衝突を避けるため、モジュールパスとバージョンの大文字は感嘆符でエスケープされます (Azure は !azure としてエスケープされます)。 パスの説明 モジュール@$version/ モジュールの zip ファイルを展開したディレクトリです。これは、ダウンロードしたモジュールのためのモジュールルートディレクトリとして機能します。元のモジュールに go.mod ファイルがない場合は、含まれません。 cache/download/ モジュールプロキシからダウンロードされたファイルや、バージョン管理システムから派生したファイルを含むディレクトリです。このディレクトリのレイアウトは GOPROXY プロトコルに従っているので、HTTP ファイルサーバーによって提供されるとき、または file:// URL で参照されるとき、このディレクトリはプロキシとして使用されるかもしれません。 cache/download/$module/@v/list 既知のバージョンのリストです (GOPROXY プロトコルを参照してください)。これは時間の経過とともに変化する可能性があるので、通常 go コマンドはこのファイルを再利用するのではなく、新しいコピーを取得します。 cache/download/$module/@v/$version.info バージョンに関する JSON メタデータです。(GOPROXYプロトコル参照)。これは時間の経過とともに変化する可能性があるので、goコマンドは通常このファイルを再利用するのではなく、新しいコピーを取得します。 cache/download/$module/@v/$version.mod このバージョンの go.mod ファイルです(GOPROXY プロトコルを参照してください)。元のモジュールに go.mod ファイルがない場合、これは何も要求されない合成されたファイルです。 cache/download/$module/@v/$version.zip モジュールの zip された内容です(GOPROXY プロトコルとモジュールの zip ファイルを参照してください)。 cache/download/$module/@v/$version.ziphash .zip ファイル内のファイルの暗号化ハッシュです。.zip ファイル自体はハッシュ化されないので、ファイルの順番、圧縮、アライメント、メタデータはハッシュに影響を与えないことに注意してください。モジュールを使用する場合、go コマンドはこのハッシュが go.sum の対応する行と一致するかどうかを確認します。go mod verify コマンドは、モジュールの .zip ファイルと展開されたディレクトリのハッシュがこれらのファイルと一致するかどうかをチェックします。 cache/download/sumdb/ チェックサムデータベース(通常はsum.golang.org)からダウンロードしたファイルを格納するディレクトリです。 cache/vcs/ ソースから直接取得したモジュールのバージョンコントロールリポジトリをクローンしたものが含まれています。ディレクトリ名は、リポジトリの種類とURLから得られる16進数のハッシュです。リポジトリは、ディスク上のサイズに最適化されています。例えば、クローンされた Git リポジトリは、可能な限りベアで浅いものとなっています。

モジュールの認証

go コマンドがモジュールの zip ファイルや go.mod ファイルをモジュールキャッシュにダウンロードするとき、暗号ハッシュを計算して既知の値と比較し、ファイルが最初にダウンロードされたときから変更されていないことを確認します。ダウンロードされたファイルが正しいハッシュを持っていない場合、go コマンドはセキュリティエラーを報告します。

go.mod ファイルの場合、go コマンドはファイルの内容からハッシュを計算します。モジュールの zip ファイルでは、go コマンドはアーカイブ内のファイルの名前と内容から決定論的な順序でハッシュを計算します。ハッシュはファイルの順番、圧縮、アライメント、その他のメタデータの影響を受けません。ハッシュの実装の詳細については golang.org/x/mod/sumdb/dirhash を参照してください。

goコマンドは、各ハッシュをメインモジュールのgo.sumファイルの対応する行と比較します。ハッシュがgo.sumのハッシュと異なる場合、goコマンドはセキュリティエラーを報告し、ダウンロードしたファイルをモジュールキャッシュに追加することなく削除します。

go.sum ファイルが存在しないか、ダウンロードしたファイルのハッシュが含まれていない場合、go コマンドはチェックサムデータベース、一般に利用可能なモジュールのハッシュのグローバルソースを使用してハッシュを検証することができます。ハッシュが検証されると、go コマンドはそのハッシュを go.sum に追加し、ダウンロードされたファイルをモジュールキャッシュに追加します。モジュールがプライベートな場合 (環境変数 GOPRIVATE や GONOSUMDB でマッチします) や、チェックサムデータベースが無効な場合 (GOSUMDB=off に設定します) は、 go コマンドはハッシュを受け入れ、それを検証せずにモジュールキャッシュにファイルを追加します。

モジュールキャッシュは通常、システム上のすべての Go プロジェクトで共有され、各モジュールは潜在的に異なるハッシュを持つ独自の go.sum ファイルを持つ可能性があります。他のモジュールを信頼する必要をなくすために、go コマンドはモジュールキャッシュのファイルにアクセスするときはいつでも、メインモジュールの go.sum を使ってハッシュを検証します。Zip ファイルのハッシュは計算コストが高いので、go コマンドはファイルを再ハッシュする代わりに、Zip ファイルと一緒に保存された事前計算されたハッシュをチェックします。go mod verify コマンドは、zip ファイルと展開されたディレクトリがモジュールキャッシュに追加されてから変更されていないことを確認するために使用されるかもしれません。

go.sum ファイル

モジュールはルートディレクトリに go.sum という名前のテキストファイルを go.mod ファイルと一緒に持つことができます。go.sum ファイルには、モジュールの直接および間接的な依存関係の暗号化ハッシュが含まれています。go コマンドはモジュールキャッシュにモジュール .mod や .zip ファイルを ダウンロードする際にハッシュを計算し、そのハッシュがメインモジュールの go.sum ファイルにある対応するハッシュと一致するかどうかをチェックします。

go.sum の各行は空白で区切られた三つのフィールドを持っています: モジュールパス、バージョン (おそらく /go.mod で終わる)、そしてハッシュです。

モジュールパスは、ハッシュが属するモジュールの名前です。
バージョンは、ハッシュが属するモジュールのバージョンです。バージョンが /go.mod で終わっている場合、ハッシュはそのモジュールの go.mod ファイルのみです。それ以外の場合、ハッシュはモジュールの .zip ファイル内のファイルです。
ハッシュの列は、アルゴリズム名(h1など)とbase64エンコードされた暗号ハッシュからなり、コロン(:)で区切られています。現在、SHA-256 (h1) が唯一の対応ハッシュアルゴリズムです。将来、SHA-256の脆弱性が発見された場合、別のアルゴリズム(h2という名前など)のサポートが追加される予定です。

go.sum ファイルには、あるモジュールの複数のバージョンに対するハッシュが含まれていることがあります。go コマンドは、最小限のバージョン選択を行うために、依存関係のある複数のバージョンから go.mod ファイルをロードする必要があるかもしれません。go.sum はまた、(例えば、アップグレード後に)もう必要ないモジュールのバージョンのためのハッシュを含むかもしれません。

チェックサムデータベース

チェックサムデータベースはgo.sumの行のグローバルなソースです。go コマンドは、プロキシやオリジンサーバによる不正な振る舞いを検出するために、 多くの状況でこれを使うことができます。

チェックサムデータベースは、一般に公開されているすべてのモジュールの バージョンについて、グローバルな一貫性と信頼性を確保することを可能にします。これは信頼できないプロキシが、気づかれずに間違ったコードを提供することを可能にします。また、特定のバージョンに関連するビットが、たとえモジュールの作者がその後リポジトリにあるタグを変更したとしても、1日から次の日に変更されないことを保証します。

チェックサムデータベースはGoogleが運営する sum.golang.org によって提供されています。これはgo.sumの行ハッシュの透明なログ(または「Merkle Tree」)であり、Trillianによってバックアップされています。Merkle ツリーの主な利点は、独立した監査人が改ざんされていないことを確認できることで、単純なデータベースよりも信頼性が高くなります。

goコマンドは、Proposalで概説されたプロトコルを使ってチェックサムデータベースとやり取りをします。Proposal: Secure the Public Go Module Ecosystemで説明されているプロトコルを使用して、goコマンドはチェックサムデータベースと対話します。

以下の表は、チェックサムデータベースが応答しなければならないクエリを指定します。各パスについて、$baseはチェックサムデータベースのURLのパス部分、$moduleはモジュールパス、$versionはバージョンです。たとえば、チェックサムデータベースの URL が https://sum.golang.org で、クライアントがモジュール golang.org/x/text のバージョン v0.3.2 のレコードを要求している場合、クライアントは https://sum.golang.org/lookup/golang.org/x/text@v0.3.2 に対する GET リクエストを送信することになります。

大文字小文字を区別しないファイルシステムから提供する場合のあいまいさを避けるために、 $module と $version の要素では大文字をすべてエクスクラメーションマークに置き換え、 その後に対応する小文字を並べるようにしています。これにより、example.com/M と example.com/m の両方のモジュールをディスクに格納することができます。前者は example.com/!m とエンコードされるからです。

.p/$W]のように角括弧で囲まれたパスの部分は、オプションの値を表します。 パスの説明 base/latest 最新のログの符号化されたツリーの説明を返します。この署名された説明は note の形式をとっており、一つ以上のサーバの鍵で署名され、サーバの公開鍵で検証可能なテキストとなっています。ツリーの説明では、ツリーのサイズと、そのサイズでのツリーヘッドのハッシュが提供されます。このエンコーディングgolang.org/x/mod/sumdb/tlog#FormatTree で説明されています。 base/lookup/$module@$version $version における $module についてのエントリのログレコード番号と、そのレコードのデータ (つまり $version における $module の go.sum 行) と、そのレコードを含む符号化されたツリーの説明を返します。 base/tile/$H/$L/$K[.p/$W] log tile を返します。これは、ログのセクションを構成するハッシュのセットです。各タイルは、タイルレベル$L、左から$K番目の二次元座標で定義され、タイルの高さは$Hである。オプションの.p/$Wサフィックスは、$Wハッシュのみを含む部分的なログ・タイルを示す。クライアントは、部分タイルが見つからない場合、完全なタイルをフェッチするようにフォールバックしなければなりません。 base/tile/$H/data/$K[.p/$W] /tile/$H/0/$K[.p/$W]のリーフハッシュのレコードデータを返します(リテラルのデータパス要素付き)。

goコマンドがチェックサムデータベースを参照する場合、最初のステップは/lookupエンドポイントを介してレコードデータを取得することである。モジュールのバージョンがまだログに記録されていない場合、チェックサムデータベースは返信する前にオリジンサーバーからそれを取得しようとします。この/lookupデータは、このモジュールバージョンの合計とログ内の位置を提供し、証明を実行するためにどのタイルがフェッチされるべきかをクライアントに通知します。go コマンドは、メインモジュールの go.sum ファイルに新しい go.sum 行を追加する前に、「包含」証明 (特定のレコードがログに存在すること) と「一貫性」証明 (ツリーが改竄されていないこと) を実行します。重要なのは、/lookup からのデータは、最初に署名済みツリーハッシュに対する認証と、署名済みツリーハッシュのクライアントのタイムラインに対する認証なしに、決して使用されるべきでないということです。

署名済みツリーハッシュとチェックサムデータベースによって提供される新しいタイルはモジュールキャッシュに格納されるため、goコマンドは不足しているタイルを取得する必要があるだけです。

go コマンドはチェックサムデータベースに直接接続する必要はありません。チェックサムデータベースをミラーし、上記のプロトコルをサポートするモジュールプロキシを通じて、モジュールサムを要求することができます。これは、組織外の要求をブロックするようなプライベートな企業のプロキシに特に有用です。

環境変数 GOSUMDB は、使用するチェックサムデータベースの名前と、 オプションでその公開鍵や URL を次のように指定します。

GOSUMDB="sum.golang.org" GOSUMDB="sum.golang.org+"のようになります。 GOSUMDB="sum.golang.org+ https://sum.golang.org "のようにします。

goコマンドはsum.golang.orgの公開鍵を知っており、sum.golang.google.cn(中国本土で利用可能)という名前はsum.golang.orgチェックサムデータベースに接続することも知っています。URLのデフォルトは https:// で、その後にデータベース名が続きます。

GOSUMDB のデフォルトは sum.golang.org で、これは Google が運営する Go チェックサムデータベースです。このサービスのプライバシーポリシーについては、https://sum.golang.org/privacy を参照してください。

GOSUMDB を off に設定した場合、または go get を -insecure フラグで起動した場合、チェックサムデータベースは参照されず、すべての認識されないモジュールが受け入れられますが、すべてのモジュールに対して検証済みの繰り返しダウンロードというセキュリティ保証はあきらめることになります。特定のモジュールのためにチェックサムデータベースをバイパスする良い方法は、GOPRIVATE または GONOSUMDB 環境変数を使用することです。詳しくは Private Modules を参照してください。

go env -w コマンドは今後の go コマンドの起動時にこれらの変数を設定するために使用されます。

環境変数

go コマンドにおけるモジュールの動作は、以下に挙げる環境変数を使って設定することができます。このリストには、モジュールに関連する環境変数のみが含まれています。go コマンドが認識するすべての環境変数の一覧は go help environment を参照してください。 変数 説明 GO111MODULE

go コマンドがモジュールを認識するモードで実行されるか、GOPATH モードで実行されるかを制御します。3つの値が認識されます。

off: go コマンドは、go.mod ファイルを無視し、GOPATH モードで実行されます。
on (または unset): go コマンドは、go.mod ファイルが存在しない場合でも、モジュールを認識するモードで実行されます。
auto: go.mod ファイルがカレントディレクトリや親ディレクトリに存在する場合、go コマンドはモジュールを意識したモードで実行されます。Go 1.15 およびそれ以下では、これがデフォルトでした。

詳しくは、モジュールを意識したコマンドを参照してください。 GOMODCACHE

go コマンドがダウンロードしたモジュールと関連ファイルを保存するディレクトリ。このディレクトリの構造の詳細については、モジュールキャッシュを参照してください。

GOMODCACHE が設定されていない場合、デフォルトは $GOPATH/pkg/mod です。 GOINSECURE

常に安全でない方法で取得される可能性があるモジュールパスのプレフィックスのグロブパターン (Go の path.Match の構文) のカンマ区切りのリストです。直接取得される依存関係にのみ適用されます。

go get の -insecure フラグとは異なり、GOINSECURE はモジュールのチェックサムデータベース検証を無効にしません。GOPRIVATE や GONOSUMDB がそのために使用されるかもしれません。 GONOPROXY

モジュールプロキシからではなく、常にバージョンコントロールリポジトリから直接取得されるべきモジュールパスのプレフィックスのグロブパターン(Go の path.Match の構文)のカンマ区切りのリストです。

GONOPROXY が設定されていない場合、デフォルトは GOPRIVATE です。プライバシーを参照してください。 GONOSUMDB

チェックサムデータベースを使ってチェックサムを検証しないモジュールパスのプレフィックスのグロブパターン (Go の path.Match の構文) をカンマ区切りにしたリストです。

GONOSUMDB が設定されていない場合、デフォルトは GOPRIVATE です。プライバシー を参照してください。 GOPATH

GOPATH モードでは、GOPATH 変数は Go コードを含む可能性のあるディレクトリのリストです。

モジュール対応モードでは、モジュールキャッシュは最初の GOPATH ディレクトリの pkg/mod サブディレクトリに格納されます。キャッシュの外にあるモジュールのソースコードは、任意のディレクトリに格納することができます。

GOPATH が設定されていない場合、デフォルトではユーザのホームディレク トリにある go サブディレクトリに格納されます。 GOPRIVATE プライベートとみなされるモジュールパスのプレフィックスのグロブパターン(Go の path.Match の構文)のカンマ区切りのリストです。GOPRIVATE は GONOPROXY と GONOSUMDB のデフォルト値です。プライバシーを参照してください。GOPRIVATE はまた、GOVCS でモジュールがプライベートとみなされるかどうかを決定します。 GOPROXY

カンマ(,)またはパイプ(|)で区切られたモジュールプロキシURLのリストです。go コマンドがモジュールに関する情報を検索するとき、成功した応答かターミナルエラーを受け取るまで、リストの各プロキシに順番に連絡します。プロキシは 404 (Not Found) や 410 (Gone) ステータスで応答して、 そのサーバがモジュールを利用できないことを示すことがあります。

go コマンドのエラーフォールバックの動作は、URL の間のセパレータ文字によって決まります。プロキシ URL の後にカンマがある場合、go コマンドは 404 または 410 エラーの後に次の URL にフォールバックし、他のすべてのエラーはターミナルと見なされます。プロキシ URL の後にパイプが続く場合、go コマンドはタイムアウトのような非 HTTP エラーを含む、あらゆるエラーの後に次のソースにフォールバックします。

GOPROXY URL は https, http, file のスキームを持つことができます。URL がスキームを持たない場合、https と見なされます。モジュールキャッシュは、ファイルプロキシとして直接使用することができます。

GOPROXY=file://$(go env GOMODCACHE)/cache/download。

プロキシ URL の代わりに、2 つのキーワードを使うことができます。

off: 任意のソースからのモジュールのダウンロードを禁止します。
direct: モジュールプロキシではなく、バージョンコントロールリポジトリから直接ダウンロードします。

GOPROXY のデフォルトは https://proxy.golang.org,direct です。この設定では、go コマンドはまず Google が運営する Go モジュールミラーに接続し、ミラーにモジュールがない場合は直接接続にフォールバックします。ミラーのプライバシーポリシーについては、https://proxy.golang.org/privacy を参照してください。GOPRIVATE と GONOPROXY 環境変数を設定して、特定のモジュールがプロキシを使ってダウンロードされるのを防ぐことができます。プライベートプロキシの設定については Privacy を参照してください。

プロキシの使い方については、モジュールプロキシとパッケージをモジュールに解決するを参照してください。 GOSUMDB

使用するチェックサムデータベースの名前と、オプションでその公開キーと URL を指定します。例えば

GOSUMDB="sum.golang.org"。 GOSUMDB="sum.golang.org+"。 GOSUMDB="sum.golang.org+ https://sum.golang.org"

goコマンドはsum.golang.orgの公開鍵と、sum.golang.google.cn(中国本土で利用可能)という名前がsum.golang.orgデータベースに接続することを認識しています; 他のデータベースを使用するには明示的に公開鍵を与える必要があります。URLのデフォルトは https:// で、その後にデータベース名が続きます。

GOSUMDB のデフォルトは sum.golang.org で、これは Google が運営する Go チェックサムデータベースです。このサービスのプライバシーポリシーについては、https://sum.golang.org/privacy を参照してください。

GOSUMDB を off に設定した場合、または go get を -insecure フラグで起動した場合、チェックサムデータベースは参照されず、すべての認識されないモジュールが受け入れられますが、すべてのモジュールに対して検証済みの繰り返しダウンロードというセキュリティ保証はあきらめることになります。特定のモジュールのためにチェックサムデータベースをバイパスする良い方法は、GOPRIVATE や GONOSUMDB 環境変数を使用することです。

詳しくは Authenticating modules and Privacy を見てください。 GOVCS

go コマンドがパブリックモジュールとプライベートモジュール(パスが GOPRIVATE のパターンに一致するかどうかで定義されます)、またはグロブパターンに一致する他のモジュールをダウンロードするために使用するバージョンコントロールツールのセットを制御します。

GOVCS が設定されていない場合、あるいはモジュールが GOVCS のどのパターンにもマッチしない場合、go コマンドはパブリックモジュールには git と hg を、プライベートモジュールには任意の既知のバージョンコントロールツールを使用することができます。具体的には、go コマンドは GOVCS が設定されているかのように動作します。

public:git|hg,private:allに設定されているように動作します。

完全な説明は、GOVCSを使ったバージョンコントロールツールの制御を参照してください。 GOWORK

環境変数 GOWORK は、 go コマンドに対して、提供された [go.work ファイル] (#go-work-file) を使ってワークスペースを定義し、ワークスペース・モードに移行するように指示します。GOWORKoffに設定すると、ワークスペース・モードは無効になります。例えば、GOWORK=off go build .はシングルモジュールモードで.パッケージをビルドします。GOWORKが空の場合、goコマンドは [Workspaces](#workspaces) セクションにあるようにgo.work` ファイルを検索して実行されます。

用語解説

ビルド制約 パッケージをコンパイルする際に、Goのソースファイルを使用するかどうかを決定する条件です。ビルド制約は、ファイル名のサフィックス(たとえば、foo_linux_amd64.go)またはビルド制約コメント(たとえば、 // +build linux,amd64 )で表現することができます。ビルド制約を参照してください。

ビルドリスト go build, go list, go test などのビルドコマンドで使用されるモジュールのバージョンのリストです。ビルドリストは、メインモジュールの go.mod ファイルと、過渡的に必要とされるモジュールの go.mod ファイルから、最小限のバージョン選択を使って決定されます。ビルドリストには、特定のコマンドに関連するモジュールだけでなく、モジュールグラフにあるすべてのモジュールのバージョンが含まれます。

正規版(canonical version)。ビルドメタデータの接尾辞に +incompatible 以外がない、正しくフォーマットされたバージョン。たとえば、v1.2.3 は正規のバージョンですが、v1.2.3+meta は正規のバージョンではありません。

current モジュール。main moduleと同義語。

deprecated module(非推奨モジュール)。作者によってサポートされなくなったモジュール (ただし、メジャーバージョンはこの目的では別モジュールとみなされます)。deprecated モジュールは、最新版の go.mod ファイルに deprecation のコメントが付けられます。

直接依存。メインモジュールのパッケージやテスト、またはそのようなパッケージを含むモジュールの .go ソースファイル内の import 宣言でパスが表示されるパッケージ。(間接依存を比較してください)。

直接モード。モジュールプロキシとは対照的に、バージョンコントロールシステムから直接モジュールをダウンロードするための環境変数の設定です。GOPROXY=direct はすべてのモジュールに対してこれを行います。GOPRIVATE と GONOPROXY は、パターンのリストにマッチするモジュールに対してこれを行ないます。

go.mod ファイル。モジュールのパス、要件、その他のメタデータを定義するファイルです。モジュールのルートディレクトリに置かれます。go.modファイルについてのセクションを参照してください。

go.workファイル ワークスペースで使用するモジュールのセットを定義するファイルです。go.workファイルのセクションを参照してください。

インポートパス。Go ソース ファイルでパッケージをインポートするために使用する文字列。パッケージパスと同義です。

間接的な依存関係。メインモジュールのパッケージまたはテストによって推移的にインポートされるが、そのパスはメインモジュールのどのインポート宣言にも現れないパッケージ、またはモジュールグラフに表示されるがメインモジュールによって直接インポートされるパッケージを提供しないモジュール。(直接依存を比較してください)

遅延モジュール読み込み。Go 1.17 以降で指定されたモジュールで、モジュールグラフを必要としないコマンドの読み込みを回避する Go 1.17 の変更点。遅延モジュール ローディングを参照してください。

main module。go コマンドが呼び出されるモジュール。メインモジュールは、現在のディレクトリまたは親ディレクトリにある go.mod ファイルによって定義されます。モジュール、パッケージ、およびバージョン」を参照してください。

メジャーバージョン。セマンティックバージョンの最初の数字 (v1.2.3 では 1)。互換性のない変更を含むリリースでは、メジャーバージョンをインクリメントし、マイナーバージョンとパッチバージョンを 0 にしなければなりません。 メジャーバージョン 0 のセマンティックバージョンは不安定とみなされます。

major version subdirectory (メジャーバージョンサブディレクトリ): バージョン管理リポジトリ内で、モジュールのメジャーバージョンサフィックスと一致する、モジュールを定義することができるサブディレクトリ。たとえば、ルートパス example.com/mod のリポジトリにあるモジュール example.com/mod/v2 は、リポジトリのルートディレクトリかメジャーバージョンサブディレクトリ v2 で定義することができます。リポジトリ内のモジュールディレクトリを参照してください。

メジャーバージョンサフィックス。メジャーバージョン番号に一致するモジュールパス・サフィックス。たとえば、example.com/mod/v2 の /v2 のようになります。メジャーバージョンサフィックスは v2.0.0 以降で必要となり、それ以前のバージョンでは許可されません。メジャーバージョンサフィックスのセクションを参照してください。

最小バージョン選択 (MVS): ビルドで使用されるすべてのモジュールのバージョンを決定するために使用されるアルゴリズム。詳しくは「最小バージョン選択」のセクションをご覧ください。

マイナーバージョン: セマンティックバージョンの2番目の数字(v1.2.3では2)。新しい後方互換性のある機能を持つリリースでは、マイナーバージョンをインクリメントし、パッチバージョンを0に設定しなければならない。

モジュール: リリースされ、バージョン管理され、一緒に配布されるパッケージの集合体。

モジュールキャッシュ: GOPATH/pkg/mod にある、ダウンロードしたモジュールを保存するローカルディレクトリ。モジュールキャッシュを参照。

モジュールグラフ。モジュール要求の有向グラフで、メインモジュールをルートとする。グラフの各頂点はモジュールで、各辺は go.mod ファイルの require 文のバージョンです (メインモジュールの go.mod ファイルの replace と exclude 文が適用されます)。

モジュールグラフの刈り込み。Go 1.17 の変更で、go 1.17 以降を指定するモジュールの推移的依存関係を省略してモジュールグラフのサイズを縮小します。モジュールグラフの刈り込み」を参照してください。

モジュールパス。モジュールを識別するためのパスで、モジュール内のパッケージのインポートパスのプレフィックスとして機能します。例えば、"golang.org/x/net" のようなものです。

モジュールプロキシ。GOPROXY プロトコルを実装した Web サーバー。go コマンドは、モジュールプロキシからバージョン情報、go.mod ファイル、およびモジュール zip ファイルをダウンロードします。

モジュールルートディレクトリ。モジュールを定義する go.mod ファイルを含むディレクトリ。

モジュールサブディレクトリ (module subdirectory)。リポジトリルートパスの後のモジュールパスの一部で、モジュールが定義されているサブディレクトリを表します。空でない場合、モジュールサブディレクトリは意味的なバージョンタグの接頭辞にもなります。メジャーバージョンサフィックスがある場合は、モジュールがメジャーバージョンサブディレクトリにある場合でも、モジュールサブディレクトリには含まれません。モジュールパス参照。

パッケージ。同じディレクトリにあるソースファイルの集まりで、一緒にコンパイルされるもの。Go 言語仕様のパッケージのセクションを参照してください。

パッケージのパス。パッケージを一意に識別するためのパス。パッケージパスは、モジュールパスとモジュール内のサブディレクトリを結合したものです。たとえば "golang.org/x/net/html" は、モジュール "golang.org/x/net" の "html" サブディレクトリにあるパッケージのパッケージパスとなります。import pathと同義です。

パッチバージョン。セマンティックバージョンの3番目の数字(v1.2.3では3)。モジュールのパブリックインターフェイスに変更がないリリースでは、パッチバージョンをインクリメントする必要があります。

プレリリースバージョン。ダッシュの後にドットで区切られた一連の識別子をパッチバージョンの直後に付けたバージョンで、例えば v1.2.3-beta4 のようなものです。プレリリースバージョンは不安定なものとみなされ、他のバージョンとの互換性は想定されていません。プレリリースバージョンは、対応するリリースバージョンの前にソートされます: v1.2.3-pre は v1.2.3 の前です。リリースバージョンも参照してください。

擬似バージョン。リビジョン識別子(Gitコミットハッシュなど)とバージョン管理システムからのタイムスタンプをエンコードしたバージョン。例えば v0.0.0-20191109021931-daa7c04131f5 のようになります。 モジュール以外のリポジトリとの互換性のためや、タグ付きバージョンが利用できない場合に使用されます。

リリースバージョン。プレリリースサフィックスを除いたバージョン。たとえば v1.2.3 のように、v1.2.3-pre ではなく、v1.2.3-pre です。プレリリースバージョンも参照してください。

リポジトリルートパス。モジュールパスのうち、バージョン管理リポジトリのルートディレクトリに対応する部分。モジュールパスを参照。

リトラクタブルバージョン。時期尚早に公開されたため、または公開後に深刻な問題が発見されたため、依存してはいけないバージョン。retract ディレクティブを参照してください。

セマンティックバージョンタグ。バージョンを特定のリビジョンにマップするバージョンコントロールリポジトリのタグ。バージョンとコミットの対応付けを参照してください。

選択されたバージョン。最小限のバージョン選択によって選ばれた、あるモジュールのバージョン。選択されたバージョンは、モジュールグラフで見つかったモジュールのパスの最高バージョンです。

vendor ディレクトリ。vendor という名前のディレクトリで、メインモジュールのパッケージをビルドするために必要な他のモジュールのパッケージが含まれています。go mod vendor で管理されます。ベンダリングを参照してください。

バージョン。モジュールのイミュータブルスナップショットの識別子で、v の後にセマンティックバージョンと書きます。バージョン のセクションを参照してください。

ワークスペース。最小バージョン選択 (MVS) を実行するときにメインモジュールとして使用される、ディスク上のモジュールの集まり。ワークスペースの項を参照

Go ガーベッジコレクタのガイド 翻訳

以下のページをdeeplにかけたもの

tip.golang.org

はじめに

このガイドは、Go ガベージコレクタに関する洞察を提供することで、Go の上級ユーザーがアプリケーションのコストをよりよく理解できるようにすることを目的としています。また、Go ユーザーがこれらの洞察を利用して、アプリケーションのリソース使用率を向上させる方法についてのガイダンスも提供します。ガベージコレクションの知識は必要ありませんが、Go プログラミング言語に精通していることが前提です。

ほとんどの場合、Goの開発者はGoの値がどこに保存されているか、また、保存されているとしてもその理由を気にする必要はありません。しかし、実際には、これらの値はしばしばコンピュータの物理メモリに格納される必要があり、物理メモリは有限の資源です。有限であるため、Goプログラムの実行中にメモリが不足しないように、注意深く管理し、再利用する必要があります。必要に応じてメモリを割り当てたりリサイクルしたりするのは、Goの実装の仕事です。

メモリを自動的にリサイクルすることを表す別の用語として、ガベージコレクションがあります。ガベージコレクタ(略してGC)は、高レベルでは、メモリのどの部分が不要になったかを特定することによって、アプリケーションに代わってメモリを再利用するシステムです。Goの標準ツールチェーンは、すべてのアプリケーションに同梱されるランタイムライブラリを提供しており、このランタイムライブラリにはガベージコレクタが含まれています。

このガイドで説明されているガベージコレクタの存在は、Goの仕様では保証されておらず、Goの値のための基礎となるストレージが言語自体によって管理されていることだけが保証されていることに注意してください。この省略は意図的なもので、根本的に異なるメモリ管理技術を使用することができます。

したがって、このガイドはプログラミング言語Goの特定の実装に関するものであり、他の実装には適用されないかもしれません。具体的には、以下のガイドは、標準的なツールチェーン(gc Goコンパイラとツール)に適用されます。GccgoとGollvmはどちらも非常によく似たGCの実装を使用しているので、同じ概念の多くが適用されますが、詳細は異なる場合があります。

さらに、この文書は生きている文書であり、Goの最新リリースを最もよく反映するために時間の経過とともに変更される予定です。このドキュメントは、現在、Go 1.19のガベージコレクタについて説明しています。

Goの値が生きる場所

GCについて説明する前に、まず、GCで管理する必要のないメモリについて説明します。

たとえば、ローカル変数に格納された非ポインタの Go 値は、おそらく Go GC によってまったく管理されず、代わりに Go は、レキシカルスコープに関連付けられたメモリが作成されるよう手配します。一般に、これはGCに依存するよりも効率的です。Goコンパイラは、メモリがいつ解放されるかを事前に決定し、クリーンアップする機械命令を出すことができるからです。一般に、この方法でGo値のメモリを確保することを「スタック確保」と呼びますが、これはその領域がgoroutineのスタックに格納されるからです。

Goコンパイラがその寿命を判断できないため、この方法でメモリを確保できないGo値は、ヒープにエスケープされると言われています。「ヒープとは、囲碁の値をどこかに置かなければならないときのための、メモリ割り当ての受け皿のようなものだと思えばよいでしょう。ヒープにメモリを割り当てる行為は、一般に「動的メモリ割り当て」と呼ばれます。これは、コンパイラとランタイムの両方が、このメモリがどのように使用され、いつクリーンアップされるかについて、ほとんど仮定できないからです。そこでGCの出番です。GCは、動的なメモリ割り当てを明確に識別してクリーンアップするシステムです。

Goの値がヒープにエスケープされなければならない理由はたくさんあります。そのひとつは、サイズが動的に決定されるからです。たとえば、スライスのバックアレイの初期サイズが定数ではなく、変数によって決定される場合を考えてみましょう。ヒープへの退避は推移的でなければならないことに注意してください。あるGo値への参照が、退避がすでに決定されている別のGo値に書き込まれた場合、その値も退避しなければなりません。

Goの値がエスケープされるかどうかは、その値が使用されるコンテキストとGoコンパイラエスケープ分析アルゴリズムによるものです。値がエスケープされるタイミングを正確に列挙するのはもろく、困難です。アルゴリズム自体はかなり高度で、Goのリリースごとに変化します。どの値がエスケープされ、どの値がエスケープされないかを識別する方法の詳細については、ヒープ割り当ての除去のセクションを参照してください。

ガベージコレクションのトレース

ガベージコレクションは、メモリを自動的にリサイクルする多くの異なる方法、例えば参照カウントを指すことがあります。この文書では、ガベージコレクションは、ポインターを推移的に追うことによって使用中の、いわゆるライブのオブジェクトを識別する、トレースガベージコレクションを指します。

これらの用語をより厳密に定義してみましょう。

  • オブジェクト-1つまたは複数のGo値を含む、動的に割り当てられたメモリの一部です。
  • ポインター - オブジェクト内の任意の値を参照するメモリアドレス。これには当然、*T 形式の Go 値が含まれますが、ビルトイン Go 値の一部も含まれます。文字列、スライス、チャンネル、マップ、およびインターフェイス値はすべて、GC が追跡する必要があるメモリ アドレスを含んでいます。

一緒に、オブジェクトと他のオブジェクトへのポインタは、オブジェクトグラフを形成しています。ライブメモリを識別するために、GCは、プログラムのルート、プログラムによって確実に使用されているオブジェクトを識別するポインタから始まるオブジェクトグラフを歩く。ルートには、ローカル変数とグローバル変数の2つの例があります。オブジェクトグラフを歩くプロセスは、スキャンと呼ばれます。

この基本的なアルゴリズムは、すべてのトレースGCに共通です。トレースGCが異なる点は、メモリが生きていることを発見したときに何をするかです。GoのGCはマークスイープ技術を使用します。これは、その進捗を追跡するために、GCはまた、それがライブとして遭遇する値をマークすることを意味します。トレースが完了すると、GCはヒープ内のすべてのメモリ上を歩いて、割り当てのために利用可能なマークされていないすべてのメモリを作成します。このプロセスは、スイープと呼ばれています。

あなたがよく知る代替技術の1つは、実際にメモリの新しい部分にオブジェクトを移動し、後ですべてのアプリケーションのポインタを更新するために使用される転送ポインタを残しておくことです。このようにオブジェクトを移動させるGCを移動GCと呼びますが、Goには移動しないGCがあります。

GCのサイクル

GoのGCはマーク・スイープGCであるため、大まかにマークフェーズとスイープフェーズの2つのフェーズで動作します。この文は同語反復に見えるかもしれませんが、重要な洞察を含んでいます:すべてのメモリがトレースされるまで、割り当てられるようにメモリを解放することはできません。なぜなら、スキャンされていないポインタがまだオブジェクトを生かしているかもしれないからです。その結果、スイーピングの動作はマーキングの動作と完全に分離されなければなりません。さらに、GCに関連する作業がない場合、GCは全く活動しないこともあります。GC は、掃引、オフ、マーキングの 3 つのフェーズを連続的に回転させ、GC サイクルとし て知られています。この文書では、GCサイクルは掃引で始まり、オフ、そしてマーキングであると考えます。

次のいくつかのセクションでは、ユーザー自身の利益のためにGCパラメータを微調整するのに役立つGCのコストに対する直感を構築することに焦点を当てます。

コストを理解する

GC は、本質的に、さらに複雑なシステムの上に構築された、複雑なソフトウェアの一部です。GCを理解し、その動作を微調整しようとすると、細部に入り込んでしまいがちです。このセクションでは、Go GCのコストとチューニングパラメータについて推論するためのフレームワークを提供することを目的としています。

まず最初に、3つの単純な公理に基づくGCコストのこのモデルを考えてみましょう。

  1. GCは、2つのリソースのみを含む。CPU 時間と物理メモリです。
  2. GCのメモリコストは、ライブヒープメモリ、マークフェーズの前に割り当てられた新しいヒープメモリ、および以前のコストに比例するとしても、比較的に小さいメタデータのためのスペースで構成されています。(注): ライブヒープメモリは、前のGCサイクルによってライブであると決定されたメモリであり、新しいヒープメモリは現在のサイクルで割り当てられた任意のメモリであり、それは終了までにライブであるかどうかわからない。
  3. GC の CPU コストは、サイクルごとの固定コストと、ライブヒープ のサイズに比例してスケールする限界コストとしてモデル化されます。 (注):漸近的に言えば、掃引はマーキングやスキャンよりもスケールが悪く、生きていない(つまり「死んでいる」)と判断されたメモリを含むヒープ全体のサイズに比例して作業を実行しなければならないからです。しかし、現在の実装では、掃引はマーキングやスキャンよりも非常に高速であるため、この議論ではその関連コストを無視することができます。

このモデルは単純ですが効果的で、GC の支配的なコストを正確に分類しています。しかし、このモデルは、これらのコストの大きさについて、また、それらがどのように相互作用するかについて、何も述べていません。それをモデル化するために、以下の状況を考えてみましょう(以下、定常状態と呼びます)。

  • アプリケーションが新しいメモリを割り当てる速度(1秒あたりのバイト数)は一定です。

(注意):この割り当て速度は、新しいメモリがライブであるかどうかとは完全に別であることを理解することが重要です。どれもがライブである可能性もあるし、すべてがライブである可能性もあるし、一部がライブである可能性もあります。(さらに、古いヒープメモリも死ぬ可能性があるので、そのメモリが生きていれば生きているヒープサイズが大きくなるとは限りません)。もっと具体的に言うと、あるWebサービスが、処理する各リクエストに2MiBの総ヒープメモリを割り当てているとします。リクエストが処理されている間、2MiBのうちせいぜい512KiBが生き続け、サービスがリクエストの処理を終えると、そのメモリはすべて死んでしまいます。さて、簡単のために、各リクエストがエンドツーエンドで処理するのに1秒程度かかるとします。そして、安定したリクエストのストリーム、例えば毎秒100リクエストの場合、200MiB/sのアロケーションレートと50MiBのピークライブヒープが発生します。

  • アプリケーションのオブジェクトグラフは、毎回ほぼ同じに見えます(オブジェクトは同じような大きさで、ポインタの数はほぼ一定、グラフの最大深度はほぼ一定です)。

もう一つの考え方は、GC限界費用が一定であることです。

注:定常状態は作為的に見えるかもしれませんが、ある一定の作業負荷の下でのアプリケーションの挙動を代表しています。もちろん、アプリケーションの実行中にも作業負荷は変化しますが、一般的にアプリケーションの動作は、この定常状態の束をつなぎ合わせて、その間にいくつかの過渡的な動作を挟んだようなものになります。

注:定常状態は、ライブヒープについて何も仮定していません。ヒープはGCサイクルのたびに増大するかもしれませんし、縮小するかもしれませんし、同じかもしれません。しかし、この後の説明でこれらの状況をすべて網羅しようとすると面倒であり、あまり説明的でないため、このガイドではライブヒープが一定である例に焦点を当てます。GOGCのセクションでは、ライブヒープが一定でないシナリオをもう少し詳しく説明します。

ライブヒープのサイズが一定である定常状態では、同じ時間が経過した後にGCが実行される限り、すべてのGCサイクルはコストモデルで同じに見えるようになる予定です。それは、アプリケーションによる割り当て速度が一定であれば、その一定時間内に、一定量の新しいヒープメモリが割り当てられるからです。つまり、ライブヒープのサイズが一定で、その新しいヒープメモリが一定であれば、メモリ使用量は常に同じになります。そして、ライブヒープが同じサイズであるため、GC の限界 CPU コストは同じになり、ある一定の間隔で固定コストが発生することになります。

ここで、GCが実行されるポイントを時間的に遅くずらしたとしたらどうでしょう。その場合、より多くのメモリが割り当てられますが、各 GC サイクルはまだ同じ CPU コストを発生させます。しかし、他の固定された時間枠では、より少ないGCサイクルが終了し、結果として全体的なCPUコストが低くなります。もしGCが時間的に早く開始することを決めたら、その逆で、より少ないメモリが割り当てられ、より頻繁にCPUコストが発生することになります。

この状況は、GC が実際に実行する頻度によって制御される、CPU 時間とメモリ間の基本的なトレードオフを表し ています。言い換えれば、トレードオフGC の頻度によって完全に定義されます。

もう一つの詳細は定義されるままであり、それはGCが開始することを決定すべき時です。これは、特定の定常状態におけるGCの頻度を直接設定し、トレードオフを定義することに注意してください。Goでは、GCがいつ開始されるべきかを決定することは、ユーザーが制御できる主なパラメータです。

GOGC

高いレベルでは、GOGC は GC の CPU とメモリの間のトレードオフを決定します。

それは、各GCサイクルの後にターゲットヒープサイズ、次のサイクルでの総ヒープサイズの目標値を決定することによって動作します。GCの目標は、ヒープサイズの合計がターゲットヒープサイズを超える前に収集サイクルを終了することです。総ヒープサイズは、前のサイクルの終了時のライブヒープサイズと、前のサイクル以降にアプリケーションによって割り当てられた任意の新しいヒープメモリとして定義されます。一方、ターゲットヒープメモリは次のように定義される。

ターゲットヒープメモリ = ライブヒープ + (ライブヒープ + GCルート) * GOGC / 100

例として、ライブヒープが8MiB、ゴルーチンスタックが1MiB、グローバル変数へのポインタが1MiBのGoプログラムを考えてみましょう。GOGC値が100の場合、次のGCが実行されるまでに割り当てられる新しいメモリの量は10MiB、つまり10MiBの作業の100%となり、ヒープの総フットプリントは18MiBとなります。GOGC値が50の場合は、50%、つまり5MiBになります。GOGCの値が200の場合は、200%、つまり20MiBになります。

注意: GOGC には、Go 1.18 以降、ルート セットのみが含まれます。以前は、ライブヒープのみをカウントしていました。多くの場合、goroutineスタックのメモリ量は非常に小さく、ライブヒープ・サイズはGC作業の他のすべての原因を支配していますが、プログラムに何十万ものgoroutineがある場合、GCは不適切な判断をしていました。

ヒープ・ターゲットはGCの頻度を制御します。ターゲットが大きければ大きいほど、GCは別のマーク・フェーズの開始をより長く待つことができ、その逆もまた然りです。正確な計算式は推定に役立ちますが、GOGCをその基本的な目的、すなわちGCのCPUとメモリのトレードオフのポイントを選ぶパラメータという観点から考えるのが最善です。重要なことは、GOGC を 2 倍にするとヒープメモリのオーバーヘッドが 2 倍になり、GC の CPU コストが約半分になり、その逆もまた然りということです。(理由についての完全な説明を見るには、付録を参照してください)。

注:目標ヒープサイズは単なる目標であり、GCサイクルがその目標ですぐには終了しないかもしれないいくつかの理由があります。1つは、十分に大きなヒープ割り当てが単にターゲットを超えることができます。しかし、他の理由は、このガイドがこれまで使用してきたGCモデルを超えるGC実装に現れます。いくつかの詳細については、レイテンシのセクションを参照してください、しかし、完全な詳細は、追加のリソースで見つけることができるかもしれません。

GOGCはGOGC環境変数(すべてのGoプログラムが認識する)、またはruntime/debugパッケージのSetGCPercent APIを通して設定されるかもしれません。

GOGCは、GOGC=offを設定するか、SetGCPercent(-1)を呼び出すことによって、GCを完全にオフにするために使用することもできます(メモリ制限が適用されない場合)ことに留意してください。概念的には、GCがトリガされる前の新しいメモリの量は無制限であるため、この設定はGOGCを無限大の値に設定することと同じです。

これまで説明したことをよりよく理解するために、先に説明したGCコスト・モデルに基づいて作られた以下のインタラクティブな視覚化を試してみてください。この可視化では、GC以外の作業に10秒のCPU時間を要するあるプログラムの実行を描いています。最初の1秒間は、定常状態に落ち着く前に、何らかの初期化ステップ(ライブヒープを成長させる)を実行します。このアプリケーションは、一度に20MiBをライブで、合計200MiBを割り当てます。これは、完了する唯一の関連するGC作業がライブヒープから来ること、および、(非現実的な)アプリケーションが追加のメモリを使用しないことを想定しています。

スライダーを使用して GOGC の値を調整し、総時間と GC オーバーヘッドの観点からアプリケーションがどのように応答するかを確認します。各GCサイクルは、新しいヒープがゼロに低下する間に終了します。新しいヒープがゼロになる間にかかる時間は、サイクル N のマークフェイズとサイクル N+1 のスイープフェイズを合わせた時間です。この可視化(およびこのガイドのすべての可視化)は、GC の実行中にアプリケーションが一時停止していると仮定しているので、GC の CPU コストは新しいヒープ・メモリがゼロになるまでの時間によって完全に表されることに注意してください。これは視覚化を簡単にするためだけで、同じ直感がまだ適用されます。X 軸は常にプログラムの完全な CPU 時間を表示するようにシフトします。GCによって使用される追加のCPU時間は、全体の時間を増加させることに注意してください。

[https://tip.golang.org/doc/gc-guide] を参照

GCは常にCPUとピークメモリのオーバーヘッドを発生させることに注意してください。GOGCが増加すると、CPUオーバーヘッドは減少しますが、ピークメモリはライブヒープサイズに比例して増加します。GOGCが減少すると、ピークメモリ要件は、追加のCPUオーバーヘッドを犠牲にして減少します。

注:このグラフは、プログラムを完了するための壁時計時間ではなく、CPU時間を表示しています。プログラムが1CPUで動作し、そのリソースをフルに活用する場合、これらは等価である。実際のプログラムは、マルチコアシステムで実行され、常にCPUを100%利用するわけではありません。このような場合、GCの壁時間の影響は小さくなります。

注: Go GCの最小の総ヒープサイズは4 MiBなので、GOGCが設定したターゲットがそれ以下であれば、切り上げられます。この可視化には、この詳細が反映されています。

もう1つ、もう少しダイナミックで現実的な例を示します。この例でも、アプリケーションは GC なしで 10CPU 秒で完了しますが、定常状態の割り当て率は途中で劇的に増加し、ライブヒープ サイズは最初のフェーズで少しシフトします。この例は、ライブヒープサイズが実際に変化しているときに定常状態がどのように見えるか、そして、高い割り当て率はより頻繁なGCサイクルにつながるかを実証しています。

[https://tip.golang.org/doc/gc-guide] を参照

メモリの上限

Go 1.19 までは、GOGC は GC の動作を変更するために使用できる唯一のパラメータでした。これはトレードオフを設定する方法としては素晴らしいのですが、使用可能なメモリが有限であることは考慮されていません。ライブヒープサイズに一時的なスパイクがあるときに何が起こるかを考えてみましょう。GCはそのライブヒープサイズに比例する総ヒープサイズを選択するので、GOGCは、たとえ通常のケースでは高いGOGC値がより良いトレードオフを提供しても、ピークライブヒープサイズ用にそのように構成されなければなりません。

以下の図は、この過渡的なヒープスパイクの状況を示しています。

[https://tip.golang.org/doc/gc-guide] を参照

この例の作業負荷が、利用可能なメモリが 60 MiB を少し超えるコンテナで実行されている場合、残りの GC サイクルがその余分なメモリを使用するために利用可能なメモリを持っているにもかかわらず、GOGC は 100 を超えて増加することができない。さらに、アプリケーションによっては、このような過渡的なピークが稀に発生し、予測が困難なため、時折、避けられない、コストのかかるメモリ不足の状態になる可能性があります。

そのため、Go は 1.19 リリースで、ランタイムメモリ制限を設定するサポートを追加しました。メモリ制限は、すべての Go プログラムが認識する GOMEMLIMIT 環境変数か、runtime/debug パッケージの SetMemoryLimit 関数で設定できます。

このメモリ制限は、Goランタイムが使用できるメモリの総量に最大値を設定します。含まれるメモリの特定のセットは、runtime.MemStatsの式で定義されます。

Sys - HeapReleased

または同等にruntime/metricsパッケージの観点で定義されます。

/memory/classes/total:bytes - /memory/classes/heap/released:bytes という式で定義されます。

Go GCはヒープメモリの使用量を明示的に制御できるため、このメモリ制限とGoランタイムが使用する他のメモリ量に基づいて、ヒープサイズの合計を設定します。

下の図は、GOGCセクションと同じ単相の定常状態のワークロードを描いたものですが、今回はGoランタイムのオーバーヘッドを10MiB追加して、メモリ制限を調整できるようにしています。GOGCとメモリ制限の両方を変更してみて、何が起こるか見てみてください。

[https://tip.golang.org/doc/gc-guide] を参照

メモリ制限をGOGCで決定されるピークメモリ(GOGCが100の場合は42MiB)より低くすると、ピークメモリを制限内に保つためにGCがより頻繁に実行されることに注意してください。

先ほどの一時的なヒープスパイクの例に戻ると、メモリ制限を設定し、GOGCを上げることで、メモリ制限の違反がなく、リソースの経済性が向上するという、両方の利点を手に入れることができるのです。以下のインタラクティブなビジュアライゼーションをお試しください。

[https://tip.golang.org/doc/gc-guide] を参照

GOGC とメモリ制限のある値では、ピークメモリ使用量はメモリ制限の値で停止しますが、プログラムの残りの実行は GOGC が設定したヒープサイズの合計のルールに従います。

GOGCをオフに設定しても、メモリ制限は守られるのです。実際、この特定の構成は、あるメモリ制限を維持するために必要な最小限のGC頻度を設定するため、リソース経済の最大化を意味します。この場合、プログラムのすべての実行は、メモリ制限を満たすためにヒープサイズを増加させます。

さて、メモリ制限は明らかに強力なツールですが、メモリ制限の使用にはコストがかかるため、確かにGOGCの有用性が無効となるわけではありません。

ライブヒープが十分に大きくなり、総メモリ使用量がメモリ制限に近づいた場合を考えてみましょう。上の定常状態の可視化で、GOGCをオフにしてから、ゆっくりとメモリ制限をさらに下げてみて、何が起こるかを見てみましょう。不可能なメモリ制限を維持するためにGCが常に実行されているため、アプリケーションが要する総時間が無制限に増加し始めることに注意してください。

このように、常にGCが巡回しているためにプログラムが合理的に進行しない状況を、スラッシングと呼びます。これは事実上プログラムを停止させるので、特に危険です。さらに悪いことに、GOGCで回避しようとしたのと全く同じ状況が発生する可能性があります:十分に大きな一時的ヒープスパイクは、プログラムを無制限にストールさせる可能性があります 過渡的ヒープスパイクの可視化でメモリ制限(約30MiB以下)を減らしてみて、最悪の動作が特にヒープスパイクで始まることに気づいてください。

多くの場合、無期限のストールはメモリ不足の状態よりも悪化し、より高速な障害につながる傾向があります。

このため、メモリの上限はソフトに定義されています。Goランタイムは、すべての状況下でこのメモリ制限を維持することを保証するものではなく、ある合理的な量の努力を約束するだけです。このメモリ制限の緩和は、GC に逃げ道を与えるので、スラッシング動作を回避するのに重要です:メモリ使用量を制限値より多くして、GC で多くの時間を費やすことを回避します。

これは内部的にどのように動作するかというと、GC はある時間ウィンドウで使用できる CPU 時間の量に上限を設定します(CPU 使用の非常に短い過渡スパイクのためのいくつかのヒステリシスを持つ)。この制限は現在、2 * GOMAXPROCS CPU-秒のウィンドウで、およそ50%に設定されています。GCのCPU時間を制限した結果、GCの作業が遅れる一方で、Goプログラムはメモリ制限を越えても新しいヒープメモリを割り当て続けることができます。

50%のGC CPU制限の背後にある直感は、十分な利用可能メモリを持つプログラムに対する最悪のケースの影響に基づいています。メモリ制限を誤って低く設定した場合、GC は CPU 時間の 50% 以上を奪うことができないため、 プログラムは最大で 2 倍遅くなります。

注意:このページのビジュアライゼーションは、GC の CPU 制限をシミュレートするものではありません。

推奨される使用方法

メモリ制限は強力なツールであり、Goランタイムは誤用による最悪の挙動を軽減するための措置を講じていますが、それでも慎重に使用することが重要です。以下に、メモリ制限が最も有効で適用できる場所と、逆に害を及ぼす可能性のある場所について、ちょっとしたアドバイスを集めました。

Go プログラムの実行環境が完全に自分のコントロール下にあり、Go プログラムだけが何らかのリソース(コンテナのメモリ制限のような、ある種のメモリ予約)にアクセスできる場合、メモリ制限を利用してください。

良い例としては、利用可能なメモリ量が決まっているコンテナにウェブサービスを展開することが挙げられます。

この場合、Go ランタイムが認識していないメモリソースを考慮し、5~10% の余裕を持たせるのがよい方法です。

状況の変化に対応するために、メモリの制限をリアルタイムで自由に調整できます。

良い例は、C ライブラリが一時的にかなり多くのメモリを使用する必要がある場合、cgo プログラムです。

Goプログラムが限られたメモリの一部を他のプログラムと共有する可能性があり、それらのプログラムは通常Goプログラムから切り離されている場合、GOGCのメモリ制限をオフに設定する必要はありません。その代わり、望ましくない一時的な動作を抑制するのに役立つので、メモリ制限を維持し、GOGCを平均的な場合のより小さく、妥当な値に設定します。

共同利用するプログラムのためにメモリを「確保」することは魅力的ですが、プログラムが完全に同期していなければ(たとえば、Goプログラムが何らかのサブプロセスを呼び出し、その呼び出し側が実行する間はブロックする)、必然的に両方のプログラムがより多くのメモリを必要とするので、結果はあまり信頼できないものになるでしょう。Goプログラムが必要としないときには、より少ないメモリを使用するようにすれば、全体としてより信頼性の高い結果を得ることができます。このアドバイスは、1 台のマシンで実行されているコンテナのメモリ制限の合計が、そのマシンで利用可能な実際の物理メモリを超える可能性のあるオーバーコミット状況にも当てはまります。

特に、プログラムのメモリ使用量が入力に比例する場合、自分がコントロールできない実行環境にデプロイするときは、メモリ制限を使わないでください。

良い例は、CLIツールやデスクトップアプリケーションです。どのような入力があるか、あるいはシステムで利用可能なメモリ量が不明なときに、プログラムにメモリ制限をかけると、混乱したクラッシュやパフォーマンスの低下を招きます。さらに、高度なエンドユーザーが望めば、いつでもメモリ制限を設定することができます。

プログラムがすでに環境のメモリ制限に近づいているときに、メモリ不足の状態を回避するためにメモリ制限を設定しないこと。

これは事実上、メモリ不足のリスクを深刻なアプリケーションのスローダウンのリスクに置き換えるもので、スラッシングを軽減するためにGoが努力しても、有利な取引とはならないことが多い。このような場合、環境のメモリ制限を増やすか(そしてメモリ制限を設定する可能性もあります)、GOGCを減らす(これはスラッシングの緩和よりもずっときれいなトレードオフを提供します)方がはるかに効果的でしょう。

## レイテンシー

この文書では、GC の実行中にアプリケーションが一時停止するように視覚化されています。このような動作をする GC の実装は存在し、それらは「Stop-the-World」GC と呼ばれ ています。

しかし、Go GCは完全にストップ・ザ・ワールドではなく、ほとんどの作業をアプリケーションと同時進行で行います。これは主に、アプリケーションのレイテンシーを減らすためです。具体的には、計算の単一ユニット(たとえば、Webリクエスト)のエンドツーエンドの持続時間です。これまで、この文書では主にアプリケーションのスループット(例:1 秒間に処理される Web リクエスト)を考慮しました。GC サイクルのセクションの各例では、実行中のプログラムの総 CPU 時間の長さに焦点を当てたことに注意してください。しかし、そのような持続時間は、例えば、ウェブサービスのためにはるかに少ない意味があります。Web サービスの場合、スループットはまだ重要ですが(つまり 1 秒あたりのクエリー)、多くの場合、個々のリクエストのレイテンシはもっと重要です。

待ち時間の点で、Stop-the-World GCは、そのマークと掃引フェーズの両方を実行するためにかなりの時間を必要とする可能性があり、その間、アプリケーション、そしてWebサービスのコンテキストでは、飛行中のリクエストは、さらに進行することができません。その代わり、Go GCは、グローバルなアプリケーションの休止の長さをヒープサイズに比例させることを避け、アプリケーションがアクティブに実行されている間、コアトレースアルゴリズムが実行されるようにします。(休止はアルゴリズム的にはGOMAXPROCSにもっと強く比例しますが、最も一般的には実行中のゴルーチンを停止するのにかかる時間に支配されています)。並行収集はコストがかからないわけではありません。実際には、同等のStop-the-Worldガベージコレクタよりも低いスループットを持つ設計になることがよくあります。しかし、レイテンシーが低いからといって、本質的にスループットが低いわけではないことに注意することが重要です。Goガベージコレクタのパフォーマンスは、レイテンシースループットの両方で、時間とともに着実に向上してきました。

Goの現在のGCの並列性は、これまでこの文書で議論されてきたことを無効にするものではありません。GCの頻度は、GCスループットのためにCPU時間とメモリをトレードオフする主要な方法であることに変わりはなく、実際、レイテンシについてもこの役割を担っています。これは、GCのコストのほとんどが、マークフェイズがアクティブな間に発生するためです。

つまり、GCの頻度を減らすと、レイテンシも改善される可能性があるということです。これは、GOGC および/またはメモリ制限の増加のような、チューニングパラメー タの変更による GC 周波数の削減だけでなく、最適化ガイドで説明した最適化にも適用されま す。

しかし、レイテンシは、単なるコストの集計ではなく、プログラムの瞬間瞬間の実行の産物であるため、スループットよりも理解が複雑な場合が多いのです。その結果、レイテンシと GC の頻度との間の関連は、より直接的ではありません。以下に、レイテンシの原因として考えられるものを列挙しますので、より深く掘り下げたい方はご覧ください。

GC がマークフェーズとスイープフェーズの間で移行する際の、短い世界停止時間。
マークフェーズのとき、GC が CPU リソースの 25% を使用するため、スケジューリングが遅延する。
高い割り当て率に応答してGCを支援するユーザーゴルーチン。
GCがマークフェーズにある間、追加の作業を必要とするポインタの書き込み、および
実行中のゴルーチンは、そのルートがスキャンされるために中断されなければなりません。

これらの待ち時間は、追加の作業を必要とするポインタの書き込みを除いて、実行トレースで見ることができます。

## 追加リソース

上記で紹介した情報は正確ですが、Go GCの設計におけるコストとトレードオフを完全に理解するための詳細が欠けています。詳細については、次の追加リソースを参照してください。

GCハンドブック-ガベージコレクタの設計に関する優れた一般的なリソースとリファレンスです。
TCMalloc-C/C++のメモリアロケータTCMallocの設計文書で、Goのメモリアロケータはこれに基づいています。
Go 1.5 GC 発表-Go 1.5 同時実行 GC を発表したブログ記事で、アルゴリズムの詳細が説明されています。
Getting to Go-2018年までのGoのGC設計の進化に関する詳細なプレゼンテーション。
Go 1.5 concurrent GC pacing-コンカレント マーク フェーズを開始するタイミングを決定するための設計文書です。
Smarter scavenging-Goランタイムがオペレーティングシステムにメモリを返す方法を修正するための設計文書です。
スケーラブル ページ アロケータ-Go ランタイムがオペレーティング システムから取得したメモリを管理する方法を修正するための設計ドキュメント。
GC pacer redesign (Go 1.18)- 同時実行マーク フェーズをいつ開始するかを決定するアルゴリズムを修正する設計ドキュメント。
ソフトメモリ制限(Go 1.19)- ソフトメモリ制限の設計文書。

仮想メモリについてのメモ

このガイドは主に GC の物理メモリの使用に焦点を当ててきましたが、定期的に出てくる質問は、それが正確に何を意味し、仮想メモリ(通常、top のようなプログラムでは "VSS" として示されます)とどう比較されるかということです。

物理メモリは、ほとんどのコンピュータの実際の物理的なRAMチップに収容されているメモリです。仮想メモリは、プログラムを互いに分離するためにオペレーティングシステムによって提供される物理メモリ上の抽象化です。また、通常、プログラムは物理アドレスに全くマッピングされない仮想アドレス空間を確保することが認められています。

仮想メモリオペレーティングシステムによって維持されているマッピングに過ぎないので、物理メモリにマッピングされない大規模な仮想メモリを予約することは、一般的に非常に安上がりです。

Goランタイムは一般に、いくつかの方法で仮想メモリのコストに関するこの見解に依存しています。

Goランタイムはマッピングされた仮想メモリを削除しません。その代わり、ほとんどのオペレーティング システムが提供する特別な操作を使用して、ある仮想メモリ範囲に関連付けられた物理メモリ リソースを明示的に解放します。

このテクニックは、メモリ制限を管理し、Goランタイムが不要になったメモリをオペレーティングシステムに戻すために明示的に使用されます。Go ランタイムはバックグラウンドで継続的に不要になったメモリを解放することもあります。詳細については、追加のリソースを参照してください。

32 ビット プラットフォームでは、Go ランタイムはヒープ用に 128 MiB から 512 MiB までのアドレス空間を前もって確保し、断片化の問題を抑制します。

Go ランタイムは、いくつかの内部データ構造の実装に、大きな仮想メモリ アドレス空間の予約を使用します。64 ビット プラットフォームの場合、これらの仮想メモリのフットプリントは通常、最小で約 700 MiB になります。32 ビット プラットフォームでは、そのフットプリントはごくわずかです。

その結果、top の「VSS」のような仮想メモリの測定基準は、一般的に Go プログラムのメモリ フットプリントを理解するのにあまり役に立ちません。代わりに、物理メモリの使用量をより直接的に反映する「RSS」と同様の測定値に注目してください。

最適化ガイド

コストの特定

GoアプリケーションがGCとどのように相互作用するかを最適化しようとする前に、まず、GCがそもそも主要なコストであることを特定することが重要です。

Goエコシステムは、コストを特定し、Goアプリケーションを最適化するための多くのツールを提供します。これらのツールの簡単な概要については、診断に関するガイドを参照してください。ここでは、GCの影響と動作を理解するために、これらのツールのサブセットとそれらを適用する合理的な順序に焦点を当てます。

  1. CPU プロファイル

    開始するのに良い場所は、CPU プロファイリングです。CPU プロファイリングは、CPU 時間が費やされる場所の概要を提供しますが、素人目には、 特定のアプリケーションで GC が果たす役割の大きさを識別することは困難かもしれません。幸運なことに、GCがどのようにフィットするかを理解することは、ほとんどの場合、 runtimeパッケージ内の異なる関数が何を意味するかを知ることに帰着します。以下は、CPUプロファイルを解釈するためのこれらの関数の有用なサブセットです。

    注意: 以下に挙げた関数はリーフ関数ではないので、 pprof ツールが top コマンドで提供するデフォルトでは表示されないかもしれない。代わりに、top -cumコマンドを使用するか、これらの関数に直接listコマンドを使用し、累積パーセント列に注目する。

    runtime.gcBgMarkWorker: 専用のマークワーカーゴルーチンへのエントリーポイント。ここで費やされる時間は、GCの頻度、オブジェクトグラフの複雑さと大きさによって変化します。これは、アプリケーションがマークとスキャンに費やす時間のベースラインを表します。
    
    注意: 大部分がアイドルのGoアプリケーションでは、Go GCは仕事を速く終わらせるために追加の(アイドルの)CPUリソースを使用することになります。その結果、このシンボルは、無料であると信じているサンプルの大きな割合を示すかもしれません。このようなことが起こる一般的な理由は、アプリケーションが完全に 1 つのゴルーチンで実行され、GOMAXPROCS が >1 である場合です。
    
    runtime.mallocgc: ヒープメモリのメモリアロケータへのエントリーポイント。ここで費やされる累積時間が大きい(15%以上)場合、一般的に多くのメモリが割り当てられていることを示します。
    
    runtime.gcAssistAlloc: 関数のゴルーチンは、スキャンとマーキングでGCを支援するために彼らの時間の一部を提供するために入力します。ここで費やされる累積時間が大きい(>5%)場合、アプリケーションは、割り当ての速さに関してGCを上回っている可能性があることを示します。これは、GC からの影響が特に大きいことを示し、また、アプリケーションがマーキングとスキャニングに費やす時間を表します。これは runtime.mallocgc のコールツリーに含まれるため、同様に膨張することに注意 してください。
    
  2. 実行トレース
    CPU プロファイルは全体としてどこに時間がかかっているかを特定するには最適ですが、より微妙な、まれな、あるいはレイテンシに特化したパフォーマンス コストを示すにはあまり役に立ちません。一方、実行トレースは、Go プログラムの実行の短いウィンドウを深く掘り下げて見ることができます。実行トレースには、Go GCに関連するさまざまなイベントが含まれており、特定の実行パスを、アプリケーションがGo GCとどのように相互作用するかと共に直接観察することができます。追跡されたすべての GC イベントは、トレースビューワでそのように便利にラベル付けされています。

実行トレースを開始する方法については、runtime/traceパッケージのドキュメントを参照してください。

  1. GCトレース

他のすべてが失敗した場合、Go GCは、GCの動作にはるかに深い洞察を提供するいくつかの異なる特定のトレースを提供します。これらのトレースは常にSTDERRに直接出力され、GCサイクルごとに1行、すべてのGoプログラムが認識するGODEBUG環境変数を通じて構成されています。これらは、GCの実装の細部に精通している必要があるため、Go GC自体をデバッグするために主に有用ですが、それでも、GCの動作をよりよく理解するために有用である場合もあります。

コアGCトレースは、GODEBUG=gctrace=1を設定することによって有効になります。このトレースによって生成された出力は、ランタイムパッケージのための文書の環境変数のセクションで文書化されています。

pacerトレース」と呼ばれる補足のGCトレースは、さらに深い洞察を提供し、GODEBUG=gcpacertrace=1を設定することによって有効にされます。この出力を解釈するには、GCの「ペーサー」(追加リソースを参照)を理解する必要がありますが、これはこのガイドの範囲外です。

ヒープアロケーションの排除

GC からのコストを削減する 1 つの方法は、GC が最初に管理する値を少なくすることです。GOGCのセクションで示したように、Goプログラムの割り当て率は、本ガイドで使用する主要なコスト指標であるGC頻度の主要な要因であるため、以下に説明するテクニックは、性能に最大の改善をもたらすことができます。

ヒーププロファイリング

GCが大きなコストの原因であることを特定した後、ヒープ割り当てを排除するための次のステップは、そのほとんどがどこから来ているのかを見つけることです。この目的のために、メモリプロファイル(実際にはヒープメモリプロファイル)は非常に便利です。その方法は、ドキュメントを参照してください。

メモリプロファイルは、ヒープの割り当てがプログラムのどこから来たかを説明し、割り当てられた時点のスタックトレースで識別します。各メモリプロファイルは、メモリを4つの方法で分解することができます。

  • inuse_objects - 使用中のオブジェクトの数を分解します。
  • inuse_space-使用中のオブジェクトを、それらが使用するメモリのバイト数で分解します。
  • alloc_objects - Go プログラムが実行を開始して以来、割り当てられたオブジェクトの数を内訳します。
  • alloc_space- Go プログラムが実行を開始して以来、割り当てられたメモリの総量を内訳します。

ヒープメモリのこれらの異なるビューを切り替えるには、pprof ツールの -sample_index フラグ、またはツールを対話的に使用するときの sample_index オプションを使用します。

注意: メモリプロファイルは、デフォルトではヒープオブジェクトの一部しかサンプリングしないので、ヒープ割り当ての一つ一つについての情報は含まれていません。しかし、これはホットスポットを見つけるには十分です。サンプリングレートを変更するには、runtime.MemProfileRateを参照してください。

GCコストを削減する目的のために、alloc_spaceは、それが直接割り当て率に対応するように、一般的に最も有用なビューです。このビューは、最も利益をもたらすであろう割り当てのホットスポットを示します。

エスケープ分析

ヒーププロファイルを使用してヒープ割り当て候補を特定したら、どのようにそれを除去するのでしょうか。重要なのは、Go コンパイラエスケープ解析を利用して、Go コンパイラにこのメモリの代替となる、より効率的なストレージ(たとえばゴルーチン スタックなど)を見つけさせることです。幸運なことに、Go コンパイラには、Go 値をヒープにエスケープすると決めた理由を記述する機能があります。この知識があれば、解析結果を変更するためにソースコードを再編成することが問題になります(これはしばしば最も困難な部分ですが、このガイドの範囲外です)。

Go コンパイラエスケープ解析の情報にアクセスする方法として、最も簡単な方法は、Go コンパイラがサポートするデバッグフラグを使用することです。このフラグには、あるパッケージに適用した、または適用しなかったすべての最適化がテキスト形式で記述されています。これには、値がエスケープされたかどうかが含まれます。以下のコマンドを試してみてください。[package]はGoパッケージのパスです。

go build -gcflags=-m=3 [パッケージ] です。

この情報は、VS Codeでオーバーレイとして視覚化することもできます。このオーバーレイはVS CodeのGoプラグイン設定で設定し、有効にします。

  • ui.codelenses 設定に gc_details を含めるように設定します。
  • ui.diagnostic.annotations を include escape に設定して、エスケープ解析のためのオーバーレイを有効にします。

最後に、Goコンパイラーはこの情報を機械読み取り可能な(JSON)フォーマットで提供し、追加のカスタムツールを構築するために使用することができます。その詳細については、Goのソースコードにあるドキュメントを参照してください。

実装に特化した最適化

オブジェクトとポインタの複雑なグラフは並列性を制限し、GC の作業を増やすため、Go GC はライブメモリの属性に敏感です。その結果、GCは特定の一般的な構造に対するいくつかの最適化を含んでいます。パフォーマンスの最適化のために最も直接的に有用なものは、以下にリストされています。

注意:以下の最適化を適用すると、意図が不明瞭になり、コードの可読性が低下する可能性がありますし、Goのリリース間で保持できない可能性があります。これらの最適化は、最も重要な場所にのみ適用することをお勧めします。そのような場所は、コストの特定に関するセクションに記載されているツールを使用して特定することができます。

  • ポインターを持たない値は、他の値から分離されます。

    その結果、厳密にポインターを必要としないデータ構造からポインターを排除することは、GCがプログラムに与えるキャッシュの圧力を減らすことになり、有利になる場合があります。その結果、ポインタの値に対するインデックスに依存するデータ構造は、あまりよく型付けされていないものの、よりよいパフォーマンスを発揮する可能性があります。これは、オブジェクトグラフが複雑で、GCがマークとスキャンに多くの時間を費やしていることが明らかな場合にのみ実行する価値があります。

  • GCは値の最後のポインターで値のスキャンを停止します。

    その結果、構造体型の値のポインタフィールドを値の先頭でグループ化することが有利になる場合があります。これは、アプリケーションがマークとスキャンに多くの時間を費やすことが明らかな場合にのみ行う価値があります。(理論的にはコンパイラが自動的にこれを行うことができますが、まだ実装されておらず、構造体フィールドはソースコードに書かれているとおりに配置されます)。

さらに、GCは目にするほぼすべてのポインタと対話しなければならないので、ポインタの代わりに、例えばスライスへのインデックスを使用すると、GCコストを削減するのに役立ちます。

付録

GOGCに関する追記

GOGCの項では、GOGCを2倍にするとヒープメモリのオーバーヘッドが2倍になり、GCのCPUコストが半分になると主張しました。その理由を知るために、数学的に分解してみましょう。

まず、ヒープターゲットでは、ヒープサイズの合計の目標値が設定されます。しかし、このターゲットは主に新しいヒープメモリに影響します。なぜなら、ライブヒープはアプリケーションにとって基本的なものだからです。

ターゲット・ヒープ・メモリ = ライブ・ヒープ + (ライブ・ヒープ + GCルーツ) * GOGC / 100

総ヒープメモリ=ライブヒープ+新規ヒープメモリ

新規ヒープメモリ = (ライブヒープ + GC roots) * GOGC / 100

このことから、GOGC を 2 倍にすることで、アプリケーションがサイクルごとに割り当てる新しいヒープメモリの量も 2 倍になり、ヒープメモリのオーバーヘッドを捉えることができることがわかります。Live heap + GC roots は GC がスキャンする必要のあるメモリ量の近似値であることに注意 してください。

次に、GC の CPU コストを見てみましょう。総コストは、サイクルあたりのコスト、いくつかの期間TにわたってGCの頻度の倍として分割することができます。

GC CPU コスト = (GC CPU コスト/サイクル) * (GC 周波数) * T

サイクルあたりの GC CPU コストは、GC モデルから導出することができます。

サイクルあたりの GC CPU コスト = (ライブヒープ + GC ルーツ) * (バイトあたりのコスト) + 固定コスト

マークとスキャンのコストが支配的であるため、スイープフェーズコストはここでは無視されることに注意してください。

定常状態は、一定の割り当て率とバイトあたりの一定のコストで定義されるので、定常状態では、この新しいヒープメモリからGC頻度を導出することができます。

GC頻度 = (割り当て率) / (新しいヒープメモリ) = (割り当て率) / ((ライブヒープ + GCルーツ) * GOGC / 100)

これをまとめると、総コストの完全な式が得られます。

GC CPU の総コスト = (アロケーション率) / ((ライブヒープ + GC root) * GOGC / 100) * ((ライブヒープ + GC roots) * (Cost per byte) + Fixed cost) * T

十分に大きなヒープ(これはほとんどの場合を表しています)の場合、GCサイクルの限界コストは、固定コストを支配します。これは、総 GC CPU コストの公式を大幅に簡略化することができます。

GC CPU コスト合計 = (アロケーション率) / (GOGC / 100) * (バイトあたりのコスト) * T

この単純化された式から、GOGC を 2 倍にすれば、GC の総 CPU コストが半分になることがわかります。(このガイドの可視化は固定コストをシミュレートしているので、GOGC が 2 倍になったときに報告される GC CPU のオーバーヘッドが正確に半分になるわけではないことに注意してください)。さらに、GC CPU コストは、主に割り当て率とメモリをスキャンするためのバイトあたりのコストによって決定されます。具体的にこれらのコストを削減する方法の詳細については、最適化ガイドを参照してください。

注:ライブヒープのサイズと、GC が実際にスキャンする必要があるメモリの量との間に不一致があります: 同じサイズのライブヒープでも、異なる構造を持つものは、異なる CPU コスト、しかし同じメモリコストになり、異なるトレードオフが生じます。これが、ヒープの構造が定常状態の定義の一部である理由です。ヒープターゲットは、GCがスキャンする必要があるメモリの近似として、スキャン可能なライブヒープのみを含むべきですが、これはスキャン可能なライブヒープが非常に少なく、ライブヒープが他に大きい場合に縮退した動作につながります。

会社と個人でgithubアクセスを分ける

前提

既に会社用のkeyがある状態

個人用のssh private keyを作る

ssh-keygen -t ecdsa -b 256 -C {個人メールアドレス} -f {作成する鍵の名前}

作成する鍵の名前は、既にあるものと同じものを使用すると、上書きされるため注意する

ssh設定(~/.ssh/config)ファイルの編集

~/.ssh/config がない場合は作成する

Host github.com # 会社アカウント
  HostName github.com
  User git
  Port 22
  IdentityFile ~/.ssh/id_ecdsa  # 会社アカウント用の鍵
  TCPKeepAlive yes
  IdentitiesOnly yes

Host github.com.private # 個人アカウント
  HostName github.com
  User git
  Port 22
  IdentityFile ~/.ssh/id_ecdsa_personal  # 個人アカウント用の鍵
  TCPKeepAlive yes
  IdentitiesOnly yes

Githubに公開鍵を登録する

ssh接続確認

ssh -T git@github.com
ssh -T git@github.com.private

以下のメッセージが返ってくることを確認する

Hi {yourname}! You've successfully authenticated, but GitHub does not provide shell access.

ディレクトリ毎にgit設定を切り替える

~/.gitconfig は以下のような値

[user]
        name = company-user
        email = company-user@example.com
[includeIf "gitdir:~/src/github.com/personal/"]
        path = ~/src/github.com/personal/.gitconfig

~/src/github.com/personal/ の場合は~/src/github.com/personal/.gitconfig を読み込むとする

~/src/github.com/personal/.gitconfig は以下のようなものになっている

[user]
        name = personal-user
        email = personal-user@example.com

clone する

メインアカウント

git clone git@github.com:username/repository-name.git

個人アカウント

git clone git@github.com.private:username/repository-name.git

確認する

git config remote.origin.url

ghq を使うと

ghq get github.com.private:username/repository-name

/ghqroot/github.com.private/repository-name ができた。 こっちもincludeした方が良さそう

Ref

qiita.com

raspberry pi setup(キーボード、マウスなし、無線)

よく調べて忘れるため、備忘録

環境

キーボード、マウス、ディスプレイ、イーサネット cable全てないにも関わらずセットアップしたい。

無線LANで、sshで接続できれば設定できる。 そのためにこれらの設定をしたいが、そもそも繋がないと設定できない。 母艦のMacからMicroSDカードへはイメージの書き込みを行う必要がるため、SD CardWriterなどを使用してmountする。 MicroSDカードは、Fat32形式にてフォーマットされている領域がある。ここに設定ファイルをおくことで、これらを有効化できる。 そこでMacにmountされているFat32領域へ書き込む。

手順

raspberry pi image download

SDカードを初期化

diskutilを使う

$ diskutil list


$ sudo diskutil unmountDisk /dev/disk2

DOS-FAT32形式でフォーマット

$ sudo diskutil eraseDisk FAT32 RASP MBRFormat /dev/disk2

ddで書き込む

フォーマットするとマウントされるため再度アンマウントする

$ sudo diskutil unmountDisk /dev/disk2

書き込みデバイスは/dev/rdisk, rあり、なしがある

rdiskの方はシーケンシャル、diskの方はランダムアクセス

ランダムアクセスだと、バッファコピーが入る。

OS image書き込みのようなシーケンシャルアクセスの場合はrdiskの方が速い

$ sudo dd if=rpi-image-name.img of=/dev/rdisk2 bs=1m
  1. Macから/bootが見えるので、そこに設定ファイルをおく /Volume/boot/にsshという名前で空ファイルをおく
touch /Volume/boot/ssh

/bootにwpa_supplicant.confをおく

raspberrypi 3でwifi設定

raspi ZeroだとそのままではUSB繋がらないたため、キーボードなどが接続可能なraspberry pi3にて初期設定を行う

wpa-cliを行なってもfailとなってしまう

$ wpa_cli -i wlan0 scan
FAIL

raspi-cofigにてwifi設定をしたところ、Under voltage detectedと表示された。そのためwifiが有効化されていなかったと思われる。

raspi-config

raspi-configwifi設定を行うと、ssidとpskの設定が促される

raspi-config/etc/wpa_supplicant/wpa_supplicant.confを確認したところ、country=JPが追加されていた。 国毎に無線の規制(技適)があるため、国の設定が必要で、これがないと有効化しないのかもしれない。

キーボード設定

USキーボードを使用していると、returnの横がSHIFTを押しながらだと、" でなく @ に なってしまう。これはキーボード設定がUS_EN PC105となっているため。 PC104に変えれば解決する

ja.wikipedia.org

これもraspi-configにて設定

dpkg-reconfigure keyboard-configuration を使えば良いのかな?

tempfs

rasppiはhddがなく、sdカードに書き込む。 SDカードの寿命を延ばすためにtempfs化は必須。 また、デフォルトでswapが有効化されている。 swap も無効化しないと、sdカードへガリガリ書き込むため、swap無効化も合わせて行なったほうが良い。 (free を行うと、swapのfreeが0でないことがわかる)

$ free
               total        used        free      shared  buff/cache   available
Mem:         3885496       94256     3411824         936      379416     3719992
Swap:         102396           0      102396

swap無効化

起動している状態でswapを無効化する

sudo swapoff --all

freeで確認するとfreefが0になることがわかる。

free
               total        used        free      shared  buff/cache   available
Mem:         3885496       93344     3411704         936      380448     3720772
Swap:              0           0           0

再起動後もswapを無効化する

swapサービスは dphys-swapfile

sudo systemctl stop dphys-swapfile.service
sudo systemctl disable dphys-swapfile.service

確認

sudo systemctl status dphys-swapfile

tempfs

常時書き込む以下のディレクトリをtmpts化する。

/etc/fstabに以下設定を追記しrebootする

tmpfs  /tmp           tmpfs defaults,size=16m,noatime,mode=1777 0 0
tmpfs  /tmp/log       tmpfs defaults,size=16m,noatime,mode=1777 0 0

tmpfs化する際の検討メモは以下を参照のこと kawawa.hatenablog.com

raspberrypi tempfs化備忘録

rasppiはhddがなく、sdカードに書き込む。 SDカードの寿命を延ばすためにtempfs化は必須。 また、デフォルトでswapが有効化されている。 swap も無効化しないと、sdカードへガリガリ書き込むため、swap無効化も合わせて行なったほうが良い。

swap

swapの確認

free を行うと、swapのfreeが0でないことがわかる

$ free
               total        used        free      shared  buff/cache   available
Mem:         3885496       94256     3411824         936      379416     3719992
Swap:         102396           0      102396

swap無効化

起動している状態でswapを無効化する

sudo swapoff --all

freeで確認するとfreefが0になることがわかる。

free
               total        used        free      shared  buff/cache   available
Mem:         3885496       93344     3411704         936      380448     3720772
Swap:              0           0           0

再起動後もswapを無効化する

swapサービスは dphys-swapfile

sudo systemctl stop dphys-swapfile.service
sudo systemctl disable dphys-swapfile.service

確認

sudo systemctl status dphys-swapfile

tempfs

tempfs化の対象ディレクトリは?

/var 配下の検討

  • /var/tmpは再起動後に残るディレクトリ。そのため通常は削除してはいけない。ただ今回はSDカードの寿命が短くなるのは許容できないためtmpfsにする。-> 更新日時が古く、そこまで問題ないと判断しtmpfs化は行わない。
$ ls -l /var
合計 102436
drwxr-xr-x  2 root root       4096  8月 14 06:25 backups
drwxr-xr-x  7 root root       4096  1月 28  2022 cache
drwxr-xr-x 24 root root       4096  1月 28  2022 lib
  -> 
drwxrwsr-x  2 root staff      4096 12月 12  2021 local
lrwxrwxrwx  1 root root          9  1月 28  2022 lock -> /run/lock
drwxr-xr-x  6 root root       4096  8月 14 00:00 log
drwxrwsr-x  2 root mail       4096  1月 28  2022 mail
drwxr-xr-x  2 root root       4096  1月 28  2022 opt
lrwxrwxrwx  1 root root          4  1月 28  2022 run -> /run
drwxr-xr-x  4 root root       4096  1月 28  2022 spool
  ->crontab=設定ファイルが書き込まれるためtmpfsにしない
-rw-------  1 root root  104857600  1月 28  2022 swap
drwxrwxrwt  4 root root         80  8月 14 00:00 tmp

/varを全てtmpfsにすると不足する。

kawawa@rpi4-2:/var $ sudo du -hs backups
584K    backups
kawawa@rpi4-2:/var $ sudo du -hs cache
105M    cache
kawawa@rpi4-2:/var $ sudo du -hs lib
145M    lib
kawawa@rpi4-2:/var $ sudo du -hs local
4.0K    local
kawawa@rpi4-2:/var $ sudo du -hs log
98M     log
kawawa@rpi4-2:/var $ sudo du -hs mail
4.0K    mail
kawawa@rpi4-2:/var $ sudo du -hs opt
4.0K    opt
kawawa@rpi4-2:/var $ sudo du -hs spool
16K     spool
  • /var/cache
rpi4-2:/var/cache $ find . -maxdepth 1 -type d | grep -v "\.$" | xargs -I{} sudo du -hs {}
100M    ./apt
4.0K    ./private
1.4M    ./man
28K     ./ldconfig
3.9M    ./debconf
  • aptのcacheは入りきらない。
  • manも無理。
  • debconfは用途が不明だが、そもそも/var/cacheのtmpfs化は諦めた方が良さそう.

  • /var/lib

rpi4-2:/var/lib $ find . -maxdepth 1 -type d | grep -v "\.$" | xargs -I{} sudo du -hs {}
20K     ./dhcpcd
8.0K    ./logrotate
4.0K    ./dhcp
125M    ./apt
28K     ./pam
8.0K    ./vim
4.0K    ./private
4.0K    ./usb_modeswitch
20M     ./dpkg
12K     ./nfs
8.0K    ./bluetooth
4.0K    ./misc
4.0K    ./udisks2
4.0K    ./dbus
12K     ./raspberrypi
8.0K    ./alsa
8.0K    ./sudo
4.0K    ./python
4.0K    ./man-db
48K     ./ucf
428K    ./systemd
36K     ./polkit-1
  • 支配的なものはapt. PGP
/var/lib $ ls -l
合計 88
drwxr-xr-x  2 root root 4096  8月 15 08:08 alsa
drwxr-xr-x  5 root root 4096  8月 13 19:33 apt
drwx------  3 root root 4096  1月 28  2022 bluetooth
drwxr-xr-x  2 root root 4096  1月 28  2022 dbus
drwxr-xr-x  2 root root 4096  5月 27  2021 dhcp
drwxr-xr-x  2 root root 4096  8月 14 08:44 dhcpcd
drwxr-xr-x  7 root root 4096  8月 13 19:33 dpkg
drwxr-xr-x  2 root root 4096  8月 16 00:00 logrotate
drwxr-xr-x  2 root root 4096  1月 28  2022 man-db
drwxr-xr-x  2 root root 4096 12月 12  2021 misc
drwxr-xr-x  4 root root 4096  1月 28  2022 nfs
drwxr-xr-x  2 root root 4096  1月 28  2022 pam
drwx------  3 root root 4096  1月 28  2022 polkit-1
drwx------  2 root root 4096  1月 28  2022 private
drwxr-xr-x  2 root root 4096  1月 28  2022 python
drwxr-xr-x  3 root root 4096  1月 28  2022 raspberrypi
drwxr-xr-x  3 root root 4096  1月 28  2022 sudo
drwxr-xr-x 11 root root 4096  1月 28  2022 systemd
drwxr-xr-x  3 root root 4096  1月 28  2022 ucf
drwx------  2 root root 4096  1月 28  2022 udisks2
drwxr-xr-x  2 root root 4096  7月 11  2020 usb_modeswitch
drwxr-xr-x  3 root root 4096  1月 28  2022 vim
rpi4-2:/var/lib $ date
2022年  8月 16日 火曜日 06:23:39 JST
  • 更新日時を確認しても1回/日程度のため許容範囲と判断。

  • tmpfsにするのは /var/log だけで良さそう

    • /var/logの更新日時が古い?
    • journalctlの保存先がSDカードだと対策不十分
      • journalctlのデフォルトログ保存先は/run配下。
      • /run はtmpfs化既にされているため問題なし.

- 容量はどうする?

Raspberry Pi OSにtmpfsを活用しSDカードに優しいシステムを - Fun Scripting 2.0

ではrpi3にて/tmp,/var それぞれ 16mとしている。 - rpi4 4GB でrpi3よりメモリは増えている。そのため16Mは問題ないように思える。

  • tmpfiles.dとfstabの棲み分け
    • fstabは起動時にマウントする設定
    • tmpfiles.dは一時ファイルのマウント?、アンマウント、初期化、後処理などができる。典型的にはマウント後の初期化処理にてtmpfsにシンボリックリンクを作成するといったことが簡便に実現できる。
    • see tmpfiles.d(5)

最終的に以下2行を/etc/fstabに書き込むことで完了

tmpfs  /tmp           tmpfs defaults,size=16m,noatime,mode=1777 0 0
tmpfs  /tmp/log       tmpfs defaults,size=16m,noatime,mode=1777 0 0

vscodeでC/C++ デバッグの設定(GDB/MIインターフェース)

vscodeの ms-vscode.cpptools はGDB/MIインターフェイスを使用している。 そのためlaunch.jsonを適切に設定するには、GDB/MIインターフェイスの理解が必要となる。

GDBが解釈するインターフェースは2つある。 一つ目がGDBコマンド、もう一つがGDB/MIインターフェイス

GDBコマンド

gdbをtuiで使って実行している時に使うもの。 ステップ実行を例にすると nextやその省略形のnGDBコマンドに該当する

(nextは本来、下位プログラムの実行を再開し、 次のソース行の先頭に到達したところで停止するものだが理解しやすさを優先しここではステップ実行と説明する)

GDB/MIインターフェイス

GDB/MIインターフェイスについては以下を参照するとよい

www.asahi-net.or.jp

そこから拝借すると以下のような説明がある.

GDB/MIは、 GDBに対する行ベース・マシン用のテキスト・インターフェイスです。

GDBコマンドとGDB/MIインターフェイスの対応づけは上記リンクに記載してあるものが多い。

ステップ実行を行うGDBコマンドnextに対応するGDB/MIインターフェースは-exec-nextとなる。

なお全てのGDB/MIインターフェースのソースコードは以下にある。

sourceware.org Git - binutils-gdb.git/blob - gdb/mi/mi-cmds.c