go generate(する方)に入門する
go generateでコード生成できることは知っているけど、具体的にどんなツールがあって何を実装すると生成できるか知らない。
また、簡単に生成するためのツールとか、ベストプラクティスがあるかもと思いつつ、やはり知らない。
そういうわけでガッと調べてみる。
結果的に、リリース時の公式ブログ、サンプル実装として今も公開されてるstringerの実装、デザインドキュメントを読んだ。
Goのツールとしていい感じにプログラムを呼び出してくれる仕組みなのだと理解した。
なので、コード生成しない使い方もできるっちゃできそう。例えばlinterを呼び出すエントリーポイントにするとか。
design docでも述べられているけど、makeの結構な部分を置き換えられそうで、コード生成に限らず色々できると思う。
Linter呼び出すのが嬉しいか微妙だけど。ぼくは新しく参加したプロジェクトで静的解析の実行のためにはgo generateしようと言われたびっくりする。makeでいいじゃんってなりそう。
ソースコードにディレクティブを書いてトリガーできるのがやはり特に嬉しいポイントなのかな。
ベストプラクティスみたいなものは結局見つけていない。そんなものはない気もする
sqlcを実装できると最強かな
こんな感じ
https://github.com/sqlc-dev/sqlc/blob/main/internal/codegen/golang/templates/template.tmpl ここに出力に用いるGoテンプレートがある。
Goテンプレート直接触るのか
uber-go/mock の依存少ないな。
https://github.com/uber-go/mock/blob/main/mockgen/mockgen.go ここをみるとインデントをバッファに追記する関数があるので、phpみたいに結果のファイルを書き出す感じなのかな
Goテンプレートを理解するのが良いかなあ
https://go.dev/blog/generate 基本に立ち返って公式ブログをみる
Programs that write programs are therefore important elements in software engineering, but programs like Yacc that produce source code need to be integrated into the build process so their output can be compiled. When an external build tool like Make is being used, this is usually easy to do. But in Go, whose go tool gets all necessary build information from the Go source, there is a problem. There is simply no mechanism to run Yacc from the go tool alone.
Until now, that is.
とのこと。なるほどgo generateはgoをコンパイルする直前にgoコードを生成するステップを組み込むために存在するのか
パッケージ名を指定できるとか、go generate以外(人が温かみのある作業で書くとか)で作られたコードにgo generateの実行を書けるがmakeで頑張ることに対する優位性かな
解説で出てきたstringerもgo mockに似たスタイルで書かれてる
読めばわかるのはありがたい知らなかったけど便利そう
https://cs.opensource.google/go/x/tools/+/refs/tags/v0.27.0:cmd/stringer/ 小さいし何を達成するものか分かり易いので全部読んでもよさそう
Cのあの本とかプログラミング作法で読んだような雰囲気だと思ったらRob Pikeのコミットだった
stringer面白かった。実装は単一ファイルで読み易い
go generateで指定するコマンドは、特に難しいこと考えずにGoコード生成するとよさそう
ただし、入力となるパッケージを拡張するコードを書きたいなら、go generateをダイレクティブから呼ばれることを気にすることになりそう
ソースコードを入力にしようと思おうと、GoのspecとAST表現を暗記すると話が早そう
出力はprintfすれば良いので簡単
あとはこれかな
The go generate tool also sets the build tag “generate” so that files may be examined by go generate but ignored during build.
build tagってなんだろ
thanks gpt、それっぽい
build
タグは、Goのビルドシステムで使用される特別なコメントです。特定の条件に基づいてファイルを含めるかどうかを制御するために使います。このタグは、ソースコードファイルの最初に記述され、ビルド時にGoツールがそのファイルをどのように扱うかを指定できます。
たとえば、// +build generate
というコメントは、そのファイルがgo generate
コマンドでのみ使用され、通常のビルドでは無視されることを示します。これにより、生成されたコードやツール固有のコードをビルドから除外できます。
簡単に言うと、build
タグは特定の環境や条件下でのみファイルをビルドに含めるかどうかを決定する仕組みです。go generate
の場合、// +build generate
タグが付けられたファイルは、通常のビルドには影響を与えませんが、go generate
コマンドで処理することができます。
例えば、次のように記述します:
// +build generate
package main
// このファイルは go generate コマンドで使用されるため、通常のビルドでは無視されます。
これにより、go build
やgo run
ではこのファイルはビルドされませんが、go generate
では処理されます。
なるほどね (thanks gpt)
-tags
オプションは、Goのコマンド(例:go build
, go test
, go run
)で使用され、ビルドタグを指定するためのものです。このオプションを使うことで、特定のビルドタグを持つファイルを含めたり除外したりできます。
Goでは、ファイルに「ビルドタグ」を記述することで、特定の条件下でのみそのファイルをビルド対象に含めることができます。-tags
オプションは、これらのタグを指定して、Goツールに特定のファイル群を処理させることを意味します。
使用方法
go
コマンドに対して-tags
オプションを使うと、指定したタグに一致するファイルだけを対象に処理します。
例えば、次のように使います:
go build -tags=generate
このコマンドは、// +build generate
タグを持つファイルのみをビルド対象として扱います。generate
タグを持たないファイルはビルドされません。
複数のタグを指定する
複数のタグを指定することもできます。タグをカンマ区切りで列挙すると、いずれかのタグが一致するファイルが対象としてビルドされます。例えば:
go build -tags="linux,arm"
この場合、// +build linux
または// +build arm
タグを持つファイルがビルド対象として選ばれます。
-tags
の使いどころ
- 環境ごとの処理: 例えば、特定のプラットフォームやアーキテクチャに依存したコードを含める場合、タグを使ってそれを管理できます。
-
ツールの生成:
go generate
やテストなどで、特定のコードを自動生成するファイルを含めるために-tags
を使います。
まとめ
-tags
オプションを使うことで、ビルド時に特定のビルドタグを持つファイルを処理対象にしたり、除外したりできます。この機能は、環境ごとの設定や生成コードを扱う際に非常に便利です。
なるほど〜
最終的にどうなったんだろう。ぼくの中では不整合を検知していない
build tagの話はこういうユースケースを想定していたのかな
一旦以上かな
次は実際にコード生成ツールを作ってみて、動かしてみるかな