GoのBuild Constraintsを使おうとしてハマったのでメモ。
Build Constraintsの後には空行が必須
Goでは、同一ディレクトリ以下のソースコードは全てビルド対象になります。これに対し「Build Constraints」というコメント的なものを使うことで、環境ごとにビルド対象に含めたり外したりといったことができます。
参照:Package build - Build Constraints
これによれば、コード先頭に次のように書けば、そのソースコードはビルド対象から外れそうに読めます。
// +build ignore
しかし、次のように書いたソースコードは何事もなかったかのようにリンクされます。
// +build ignore
package main // ダメ
実は、Build Constraintsの後には必ず空行を入れる必要があるのです。
// +build ignore
package main // バッチリ
これはよく見るとドキュメントにも書いてあります。
To distinguish build constraints from package documentation, a series of build constraints must be followed by a blank line.
Build Constraintsの詳細
以下のものがBuild Constraintsとして記述できます。(build/build.goのコメントから引用)
- OS名($GOOSの値、
linux
,windows
,darwin
など) - アーキテクチャ名($GOARCHの値、
arm
,386
,amd64
など) -
cgo
(cgoが有効なとき) -
!cgo
(cgoが無効なとき) - コンパイラ名(
go
,gccgo
) - !コンパイラ名
- tag (
ctxt.BuildTags
かctxt.ReleaseTags
に含まれるタグ) - !tag (
ctxt.BuildTags
とctxt.ReleaseTags
に含まれないタグ) - 上記のカンマ区切りリスト(AND条件)
ただし、OS名にlinux
を指定した場合は例外的にAndroidも含まれます。Androidを除くLinuxを指定したい場合はlinux,!android
とする必要があります。
ReleaseTags
はGo 1.4だと次のような値になっています。これを使えばGoの特定バージョン以降・以前という表現ができます。
c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}
ファイル名によるBuild Constraints
ところで、このBuild Constraintsの一部はファイル名で表現することもできます。OS名とアーキテクチャ名に限り、次のようなファイル名で記述可能です。
- foo_[OS名].go
- foo_[アーキテクチャ名].go
- foo_[OS名]_[アーキテクチャ名].go
これにマッチしないファイルはビルド対象になりません。これを逆手にとって、foo.go
をビルド対象から外したいときに_foo.go
にリネームするというテクニックが使えます。もちろん、foo_ignore.go
などとしてもよいでしょう。
このように単純なBuild Constraintsについてはファイル名で指定するのが良いように思います。同じことをするにしても、コメントによるBuild Constraintsの場合は全ファイルの先頭を眺めないと挙動がわからないため、保守性が下がるのではないでしょうか。ファイル名で実現できる場合はファイル名で実現し、複雑なBuild Constraintsに限りコメントで実現すべき、というのが筆者の個人的な意見になります。
文句
空行必須とか何なの。初見殺しにもほどがあるでしょ…。
コメントやファイル名でビルドの挙動を変えること自体、あまり良い選択だとは思えない。不親切というかやっつけというか。文句言いつつ慣れたころに仕様変更されそうで怖い。