go のモジュール/パッケージ管理がよくわからん
概念としては 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
となる
go 1.17 以上で使える Lazy loading の仕組みがあるらしい。
Lazy module loading
バージョン解決のために module garph なる仕組みを用いており(subdep も込みで全部の依存関係をグラフにしたもの、と理解している)、これをオンデマンドでぎりぎりまで読み込まないようにしてくれる機能が Lazy loading. go.mod
に書いてあるモジュールだけでビルドを試みる
ローカルにある複数の module をまとめる(?) workspace という概念があるらしい
GOWORK という環境変数や go.work
というファイルが関与しているらしいが、当面気にする必要がなさそうなので必要になったら調べる
「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
というサフィックスを自動追加するようになっている。
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
として動作する
go の標準の枠組みに含まれてそう、だがいまいちわからん存在の vendoring
について
Vendoring
module を使う場合は、必要になる依存関係をローカルに引っ張って依存性を満たしている。
vendering は、古い go との互換性や、ビルドに必要なファイルがすべて単一のファイルツリーにあることを保証するために 使用される
go mod vendor
は、main module のルートディレクトリに vendor/ を作成し、そこに main module のパッケージビルドとテストに必要なすべてのパッケージのコピーを格納する。main module でないパッケージのテストにのみ使うものは、ここに含まれない。
※ python でプロジェクトごとに作成する .venv/
であったり、js プロジェクトの node_modules/
のような存在に近いもの...という理解。
vendoring が有効な場合、 go build
や go test
といったビルドコマンドは、N/W やローカルのモジュールキャッシュにアクセスする代わりに vender/ か パッケージをロードする。一方、 go mod download
や go mod tidy
のような go mod コマンドは、モジュールキャッシュにアクセスする。(※ここ、いかにもハマりそうなので覚えておく)
GOPATH モードの vendoring とは動作の仕様が違っている。go コマンドは main module のルート以外の場所にある vendor を無視する。他の module の vendor/ もまた使用されない。
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 install
や npm 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
環境変数について(巷記事でよく見かけるやつだけ抜粋)
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
になる
ざっくりつまみ食いできたので、この scrap の役目はおわり