Goではbuild constraints(build tagsとも呼ばれる)を利用することで、異なるビルドを作り分けることができます。
この機能を利用することで、デバッグ用とリリース用や無償版と有償版といった複数のバリエーションのビルドを作ることができます。
本記事でのGoのバージョンについて
本記事ではGo 1.17以降を対象とします。
build constraintsの書き方はGo 1.17から変更になったのですが、1.16は既にサポートされていないバージョンなので、本記事でも扱わないこととします。
詳細については https://go.dev/design/draft-gobuild を見るようにとリリースノートにもあるので、以前の記法と「なぜ変更になったのか」が気になる方はそちらを御覧ください。
build constraintsの付け方
build constraintsは以下のようにファイルの先頭に//go:build 条件という形式のコメントを書きます。
条件の部分は単純にタグ名を指定する以外にも、if文のような(A || !B)のような条件も指定できます。
ここでは簡単な例としてreleaseというタグを使ってみます。
//go:build !release
package main
const API_SERVER_URL = "http://localhost:8080/
//go:build release
package main
const API_SERVER_URL = "https://api.example.com/
myapp.goの方は!release(! = 否定)という指定で、release以外のときに対象となるようにしています。
myapp_release.goのほうは単純にreleaseという指定です。
このconstraintsには上記のようにユーザーが独自でつけるもの以外にも、linux, 386といったOSやアーキテクチャに対応するものもあります。
既定ではGOOS, GOARCHとcgoに関するものがあるので、独自に付ける場合はこれらとかぶらないように気をつけたほうがよいでしょう。
他にも細かいルールなどがあるのですが、ここには書ききれないので、公式ドキュメントのBuild constraintsに目を通すことをお勧めします。
対象を指定してビルドする
上記のようにbuild constraintsを指定した上で、対象を指定してビルドする場合は、以下のようにgo build時に-tagsオプションを指定します。
go build -tags release
このようにするとreleaseが指定されたmyapp_release.goファイルがビルド対象となり、逆に!releaseが指定されているmyapp.goはビルド対象外となります。
今回の例のようにconstraints毎に異なる値を設定する、異なる関数の実装にするといった場合は、いずれか1ファイルのみがビルド対象になるように設定しないとエラーになってしまいます。
なぜなら、両方がビルド対象に含まれてしまうと、今回の例でいえばAPI_SERVER_URLが二重に定義されていることになってしまうからです。
上記の例で-tagsを指定しなかった場合
go build
というように、-tags releaseを指定しなかった場合はどうなるでしょうか。
この場合は!releaseの条件に合致するmyapp.goがビルド対象になり、myapp_release.goはビルド対象外になります。
どのような時に使うのか
Go自身はこの機能をOSやアーキテクチャ別の実装のために使っていますが、今回のようにデバッグとリリースで分けるといった使い方も考えられます。
他にもFree版、Pro版、Enterprise版のように複数のエディションに分けるといった使い方もできそうです。
今回の例のような設定値であれば、設定ファイルから読み込むといったことも多いかと思いますが、場合によっては安易にユーザーに変更されては困るとか、エディションによって使える機能が変わってくる、といったような場合には、build constraintsで別々のビルドにするというのは良い考えではないでしょうか。