Ant script for Scala with Lift

Today I'd like to present another Ant script for Scala development. I promise it's going to be the last one, but I felt this mini series wouldn't be complete without it. This script is for developing a Lift application with Scala. I became interested in Lift in the first half of this year, but unfortunately, I wasn't able to install it when I wanted to give it a try, because of some besetting problems with Maven. Deplorably, the Lift developers have made Maven a requirement for Lift. If you go to the Lift download page, you wont find a Lift package but a Maven package which you must install in order to retrieve Lift from the central repository. Alternatively, you can fetch the latest Lift sources from GitHub, but then you still need Maven to get the examples to work. Since Maven and I have a difficult relationship, I mentioned this to the Lift community earlier this year. The community seemed quite pleased with Maven, however, and had little interest in supporting alternatives. Hence, I've adapted the Ant script from the last article in order to work with Lift and -while we are at it- replaced the Jetty server that is bundled with Lift with Tomcat. I hope this will be useful for people interested in tinkering with Lift who want to avoid the aggravation of having to deal with Maven. As usual, you can download the project sekeleton and Ant build file right here. I have packaged the Lift 1.0 jars as well as its dependencies (commons-fileupload.jar and log4j.ar), so that you can get the helloworld example to run without additional installation steps. If you plan to use this for your work, you should replace the Lift jars with the most recent versions from GitHub. The Ant script has the same target definitions as the one I presented for general Web development including deploy, undeploy and reload. The Lift dependencies are likewise handled in the same way, as the script uses everything it finds in the WEB-INF/lib directory. Your only responsibility is to put the right jars in there. The main difference consists of a different directory tree structure that reflects the layout of a standard Lift project. This means you won't have to rearrange things if you move a project from Maven to Ant. In fact all of the Lift examples should run simply by dropping in the sources (I've tried this for a few but not all examples). So, here's the Ant script:

<project default="build" name="hellolift">

  <!-- root directory of this project -->
  <property name="project.dir" value=".">

  <!-- root directory of Scala installation -->
  <property name="scala.home" value="C:\\Program Files\\Scala">

  <!-- root directory of Tomcat installation -->
  <property name="server.home" 
    value="C:\\path\\to\\Tomcat\\installation">

  <!-- URL for Tomcat's manager application -->
  <property name="server.manager.url" value="http://dev:8080/manager">

  <!-- account name for Tomcat's admin account -->
  <property name="server.manager.username" value="admin">

  <!-- password name for Tomcat's admin account -->
  <property name="server.manager.password" value="admin">

  <!-- location of scalatest.jar for unit testing -->
  <property name="scalatest.jar"
     value="C:\\path\\to\\scalatest-1.0\\scalatest-1.0.jar">

  <target name="init">

    <!-- derived path names -->
    <property name="source.dir" value="${project.dir}/src">
    <property name="web.dir" value="${project.dir}/web">
    <property name="classes.dir" value="${web.dir}/WEB-INF/classes">
    <property name="lib.dir" value="${web.dir}/WEB-INF/lib">
    <property name="webapp.dir"
       value="${server.home}/webapps/${ant.project.name}">
    <property name="test.dir" value="${project.dir}/test">

    <!-- scala libraries for classpath definitions -->
    <property name="scala-library.jar"
        value="${scala.home}/lib/scala-library.jar">
    <property name="scala-compiler.jar"
        value="${scala.home}/lib/scala-compiler.jar">

    <!-- classpath for the compiler task definition -->
    <path id="scala.classpath">
      <pathelement location="${scala-compiler.jar}">
      <pathelement location="${scala-library.jar}">
    </pathelement>

    <!-- classpath for project build -->
    <path id="build.classpath">
      <pathelement location="${server.home}/lib/servlet-api.jar">
      <pathelement location="${scala-library.jar}">
      <fileset dir="${lib.dir}">
        <include name="*.jar">
      </include>
      <pathelement location="${classes.dir}">
    </pathelement>

    <!-- classpath for unit test build  -->
    <path id="test.classpath">
      <path refid="build.classpath">
      <pathelement location="${scalatest.jar}">
    </pathelement>

    <!-- definition for the
      "scalac" and "scaladoc" ant tasks -->
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath refid="scala.classpath">
    </classpath>

    <!-- definition for the "scalatest" ant task -->
    <taskdef classname="org.scalatest.tools.ScalaTestTask" 
      name="scalatest">
      <classpath refid="test.classpath">
    </classpath>

    <!-- definition for the "reload", "deploy"
      and "undeploy" Tomcat tasks -->
    <taskdef classname="org.apache.catalina.ant.DeployTask" 
      name="deploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.ReloadTask" 
      name="reload">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.UndeployTask" 
      name="undeploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>

  </taskdef>

  <!-- compile project -->
  <target depends="init" description="build" name="build">
    <buildnumber>
    <tstamp>
    <mkdir dir="${classes.dir}">
    <mkdir dir="${lib.dir}">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <scalac classpathref="build.classpath" deprecation="on"
        destdir="${classes.dir}" force="never" srcdir="${source.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- create a deployable web archive -->
  <target depends="build" description="war" name="war">
    <war basedir="${web.dir}" 
      destfile="${project.dir}/${ant.project.name}.war"
       webxml="${web.dir}/WEB-INF/web.xml">
  </war>

  <!-- creates a deployable web archive with
    all classes packed into a single jar file -->
  <target depends="build" description="packedwar"
    name="packedwar">
    <jar basedir="${classes.dir}" 
      destfile="${lib.dir}/${ant.project.name}.jar"
      duplicate="preserve">
      <manifest>
        <section name="Program">
          <attribute name="Title" value="${ant.project.name}">
          <attribute name="Build" value="${build.number}">
          <attribute name="Date" value="${TODAY}">
        </attribute>
      </attribute>
    </attribute>
    <delete dir="${classes.dir}">
    <war basedir="${web.dir}" 
      destfile="${project.dir}/${ant.project.name}.war"
      webxml="${web.dir}/WEB-INF/web.xml">
    <delete file="${lib.dir}/${ant.project.name}.jar">
  </delete>

  <!-- deploy project on Tomcat server -->
  <target depends="war" description="deploy" name="deploy">
    <mkdir dir="${webapp.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <deploy localwar="${project.dir}/${ant.project.name}.war"
        password="${server.manager.password}" 
	path="/${ant.project.name}"
        url="${server.manager.url}" 
	username="${server.manager.username}">
  </deploy>

  <!-- update and reload project on Tomcat server -->
  <target depends="build" description="reload" name="reload">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <reload password="${server.manager.password}" 
      path="/${ant.project.name}"
      url="${server.manager.url}" 
      username="${server.manager.username}">
  </reload>

  <!-- remove project from Tomcat server -->
  <target depends="init" description="undeploy" name="undeploy">
    <undeploy password="${server.manager.password}"
      path="/${ant.project.name}"
      url="${server.manager.url}"
      username="${server.manager.username}">
  </undeploy>

  <!-- build unit tests -->
  <target depends="build" name="buildtest">
    <mkdir dir="${test.dir}/build">
    <scalac classpathref="test.classpath" deprecation="on"
        destdir="${test.dir}/build" force="never" srcdir="${test.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- run unit tests -->
  <target depends="buildtest" description="test" name="test">
    <scalatest runpath="${test.dir}/build">
      <reporter config="YFABRT" type="stdout">
      <membersonly package="suite">
      <!-- <reporter type="graphic" config="YFABRT"/> -->
      <!-- <suite classname="suite.TestSuite"/> -->
    </membersonly>
  </reporter>

  <!-- delete all build files -->
  <target depends="init" description="clean" name="clean">
    <delete dir="${classes.dir}">
    <delete dir="${project.dir}/doc">
    <delete dir="${test.dir}/build">
    <delete file="${lib.dir}/scala-library.jar">
    <delete file="${project.dir}/${ant.project.name}.war">
  </delete>

  <!-- create API documentation in doc folder -->
  <target depends="build" description="scaladoc" name="scaladoc">
    <mkdir dir="${project.dir}/doc">
    <scaladoc classpathref="build.classpath"
      destdir="${project.dir}/doc"
      doctitle="${ant.project.name}" srcdir="${source.dir}"
      windowtitle="${ant.project.name}">
    </scaladoc>
  </target>

</project>