k4200’s notes and thoughts

Programmer side of k4200

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