🌽

豆知識:この中に1つ、無視されるgo.modがいる!

2022/04/16に公開

タイトルは「この中に1人、妹がいる!」のインスパイアです。懐かしいです。

この記事は何

最近ふと知ったGoの仕様に関する紹介記事です。

要点

現在のGoが採用しているモジュール管理において不可欠なgo.modファイルですが、あえて無視するように設計されているケースが1つだけ存在します。それはOSが規定するテンポラルディレクトリ直下のgo.modです。Linuxであれば一般的に/tmp/go.modのことですね。

どうしてそんなことに?

こちらのIssueで報告された困りごとを解決するために、この仕様が導入されたようです。

https://github.com/golang/go/issues/26708

The actual cause is not particularly obvious when you have forgotten there is a bogus go.mod under /tmp from days before.. 🤔
Several tests create temporary build directories under /tmp and run go build, which then finds the go.mod and attempts to fetch unrelated dependencies.

少し補いながら和訳すると

  1. Goのテスト実行時にはシステムのtmpにビルド用のディレクトリを作成し、その中でビルドする。
  2. テストはパッケージ単位で実行できるため上記ビルドディレクトリ内にはgo.modが存在しないことがある。(モジュールに慣れすぎてパッケージ単位でのビルドイメージがピンとこない場合は、この記事が参考になります:Using Go Modules
  3. なにかの拍子で/tmp/go.modが作成された。
  4. それ以降tmp下でビルドすると、ビルドディレクトリ内にgo.modが無ければ親ディレクトリに遡っていくため/tmp/go.modが参照され、依存関係が不一致になってビルドに失敗する。

これを解消するために/tmp/go.modを無視する設計が採用されたようです。例えばGo1.18のソースコードを見ると次の部分がそれに該当します。

https://cs.opensource.google/go/go/+/refs/tags/go1.18:src/cmd/go/internal/modload/init.go;l=402;drc=9f40b4f7a45e8317cffb51675162eacf5ef29ac1

※記事にするため適宜改行などを調整しています

src/cmd/go/internal/modload/init.go
} else if search.InDir(modRoot, os.TempDir()) == "." {
	// If you create /tmp/go.mod for experimenting,
	// then any tests that create work directories under /tmp
	// will find it and get modules when they're not expecting them.
	// It's a bit of a peculiar thing to disallow but quite mysterious
	// when it happens. See golang.org/issue/26708.
	fmt.Fprintf(
		os.Stderr, 
		"go: warning: ignoring go.mod in system temp root %v\n",
		os.TempDir()
	)

一見何のために行っているかわからない処理なので、ガッツリコメントが書いてありますね。

この仕様を鑑みて気をつけることってある?

ほとんどないと思います。
例外としては、例えばコンテナ環境内でビルドを行う際に「せや、ディレクトリ作るのもなんやし、/tmp直下にビルドしたいモジュールのファイル全部置いたろ!」ってなった時ぐらいですかね…?

おわりに

こういう豆知識って、知るとおもしろいですよね!

GitHubで編集を提案

Discussion