Clojure Web Development勉強 - ClojureScript(その2)cljsbuildでコンパイル
Clojureビルドツールあれこれ
Clojureビルドツール界隈ではleiningenが圧倒的シェアを誇っておりそこにbootが食らいついている、というような状況になっている。
私はbootは使ったことはないが、leiningenにはいつも非常にお世話になっている。project.cljに依存ライブラリなども含めたビルドオプションを記述して、あとはライブラリのダウンロードからコンパイルから実行までleinコマンドでできてしまうのは本当に便利だ。
前回のClojureScriptのコンパイルではleiningenもbootも出る幕がなく、自分でClojureScriptコンパイラのjarを落としてきてjavaコマンドで実行していた。しかし、他のライブラリへの依存性管理や、より便利なワークフローツール(特にfigwheel!)の利用などのためにも、ビルドツールとしてleinを使っていきたい。
普段はプロジェクトの雛形すらlein new <project-name>
あるいはlein new reagent <project-name>
といったコマンドでleiningenに用意してもらうのだが、今回はそこは手動でやって最低限のプロジェクトの条件を見ていく。実は似たようなことをQiitaでもやったが、その後いろいろと試していくうちに知見も溜まったので・・・
ClojureScript/cljsbuild用のproject.clj
用意するディレクトリとファイルは以下の通り:
. ├── index.html ├── project.clj └── src └── min_cljsbuild └── core.cljs
src/min_cljsbuild/core.cljsとindex.htmlは前回でもおなじみ。内容も前回と全く同じ。
ClojureScriptコンパイラcljs.jarとビルドのために実行するスクリプトであるbuild.cljが消え、そのかわりleiningenがビルドに必要な情報を記述したproject.cljが加わっている。
本当の最低限
いろいろと試した結果、cljsbuildがエラーを吐かずに「動作」するproject.cljは以下の通り:
(defproject min-cljsbuild "0.0.1" :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.293"]] :plugins [[lein-cljsbuild "1.1.4"]] :cljsbuild {:builds []})
上記のようなproject.cljを置いて、bashでlein cljsbuild once
とコマンドを走らせると
Compiling ClojureScript...
とメッセージが表示される。その後ディレクトリを調べると、いろんなファイルが出力されているのがわかる。
. ├── dev-resources ├── index.html ├── project.clj ├── resources ├── src │ └── min_cljsbuild │ └── core.cljs ├── target │ ├── classes │ │ └── META-INF │ │ └── maven │ │ └── min-cljsbuild │ │ └── min-cljsbuild │ │ └── pom.properties │ ├── cljsbuild-crossover │ └── stale │ └── extract-native.dependencies └── test
しかし肝心のJavaScriptファイルがない。というのもcljsbuildに「どこにコンパイルしてほしいClojureScriptファイルが入っているのか」という情報を渡していなかったため、単にテンプレ的に必要なファイルやディレクトリを作成したのみで終わってしまったわけだ。
なので「コンパイルしてほしいClojureScriptはsrcディレクトリに入っていますよー」という情報を渡してやる。
JavaScriptが出力される最低限
(defproject min-cljsbuild "0.0.2" :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.293"]] :plugins [[lein-cljsbuild "1.1.4"]] :cljsbuild {:builds [{:source-paths ["src"]}]}) ;;ここ
まずは先ほどcljsbuildが作成したディレクトリを全部削除してしまう
rm -rf dev-resources;rm -rf test;rm -rf resources;rm -rf target
そしてもう一回実行:
lein cljsbuild once
そうするとこんなメッセージが出て:
Compiling ClojureScript...
Compiling "/min-cljsbuild/target/cljsbuild-main.js" from ["src"]...
Successfully compiled "/min-cljsbuild/target/cljsbuild-main.js" in 0.82 seconds.
こんなディレクトリ構成が出力される:
. ├── dev-resources ├── index.html ├── project.clj ├── resources ├── src │ └── min_cljsbuild │ └── core.cljs ├── target │ ├── classes │ │ └── META-INF │ │ └── maven │ │ └── min-cljsbuild │ │ └── min-cljsbuild │ │ └── pom.properties │ ├── cljsbuild-compiler-0 │ │ ├── cljs │ │ │ ├── core.cljs │ │ │ ├── core.js │ │ │ └── core.js.map │ │ ├── goog │ │ │ ├── array │ │ │ │ └── array.js │ │ │ ├── asserts │ │ │ │ └── asserts.js │ │ │ ├── base.js │ │ │ ├── debug │ │ │ │ └── error.js │ │ │ ├── deps.js │ │ │ ├── dom │ │ │ │ └── nodetype.js │ │ │ ├── math │ │ │ │ ├── integer.js │ │ │ │ └── long.js │ │ │ ├── object │ │ │ │ └── object.js │ │ │ ├── reflect │ │ │ │ └── reflect.js │ │ │ └── string │ │ │ ├── string.js │ │ │ └── stringbuffer.js │ │ └── min_cljsbuild │ │ ├── core.cljs │ │ ├── core.cljs.cache.edn │ │ ├── core.js │ │ └── core.js.map │ ├── cljsbuild-crossover │ ├── cljsbuild-main.js │ └── stale │ └── extract-native.dependencies └── test
前回見たようなJavaScriptファイルがtargetディレクトリに入っている。とくに重要なのがtarget/cljsbuild-compiler-0/goog/base.jsとtarget/cljsbuild-main.jsの2ファイルで、これが前回のout/goog/base.jsとout/main.jsに相当する。
ただ、target/cljsbuild-compiler-0/googといったディレクトリパスも「コンパイル中一時的な置き場として名前衝突が起きないようつけました」感満載だしcljsbuild-main.jsと最終的にロードするJavaScriptファイルにコンパイラツールの名前が付いているのもちょっとダサい。
ということで、後もう一歩、出力ファイルの名前とパスをproject.cljに記述する。
出力先を指定したproject.clj
(defproject min-cljsbuild "0.0.3" :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.293"]] :plugins [[lein-cljsbuild "1.1.4"]] :cljsbuild {:builds [{:source-paths ["src"] :compiler {:output-to "out/main.js" ;;ここ :output-dir "out"}}]})
コンパイラにmain.jsの出力先と名前、そしてgoogを含めた必要とされるJavaScriptライブラリの出力先となるディレクトリを指定している。
後は先ほどと同じく、cljsbuildに作成されたディレクトリを削除してからlein cljsbuild once
を実行する。
. ├── dev-resources ├── index.html ├── out │ ├── cljs │ │ ├── core.cljs │ │ ├── core.js │ │ └── core.js.map │ ├── goog │ │ ├── array │ │ │ └── array.js │ │ ├── asserts │ │ │ └── asserts.js │ │ ├── base.js │ │ ├── debug │ │ │ └── error.js │ │ ├── deps.js │ │ ├── dom │ │ │ └── nodetype.js │ │ ├── math │ │ │ ├── integer.js │ │ │ └── long.js │ │ ├── object │ │ │ └── object.js │ │ ├── reflect │ │ │ └── reflect.js │ │ └── string │ │ ├── string.js │ │ └── stringbuffer.js │ ├── main.js │ └── min_cljsbuild │ ├── core.cljs │ ├── core.cljs.cache.edn │ ├── core.js │ └── core.js.map ├── project.clj ├── resources ├── src │ └── min_cljsbuild │ └── core.cljs ├── target │ ├── classes │ │ └── META-INF │ │ └── maven │ │ └── min-cljsbuild │ │ └── min-cljsbuild │ │ └── pom.properties │ ├── cljsbuild-crossover │ └── stale │ └── extract-native.dependencies └── test
となって、手動でcljs.jarを使った時と概ね似た構成になる。違いはdev-resources、resources、testという空ディレクトリと、コンパイル時の一時ファイルの残りであるtargetディレクトリで、これらは挙動に何ら影響しない。この時点では削除してしまっても構わないくらいである。
あとは前回と同じindex.htmlを開けば同じアラートが表示される。
今回のコード:
次回はcljsbuildのオプションを少し調べていきたい。