ScalaでSBTを試してみる。

ScalaベースのビルドツールであるSBT(Simple Build Tool)を試してみる。

「始める sbt」に沿って学習

ぎこちない和訳だが「始める sbt」(http://scalajp.github.io/sbt-getting-started-guide-ja/setup/)を参照しながら進めた。まず、sbt-launch.jarとsbt.batの2つのファイルが必要だ。sbt-launch.jarはダウンロードしてくる。sbt.batは新規に作成して、


set SCRIPT_DIR=%~dp0
java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar `dirname $0`/sbt-launch.jar "$@"
と記述せよとある。しかし、実際に実行すると「Error occurred during initialization of VM Could not reserve enough space for object heap」といったエラーが出た。VMのサイズを512Mくらいに小さくする必要があるようだ。

set SCRIPT_DIR=%~dp0
java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
これだとうまくいく。これら2ファイルをPATHの通ったところに配置する。とりあえず、C:\scala\bin に押し込んでおいた。「始める sbt」の手順に沿って、Hello, Worldを試してみる。私は、C:\pleiades\workspace\hiというディレクトリを作って、Hi.scala に以下のように記述した。

object Hi {
def main(args: Array[String]) = {
println("Hi!")
}
}
Linux風に echo 'object Hi { def main(args: Array[String]) = println("Hi!") }' > Hi.scala とやると、シングルquoteまで書き込まれてcompile時にエラーとなってしまうではないか。手作業で修正し、いよいよsbtを実行する。必要なjarファイルが各所からretrieveされてくる。資料によると、デフォルトでは、Maven,Typesafe,Sonatypeの3つのリポジトリを参照するようだ。そのため、初回のsbtは結構時間(3〜5分)がかかる。retrieveが終わると、sbtのコンソールが起動された状態になる。(※以下ではecho offしていないので、setやjavaのコマンドもechoとして表示されている。)

$ sbt
$ set SCRIPT_DIR=C:\scala\bin\
$ java -Xmx512M -jar "C:\scala\bin\sbt-launch.jar"

Getting org.fusesource.jansi jansi 1.11 ...
downloading http://repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.11/jansi-1.11.jar ...
[SUCCESSFUL ] org.fusesource.jansi#jansi;1.11!jansi.jar (785ms)

retrieving :: org.scala-sbt#boot-jansi

confs: [default]
1 artifacts copied, 0 already retrieved (111kB/86ms)
Getting org.scala-sbt sbt 0.13.0 ...

...(snip)

[info] Set current project to hi (in build file:/C:/pleiades/workspace/hi/)
sbt>

そしてrunコマンドで先ほどのHiオブジェクトを実行させる。scalaに必要なjarがチェックされ、Hiオブジェクトをコンパイルして実行してくれる。一応「Hi!」と出た。

sbt> run
[info] Updating {file:/C:/pleiades/workspace/hi/}hi...
[info] Resolving org.scala-lang#scala-library;2.10.2 ...
[info] Resolving org.scala-lang#scala-compiler;2.10.2 ...
[info] Resolving org.scala-lang#scala-reflect;2.10.2 ...
[info] Resolving org.scala-lang#jline;2.10.2 ...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to C:\pleiades\workspace\hi\target\scala-2.10\classes...
[info] Running Hi
Hi!
[success] Total time: 2 s, completed
sbt> exit
>
sbtには、基本コンフィグ(build.sbt)とフルコンフィグがある。とりあえず、各プロジェクトでは基本コンフィグ用のbuild.sbtスクリプトファイルを作成しておけというようだ。フルコンフィグでは、Build.scalaとしてScalaで記述することになる。build.sbtには以下の5行を記述した。空行がScala式のセパレータになるので削ってはいけない。最後行には空行はなくてもいい。

name := "Hi"

version := "1.0"

scalaVersion := "2.10.2"

build.sbtはプロジェクトの直下におく。Hi.scalaもsrc/main/scalaの下に移動する。テスティングフレームワークの利用も考慮して、main,testで分けているようだ。またscalajavaの共存を意識したディレクトリ構成だ。

Hiプロジェクト
+-- project/ // フルコンフィグ用
+-- src/
| +-- main/
| | +-- java/
| | +-- scala/
| | +-- Hi.scala // Hiオブジェクトはここに配置する
| +-- test/
+-- target/
+-- build.sbt // 前述の基本コンフィグ用のスクリプトファイル
構成を変更した後、再度sbtを実行し、sbtコンソールでcompileとrunを順に実行する。「Hi!」と出ている。

> sbt
> set SCRIPT_DIR=C:\scala\bin\
> java -Xmx512M -Xss1M -jar "C:\scala\bin\sbt-launch.jar"
[info] Set current project to Hi (in build file:/C:/pleiades/workspace/hi/)
sbt> compile
[info] Updating {file:/C:/pleiades/workspace/hi/}hi...
[info] Resolving org.scala-lang#scala-library;2.10.2 ...
[info] Resolving org.scala-lang#scala-compiler;2.10.2 ...
[info] Resolving org.scala-lang#scala-reflect;2.10.2 ...
[info] Resolving org.scala-lang#jline;2.10.2 ...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to C:\pleiades\workspace\hi\target\scala-2.10\classes...
[success] Total time: 6 s, completed
sbt> run
[info] Running Hi
Hi!
[success] Total time: 0 s, completed
sbtのコマンドは、compile,run,reload,test,exitだけ覚えておけば十分そうだ。reloadは構成を変更したとき、testはテスティングフレームワーク(scalatestなど)で書いたテストコードを実行するときだ。次は、Hi.scalaを以下のように編集してみる。mainの関数をやめ、Appを継承させるのだそうだ。再度sbtを実行し、sbtコンソールでcompileとrunを順に実行する。

object Hi extends App {
println("Hi! Hi! App")
}
今度はたしかに「Hi! Hi! App」と出る。Appを継承するとmainを書かなくていい。似たようなApplicationでも同様だそうだ。いまいちこのあたりの仕組みが分かっていない。Appはなんのため。似たようなApplicationは。sbtだと[success] Total time ...も表示されるが、それはsbtがやっているのか、Appがやっているのか。早くScalaTestを取り込めるようにbuild.sbtの依存関係に追加しておきたいのだが、AppとApplicationが気になって次に進めない。