【golang】vendoring時はビルドタグでのフィルタリングは使わない方がいい

English version:【golang】Don't use filtering by build tags to vendoring

TL;DR

  • govendor ではvendoringする際に無視するビルドタグが指定できる
  • しかし、ビルドに必要なファイルがvendorディレクトリ配下にコピーされないケースがある
  • 必要なファイルがコピーされないことにより、クロスコンパイルできなくなるといった問題が起こるかもしれない
  • なので、vendorディレクトリのサイズは増えてしまうが、ビルドタグでのフィルタリングはしない方がシンプルで良い

Introduction

現在、Go言語には次のようにたくさんのパッケージ管理(vendoring)ツールがあります。

こちらのページでは他にもたくさんのツールが紹介されています。

PackageManagementTools · golang/go Wiki · GitHub

私はその中でも、govendorをよく利用しています。

github.com

govendor は簡単/手軽に利用できるため、これまでたくさんのプロジェクトで利用してきました。
でもある時、govendorの設定次第では面倒な問題が発生することがわかりました。。。

What is the problem?

先月(2016/12)、Terraformに対して以下のPullRequestを作成しました。

github.com

このPRは、Arukasプロバイダを追加するものです。
この新しいプロバイダは以下のリポジトリにてサードパーティープラグインとしてリリース済みであったものを Terraform本体に取り込んでもらうためのものでした。

github.com

このPRはすぐにマージされ、Terraform v0.8.3としてリリースされました。

しかしその時、、、

f:id:febc_yamamoto:20170115213957p:plain

Windowsでビルドできなくなった!?!?

What's!? What's happened!?!?

Arukasプロバイダーはサードパーティープラグインとしてのリリース時にWindows版を含んでおり、(Windows上でも)問題なくビルドできていました。
Terraform本体に取り込んでもらう際、ソースコードへの変更は行われていないのに、なぜビルドが壊れてしまったのでしょうか?

Who broke the build on Windows?

原因を探るため、@jbardinからのメッセージで指摘されているライブラリgopkg.in/alecthomas/kingpin.v2から調査を始めることにしました。

ライブラリgopkg.in/alecthomas/kingpin.v2はArukasプロバイダによってimportされているものです。
このライブラリは、Arukasプロバイダをサードパーティープラグインとしてリリースしていた頃から利用しているため、Windowsでも問題なく利用できることが判明しています。

かつ、Terraform本体 / サードパーティープラグインとしてリリースしていたArukasプロバイダ共にvendoringにはgovendorを利用しています。

このため、(govendorによって)vendorディレクトリ配下にコピーされたgopkg.in/alecthomas/kingpin.v2ライブラリのファイルを比較してみることにしました。

Compare gopkg.in/alecthomas/kingpin.v2 files under vendor directory

比較結果は以下の通りです。
f:id:febc_yamamoto:20170115233145p:plain

guesswidth.goがなんらかの理由でコピーされていないようです。

なぜでしょうか???

Answer: govendor was setted to ignore some build tags

(Terraformにおける)govendorの設定ファイルvendor/vendor.json で以下のような設定が行われています。

https://github.com/hashicorp/terraform/blob/v0.8.4/vendor/vendor.json#L3

   "ignore": "appengine test",

govendorvendor/vendor.jsonの"ignore"に設定された各値とソースファイルの接尾詞 or ソースコード中のビルドタグを比較し、 対象のファイルを無視すべきか判定しています。

この設定ではappenginetestというタグが無視対象として設定されているということです。

そして、今回コピーされていなかったguesswidth.goのビルドタグは以下のようになっていました。

https://github.com/alecthomas/kingpin/blob/v2.2.3/guesswidth.go#L1

// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd

なお、*nix系プラットフォーム(Linx/BSD/MacOSなど)ではこのファイルの代わりにguesswidth_unix.goが利用されます。
こちらのファイルのビルドタグは以下のようになっています。

https://github.com/alecthomas/kingpin/blob/v2.2.3/guesswidth_unix.go#L1

// +build !appengine,linux freebsd darwin dragonfly netbsd openbsd

これらのビルドタグが与えられている場合、(対象プラットフォームが)Windowsでのビルドではguesswidth.goの方が利用されます。
しかし、vendor.jsonの"ignore"にてappengineが指定されているため、govendorguesswidth.goを無視してしまうのです!!!
(日本語版注 : govendorはビルドタグを解析し、一つでもignoreに設定されたタグとマッチするタグが指定されている場合は該当ファイルをvendor配下にコピーしないのです)

この状況では、*nix系プラットフォームではビルド出来ても、Windowsではビルド出来ないのです。

ではどうすれば良いのでしょうか??

Conclusion

vendoring時にビルドタグでフィルタリングすることは、vendorディレクトリのサイズ削減ができるという利点はありますが、 いくつかのプラットフォームでビルドできなくなるといった問題が発生する可能性があります。
さらに、これらの問題解決の際は、importしている各ファイルのビルドタグの確認といった面倒な調査が必要になるでしょう。

このため、vendoring時のビルドタグによるフィルタは利用しない方が良いです。
(注:ただし、ignoreタグとtestタグについてはフィルタしても問題なく動くと思います)

これらの問題があってもビルドタグによるフィルタリングを利用するユースケースはあるのでしょうか?
少なくとも私は思いつきませんでした。

あなたはどう思いますか?
もし良い方法があればこの記事にコメントをください!