Modules

モジュールは、リリース、バージョン管理や配布されるパッケージの集合。
モジュールパスによって識別され、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 getgo mod tidyはこれを行う。

パッケージパスのモジュールを探すときにGOPROXYを調べる。
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でで追加する要因になる
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ファイルだけで適用される
    • 別のモジュールのパス、またはプラットフォーム固有のファイルパスを指定する
      • 矢印の左側にバージョンがある場合、そのバージョンだけ置換される
      • 矢印の右側が絶対パスや相対パスの場合、ローカルのファイルパスとして解釈される
        • 置換後のバージョンは省略する
      • 矢印の右側がローカルパス出ない場合、モジュールパスでなければならない
        • 置換後のバージョンは必須
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でないとバージョンリストからは隠される
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,=>が句読点
  • Keywordsmodulegorequirereplaceexcluderetract
  • 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でエンコードされたファイル。

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ディレクトリからパッケージをロードする。