Clojure の Boot というビルドツールについて
めちゃくちゃ可愛いロゴが気になる Clojure のビルドツール Boot について色々と調べたので書き残しておく。
だらだら書いてたら長くなってしまったので Boot のインストール記事を分けた -> Boot を使いはじめる(インストールから基本的な使い方まで) - (define -ayalog '())
この記事を書いたモチベーション
日本語の記事を検索しても 1 件足りとも触れられている記事がなかった。
日本人の Clojure チョットデキル界隈の人は英語が堪能なので、英語の記事を読んで満足しているんだと思う。うん。
Boot とは
Clojure のビルドフレームワークであり Clojure スクリプト*1をアドホックに評価するものです。 Boot は Clojure で書かれたスクリプトをビルドするために、プロジェクトの文脈で必要な全てのツールを含むランタイム環境を提供します。
Boot is a Clojure build framework and ad-hoc Clojure script evaluator. Boot provides a runtime environment that includes all of the tools needed to build Clojure projects from scripts written in Clojure that run in the context of the project.
具体的な特徴としては
- 動的にクラスパスの依存性を Maven リポジトリから解決することが出来る
- タスクを関数の形で定義でき、それをパイプラインのように繋げていくことが出来る
- 特別なタスクをその場に応じてビルドスクリプトや REPL で定義することが出来る
- タスクはプロジェクト、ビルドスクリプト、 REPL それからコマンドライン上で結合することが出来る
- いわゆる clean タスクが必要ない
などがあります。
よく言われているのが JS でいう Grunt が Leiningen で gulp が Boot 相当だということ。
何故 Boot が必要なのか
Boot の README を読むと曰く
ビルドツールというのは最後には複雑なものになってしまう。シンプルな Web アプリの場合、アセットパイプラインや違う環境へのデプロイやパッケージングというインテグレーションが必要になる。
もっと複雑なビルドプロセスにはもっと柔軟なビルドツールが必要で、静的なビルド仕様書はプロジェクトの完了に向かうにつれて、次第に便利でなくなってしまう。 Lisper の私達はどうすればいいか知っている: Lambda は究極の宣言型だと。
グローバルな形状地図の代わりに Boot は Clojure で評価可能なビルドスクリプトにランタイム環境を提供する。そのスクリプトは -- チューリング完全なビルド仕様書 -- プロジェクトをビルドする。
雑翻訳ですが、まぁこんな感じだと思います。僕が Clojure を触り始めて一番初めにに「うっ」と思ったのは ClojureScript をビルドする Leiningen での設定でした。どのくらい見る気が失せるかというとこの辺が参考になります。
chestnut/project.clj at master · plexus/chestnut · GitHub
Chestnut という有名な cljs を組み込んだテンプレートです。いや、正直初学者じゃなくてもこれは「読みたくない」と思うはずです。もちろん、これをこのまま使う分には読む必要がないと思うんですけど、結局何か自分が気に食わないと思ったりしたときに書き換えようと思ってもいきなりこれを使ってた人はつらいはずです。なんというか、プロジェクト作ったら最初から秘伝のタレが入っているという感覚です。
ちなみに Chestnut の project.clj を秘伝のタレ扱いしたので、 Boot 版を一応載せておきます。
saapas/build.boot at master · Deraen/saapas · GitHub
比較的、読みやすいですよね。 cljs のビルドと reload まで含まれていて結構いい感じです。
ClojureScript の流行りと闇
この Boot が注目を浴びている*2背景には ClojureScript が流行ったというのが影響していると思います。上のほうで既に書いたように cljs をビルドする Leiningen での設定はかなりつらいです。それを読むだけで JS で良くね?って言いたくなってしまう僕がいました*3。そんな闇を解消するための答えのひとつが Boot です。
この記事で言及されているように ClojureScript を書くときに、ほとんどの開発者は次のような機能を欲しいと思ってしまうはずです。
- CLJS compiler
- incremental builds
- browser REPL
- live-reload
そして実際、これらは Leiningen のプラグインとして提供されています。そうするとほとんどの場合、設定は Chestnut のようになってしまうと思います。つまり Chestnut を使いましょう、になるんですが僕らは巨大なボイラーテンプレートを生成したいんじゃなくて、僕らのツールが何をしているのかを知りたいんだ、というのがこの記事の冒頭に書かれています。
さて、もうひとつ違う闇があります。 Clojure 書きはなんでもかんでも Lisp で解決しようとする嫌いがあります。 HTML を Clojure ぽく -> Hiccup, Enlive 、 JS を Clojure ぽく -> ClojureScript 、 CSS を Clojure ぽく -> Garden というように Web 開発する上で必要なものは基本的に Clojure と親和性の高い表現で記述することが出来るようになっています。ですが、これを使うと絶対問題になるのが AltJS 系でもよくありますがコンパイルが絶対必要になってくるんですね。例えば cljs と garden を併用するとして、開発中はそれらを監視してコンパイルしたくなりますよね。すると僕らにはふたつの選択肢が生まれます。ふたつのターミナルを開いてそれらのタスクを同時に実行するか、そうでなければ以下のようなタスクを dev profile の中に書きたくなるはずです。
(defn start-garden [] (future (print "Starting Garden.\n") (lein/-main ["garden" "auto"])))
Leiningen しかなかったときはふたつのターミナルを開いてそれぞれのタスクを実行するしかなかったわけですが、 Boot はユーザーが結合可能な小さいタスクを定義することを許しているのです。という始まり方をして Boot の良さを伝えているのが以下の記事です。
そして、この記事のいいところは Boot と Leiningen を使い分けるといいんじゃないでしょうかという話をしているところです。
僕も数日 Boot を使ってみましたがライブラリなんかを書くときは Leiningen でいいんじゃないかなーというところで落ち着いています。
Boot のデメリット?
ここまで Boot にはメリットしかないようなことを書いてきましたが、実はそうでもないんです。
直前のブログ記事で触れられていましたが、 Boot 使うと副作用が散りばめられてしまうというのがひとつ懸念としてあるわけです。その点 Leiningen は綺麗で完璧な宣言的記述が出来るただの不変なマップなので副作用がないと言っても過言ではないです。 Boot はデータというよりプログラムなので副作用を持った関数を含んでしまう可能性があります。ただ、 Leiningen にも問題があるわけでして、先のブログでは Leiningen の宣言に関するマナーを守るのは大変だと言っています。その原因はこれ。簡潔に言えば compile とか reload のためだけに 2~3 プロセスくらい軽く必要なんだけど、それってどうなの?という話。そのマナーを守るのに費やす時間に比べたら、 Boot の副作用に費やす時間なんて大した問題じゃないよねとのこと。そして、以下のスレッドを読んでおくよう勧められます。
ClojureScript Builds, Rebooted | Hacker News
まとめ
- Boot は gulp のようにタスクを小さく定義してそれを組み合わせることが出来ます
- Leiningen に比べると宣言的な記述は出来ませんが、 ClojureScript などを使うプロジェクトの場合は簡素な設定で済むので便利です
- 全てのタスクは同一 JVM 上で実行されるのでメモリとかの心配が必要ないです
- 現在開発も結構アクティブなので今後の成長に期待です
Boot 使っていきましょう!!