Closed10

go のモジュール/パッケージ管理がよくわからん

hassaku63hassaku63

概念としては module -> package -> version の粒度になっている

module は、ディストリビューション(配布)の単位

module の特定は、 go.mod で宣言されている module path 情報

module path: A path that identifies a module and acts as a prefix for package import paths within the module. For example, "golang.org/x/net".

module のルートディレクトリは、 go.mod がいる場所。 main module (?) は、 go コマンドが実行される場所...と書いてあるがよくわからん

※ module root と、各種 go コマンド (go install とか) の起点になるディレクトリは分離できるぜ、と言っているように見える

module の中にいる package は、同一ディレクトリ内に存在し、一緒にコンパイルされるファイルの集まり

※同一ディレクトリにあるファイルはコンパイラ的にはまとめて1つの対象として認識されるっぽい。だとすれば、 import 文で個別のファイル名の指定が必要ないのは納得できる(Python や JavaScript 的にはファイルが基本単位なので、このへんの感覚は go とは異なるように見える)

package path は、module root からの相対パスを module path に対して join した表記。例えば、

golang.org/x/net

という module は html というパッケージを備えており、html パッケージのパスは golang.org/x/net/html となる

hassaku63hassaku63

go 1.17 以上で使える Lazy loading の仕組みがあるらしい。

Lazy module loading

バージョン解決のために module garph なる仕組みを用いており(subdep も込みで全部の依存関係をグラフにしたもの、と理解している)、これをオンデマンドでぎりぎりまで読み込まないようにしてくれる機能が Lazy loading. go.mod に書いてあるモジュールだけでビルドを試みる

hassaku63hassaku63

ローカルにある複数の module をまとめる(?) workspace という概念があるらしい

GOWORK という環境変数や go.work というファイルが関与しているらしいが、当面気にする必要がなさそうなので必要になったら調べる

hassaku63hassaku63

GOPATH からの移行」を念頭に置いた話らしいので少しだけ見ておく(結局 GOPATH が何者でいつ使う必要があるのか、よくわかっていないので)

レガシーが GOPATH で、今は go module という位置づけと理解している。このへんの動作モードに関する設定が環境変数 GO111MODULE でコントロールできる

Compatibility with non-module repositories

module を使っていないリポジトリから、module を意識した動作モードで download -> build できる...らしい。

要するに、 go.mod の仕組みを使っていないものでもうまいことインストールできるような仕組み、という解釈になりそう

また、 v2.0 以上でリリースしたモジュールは、そのモジュールパスのサフィックスに /v2 が必要らしい。こうすることで複数のメジャーバージョンを区別できるようになる。この機能は module のサポートが導入されたタイミングで導入された。しかし、これがリリースされた時点で v2 以上のバージョンを持つモジュールが多数あった。これらに対する対応として、 go.mod がなく v2 以上のバージョンを持つモジュールに +incompatible というサフィックスを自動追加するようになっている。

hassaku63hassaku63

module-aware command

  • go build
  • go fix
  • go generate
  • go get
  • go install
  • go list
  • go run
  • go test
  • go vet

"module-aware mode" で動作する場合は、 go.mod が前提になっている。コマンドラインあるいはソースの内容に基づいて import パスを解釈する。

これらのコマンドは、以下の共通の引数を持つ


-mod=mod

go コマンドは、 vendor ディレクトリ(?) を無視する。import されたパッケージが既知(?)のモジュールによって提供されない場合に自動的に go.mod を更新する

-mod=readonly

vendor ディレクトリを無視する。 go.mod を更新する必要がある場合はエラーを報告する

-mod=vendor

go コマンドは、vendor ディレクトリを使用する。このとき、go コマンドは N/W やモジュールキャッシュを使用しない。

デフォルトでは、 go.mod で go 1.14 以上かつ vendor/ が存在する場合、 -mod=vendor を指定したかのように動作する。そうでなければ、 -mod=readonly として動作する

hassaku63hassaku63

go の標準の枠組みに含まれてそう、だがいまいちわからん存在の vendoring について

Vendoring

module を使う場合は、必要になる依存関係をローカルに引っ張って依存性を満たしている。

vendering は、古い go との互換性や、ビルドに必要なファイルがすべて単一のファイルツリーにあることを保証するために 使用される

go mod vendor は、main module のルートディレクトリに vendor/ を作成し、そこに main module のパッケージビルドとテストに必要なすべてのパッケージのコピーを格納する。main module でないパッケージのテストにのみ使うものは、ここに含まれない。

※ python でプロジェクトごとに作成する .venv/ であったり、js プロジェクトの node_modules/ のような存在に近いもの...という理解。

vendoring が有効な場合、 go buildgo test といったビルドコマンドは、N/W やローカルのモジュールキャッシュにアクセスする代わりに vender/ か パッケージをロードする。一方、 go mod downloadgo mod tidy のような go mod コマンドは、モジュールキャッシュにアクセスする。(※ここ、いかにもハマりそうなので覚えておく

GOPATH モードの vendoring とは動作の仕様が違っている。go コマンドは main module のルート以外の場所にある vendor を無視する。他の module の vendor/ もまた使用されない。

hassaku63hassaku63

go コマンドについて

go get

Since Go 1.16, go install is the recommended command for building and installing programs. When used with a version suffix (like @latest or @v1.4.6), go install builds packages in module-aware mode, ignoring the go.mod file in the current directory or any parent directory, if there is one.

go get is more focused on managing requirements in go.mod. The -d flag is deprecated, and in Go 1.18, it will always be enabled.

go 1.16 からは、プログラムのビルド&インストールに関しては go install が推奨になっている。 go get は、 go.mod の要件を管理することに重点を置いている

(Python, JavaScript の世界で置き換えると、 Pipfile や pacakge.json を手動で書き換えるのではなく、代わりに pipenv install <package-name>npm i --save <package-name> コマンドを使おう、みたいな話だと理解している)

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

examples

# Upgrade a specific module.
$ go get -d golang.org/x/net

# Upgrade modules that provide packages imported by packages in the main module.
$ go get -d -u ./...

# Upgrade or downgrade to a specific version of a module.
$ go get -d golang.org/x/text@v0.3.2

# Update to the commit on the module's master branch.
$ go get -d golang.org/x/text@master

# Remove a dependency on a module and downgrade modules that require it
# to versions that don't require it.
$ go get -d golang.org/x/text@none

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

このコマンドはモジュールを更新するが、パッケージはビルドしない。
(たぶん、依存関係のインストールのみが仕事)

go install

go install [build flags] [packages]

examples

# Install the latest version of a program,
# ignoring go.mod in the current directory (if any).
$ go install golang.org/x/tools/gopls@latest

# Install a specific version of a program.
$ go install golang.org/x/tools/gopls@v0.6.4

# Install a program at the version selected by the module in the current directory.
$ go install golang.org/x/tools/gopls

# Install all programs in a directory.
$ go install ./cmd/...

コマンドライン上のパスで指定されたパッケージをビルドしてインストールする。メインパッケージの実行バイナリを作成して GOBIN 環境変数で指定されたディレクトリにインストールされる。

※デフォルトでは $GOPATH/bin または $HOME/go/bin になる

実行可能でないパッケージは、ビルドされキャッシュもされるが、「インストール」はされない

go list -m

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

example

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

インストール済みの module をリストする

go mod download

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

example

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

モジュールキャッシュへのダウンロードを行う。おそらく、pipenv installnpm install --save と同じような位置づけと理解してOKのはず

go mod tidy

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

go.mod がモジュール内のソースコードと一致することを確認する。過不足を訂正して、go.sum を更新する。

go 1.16 からは、 -e オプションが追加された。これをつけるとパッケージのロード時にエラーを吐いても続行するようになる。

-v は、削除されたモジュールに関する情報を標準エラーに吐き出させる。

go mod vendor

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

main module の配下に vendor/ を作成する。さっきの vendoring を有効にするためのコマンドということらしい

go mod verify

go mod verify

モジュールキャッシュにいる依存関係が、ダウンロード後に変更されてないことを確認するためのコマンド

※たぶん、 go.sum を正としてキャッシュのチェックサムを計算しているのだと思われる

※故意にキャッシュディレクトリ内のファイルを変更する以外で、これが有効な場面がぱっと浮かばなかった。

go clean -modcache

go clean [-modcache]

モジュールキャッシュ全体を削除する

Version queries

バージョン指定できる書き方の紹介。不等号なども使用可能。

go.mod でバージョン指定したくなったらここ読むこと。

example

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

環境変数について(巷記事でよく見かけるやつだけ抜粋)

GO111MODULE

GOPATH mode かどうかを指定するもの。1.16 以降なら GOPATH mode にする必要はなく、module-aware mode を使っておけば良い、はず。無指定の場合は module-aware mode

GOPATH

"GOPATH mode" では go のコードを含む可能性のあるディレクトリのリスト(たぶん、Python で言う sys.path あたりが近い概念と思われる)

"module-aware mode" ではモジュールキャッシュを格納する場所($GOPATH/pkg/mod)。

無指定の場合は $HOME/go になる

hassaku63hassaku63

ざっくりつまみ食いできたので、この scrap の役目はおわり

このスクラップは2022/05/08にクローズされました