📌

bincr, 世界一簡単な incremental build tool

2021/08/25に公開

依存を解析しつつのインクリメンタルビルド、各コンパイラについてたりついてなかったりするんですが、どのビルド環境にもほしいですよね。

真面目にやるんだったら bazel が鉄板みたいなんですが、基本的に超大規模向けでエコシステムのいろんな都合でセットアップが非常にだるいです。後付の導入も結構だるいです。

Bazel - a fast, scalable, multi-language and extensible build system" - Bazel

というわけで、今回は自分が欲しい機能、「ソースのハッシュが過去のものと違う場合にビルドを実行」という機能に特化して作りました。逆にいえば他はなにもないです。依存解析も環境ごとの最適化もないです。

npm が入ってれば npm i -g bincr or npx bincr ... で実行可能です。特に node の機能を前提にしてないので、何にでも使えると思います。

https://github.com/mizchi/bincr

使い方

npx bincr init

npx bincr # `.bincr.json` の cmd を実行
npx bincr -f # 差分無視の強制実行

npx bincr -w # ファイル監視による watch mode

# status code
npx bincr changed && echo "changed!!"

# ↑ の update しながら版
npx bincr changed -u && echo "changed!!"

bincr changedcmd を実行せず、status code だけを返します。

bincr の機能

  • .bincr.json の watch 対象のコードで hash を計算
  • .bincr-hash がない、または最後の実行の .bincr-hash と値が違う場合、.bincr.jsoncmd を実行
  • .bincr-hash を最新の hash に更新

この .bincr-hash.gitignore に入れる想定で、 git pull / rebase した際に実行して変更があったものだけビルドする、といったユースケースを想定しています。

bincr のやることは単純で、指定された glob pattern を監視し、それらのファイル内容で hash を作ります。hash が変わったときだけ .bincr.jsoncmd が実行されます。

複数タスクの実行や、依存解析なんて高度なものはありません。欲しい場合は watch 対象で依存ごと監視してください。

想定しているユースケース

大規模モノレポで部分的にビルドを分割している時、一部の変更で全部をビルドすると非常に重くなります。なので、そのビルドの冪等性を保証できる範囲のハッシュを計算し、そこで変化があった場合に再ビルドします。

例えばこういう構成だとします。

packages/
  a/
    src/...
  b/
    src/...
Makefile

各パッケージごとに .bincr.json 仕込んで回ります

cd packages/a && npx bincr init
# .bincr.json の cmd を編集
cd packages/b && npx bincr init
# .bincr.json の cmd を編集

("watch": [...] を編集する)

bincr は workspace 機能を用意しています。.bincr.json の workspaces に複数の bincr プロセスを立ち上げます。

{
  "cmd": "echo 'This is workspace root'",
  "watch": [],
  "workspaces": ["packages/a", "packages/b"]
}

実行

$ npx bincr workspace
$ npx bincr workspace -w

[bincr] spawn npx [ 'bincr', '--prefix', 'packages/a', '-w' ] /Users/mizchi/mizchi/bincr/example/packages/a
[bincr] spawn npx [ 'bincr', '--prefix', 'packages/b', '-w' ] /Users/mizchi/mizchi/bincr/example/packages/b
[bincr:packages/b] [watch] received...
[bincr:packages/a] [watch] received...
[bincr:packages/b] [watch] exec
[bincr:packages/a] [watch] exec
[bincr:packages/a] skip /Users/mizchi/mizchi/bincr/example/packages/a
[bincr:packages/b] skip /Users/mizchi/mizchi/bincr/example/packages/b
# ...

おわり

現状、出力の冪等性の保証がないので、後で生成物の hash の検証機能を作ろうと思っています。

実はこれのプロトタイプ版があって、最初は git diff --exit-code --silent . をベースの shell script として作ったのですが、 git の対象外の unstaged な変更を取り込む際に hash が生成できなくて、diff content の sha1 を無理やり生成したりもやってみたのですが、だるくて書き直しました。結局 git hash もただの sha1 なので、それを計算しているだけです。しかも今回はサボってるので md5 です。

自分が必要なものを作ったので、想定しているユースケースが非常に狭いです。これに機能追加したい場合、PR もいいですが実装は単一ファイルで簡単なので fork するのがいいと思います。

Discussion