sbt(Simple Build Tool)を試す
scalaで実装するのにこれまで、
Eclipseで手動で環境作っていて、sbtを使っていませんでした。恥ずかしい・・・。
実際そんなに困ってなかった状況で、
ScalaTestをJunitRunnerで実行していて、
どうも空振るテストクラスがあり、Scala-IDEの不具合なのか分からず、
だったらsbtで実行しようと思ったわけです。
また、jenkins導入も考えていて、ビルドツールはどのみち必要なので。
んで、sbtをインストールしようかと思ったら、
なにやらTypesafe Stackというものがあるらしく、
これを使ったほうが楽に環境構築出来そうなので、ちょっと一からやってみることにしました。
ScalaJPのGithubの環境構築のページにもTypesafe Stackインストールするよと書いてあるので。
scala develop environment · scalajp/scalajp.github.com Wiki · GitHub
ちょっとやる前に、自分の想定(やりたい事)をまとめておきます。
1.sbtで作ったプロジェクトのひな形を作る。eclipseにインポート。既存のソースを入れる。
2.sbtでビルド出来るようにする。テストも実行出来るようにする。実装は、Eclipse。
3.jettyを使っているので、sbtのプロジェクト構成でも、Eclipseでデバッグ出来るようにする。
4.pureなJettyにsbtから作ったWARをデプロイする。
・typesafe-stackのインストール
http://typesafe.com/stack/downloadからダウンロード。
typesafe-stack-2.0.1.exeをダウンロードしました。
Windows7です。
はじめにsbtのインストールが行われます。
次にGiter8のインストールが行われます。Giter8とは・・・2011-12-13
次にScalaのインストールが行われます。
これで終わるんですが、Giter8が見当たらない・・・。
予習してると、g8コマンドで、プロジェクトのテンプレート作るらしいのですが、g8が見つからない。
どこにインストールしたんだ・・・。
とおもったら、Program Filesにありました。sbtのインストール先を変更したので、
Giter8も変わってくれてると思ってました(変更出来無いので)。Giter8は、Program Files固定なんですね。
うーん・・・。固定でいいか。再インストールっと。
・プロジェクトの作成
プロジェクトを作成するディレクトリに移動して、
>g8 typesafehub/scala-sbt Scala Project Using sbt organization [org.example]: org.hoge package [org.example]: org.hoge name [Scala Project]: hoge_project scala_version [2.9.1]: version [0.1-SNAPSHOT]: Applied typesafehub/scala-sbt.g8 in hoge_project
プロジェクトのひな形が作成されます。初回なにやら色々ダウンロードされます。
Windows 7の場合、ダウンロードされたファイルは、
C:\Users\770032\.ivy2\cache
に保存されるようです。
作成されたプロジェクトに移動して、sbt runすると、ビルド、実行されます。
作成されたプロジェクトの中に、projectというディレクトリがあるので、
そこにplugins.sbtというファイルを作ります。
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0")
sbteclipseが、eclipseの.projectなどを作ってくれるようです。
GitHub - sbt/sbteclipse: Plugin for sbt to create Eclipse project definitions
sbt、eclipseでEclipseにインポート可能なプロジェクトになります。
Eclipseから、既存プロジェクトをインポートで、さくっとインポート出来ます。
mavenでのresourcesが無いので、
src/main/resources
src/test/resources
を追加しました。
・Build.scala
import sbt._ import sbt.Keys._ object ProjectBuild extends Build { lazy val root = Project( id = "root", base = file("."), settings = Project.defaultSettings ++ Seq( name := "hoge_project", organization := "org.hoge", version := "0.1-SNAPSHOT", scalaVersion := "2.9.1", // add other settings here libraryDependencies ++= dependencies ) ) val junit = "junit" % "junit" % "4.10" % "test" val scalaTest = "org.scalatest" %% "scalatest" % "1.7.1" % "test" lazy val dependencies = Seq( junit, scalaTest ) }
これで、TestClassを作って、sbt testを実行すると、テストが実行されます。
ただ、sbtだけを意識するのであれば、これでいいのですが、
この状態だと当然の如く、jUnit、ScalaTestがビルドパスに通ってないので、Eclipse上では、エラーが出ます。
ここで再びsbt eclipseを実行すると、ビルドパスをBuild.scalaから生成してくれます。素敵すぎる・・・。
安全の為には、プロジェクトを一旦クローズするなりしたほうが良いのかもしれません。
と安心してたら、resourcesのソースフォルダが消えてる。
デフォルトだとresourcesは作ってくれないので、手動で作っても、再度sbt eclipse実行すると消えてしまいます。
ですので、resourcesも作ってもらいましょう。
Biuld.scalaにて、
import com.typesafe.sbteclipse.plugin.EclipsePlugin._
上のインポートを追加し、settingに
EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource
を含めます。
Using sbteclipse · sbt/sbteclipse Wiki · GitHub
ちゃんと読めって事ですね。
sbt eclipse実行すると、ちゃんとresourcesを作ってくれます。
参考:
This page has moved
http://blob.geishatokyo.com/archives/22401
[Scala]sbtを使ってScalaのプロジェクトをeclipseで編集する | GENDOSU@NET
・Build.scala(Jetty)
sbtで、WebApplicationを実装する場合は、xsbt-web-pluginを使うらしい。
GitHub - aolshevskiy/xsbt-web-plugin: Web app support plugin for XSbt using Jetty Web Server
plugins.sbtに追加します。
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") libraryDependencies <+= sbtVersion(v => "com.github.siasia" %% "xsbt-web-plugin" % (v+"-0.2.11"))
改行しないと、reloadでエラーになります。
[error] Error parsing expression. Ensure that settings are separated by blank lines.
ここでちょっと脱線しますが、
Webの情報見てると、Build.scala、build.sbt、plugins.sbtの役割ってなんなのと今更、疑問が湧きました。
Build.scalaとbuild.sbtの違い。sbt の Build.scala を書く - tototoshi の日記
Build.scalaの方が柔軟に記述が出来るようですね。Build.scalaで設定します。
plugins.sbtは、sbtのプラグインを記述する為の設定ファイルのようです。http://scalajp.github.com/sbt-getting-started-guide-ja/using-plugins/
jetty-webappのjar依存を追加します。
val jetty_webapp = "org.eclipse.jetty" % "jetty-webapp" % "8.1.0.v20120127" % "container" val jetty_websocket = "org.eclipse.jetty" % "jetty-websocket" % "8.1.0.v20120127" % "compile" val servlet_api = "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided"
Jettyのバージョンですが、8.1.2を使うとエラーが出ます。SBT, Jetty and Servlet 3.0 - Stack Overflow
ですので、8.1.0を使うとよいそうです。sbt reload, updateするとだーっと依存してるライブラリがダウンロードされます。
また、
jetty-webappは、scopeをcontainerにする必要があります。container以外を指定するとエラーが出ます。
servlet-apiは、jetty-webappの中で、org.mortbay.jetty#servlet-api;3.0がダウンロードされますが、
containerを指定すると、依存しているライブラリは、ビルドパスに含まれないので、providedで使用します。
web-socketは、コンパイル&デプロイで必要なので、compileとしています。
次に、webappディレクトリを作成します。(誰もが思う、これeclipseコマンドでやってくれないかと)
sbtは、mavenとディレクトリ構造が同じなので、
src/main/webapp
を作成します。srcの直下にwebapp作ってる例とかありますが、mavenを踏襲したほうがいいですね。
WEB-INFも作ります。web.xmlも作ります。
container:startで起動するんですけど、jettyの設定をBuild.scalaに記述するのは、loggingとかsslとか正直面倒。
これまでの資産のRunJettyRunの方が楽そう。Eclipseからデバッグすぐ出来るし。
「xsbt-web-plugin使う必要無いんじゃないの?」・・・。
・WAR
ただwarを作るだけなら、packageすればいいんですが、
ちょっとWAR化するのに、excludeしたかったりするので、また今度にします。
最後に自分のBuild.scalaとplugins.sbtです。
Build.scala
import sbt._ import sbt.Keys._ import com.typesafe.sbteclipse.plugin.EclipsePlugin._ import com.github.siasia.WebPlugin.webSettings import sbtassembly.Plugin._ import AssemblyKeys._ object ProjectBuild extends Build { lazy val root = Project( id = "root", base = file("."), settings = Project.defaultSettings ++ webSettings ++ assemblySettings ++ Seq( name := "hoge_project", organization := "org.hoge", version := "0.1-SNAPSHOT", scalaVersion := "2.9.1", // add other settings here libraryDependencies ++= dependencies, EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource ) ) // test val junit = "junit" % "junit" % "4.10" % "test" val scalaTest = "org.scalatest" %% "scalatest" % "1.7.1" % "test" // jetty val jetty_webapp = "org.eclipse.jetty" % "jetty-webapp" % "8.1.0.v20120127" % "container" val jetty_websocket = "org.eclipse.jetty" % "jetty-websocket" % "8.1.0.v20120127" % "compile" val servlet_api = "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided" // log val slf4j = "org.slf4j" % "slf4j-api" % "1.6.4" % "compile" val logback_core = "ch.qos.logback" % "logback-core" % "1.0.1" % "runtime" val logback_classic = "ch.qos.logback" % "logback-classic" % "1.0.1" % "runtime" // h2 val h2 = "com.h2database" % "h2" % "1.3.166" % "runtime" // commons val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" // ant val ant = "org.apache.ant" % "ant" % "1.8.3" % "compile" lazy val dependencies = Seq( junit, scalaTest, jetty_webapp, jetty_websocket, servlet_api, h2, slf4j, logback_core, logback_classic, commons_io, ant ) }
plugins.sbt
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") libraryDependencies <+= sbtVersion(v => "com.github.siasia" %% "xsbt-web-plugin" % (v+"-0.2.11")) addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.8.0") resolvers += Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns)
お疲れ様でした。