golang-1.16あたりで開発者にとってお役立ちかもしれない情報


module機能に関するgopath慣習からの変化ついて

  1. GOPATH環境変数の時代
    • GOPATHで指定したディレクトリを基準にバージョンも考慮せずにパッケージをいい感じにハードコーディングする実装だった
  2. module(go.mod)の時代
    • 依存する内部パッケージと外部パッケージをバージョンを指定してコーディング可能とした

依存関係の解決

 では、内部パッケージと外部パッケージをバージョンを指定して参照する方法を如何様(いかよう)に指定するのか?
 それがモジュール機能であると思われる。
 検索して、ここに辿り着いた人へ理解を促すヒントとなれば幸いです。

共通理解

ファイルに記した識別子とディレクトリ情報に記した識別子は、ファイルに記した識別子の方がコンパイラで処理できる分だけ機械処理が楽

 ファイルの中に書くと、コンパイラ(字句解析器と構文解析器)が一つのプログラム構造で取り扱える。
 ディレクトリ名は、コンパイラが取り扱うには、それ専用に別のプログラム構造を実装する事を要求してしまう。
 なので、ファイルの中に書かせるようにプログラマを躾けた方が監理面で都合がよろしい。 ポジティブに言えば合理的に作用する。

ソースコード(*.go)に宣言するpackageの名前は、何を意味するの?

 ソースが所属するパッケージの名称を指す。
 ここは、1.16前後で変化がないので、これで全部です。

依存関係ファイル(go.mod)に宣言するmoduleの名前は、何を意味するの?

 外部参照に用いるパッケージの名称を意味する。
 理解する上での要点は「リンケージやファイル群のフェッチに用いる識別子」が「モジュール名称」との理解です。
 つまり

module ${外部参照名称:慣習的にはドメイン名}/${パッケージ名称}

go 1.16
 となります。
 「${外部参照名称}/${パッケージ名称}」が「モジュール名称」です。

ユニバーサル参照(造語:グローバルだとリンカを理解した人に誤解を生むので…)なんかせず、モジュール分割した内部モジュールを参照して欲しいンだけど?

 この宣言だけでは、内部モジュールを参照せずに、示された何処かのリポジトリを参照してモジュールをフェッチしようと動作します。
 そこで「replace」宣言を用いて、ユニバーサルにモジュールを参照してビルドしようとするgoのbuild機能をコンピュータのファイルシステム上にあるファイルに置換させます。

module ${外部参照名称:慣習的にはドメイン名}/${パッケージ名称}

go 1.16

replace ${外部参照名称:慣習的にはドメイン名}/${パッケージ名称} => ./${ビルド環境上に存在するパッケージまでの相対パス}
 これは、以下のコマンドでもgo.modに追記可能です。
$ go mod edit --replace=${外部参照名称}/${パッケージ名称}=./${ビルド環境上に存在するパッケージまでの相対パス}
 蛇足で細かい話を追加すると、コマンドはカレントディレクトリにある「go.mod」を編集しようと動作します。

ビルド、通らないンだけど…?

 ここまでの宣言では、必須(require)モジュールとフェッチするべきモジュールのバージョンを指定していない。
 これだけの宣言では、実際にユニバーサル参照する上でバージョン管理システムから「どのバージョンをフェッチすればいいのか?」が明示できていません。
 これを手動で指定することもできるようですが、下記のコマンドを実行することで「とりあえず、いい感じ(=とりま最新版)」を参照するようになります。

 $ go mod tidy
 これを各モジュール毎に(恐らくは、mainパッケージを納めたディレクトリ上で)実行すれば、必須モジュールとする宣言をgo.modに記入してくれるようです。
 実際には、それぞれの内部モジュールを納めたディレクトリにカレントディレクトリを移動してコマンドを実行する事になると思います。
 ここまでくれば、もう機械作業なので頑張ってください。

まとめ

 さあ、仕事へお戻りください(That all. Go back to work. May the force be with you.)。