Lift with sbt 0.10
Background
People around me have talked about sbt 0.9 and then 0.10 for the past few months. As you may know, I like something new, so I decided to try it out for no reason.
I started playing around with it around two weeks ago. sbt 0.10 is so much different from 0.7, and I was confused for the first few days. I was gradually getting used to it, and gave a small presentation for a local Scala study group. Here are the slides. A few days later, I finally got a Scala/Lift project working with sbt 0.10 (more or less). If you find any mistakes or have questions/comments, please let me know.
Goal
I had a Lift project working with sbt 0.7 composed of sub projects. My goal was to migrate the project to sbt 0.10. I thought the following two things were the challenges.
- Sub projects
- Web project
Some big differences
Basic Configuration and Full Configuration
There are two ways of writing configurations in sbt 0.10, the one of which is called "Basic Configuration", and the other of which is "Full Configuration". Here are examples of the two.
DSL is used for Basic Configuration. They say it's easier, but has some limitations. On the other hand, for Full Configuration, you write configuration in Scala like sbt 0.7, so most of us are more familiar with it.
IMHO, the DSL used for Basic Configuration isn't that easy when you want to do more than very simple settings, and it has limitations as I wrote above. So, I don't see the point why we need it and decided to stick with Full Configuration.
Jetty support is split into a plug-in
Older versions of sbt have Jetty support, but it's no longer part of the package but a plug-in. Here is the Github page of the plugin.
Steps
Install sbt 0.10 and moved the old "project" directory
This is simple. Just follow the Step 1 and the first part of Step 2 on this page.
Create Build.scala and build.properties
You need to create Build.scala under "project" directory. At this moment, you still need build.properties in "project" with the following line.
sbt.version=0.10.0
Sub projects
I have sub projects shown belown:
- "core" : Common functionality for the other projects
- "web" : Web site using Lift
- "bgtasks" : Background tasks like maintaining the database etc.
So, the initial version of Build.scala looked like this:
import sbt._ import Keys._ object BuildSettings { val buildOrganization = "k4200" val buildScalaVersion = "2.8.1" val buildVersion = "0.3.0" val buildSettings = Defaults.defaultSettings ++ Seq ( organization := buildOrganization, scalaVersion := buildScalaVersion, version := buildVersion) } object Dependencies { // snip } object MyLifthubBuild extends Build { import Dependencies._ import BuildSettings._ lazy val root = Project ("root", file ("."), settings = buildSettings) aggregate (core, web, bgtasks) lazy val core = Project ("core", file ("core"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) lazy val web = Project ("web", file ("web"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) dependsOn (core) lazy val bgtasks = Project ("bgtasks", file ("bgtasks"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) dependsOn (core) }
Looks simple, doesn't it? I wrote an article about sub projects with sbt 0.7 the other day, so it might be interesting to compare it with the above code.
xsbt-web-plugin
It took me a few days to figure out how the things should be/work. At last, I decided to use Basic Configuration for the plugin settings.
I created "project/plugins/build.sbt" as written here:
resolvers += "Web plugin repo" at "http://siasia.github.com/maven2" //Following means libraryDependencies += "com.github.siasia" %% "xsbt-web-plugin" % "0.1.0-<sbt version>"" libraryDependencies <+= sbtVersion(v => "com.github.siasia" %% "xsbt-web-plugin" % ("0.1.0-"+v))
The README says the following, but didn't know how to do that with Build.scala.
Inject plugin settings into project in build.sbt:
I spent some time on searching for the info on the net, and just gave up, and read the source code of the plugin. This file was it. "webSettings" is defined in WebPlugin object, which extends Plugin.
I made same changes on Build.scala and it was changed like this:
object MyLifthubBuild extends Build { import Dependencies._ import BuildSettings._ import com.github.siasia.WebPlugin._ // web plugin lazy val root = Project ("root", file ("."), settings = buildSettings) aggregate (core, web, bgtasks) lazy val core = Project ("core", file ("core"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) lazy val web = Project ("web", file ("web"), settings = buildSettings ++ Seq (libraryDependencies := allDeps ++ webPluginDeps) ++ // "Inject" the settings webSettings) dependsOn (core) lazy val bgtasks = Project ("bgtasks", file ("bgtasks"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) dependsOn (core) }
jars necessary for "jetty-*" tasks
I think the above code compiles, but should get an error complaining about class not found when you execute "jetty-run".
You should add the following dependency to the web project.
The final version of Buildl.scala looks like the following:
import sbt._ import Keys._ object BuildSettings { val buildOrganization = "k4200" val buildScalaVersion = "2.8.1" val buildVersion = "0.3.0" val buildSettings = Defaults.defaultSettings ++ Seq ( organization := buildOrganization, scalaVersion := buildScalaVersion, version := buildVersion) } object Resolvers { val webPluginRepo = "Web plugin repo" at "http://siasia.github.com/maven2" val jettyRepo = "Jetty Repo" at "http://repo1.maven.org/maven2/org/mortbay/jetty" } object Dependencies { // web plugin val webPluginDeps = Seq( "org.mortbay.jetty" % "jetty" % "6.1.26" % "jetty", // The last part is "jetty" not "test". "javax.servlet" % "servlet-api" % "2.5" % "provided->default" ) val liftDeps = { val liftVersion = "2.2" // I'll switch to 2.3 soon! Seq( "net.liftweb" %% "lift-webkit" % liftVersion % "compile->default", "net.liftweb" %% "lift-mapper" % liftVersion % "compile->default" ) } val xerces = "xerces" % "xercesImpl" % "2.8.1" % "compile" val slf4j = "org.slf4j" % "slf4j-api" % "1.6.1" % "compile" val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "compile" val slf4s = "com.weiglewilczek.slf4s" %% "slf4s" % "1.0.6" // : val mysql = "mysql" % "mysql-connector-java" % "5.1.14" val compileDeps = Seq(xerces, slf4j, logback, slf4s, .... , mysql) ++ liftDeps ++ val testDeps = Seq(specs, scalacheck, mockito, junit) val allDeps = compileDeps ++ testDeps } object MyLifthubBuild extends Build { import Dependencies._ import BuildSettings._ import com.github.siasia.WebPlugin._ lazy val root = Project ("root", file ("."), settings = buildSettings) aggregate (core, web, bgtasks) lazy val core = Project ("core", file ("core"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) lazy val web = Project ("web", file ("web"), settings = buildSettings ++ Seq (resolvers := Seq(webPluginRepo, jettyRepo), libraryDependencies := allDeps ++ webPluginDeps) ++ webSettings) dependsOn (core) lazy val bgtasks = Project ("bgtasks", file ("bgtasks"), settings = buildSettings ++ Seq (libraryDependencies := allDeps)) dependsOn (core) }
Hope this entry is of some use.
Some notes
Information on the net
More people are using sbt 0.10, but most of the information out there is of Basic Configuration, which I won't use.
At first, I googled "sbt 0.10 lift" and this page popped up. Pretty good, concise article, but unfortunately, this is for Basic Configuration.
Useful resources
- Full Configuration example
- This page explains basics of the plugin architecture of sbt 0.10, which helps you understand xsbt-web-plugin.
- The page I mentioned above gave me directions on how to use sbt 0.10 and xsbt-web-plugin with Lift although it's for Basic Configuration.
- This post and discussion in the sbt mailing list is also useful.