fsc は速くなった のだけど、コンパイルしたらテストしたいし、テストしたら普通に実行したい。コンパイルだけ速くなっても開発のサイクル全体はそんなに速くならないのでは、といまさら気づいた。scalac, fsc 同様に scala コマンドの実行のために Java VM をあげさげするのを減らす必要がある。
Java VM のあげさげを減らすのは Java 界にも需要があるらしく、具体的な実装として、Ant を対話的にした antshell や Ant Console がある。Scala は Ant からも使えるので、これらもまま使えるはずだけど、実はページがどちらも消えてしまっていて試せていない。
どうしようと探していたら sbt が良さげだった。
sbt - simple build tool
sbt は Scala で書かれていて、Scala で設定できるビルドツールだ。Ant とはちがいデフォルトで対話的に実行できる。
実際に使ってみよう。
% mkdir hello % cd hello % sbt Project does not exist, create new project? (y/N/s) : s :: retrieving :: sbt#boot confs: [default] 2 artifacts copied, 0 already retrieved (9911kB/194ms) :: retrieving :: sbt#boot confs: [default] 3 artifacts copied, 0 already retrieved (3409kB/27ms) [info] Building project scratch 1.0 using sbt.DefaultProject [info] with sbt 0.5.6 and Scala 2.7.7 [info] No actions specified, interactive session started. Execute 'help' for more information. >
最初の質問には s (scratch らしい) と答える。一度 Ctrl-D でぬけて sbt が作ったプロジェクトをみてみると、なんかいろいろフォルダが作られているのがわかる。
% find . . ./project ./project/boot ./project/boot/scala-2.7.7 ./project/boot/scala-2.7.7/lib ./project/boot/scala-2.7.7/lib/scala-compiler.jar ./project/boot/scala-2.7.7/lib/scala-library.jar ./project/boot/scala-2.7.7/sbt-0.5.6 ./project/boot/scala-2.7.7/sbt-0.5.6/ivy-2.0.0.jar ./project/boot/scala-2.7.7/sbt-0.5.6/jsch-0.1.31.jar ./project/boot/scala-2.7.7/sbt-0.5.6/sbt_2.7.7-0.5.6.jar ./project/boot/scala-2.7.7/update.log ./project/build.properties ./target ./target/.history %
このフォルダ構成は Maven と同じだと Setup にはあった。ソースは src/main/scala/Hello.scala とかに置くらしいので、以下のようなものを用意した。
object Hello { def main(args: Array[String]) { println("hello world") } }
(さっき終了させてしまったので sbt を立ち上げ直して) sbt に run と打ち込む。
% sbt [info] Building project scratch 1.0 using sbt.DefaultProject [info] with sbt 0.5.6 and Scala 2.7.7 [info] No actions specified, interactive session started. Execute 'help' for more information. > run [info] [info] == copy-resources == [info] == copy-resources == [info] [info] == compile == [info] Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed. [info] Compiling main sources... [info] Compilation successful. [info] Post-analysis: 2 classes. [info] == compile == [info] [info] == run == [info] Running Hello ... hello world [info] == run == [success] Successful. [info] [info] Total time: 3 s >
動いた! というように、編集 -> 立ち上げっぱなしの sbt で run と打ち込むほうが、シェルから scalac と scala を別々に実行するより速い。
トリガ
sbt には、さらに Triggered Execution という機能がある。アクションの前に「~」をつけると
- ファイルの更新をポーリング
- 更新されたらアクションを実行
- ポーリングにもどる
というループがまわるのだ。Rails だと ZenTest の autotest に似てますね。
> ~run [info] [info] == compile == [info] Source analysis: 0 new/modified, 0 indirectly invalidated, 0 removed. [info] Compiling main sources... [info] Nothing to compile. [info] Post-analysis: 2 classes. [info] == compile == [info] [info] == copy-resources == [info] == copy-resources == [info] [info] == run == [info] Running Hello ... hello world [info] == run == [success] Successful. [info] [info] Total time: 0 s Waiting for source changes... (press enter to interrupt)
この状態で、さっきの println に「!」を足してみた。すると
... Waiting for source changes... (press enter to interrupt) [info] [info] == compile == [info] Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed. [info] Compiling main sources... [info] Compilation successful. [info] Post-analysis: 2 classes. [info] == compile == [info] [info] == copy-resources == [info] == copy-resources == [info] [info] == run == [info] Running Hello ... hello world! [info] == run == [success] Successful. [info] [info] Total time: 1 s Waiting for source changes... (press enter to interrupt)
ファイルの更新を検知して、コンパイルと再実行が走る。速い。これが動いている端末とエディタとをならべて開発すると大分便利そうだ。
もちろんテストも走らせられる (というかそれが主な目的だと思う) のだけど、それはまたあとで。