Golangのビルドタグについて

Golangのビルドタグというものを知ったのでその話。

ビルドタグとは

Golangクロスプラットフォームに対応しているので、基本的には環境に依存しないプログラムを書くべきですが、ときにはどうしても環境依存の処理を書きたかったり、開発環境と本番環境で別のスタブを差し込みたかったり、とにかくターゲット環境に合わせてどのソースコードを使ってビルドを指定したくなるときがあります。

そういうときは

$ go build a.go b.go c.go

みたいな感じでビルドに含めたいファイルだけを列挙すればできますが、これは明らかに煩雑なので使いたくないです。

このような問題はGolangではビルドタグを使うことで解決することができます。

ビルド制約と呼ばれることもあるらしい(厳密にはビルド制約をビルドタグを使って記述している)

 

使い方

ファイルの先頭に次のようなコメントを書くとビルドタグになります。

    // +build

buildに続いて記述した内容がビルドタグとして認識され、ソースの扱いを定めることができます。

コンマ区切りでAND、空白区切りでOR、!でNOT、改行して別のビルドタグを記述した場合もORになります。

例えば、

    // +build linux,386 darwin,!cgo
    // +build develop

をファイルの先頭に記述した場合のビルド制約は

( ( linux AND 386 ) OR ( darwin AND ( NOT cgo ) ) ) AND develop

となります。

 

OS,CPUアーキテクチャごとに指定する

GOOSとGOARCHをビルドタグに指定することで、特定のOSやCPUアーキテクチャに向けて指定することができます。

指定できるGOOSとGOARCHについては公式のドキュメントを参照してください。

急にGOOSとGOARCHをビルドタグに指定とか言われても分かりづらいと思いますが、とりあえず使ってみるとなんとなく分かると思います。

$ ls
Print1.go Print2.go main.go
$ cat main.go
package main

import (
        "fmt"
)

func main() {
        fmt.Println(str)
}
$ cat Print1.go
// +build 386

package main

var str = "this is 386 stub"
$ cat Print2.go
// +build amd64

package main

var str = "this is amd64 stub"
$ GOARCH=amd64 go build -o Print_amd64
$ GOARCH=386 go build -o Print_386
$ ./Print_amd64
this is amd64 stub
$ ./Print_386
this is 386 stub

GOARCHがamd64のとき、ビルドタグにamd64を指定したPrint1.goが、GOARCHが386のとき、ビルドタグに386を指定したPrint2.goがビルドに使用されていることが分かります。もしビルドタグを使用しなかった場合は変数strの二重宣言でコンパイルエラーになりますが、このようにビルド制約を課すことで特定のファイルをビルドに使用できることができます。

またこの場合は、ビルドタグを使わなくてもファイル名にGOOS、GOARCHのサフィックスをつけることでも同様のことが実現できます。

具体的には

  • *_$GOOS.go
  • *_$GOOS_test.go
  • *_$GOARCH.go
  • *_$GOARCH_test.go
  • *_$GOOS_$GOARCH.go
  • *_$GOOS_$GOARCH_test.go

の6種類の指定方法があります。$GOOS、$GOARCHにはターゲットの環境に合わせてよしなに置換してください。

$ ls
Print_386.go   Print_amd64.go main.go
$ cat Print_386.go
package main

var str = "this is 386 stub"
$ cat Print_amd64.go
package main

var str = "this is amd64 stub"
$ GOARCH=amd64 go build -o Print_amd64
$ GOARCH=386 go build -o Print_386
$ ./Print_amd64
this is amd64 stub
$ ./Print_386
this is 386 stub

これはファイルの命名だけで制御できるので便利な反面、意図せずこの機能を使ってしまうと、かなり気づきにくい気がするので気をつけないとなぁ(実際にそんなサフィックスのついたファイル命名をするかどうかは置いておいて)

 

tagsオプションによる指定

GOOSやGOARCHのような予約語としてのビルドタグだけでなく、ユーザ定義のタグを使用することができます。

ユーザ定義のタグはビルド時にtagsオプションで指定することで有効にすることができます。

$ ls
develop.go main.go    release.go
$ cat develop.go
// +build develop

package main

var str = "this is develop stub"
$ cat release.go
// +build release

package main

var str = "this is release stub"
$ go build -tags=release -o release
$ go build -tags=develop -o develop
$ ./release
this is release stub
$ ./develop
this is develop stub

開発環境と本番環境で差し替えたいときとかは便利ですね。 

 

飛び道具的な使い方

tagsオプションによる指定を使った場合、ビルド時にオプションで追加したタグのみが有効となりますが、これは逆に言うとオプションで追加しない限り必ず無効になるということです。

このユーザ定義タグの性質を利用すると、「このファイルは常にビルドに使わないことを明示する」という使い方もできます。コンパイルエラーを大量に吐くファイルをとりあえず除外したいときなんかは便利かもしれません。

このような使い方をするときは慣習的にignoreをビルドタグに指定することが多いようです。

またファイル名をアンダースコアから始めても同様のことが実現できます。

 

まとめ

真面目に開発するときはビルドタグのお世話になる機会は多そうですね。特にDevelopとReleaseを切り替えるやつ。Makefileとの相性も良さげなので、積極的に使っていきたいと思います。

あとGolangはテスト然り、ファイル名についてはけっこう制約を課してくるタイプの言語であることがだんだん分かってきたので、ファイル名の付け方には割と注意しておく必要がありそうですね。