詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

最近のGoプロジェクトのMakefile

最近は仕事でも新しくGoのプロジェクトをイチからはじめることが増えてきて、コピペ元が欲しくなるので、スナップショットとして残しておきます。とくに Go でウェブアプリケーションを書くような場合を想定していて、npm エコシステムにも乗っていきます。

大まかな方針としては、

  • self-contained である
  • グローバルな環境を汚染しない
  • コマンド一発で開発環境が再現できる

……というところを目指します。

motemen/prchecklist がこれを達成しているつもりなので、以下、これを例に見ていきます。

依存ライブラリは dep なり何かしらのツールと Go 標準の vendoring で管理すればよい一方、そのツール自体であったり、他の開発中に必要なツール(golint とか gobump とか)であったりのインストールをどうするかという話。

npm であれば devDependencies というやつでうまいことやってくれますが、現行の Go の依存管理ツールではデファクトがない、というか依存管理ツールのデファクトが(少なくとも自分の中では)まだ定まってないので、これを Makefile でやる必要がある。

かといって依存ツールを単純に go get してはグローバルな環境を汚染してしまうことになるので、npm を真似て Go 製ツールのバイナリを .bin/ 以下に置くことにします。以下のように GOBIN 環境変数を指定すれば、バイナリを作業ディレクトリ以下にインストールできます*1

.PHONY: setup-go
setup-go:
   GOBIN=$(abspath .bin) go get -v \
        gobin.cc/go-bindata \
        gobin.cc/reflex \
        gobin.cc/mockgen \
        github.com/motemen/go-generate-jsschema/cmd/gojsschemagen

gobin.cc を使えばすっきり。

最初に make setup-go しなくてもうまいこと動くように、依存関係をきちんと書いてやります。

.PHONY: test
test: lib/web/web_mock_test.go
    ...

lib/web/web_mock_test.go: lib/web/web.go .bin/mockgen
    .bin/mockgen -package web -source $< GitHubGateway > $@

.bin/%: Makefile
    @$(MAKE) setup-go
    @touch $@

テストがモックのソースコードに依存し、それがさらに .bin/mockgen に依存するように宣言しています。そして .bin/ 以下のファイルは先ほどの setup-go タスクで生成するよう指定します。.bin/% の依存先として Makefile を書いているのは、go get するパッケージの一覧が Makefile に書いてあるから。

ここに書いたのとまったく同じ方法で npm パッケージ向けの設定も書けますが、その場合は

node_modules/.bin/%: package.json

のように書けばいいわけです。

このようにして、gonpm または yarn さえ入っていればだいたい再現可能な開発環境が作れました(だいたいというのは、Go ツールに非互換な変更が入ったときに壊れる、という懸念があるよね、というところです)。

余談になるけれど、Go ソースコードの watch & build にこれまでよく entr を使っていて、これが大変便利なんですが、パッケージマネージャによるユニバーサルなインストール方法というのがなく、このエントリに書いたような再現可能な環境を作るために Go 製の reflex を使ってみました。

ls **/*.go | entr -r sh -c '...'

reflex -r '\.go\z' -s -- sh -c '...'

になる感じで、汎用的に使えるツールです。わりとよさそう。

*1:$GOPATH/pkg は汚染されますが。これは自分にとっては問題にならない

はてなで一緒に働きませんか?