For the longest time I’ve been writing Java build files in XML and I’ve always felt a little dirty. Not too long ago I was re-introduced to rake by a colleague (thanks Fabio) and how nicely it integrates with ant.
This means that you can now turn this:
<project name="spring-conversations" default="build" basedir=".">
<property name="src.dir" location="src/main" />
<property name="test.dir" location="src/test" />
<property name="build.dir" location="build" />
<property name="dist.dir" location="${build.dir}/dist" />
<property name="report.dir" location="${build.dir}/report" />
<property name="compile.dir" location="${build.dir}/compile" />
<path id="classpath">
<fileset dir="${compile.dir}" />
<fileset dir="lib/buildtime" />
<fileset dir="lib/runtime" />
</path>
<target name="build"
depends="clean,run-tests,make-war"
description="Main target to create WAR file." />
<target name="run-tests"
depends="make-jars,run-unit-tests,check-tests"
description="Run all tests" />
<target name="clean">
<delete dir="${build.dir}" />
</target>
<macrodef name="make-jar">
<attribute name="srcdir" />
<attribute name="jarfile" />
<sequential>
<mkdir dir="${compile.dir}/classes" />
<javac srcdir="@{srcdir}" destdir="${compile.dir}/classes" classpathref="classpath"
debug="yes" includeantruntime="no" />
<jar jarfile="${compile.dir}/@{jarfile}" basedir="${compile.dir}/classes" />
<delete dir="${compile.dir}/classes" />
</sequential>
</macrodef>
<target name="make-jars">
<make-jar srcdir="${src.dir}/java" jarfile="${ant.project.name}.jar" />
<make-jar srcdir="${test.dir}/java" jarfile="${ant.project.name}-tests.jar" />
</target>
<target name="run-unit-tests">
<mkdir dir="${report.dir}" />
<junit fork="yes" forkmode="once" printsummary="yes"
haltonfailure="no" failureproperty="tests.failed">
<classpath location="${src.dir}/resources" />
<classpath refid="classpath" />
<formatter type="xml" />
<batchtest todir="${report.dir}">
<fileset dir="${test.dir}/java">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>
</target>
<target name="check-tests" if="tests.failed">
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}" includes="TEST-*.xml" />
<report todir="${report.dir}/html" />
</junitreport>
<fail message="One or more tests failed. Please check the logs for more info." />
</target>
<target name="make-war" depends="make-jars">
<mkdir dir="${dist.dir}" />
<war warfile="${dist.dir}/${ant.project.name}.war" webxml="src/main/webapp/WEB-INF/web.xml">
<fileset dir="src/main/webapp" excludes="**/web.xml" />
<lib dir="${compile.dir}" excludes="*-tests.jar" />
<classes dir="${src.dir}/resources" />
<lib dir="lib/runtime" />
</war>
</target>
<target name="run-jetty" depends="clean,make-jars" description="Run application in Jetty.">
<java classname="example.jetty.WebServer" fork="true" failonerror="true">
<classpath location="${src.dir}/resources" />
<classpath refid="classpath" />
</java>
</target>
</project>
Into this:
require 'ant'
PROJECT_NAME = 'spring-conversations'
MAIN_SRC_DIR = 'src/main/java'
TEST_SRC_DIR = 'src/test/java'
RUNTIME_LIB_DIR = 'lib/runtime'
BUILDTIME_LIB_DIR = 'lib/buildtime'
BUILD_DIR = 'build'
DIST_DIR = "#{BUILD_DIR}/dist"
COMPILE_DIR = "#{BUILD_DIR}/compile"
CLASSES_DIR = "#{COMPILE_DIR}/classes"
TEST_REPORT_DIR = "#{BUILD_DIR}/report"
task :default => [:clean, :run_tests, :make_war]
task :clean do
ant.delete :dir => BUILD_DIR
puts
end
task :setup do
ant.path :id => 'classpath' do
fileset :dir => COMPILE_DIR
fileset :dir => RUNTIME_LIB_DIR
fileset :dir => BUILDTIME_LIB_DIR
end
end
task :make_jars => :setup do
make_jar MAIN_SRC_DIR, "#{PROJECT_NAME}.jar"
make_jar TEST_SRC_DIR, "#{PROJECT_NAME}-tests.jar"
end
def make_jar(source_folder, jar_file_name)
ant.mkdir :dir => CLASSES_DIR
ant.javac :srcdir => source_folder, :destdir => CLASSES_DIR, :classpathref => 'classpath',
:source => "1.6", :target => "1.6", :debug => "yes", :includeantruntime => "no"
ant.jar :jarfile => "#{COMPILE_DIR}/#{jar_file_name}", :basedir => CLASSES_DIR
ant.delete :dir => CLASSES_DIR
puts
end
task :run_tests => :make_jars do
ant.mkdir :dir => TEST_REPORT_DIR
ant.junit :fork => "yes", :forkmode => "once", :printsummary => "yes",
:haltonfailure => "no", :failureproperty => "tests.failed" do
classpath :refid => 'classpath'
formatter :type => "xml"
batchtest :todir => TEST_REPORT_DIR do
fileset :dir => TEST_SRC_DIR, :includes => '**/*Test.java'
end
end
if ant.project.getProperty("tests.failed")
ant.junitreport :todir => TEST_REPORT_DIR do
fileset :dir => TEST_REPORT_DIR, :includes => "TEST-*.xml"
report :todir => "#{TEST_REPORT_DIR}/html"
end
ant.fail :message => "One or more tests failed. Please check the test report for more info."
end
puts
end
task :make_war => :make_jars do
ant.mkdir :dir => DIST_DIR
ant.war :warfile => "#{DIST_DIR}/#{PROJECT_NAME}.war", :webxml => "src/main/webapp/WEB-INF/web.xml" do
fileset :dir => "src/main/webapp", :excludes => "**/web.xml"
lib :dir => COMPILE_DIR, :excludes => "*-tests.jar"
classes :dir => "src/main/resources"
lib :dir => RUNTIME_LIB_DIR
end
puts
end
task :run_jetty => [:clean, :make_jars] do
ant.java :classname => "example.jetty.WebServer", :fork => 'yes', :failonerror => 'yes' do
classpath :location => "src/main/resources"
classpath :refid => "classpath"
end
end
Damn, this feels like code.