Go Modules
#gospec #go #mod
モジュールは、リリース、バージョン管理や配布されるパッケージの集合。
モジュールパスによって識別され、Moduleの依存関係の情報とともにgo.modで宣言される。
go.modファイルを含むディレクトリがモジュールのルートディレクトリとなります。
モジュール内の各パッケージは同じディレクトリにあるソースファイルをまとめてコンパイルしたもの
モジュールパス
パッケージのパスはモジュールのパスにパッケージを含むモジュールルートからの相対パス。
リポジトリのルートパスは、バージョン管理リポジトリのルートディレクトリに対応する
パス構成
リポジトリのルートパス/ディレクトリ/メジャーバージョンサフィックス
メジャーバージョンサフィックス
メジャーバージョン2以上の場合つける
必ずしもサブディレクトリの名前の一部である必要はない
バージョン
セマンティックバージョンで管理
v{メジャー}.{マイナー}.{パッチ}
また、オプションで以下の2つをつけられる
ハイフンで始まるプレリリース文字列
プラスで始まるビルドメタタグ文字列
v1.2.3-pre
v1.2.3+meta
v1.2.3+imcompatible
バージョンアップについて
メジャーバージョン
後方互換性がない変更がされた場合
パケージ削除や、インターフェースの変更など
推移的依存関係により2つの異なるバージョンが必要とされる場合、より高いバージョンが利用される
v2以降は、モジュールのパスに/v2のようなサフィックスが必要
1つのビルド内で複数のメジャーバージョンが利用される場合がある
常に最新を利用する場合、バージョン間に互換性がない場合、依存しているモジュールが動かない場合があるため。
マイナーバージョン
後方互換性がある変更がされた場合
関数が追加されたなど
パッチバージョン
バグフィックスや最適化された場合などインターフェースに影響が与えない場合
pre-releaseサフィックス
あるバージョンをプレリリースであることを示す
プレリリースバージョンはリリースバージョンの前になる
ビルドメタデータ接尾辞
バージョン比較では無視される
go.modファイルでは保持
imcompatible接尾辞
モジュールバージョンのメジャーバージョンに移行する前にリリースされたバージョン
メジャーバージョンが2以上のgo.modファイルのないリポジトリとの互換性のためのサフィックス
作成者が自分でつけるものではなく、goコマンドが判断して付与されるサフィックス
疑似バージョン
バージョン管理リポジトリの特定のリビジョンの特別な書式のプレリリースバージョン
バージョンタグが利用できないリビジョンの参照で利用される
vX0.0-{リビジョンが作られたUTC時間}-{コミットハッシュの12文字}
モジュール解決
パッケージパスの接頭辞を持つモジュールをビルドリストからから検索する。
ビルドリストにある場合、ディレクトリにパッケージを含んでいるかチェックする。
ビルドリストの中で1つのモジュールがパッケージを提供していなかったり、2つ以上のモジュールが同じパッケージを提供しているとエラーになる。
go build -mod=modでコンパイルをおこなうと不足しているパッケージを提供するモジュールをみつけにいきます。
go getとgo mod tidyはこれを行う。
パッケージパスのモジュールを探すときにGOPROXを調べる。
GOPROXYリストの各エントリに対して、パッケージパスのプレフィックスの最新版を要求する。
要求に成功したら、そのモジュールをダウンロードし必要なパッケージが含まれているか検証する。
1つ以上のモジュールがパッケージを含んでいる場合、最も長いパスをもつモジュールを使用する
ただし、パッケージが見つからない場合はエラー
解決されたモジュールはgo.modファイルに追加される。
メインモジュールで利用されていない場合、// indirect commentのコメントをもつ。
GOPROXY:カンマ区切りのプロキシURLのリスト、direct、off
modファイル
ルートディレクトリに行志向で記述されるUTF-8でエンコードされたファイル。
go get:依存関係のアップグレード、ダウングレードが可能
go mod edit:より低レベルな編集が可能
golang.org/x/mod/modfile:プログラム的にmodファイルを変更できる
moduleディレクティブ:メインモジュールのパスを定義する。modファイルに1つ必ず含める。
ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .
module fuga
Deprecated:moduleディレクティブの前か後で、コメントブロックの中で非推奨をマークできる。
// Depecated: comment
そのモジュールのすべてのマイナーバージョンで適用される。v2より高いメジャーバージョンは別モジュール扱いとなる。
Go1.17以降、go list -m -uはビルドリストにあるすべての非推奨モジュールをチェックする。go getはコマンドラインで指定されたパッケージをビルドするために必要な非推奨モジュールをチェックする。
goディレクティブ:モジュールが期待するGoのセマンティックバージョンを記載する。
go.modファイルの中に、最大で1つ含めることができる
後方互換性のない変更をサポートするためのものだった
指定されたバージョンよりあとに導入された言語機能の使用ができないようにする
goコマンドの動作が変更される
go1.14以降
vendor/modules.txtファイルが存在すると、自動ベンダリングが有効になる
go1.16以降
mainモジュールでimportされたパッケージとテストでインポートされたものだけにマッチ する
より低いgoのバージョンでは、メインモジュールのパッケージによってインポートされたパッケージも含んでいた
go.modファイルがないパッケージを読み込んだ場合、間接的な依存関係が発生する
go1.17以降
go.modファイルに、モジュールのパッケージやテストでインポートされるパッケージで利用される間接的に必要なモジュールも明示的にrequireに含まれるようになる
importしたモジュールとして必要なモジュールであったとしても、パッケージで利用されていない場合、読み込む必要がないため、刈り込みの対象になる。
モジュールグラフの刈り込みやモジュールの遅延読み込みが可能になる
※1.16以下では間接的な依存関係は最小限のバージョンしか含まれなかった
間接的な依存関係(//indirect)が存在する可能性があるため、go.modファイル内の別のブロックに記載される
go mod vendorはgo.modとgo.sumを省略する
依存しているモジュールそれぞれのgo.modファイルからvendor/module.txtにgoのバージョンを記録する
requireディレクティブ
依存モジュールの中で最小の必須のバージョン(MVS)を選択し定義される
// indirectコメントがある場合、モジュールが直接依存していないモジュールを表す
go 1.16以下
メインモジュールの依存で利用されているモジュールのバージョンより高いバージョンの場合、indirectとして追加する
go get -uによる明示的なアップグレードや、依存関係の削除、go.modを持たないパッケージをインポートする場合に発生する
go 1.17以降
メインモジュールのテストやパッケージなどによって(間接的にでも)importされたそれぞれのパッケージも間接的な要件として追加される。
これによって、go.modの再帰読み込みが最小限になり、モジュールグラフの刈り込みやモジュールの遅延読み込みが可能になる
excludeディレクティブ
指定のモジュールのあるバージョンがgoコマンドでロードされるのを防ぐ
メインモジュールのgo.modファイルだけで適用される
go 1.16以前
requireディレクティブで参照されているバージョンがメインモジュールのexcludeディレクティブで除外されている場合、利用可能なバージョンをリストアップし(go list -m -versions)代わりに除外されていないより高いバージョンをロードする。
時間経過で変化する可能性があります。
高いバージョンがない場合、エラーを出力する
go 1.16以降
requireディレクティブで参照されているバージョンがメインモジュールのexcludeディレクティブで除外されている場合、requireは無視されます。
go getやgo mod tidyなどでより高いバージョンをinderectでで追加する要因になる
code: go.mod
exclude golang.org/x/net v1.2.3
exclude (
golang.org/x/crypto v1.4.5
golang.org/x/text v1.6.7
)
replaceディレクティブ
あるモジュールの特定のバージョン、またはすべてのバージョンを他の内容に置き換える
メインモジュールのgo.modファイルだけで適用される
別のモジュールのパス、またはプラットフォーム固有のファイルパスを指定する
矢印の左側にバージョンがある場合、そのバージョンだけ置換される
矢印の右側が絶対パスや相対パスの場合、ローカルのファイルパスとして解釈される
置換後のバージョンは省略する
矢印の右側がローカルパス出ない場合、モジュールパスでなければならない
置換後のバージョンは必須
code: go.mod
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ディレクティブ(Go 1.16以降)
go.modで定義されたモジュールのバージョンや範囲が利用されるべきでないことを示す
リリースが早かった場合や、深刻な不具合が見つかった場合に利用する
retractされても依存されたモジュールのビルドが壊れないように利用可能なままにすべき
retractの根拠を説明するコメントを持つべきだが、必須ではない
撤回するにはretractを含んだ最新のバージョンを公開する必要がある(@latestで解決できるようにする)
retractで撤回されるとgo getやgo mod tidyで自動的にそのバージョンにアップグレードすることはなくなる
go getやgo list -m -uで更新をチェックすると通知される
go list -m -retractedでないとバージョンリストからは隠される
code: go.mod
retract v1.0.0
retract v1.0.0, v1.9.9
retract (
v1.0.0 // Published accidentally.
v1.0.0, v1.9.9
)
字句要素
White Space:スペース、タブ、キャリッジリターン、ニューリターン
Comments://,/* */がコメントとして許容されている
Punctuation:,や=>が句読点
Keywords:module、go、require、replace、exclude、retract
Identifiers:モジュールパスやセマンティックバージョンなどの並び
Strings:"", \`\`で囲まれた文字の並びを文字列とする
モジュールパス
パスは1つ以上のスラッシュで区切られている必要がある
また、文頭と文末がスラッシュになってはいけない
ASCII文字、ASCII数字、制限付きASCII句読点からなるからでない文字列であること
パス要素はドットから始まったり終わったりしてはいけない
最初のドットまでの要素名はWindwsの予約語であってはならない
最初のドットまでの要素名はチルダに続く1桁以上の数字で終わってはいけない
モジュールパスがrequireに含まれていてreplaceされていない場合、またはモジュールパスがreplace命令の右側にある場合、そのパスでダウンロードする必要があり、次の要件を満たす必要がある。
最初のスラッシュまでの先頭のパス要素は慣習的にドメイン名であり、少なくとも1つ以上のドットを含む必要があり、ダッシュ(-)から初めてはならない。
パス要素の末尾が/vN(Nは数字とドット)である場合、
Nの先頭文字が0であってはならない
/v1であってもならない
ドットを含んではならない
自動更新
go.modに情報が欠けたり、実態と異なっているとエラーになる。
go get やgo mod tidy、または-mod=modフラグはこれらの問題を自動で修正する。
go1.15以下では-mod=modが有効になっておりgoコマンドで自動実行されていた。
go1.16以降では-mod=readonlyが設定されているように動作する。go.modへの変更が必要な場合、エラーを報告する。
非モジュールのリポジトリとの互換性
GOPATHからの移行の保証のために、goコマンドでgo.modを追加することでモジュールに移行していないリポジトリもモジュールのリポジトリに移行が可能。
go.modファイルのないモジュールをダウンロードする場合、goコマンドはモジュールディレクティブ以外なにもないモジュールキャッシュにあるgo.modファイルを統合します。
統合されたgo.modファイルにはgo.modファイルのないモジュールの依存関係をあらわすrequireディレクティブをもたないので、それぞれの依存関係が同じバージョンでビルドできることを保証するために、追加のreuireディレクティブ(// require)を必要とします。
MVS(Minimal versins selection)
詳細はRus Cox著のresearch!rsc: Minimal Version Selection (Go & Versioning, Part 4)
GoはMVSというアルゴリズムを使用してパッケージを構築するモジュールのバージョンを選択する。
MVSはビルドで使用されるモジュールのバージョンのリストであるビルドリストを出力として生成する。
各モジュールで必要なバージョンを追跡しながらグラフを横断する。
グラフをすべて巡回した時点で最も要求の高いバージョンでリストが構築される。
このリストがすべての要求を満たす最小のバージョン。
go list -m allで確認できる。
ビルドリストは決定論的に決まっているので、新しいバージョンの依存関係がでても変更されない。
Replacement
モジュールの内容は、メインモジュールのgo.modファイルにあるreplaceディレクティブを使用して置き換えられる。置換されたモジュールは、置換前と異なる依存関係を持つ可能性があるため、モジュールグラフが変化する。
Exclusion
モジュールの内容は、メインモジュールのgo.modファイルにあるexcludeディレクティブで特定のバージョンをモジュールから除外できる。
除外されるとモジュールグラフから除外されるため、上位のバージョンの要求に置き換えられる。
Upgrades
go getコマンドでモジュールのアップグレードするために使用される場合、MVSを実行する前にモジュールグラフを変更して、アップグレードされたバージョンのエッジを追加する。
Dowgrade
ダウングレードする場合、ダウングレードするバージョンより上のバージョンをモジュールグラフから削除することでモジュールグラフの変更を行います。削除されたバージョンに依存する他のモジュールのバージョンの情報も削除されます。
また、メインモジュールがダウングレードによって削除されたバージョンを要求している場合、削除されたバージョンより前のバージョンを要求するモジュールのバージョンに変更されます。
モジュールグラフの刈り取り
メインモジュールがgo1.17以上の場合、MVSで使われるモジュールグラフには、go1.16以下のモジュールの依存関係で要求されない限り、go.modファイルでgo1.17以上が指定されている各モジュールの依存関係の直接的な要件のみ含まれる。(推移的依存関係はグラフから切り捨てられる。)
go1.17のgo.modファイルからは、そのモジュールのパッケージやテストをビルドするために必要なすべての依存関係のためのrequireディレクティブを含む。(明示的に必要でない依存関係はモジュールグラフから刈り取られる)
あるモジュールにとって必要のないモジュールは動作に影響を与えることはできないので、モジュールグラフで刈り取られた依存関係が残っていると無関係なモジュール間の干渉を引き起こす
go1.16以前はgo.modファイルに直接の依存関係しか含まれていなかったため、より大きなすべての間接的な依存関係がロードされないといけなかった。
go mod tidyによって記録されたgo.sumは、go.modのgoディレクティブより1つ下のバージョンで必要なチェックサムを含む。
go1.17では、go1.16でロードされた完全なモジュールグラフに必要なチェックサムをすべて含む。
go1.18では、go1.17で刈り取られたモジュールグラフで必要なチェックサムだけを含む。
遅延読み込み
メインモジュールがgo1.17以降の場合、モジュールグラフ全体の読み込みを必要になるまで遅延させられる。代わりにメインモジュールのgo.modファイルのみを読み込んで、その要件だけをつかってビルドするパッケージのロードを試みる。メインモジュール外のパッケージのテストの依存などで必要なパッケージが見つからない場合、残りのモジュールグラフを要求に応じてロードする。
モジュールグラフを読み込まずにgo.modファイルからインポートされたパッケージが全て見つかった場合、go.modファイルをロードする。ロードした結果、メインモジュールの要件と照合されてローカルで整合していることが確認される。
Workspaces
ワークスペースはMVSを実行するときに、ルートモジュールとして使用されるディスク上のモジュールの集合です。
ワークスペース各モジュールのディレクトリへの相対パスを指定したgo.workファイルで宣言できる。
go.workが存在しない場合、ワークスペースはカレントディレクトリを含む単一のモジュールで構成される。
-workfileフラグを確認することで、ワークスペースのコンテキスト上にあるかを判断する。
フラグは.workで終わる既存のファイルへのパスを指定した場合、有効化される。
go.work
ルートディレクトリに行志向で記述されるUTF-8でエンコードされたファイル。
code:go.work
go 1.18
use ./my/first/thing
use ./my/second/thing
use (
./my/first/thing
./my/second/thing
)
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
goディレクティブ
go.workファイルに必須
有効なリリースバージョンを記載
useディレクティブ
ディスク上のモジュールをワークスペースのメインモジュールの集合に追加する。
go.modファイルがあるディレクトリの相対パス
replaceディレクティブ
go.modのreplaceディレクティブと同様にすべて、もしくは特定のバージョンのモジュールの内容を置き換える
go.workのワイルドカードの置換は、go.modファイルのバージョン指定のreplaceをすべて置き換える
Module-aware commands
goコマンドはモジュール対応モードとGOPATHモードで実行できる。
GO111MODULE=off
go.modファイルがあってもGOPATHモードで実行される
GO111MODULE=on
go.modファイルの存在有無に関わらずモジュールモードで動作する
go.modファイルを使って依存関係を探すようになります
go1.16以降ではデフォルトで有効
GO111MODULE=auto
go1.15以前でデフォルトで有効
go.modファイルがあるとモジュールモードが有効化される
Build commands
go build, go fix, go generate, go get, go install, go list, go run, go test, go vet
モジュールコマンドに共通する以下のフラグを利用できる
-mod
go.modが自動的に更新されるか、vendorディレクトリが使われるかを制御する
go1.14 以上ではvendorディレクトリが存在する場合、-mod=vendorを指定されたように動作する。
そうでない場合、mod=readonlyが指定されたように動作する
-mod=mod
vendorディレクトリを無視してgo.modを更新するように指示する
-mod=readonly
vendorディレクトリを無視してgo.modを更新が必要な場合、エラーを報告する
-mod=vendor
vendorディレクトリを利用するように指示する
-moodcacherw
モジュールキャッシュに作成するディレクトリを書き込みも可能なパーミッションを指定する。
パーミッションを変更せずにモジュールキャッシュを削除できる
-modfile=file.mod
モジュールルートディレクトリのgo.modファイルの代わりのファイルを読み書きするように指定できる。拡張子は.modである必要がある。ただし、go.modファイルはモジュールルートディレクトリを決めるために必要だが、使用されることはない。
-workfile
ワークスペースを定義するgo.workファイルを使用してワークスペースモードを使用するようにgoコマンドに指示する。
Vendoring
go mod vendorコマンドでメインモジュールのルートディレクトリにvendorディレクトリを作成。
メインモジュールのパッケージのビルドやテストに必要なすべてのパッケージのコピーを格納する。
メインモジュール以外のパッケージのテストでしか使われていないパッケージは含まれない。
また、vendor/modules.txtというファイルが作成される。vendoringが有効な場合、go list -mなどで出力されるバージョン情報のソースとして使用される。
このとき、modules.txtはgo.modファイルと一致するかどうかチェックする。
go1.14以上でメインモジュールのルートディレクトリにvendorディレクトリがあれば自動で使用される。
vendorが使用される場合、ビルドコマンドはネットワークやモジュールキャッシュにアクセスせずにvendorディレクトリからパッケージをロードする。
Go Commands