コンピューターにまつわるたくさんの知見が存在する2020年。GoogleやGitHubを通じて世界の知見を見渡しても、意外と自分が欲しい道具が見つからないことがある。
今回自作したのは、既に星の数ほどありそうな道具、ファイルウォッチャー。名前から連想できるように、「ファイルの変化を監視して、変化をトリガーにコマンドを実行するソフト」だ。
開発の背景紹介や既存との比較は後の方でするとして、まずは今回作ったファイルウォッチャーその名も "r3build" の特徴をピッチしていく。
r3build紹介ピッチ
さて、あなたはいま組み込みLinuxのカーネルをいじっているとしよう(まあ、PythonでもGoでも対象は何でも良い)。試行錯誤のたびに make
を手で起こすのは辛いので、makeを自動化したいとする。
ここでr3buildが登場する。r3buildの真髄は r3build.toml
という設定ファイルで容易かつ直感的に設定できることにある。以下のような要求を、非常に簡潔に記述して監視させることができる。
- コード(c, h, dts)を保存したら勝手にmake allしてほしい
- defconfigを保存したら勝手にdefconfigしてほしい
- 関係ないファイルの変更は無視して欲しい
- make中のファイルの変化は無視して欲しい
この場合、r3buildに読ませる r3build.toml
は以下のようになる。
[[job]] name = "All" type = "make" regex = [ ".+\.[ch]$", ".+\.dts$", ] environment.ARCH = "arm" environment.CROSS_COMPILE = "arm-linux-gnueabi-" [[job]] name = "Defconfig" type = "make" target = "mxs_defconfig" glob = [ "./**/mxs_defconfig", ] environment.ARCH = "arm" environment.CROSS_COMPILE = "arm-linux-gnueabi-"
キーとバリューの説明は以下の通り。
name
Human-readableなJobの名前。処理には影響せず、ログでのみ登場する。
type
検知したファイルイベントを処理する実装を指定する。この「実装」はprocessorと呼ぶ。processorには make
(makeのターゲットを実行), command
(任意のコマンド実行), pytest
(pytestを実行)がある。今回はターゲット指定(任意)がないので make
がそのまま(= make all)実行される。
regex
ファイルパスをマッチするルール。名前の通りregexで指定する。これにマッチするファイルでイベントが検知されると、processorがトリガーされる。文字列を置くと単一ルールのマッチになり、文字列のArrayを置くとOR条件扱いでマッチする。同様に regex_exclude
を指定すると、それにマッチするファイルは無視される。
glob
ファイルパスをマッチするルール。こちらはglobで指定する。使い方はexcludeも含めてregexと一緒、どちらでも好きな方を使える。一応両方同時に使うこともでき、OR条件としてマッチする。
environment
環境変数を指定する。今回は組み込みLinuxなのでARCHとCROSS_COMPILEを指定している。
target
makeのtarget。make processor特有のオプション。
その他
今回は盛り込んでいないが、みんな大好き -j
オプションの数字は標準で自動推定される。自分で指定したい場合は、 jobs
キーで設定できる。
[[job]] ... type = make target = hoge jobs = 8 # 0にするとCPUコア数から推定させられる
このようにして設定を記述し、 r3build
を常駐させておくと *.c
, *.h
, *.dts
, mxs_defconfig
の変更を検知次第makeが走る。r3buildを起動するには以下のように実行する。
$ r3build もしくは $ python3 -m r3build
r3buildのアーキテクチャ
r3buildでは、こういった「ファイルのマッチ条件」と「イベントを受け取るprocessor」と「processorの設定」をまとめた1単位を Job
と呼んでいる。[[job]]
はTOMLの Array-of-Tables文法を使ったもので、見た目にもスッキリと監視対象を列挙することができる。
また、Jobが実行中に検知したイベントは標準で捨てるようになっている(注: 並列実行はまだ実装していない)。これは event.ignore_events_while_run = false
と指定することで無効化できる。
[event] ignore_events_while_run = false [[job]] ...
この例ではコードのコンパイルと自動configしかやっていないが、任意のコマンドをJobに指定することができるので、例えばコンパイルして実機に自動デプロイまでやるとか、もっと大掛かりな仕組みを作ることも可能なハズ。想像は無限大!
ドキュメント
r3build.toml の書き方についてはリファレンスやサンプルを色々用意した。
- いろんな仕様パターンを用意したexampleディレクトリ
- 割とちゃんと書いたREADME
- r3build.tomlの持つ全機能をしたためたスケルトン
残念ながらSphinxで生成したような「いわゆるドキュメント」はまだない。疑問があれば随時Twitterで質問してもらって構わないし、バグや機能リクエストがあればCONTRIBUTING.mdに従ってIssueやPRを立てて欲しい。知り合いからのバグ報告であればASAPで直しにかかる…はず!
開発の背景
言うまでもなく、「ファイルの変化を監視して、変化をトリガーにコマンドを実行するソフト」は既にたくさんあるし、気軽に使えるCLIツールの範囲でいくつか試した。
例えば、joh/when-changed(gorakhargosh/watchdogのラッパー)。when-changedは、監視したいファイル名と実行したいコマンドを指定すればそれっぽく起動してくれる。秒で自動化できるのは良いが、複数の問題をはらんでいる。
- exclude対象が作者の好みにハードコードされている
- コマンドを発火させている間のファイルの変更も全部拾ってしまい、永遠に実行が終わらない
- イベントの濁流みたいなことになる(debounceされていない)
別な例として、when-changedの中身である gorakhargosh/watchdog もまたCLIツール watchmedo を内蔵しているが、Tricksなどの便利そうな仕組みがどうも見た目に直感的でないというか、好みではなかった。ちなみにwatchdog自体はマルチプラットフォーム対応も手厚く使いやすいモジュールなので、r3build内部でイベントを拾うソースとして使用している。
今は
— 𝕋𝕒𝕜𝕦𝕞𝕚 𝕊𝕦𝕖𝕕𝕒 (@puhitaku) 2020年1月23日
1. joh/when-changed でファイル変更を監視
2. Pythonスクリプト起動
3. Pythonからmake起動
4. (未実装)Pythonから基板リセット+デプロイ
を繰り返すようになっている
しかし、makeの副作用でファイル変更イベントがまた発火してしまうので、1→2の間に中間出力ファイル等をフィルタするshellが挟まっている。そこが美しくない。ところでwhen-changedはPythonで書かれているので、こいつをwrapすれば単一実行ファイルになりエレガントになれるかも。
— 𝕋𝕒𝕜𝕦𝕞𝕚 𝕊𝕦𝕖𝕕𝕒 (@puhitaku) January 23, 2020
上記では「そこが美しくない」とだけ書いているが、この中間処理の記述がめちゃくちゃめんどくさい。自分はただ「Linuxのソースコードをいじったらmakeしてほしい」だけなのに、なぜお手製Pythonスクリプトをわざわざ書かなければならないのか。これから先も似たような汚い手法の繰り返しは嫌だな、と早々にうんざりしてしまった。
結局の所、上に書いたことをそのままエレガントにしたツールが欲しいという結論に達した。
- エレガントな設定ファイルで秒で自動化できて欲しい(自動化のための苦労をゼロに近づけたい)
- 監視・除外対象をglobやregexでいい感じに指定したい
- コマンドを発火させている間のファイルの変更は捨てたい(わざと拾うのも可能にしたい)
- イベントの種類を絞り込みたい(作成・変更・削除など)
- 謎の立て続けにくるイベントとかはdebounceしたい
2月の頭に「ぼくのかんがえたさいきょうの設定ファイル」の構想から着手し、だいたい4ヶ月半経った今日ついに完成した。せっかくなのでロゴも作った。
ちなみにr3buildという名前はどこかの国のプロテインと被ってるっぽいけど、それ以外はなかったのでGoogleabilityも良い。
・kreload(カーネルに関連してそうだがツール自体はカーネルと関係ない)
— 𝕋𝕒𝕜𝕦𝕞𝕚 𝕊𝕦𝕖𝕕𝕒 (@puhitaku) January 31, 2020
・automake(GNU Autotoolsと盛大に衝突)
・rebuild(Googleabilityに欠く)
・r3build(検索したらプロテインが出るけど良さげ)
今後
そもそも日頃の課題を解決したくて作ったものなので、これからもBrainハックとかで使いつつ改善を続ける。今のところ実装したいことは以下の通り。
- 同じJobで設定を変化させながら何度も実行する
- もっとエレガントなConfigの模索?
- Coverageを上げてテストケースを増やす
あとはとにかくいろんな人に使って欲しい!これを呼んでいるあなたも、ファイルの変化をトリガーに自動化したい事があれば、是非r3buildを思い浮かべて欲しい。
フィードバック、お待ちしております!