Introduction
This is a small writeup which gives one way of setting up a build system for your Scala project. Here Apache Ant is used for managing this process, where the whole configuration is contained within a single build.xml file placed in the project’s root directory. Execution is done from command line or within your IDE environment.
Prerequisites
Install and configure Scala:
- Define and set the
SCALA_HOMEvariable in the OS environment - Update the
PATHvariable in the OS environment to includeSCALA_HOME\bindirectory
Install and configure Ant:
- Define and set the
ANT_HOMEvariable in the OS environment - Update the
PATHvariable in the OS environment to includeANT_HOME\bindirectory - Optional: If you encounter memory problems, define and set
ANT_OPTS=-Xmx500Mwith JVM memory configuration in the OS environment
Install and configure Java:
- Define and set the
JAVA_HOMEvariable in the OS environment - Update the
PATHvariable in the OS environment to includeJAVA_HOME\bindirectory
Find a suitable file structure for the project:
project-name/ build/ lib/ src/ test/ build.xml
The Ant build script
Order of Ant targets:
build <- package <- doc <- compile <- initclean <- init
Configure project-name/build.xml:
<?xml version="1.0" encoding="UTF-8"?> <project name="ProjectName" default="build" basedir="."> <description>Project Build Script</description> <!-- targets --> <target name="build" depends="package" description="Build whole project"/> <target name="clean" depends="init" description="Remove previous build files"> <delete dir="${build.dir}" includeemptydirs="true" quiet="true"/> </target> <target name="init"> <property environment="env"/> <!-- check for required tools --> <fail message="Missing SCALA_HOME variable in OS environment"> <condition><isset property="${env.SCALA_HOME}"/></condition> </fail> <fail message="Missing JAVA_HOME variable in OS environment"> <condition><isset property="${env.JAVA_HOME}"/></condition> </fail> <!-- variables for paths and files --> <property name="src.dir" location="${basedir}/src"/> <property name="lib.dir" location="${basedir}/lib"/> <property name="build.dir" location="${basedir}/build"/> <property name="build-classes.dir" location="${build.dir}/classes"/> <property name="build-lib.dir" location="${build.dir}/lib"/> <property name="build-doc.dir" location="${build.dir}/doc"/> <property name="java.dir" location="${env.JAVA_HOME}"/> <property name="scala.dir" location="${env.SCALA_HOME}"/> <property name="scala-library.jar" location="${scala.dir}/lib/scala-library.jar"/> <property name="scala-compiler.jar" location="${scala.dir}/lib/scala-compiler.jar"/> <path id="project.classpath"> <pathelement location="${scala-library.jar}"/> <pathelement location="${build-classes.dir}"/> <!-- used during recompilation --> </path> <path id="scala.classpath"> <pathelement location="${scala-compiler.jar}"/> <pathelement location="${scala-library.jar}"/> </path> <!-- load scala's ant tasks --> <taskdef resource="scala/tools/ant/antlib.xml" classpathref="scala.classpath"/> <!-- print where this project will get scala and java from --> <echo message="Init project"/> <echo message=" with scala.dir = ${scala.dir}"/> <echo message=" with java.dir = ${java.dir}"/> <!-- check if any files has been modified since last build --> <uptodate property="build.uptodate" targetfile="${build.dir}/build.done"> <srcfiles dir= "${src.dir}" includes="**"/> <srcfiles dir= "${lib.dir}" includes="**"/> </uptodate> </target> <target name="compile" depends="init" unless="build.uptodate"> <mkdir dir="${build-classes.dir}"/> <scalac destdir="${build-classes.dir}" classpathref="project.classpath"> <include name="**/*.scala"/> <src><pathelement location="${src.dir}"/></src> </scalac> </target> <target name="doc" depends="compile" unless="build.uptodate"> <mkdir dir="${build-doc.dir}"/> <scaladoc srcdir="${src.dir}" destdir="${build-doc.dir}" doctitle="Project API documentation" classpathref="project.classpath"> <include name="**/*.scala"/> </scaladoc> </target> <target name="package" depends="doc" unless="build.uptodate"> <mkdir dir="${build-lib.dir}"/> <jar destfile="${build-lib.dir}/project.jar"> <fileset dir="${build-classes.dir}"/> </jar> <jar destfile="${build-lib.dir}/project-src.jar"> <fileset dir="${src.dir}" includes="**/*.scala"/> </jar> <jar destfile="${build-lib.dir}/project-doc.jar"> <fileset dir="${build-doc.dir}"/> </jar> <touch file="${build.dir}/build.done"/> <!-- mark build as up-to-date --> </target> </project>
Reference for Scalac Ant task
Arguments: srcdir, srcref, destdir, classpath, classpathref, sourcepath, sourcepathref, bootclasspath, bootclasspathref, extdirs, extdirsref, encoding, target={jvm-1.5,msil}, deprecation={yes,no}, optimise={yes,no}, unchecked={yes,no}, force={yes,no}, fork={yes,no}, logging={none,verbose,debug}, logphase={namer,typer,pickler,uncurry, tailcalls,explicitouter,erasure, lambdalift,flatten,constructors, mixin,icode,jvm,terminal}, debuginfo={none,source,line,vars,notailcalls}, addparams, scalacdebugging, failonerror={yes,no}, assemname='msil-specific', assemrefs='msil-specific'
Nested arguments: src,classpath,sourcepath,bootclasspath,extdirs
Reference for Scaladoc Ant task
Arguments: srcdir, srcref, destdir, classpath, classpathref, sourcepath, sourcepathref, bootclasspath, bootclasspathref, extdirs, extdirsref, encoding, docgenerator, doctitle, docsourceurl, docversion, header, footer, top, bottom, addparams, deprecation, unchecked
Nested arguments: src, classpath, sourcepath, extdirs
Alternative build systems
Simple Build Tool (SBT) - a Scala-centric build system with support for continuous compilation
Testing frameworks
ScalaTest - supports various testing methodologies
ScalaCheck - specification verified using automatic test-case generation
Specs2 - supports various testing methodologies
Strange, my system’s installation of Scala doesn’t have a “scala/tools/ant/antlib.xml” anywhere. Where are we supposed to find that?
The “scala/tools/ant/antlib.xml” is located within the scala-compiler.jar, which itself is located at %SCALA_HOME%/lib/. Might be related to a problem with the script. I updated the check in init to reference env.SCALA_HOME and env.JAVA_HOME properly, and verified it that it ran when everything is configured correctly.