ant build.xml—a specific example

This is a pretty extensive example of an ant build.xml file from a real, commercial project. If you run it to get help (which is an option), you'll see this:

Targets:

 build-compile                    Build Java code.
 build-dto-jar                    Create DTO package JAR for client--required after DTO change!
 build-war                        Create WAR file.
 buildnumber                      Display build.number value that will be used in package.
 clean                            Clean up temporary files and subdirectories.
 compile-java                     Compile Java code.
 debian-build                     Build Debian package (4).
 debian-build-number              Handle build.number (2).
 debian-build-with-test           Run JUnit tests, build Debian package (4).
 debian-clean                     Clean out Debian-package intermediate files (1).
 debian-prepare                   Set up to create Debian package (3).
 erase-database-on-localhost      Erase MongoDB database on localhost.
 jacoco-clean                     Clean Jacoco intermediate objects (1).
 jacoco-report                    Generate Jacoco report (3).
 jacoco-run                       Run Jacoco (2).
 jenkins-build                    Build WAR for developer sandbox (Jenkins-only target).
 jenkins-deploy                   Deploy built WAR to developer sandbox (Jenkins-only target).
 load-database-on-localhost       Reload data into MongoDB database on localhost.
 load-test-database-on-localhost  Reload test data into MongoDB database on localhost.
 prepare-to-publish               Prepare to publish Debian package (5). Best semi-final target to see if build's okay.
 promote-production               Promote package to production (stable) status (6).
 promote-staging                  Executes script that promotes package to staging (testing) status (6).
 publish                          Publish Debian package (6).
 refresh-database-on-localhost    Refresh MongoDB database on localhost.
 show-properties                  See property definitions in force during build.
 sonar-local                      Deploy build to local Sonar.
 sonar-remote                     Deploy build to remote Sonar.
 test-clean                       Clean and build tests (1).
 test-compile                     Compile Java test code (2).
 test-report                      Report JUnit test results (4).
 test-run                         Run JUnit tests (3).
Default target: help

BUILD SUCCESSFUL
Total time: 0 seconds
<?xml version="1.0" ?>
<!DOCTYPE project>
<project name="accountmgr" default="help">
  <property name="dummy" value="Fix Eclipse's ant wagon: take this out and you'll see why it's here!" />

  <property name="src.level"                 value="1.6" />
  <property name="target.level"              value="1.6" />
  <property name="test.compile.verbose"      value="true" />

  <!-- Here's how to prime the pump:
       You should keep your own build.properties in extras/private/you/build.properties. Create
       a link to this file on the local path /opt/acme/apps/accountmgr/conf/build.properties.
    -->
  <property file="/opt/acme/apps/accountmgr/conf/build.properties" />

  <!-- Everything from here down can be overriden by providing a value in the properties file -->
  <!-- Add more calculated subdirectory names... -->
  <property name="app.properties.rootdir"    location="/opt/acme/apps/accountmgr" />
  <property file="${app.properties.rootdir}/conf/application.properties" />

  <property name="build.dir"                 location="${basedir}/build" />
  <property name="dist.dir"                  location="${basedir}/dist" />
  <property name="lib.dir"                   location="${basedir}/lib" />
  <property name="test.lib.dir"              location="${lib.dir}/test-only" />
  <property name="src.dir"                   location="${basedir}/src" />
  <property name="test.dir"                  location="${basedir}/test" />
  <property name="web.dir"                   location="${basedir}/WebContent" />
  <property name="deploy.dir"                location="${build.dir}/deploy" />

  <property name="build-classes.dir"         location="${build.dir}/classes" />
  <property name="test-classes.dir"          location="${build.dir}/test-classes" />
  <property name="test-reports.dir"          location="${build.dir}/test-reports" />
  <property name="extras.dir"                location="${basedir}/extras" />
  <property name="scripts.dir"               location="${extras.dir}/debian/scripts" />
  <property name="flat_libs.dir"             value="deployed-libraries" />

  <property name="war.file"                  value="${dist.dir}/${ant.project.name}.war" />

  <property name="debian.dir"                location="${build.dir}/debian" />
  <property name="debian.control.file"       location="${extras.dir}/debian/control" />
  <!-- location that the .war file will be placed when the debian package is installed -->
  <property name="debian.deploy.loc"         value="/var/lib/tomcat6/webapps/" />

  <property name="app.properties.file"       location="${src.dir}/com/acme/web/user/configuration/ApplicationProperties.java" />
  <!-- property name="staging.local.dir"         location="${extras.dir}/private/staging" / -->

  <property name="database.shell.script"     value="${extras.dir}/reinit_local.sh" />
  <property name="erase.database.script"     value="${extras.dir}/erase-database.js" />
  <property name="load.database.script"      value="${extras.dir}/load-database.js" />
  <property name="load.test.database.script" value="${extras.dir}/load-test-database.js" />
  <property name="deploy.to.sandbox.script"  value="${extras.dir}/deploy.sh" />

  <property name="dtojar.filename"           value="dto.jar" />
  <property name="dtojar.version"            value="1.0" />

  <property name="sonarProjectName"          value="Account Management" />
  <property name="sonarProjectKey"           value="com.acme.web:${ant.project.name}" />
  <property name="sonarProjectVersion"       value="1.0-SNAPSHOT" />

  <property name="jacoco.dir"                location="${build.dir}/jacoco" />
  <property name="jacoco.report.dir"         location="${jacoco.dir}/reports" />
  <property name="jacoco.exec.file"          value="${jacoco.dir}/jacoco.exec" />

  <property file="${basedir}/sonar.properties" />

  <property name="sonar.host" value="localhost" />
  <property name="sonar.port" value="9000" />
  <property name="sonar.context" value="/" />

  <property name="sonar.mimosa.host" value="mimosa-crm-sonarserver.usa.acme.com" />
  <property name="sonar.mimosa.port" value="9000" />
  <property name="sonar.mimosa.context" value="/" />

  <!-- P A T H S ========================================================= -->
  <path id="build-classpath">
    <fileset dir="${lib.dir}">
      <include name="*/*.jar" />
    </fileset>
  </path>

  <path id="test-classpath">
    <path refid="build-classpath" />
    <fileset dir="${test.lib.dir}">
      <include name="*/*.jar" />
    </fileset>
    <pathelement path="${build-classes.dir}" />
  </path>

  <path id="junit-classpath">
    <path refid="test-classpath" />
    <pathelement path="${test-classes.dir}" />
  </path>

  <!-- T A R G E T S ===================================================== -->
  <target name="help">
    <echo message="Numbers on target descriptions indicate relative order in functionality grouping." />
    <java classname="org.apache.tools.ant.Main">
      <arg value="-projecthelp" />
      <arg value="-buildfile" />
      <arg value="${ant.file}" />
    </java>
  </target>

  <target name="show-properties" description="See property definitions in force during build.">
    <echoproperties />
  </target>

  <!-- c l e a n ========================================================= -->
  <target name="clean" depends="test-clean" description="Clean up temporary files and subdirectories.">
    <echo message="Cleaning up old targets..." />
    <delete dir="${build.dir}" />
    <delete dir="${dist.dir}" />
    <delete dir="${flat_libs.dir}" />
    <delete file="${extras.dir}/user-management.sql" />
  </target>

  <!-- i n i t =========================================================== -->
  <target name="init" depends="clean">
    <echo message="Initializing..." />
    <tstamp />
    <mkdir dir="${build-classes.dir}" />
    <mkdir dir="${dist.dir}" />
    <mkdir dir="${flat_libs.dir}" />
  </target>

  <!-- b u i l d n u m b e r ============================================= -->
  <target name="buildnumber" description="Display build.number value that will be used in package.">
    <property file="${basedir}/build.number" />
    <echo message="If you publish now, the Debian package build number will be ${build.number}." />
  </target>

  <!-- b u i l d - c o m p i l e ========================================= -->
  <target name="build-compile" depends="init,compile-java" description="Build Java code.">
    <echo message="Building Java sources..." />
  </target>

  <!-- c o m p i l e - j a v a =========================================== -->
  <target name="compile-java" description="Compile Java code.">
    <echo message="Compiling Java sources..." />

    <!-- This needs to be true or Sonar will not work properly -->
    <property name="compiler.debug" value="true" />

    <!-- Java compilation done here... ================================= -->
    <javac srcdir="${src.dir}" destdir="${build-classes.dir}"
           source="${src.level}" target="${target.level}" classpathref="build-classpath"
           debug="${compiler.debug}" debuglevel="lines,vars,source"
           includeantruntime="false" />
  </target>

  <!-- b u i l d - d t o - j a r ========================================= -->
  <target name="build-dto-jar" depends="init" description="Create DTO package JAR for client--required after DTO change!">
    <echo message="Compiling Java sources..." />

    <!-- Java compilation done here... ================================= -->
    <javac srcdir="${src.dir}" destdir="${build-classes.dir}"
           source="${src.level}" target="${target.level}" classpathref="build-classpath"
           includes="com/acme/web/user/dto/**"
           debug="true" debuglevel="lines,vars,source"
           includeantruntime="false" />
    <javac srcdir="${src.dir}" destdir="${build-classes.dir}"
           source="${src.level}" target="${target.level}" classpathref="build-classpath"
           includes="com/acme/web/staticdata/**"
           debug="true" debuglevel="lines,vars,source"
           includeantruntime="false" />

    <!-- The DTO classes are used for incoming REST requests to consolidate JSON into objects.
         External clients wanting to generate those requests programmatically should use the
         same classes so that they don't get out of sync.

         October, 2013: added the static data classes too. So these packages:

            com.acme.web.user.dto
            com.acme.web.staticdata

         Important note!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

         If a DTO is modified in accountmgr, e.g.: a new field and/or method is added or one
         is taken away, you must use this target to build the DTO JAR, copy it to client,
         build client, then copy the client JAR back to here. If you do not do this,
         a) the Jenkins build, which runs tests using client (CLASSPATH ordering problem),
         will fail due to failed test
         b) ant test-run will fail locally, but...
         c) JUnit tests will not fail from Eclipse (i.e.: you won't notice anything's wrong.)
      -->
    <echo message="Jaring DTO package..." />

    <jar jarfile="${dist.dir}/${dtojar.filename}">
      <fileset dir="${build-classes.dir}">
        <include name="com/acme/web/user/dto/*.class" />
        <include name="com/acme/web/staticdata/*.class" />
      </fileset>
      <fileset dir="${src.dir}">
        <include name="com/acme/web/user/dto/*.java" />
        <include name="com/acme/web/staticdata/*.java" />
      </fileset>
      <manifest>
        <attribute name="Built-By"               value="${user.name}" />
        <attribute name="Specification-Title"    value="DTO and static data classes" />
        <attribute name="Specification-Version"  value="${dtojar.version}" />
        <attribute name="Specification-Vendor"   value="Hewlett-Packard Development Company, L.P." />
        <attribute name="Implementation-Title"   value="dto" />
        <attribute name="Implementation-Version" value="${dtojar.version}" />
        <attribute name="Implementation-Vendor"  value="Hewlett-Packard Development Company, L.P." />
      </manifest>
    </jar>
  </target>

  <!-- b u i l d - w a r ================================================= -->
  <target name="build-war" depends="build-compile" description="Create WAR file.">
    <!-- Get build.number, if not already set -->
    <property file="${basedir}/build.number" />

    <echo message="Creating WAR file: BUILD ${build.number}" />
    <copy file="${src.dir}/log4j.properties" todir="${build-classes.dir}" />
    <copy todir="${flat_libs.dir}" flatten="true">
      <fileset dir="${lib.dir}">
        <include name="**/*.jar" />
        <exclude name="**/misc-doc/**" />
        <exclude name="**/non-deploy/**" />
        <exclude name="**/test-only/**" />
        <exclude name="**/*-sources.jar" />
        <exclude name="**/*-javadoc.jar" />
      </fileset>
    </copy>

    <war destfile="${war.file}" webxml="${web.dir}/WEB-INF/web.xml">
      <fileset dir="${web.dir}" />
      <lib dir="${flat_libs.dir}" />
      <classes dir="${build-classes.dir}" />
      <manifest>
        <attribute name="Built-By"               value="Web Platform Team" />
        <attribute name="Implementation-Title"   value="User Account Service" />
        <attribute name="Implementation-Version" value="1.00.${build.number}" />
        <attribute name="Implementation-Vendor"  value="Hewlett-Packard Development Company, LP." />
      </manifest>
    </war>

    <copy file="${war.file}" todir="${deploy.dir}" />
  </target>

  <!-- == D E B I A N   P A C K A G E   A N D   B U I L D ======================================= -->
  <!-- Sources for what goes into a package comes out of extras.

       extras
       +- debian
       | `- control
       +- scripts
       +- private
       |   `- staging
       |      +- certs
       |      +- conf
       |      +- keys
       |      |  `- oauth
       |      +- keystore
       +- accountmgr_db.js
       +- crontab.sh
       +- reinit_db.js
       `- update-installation.sh

       These all go to creating something like this:

       build
       `- debian
          +- accountmgr
          |  `- DEBIAN
          |    `- control
          +- opt
          |  `- acme
          |     `- apps
          |        `- accountmgr
          |           +- certs
          |           |  `- mimosa.qa.acme.com_ssl.cer
          |           +- keys
          |           |  +- oauth
          |           |  |  +- sf_v1a-int_public.key
          |           |  |  +- sf_v1a-stg_public.key
          |           |  |  `- v1.a_public.key
          |           |  `- secret.key
          |           +- keystore
          |           |  `- client.keystore
          |           `- scripts
          |              +- accountmgr_db.js
          |              +- crontab.sh
          |              +- reinit_db.js
          |              `- update-installation.sh
          `- var
             `- lib
                `- tomcat6
                   `- webapps
                      `- accountmgr.war
    -->
  <!-- == D E B I A N   P A C K A G E   A N D   B U I L D ======================================= -->
  <!-- d e b i a n - c l e a n =========================================== -->
  <target name="debian-clean" description="Clean out Debian-package intermediate files (1).">
    <echo message="Cleaning up Debian work area (${debian.dir})..." />
    <delete dir="${debian.dir}" />
  </target>

  <!-- d e b i a n - b u i l d - n u m b e r -->
  <target name="debian-build-number" description="Handle build.number (2).">
    <!-- Retreive value into build.number and THEN increment for next time -->
    <buildnumber />
    <echo message="Using build.number --> ${build.number}" />

    <!-- Inject build number into Debian control file ================== -->
    <echo message="    Build number is: ${build.number}" />
    <echo message="    Injecting new build number into Debian control file..." />
    <replaceregexp file="${debian.control.file}" byline="true">
      <regexp pattern="Version: 1.00.[0-9]*" />
      <substitution expression="Version: 1.00.${build.number}" />
    </replaceregexp>

    <echo message="Update build timestamp into ApplicationProperties.java..." />
    <tstamp><format property="BUILD_TIME" pattern="yyMMddHHmmssZ" /></tstamp>

    <!-- Tweaking of ApplicationProperties.java code... ================ -->
    <!-- This must be done before compiling code (build-war) or it won't be included -->
    <echo message="Inject new build number and build timestamp into ApplicationProperties.java..." />
    <echo message="    build number: ${build.number}, build timestamp: ${BUILD_TIME}" />
    <replaceregexp file="${app.properties.file}" byline="true">
      <regexp pattern="REVISION = "[0-9]*";" />
      <substitution expression="REVISION = "${build.number}";" />
    </replaceregexp>
    <replaceregexp file="${app.properties.file}" byline="true">
      <regexp pattern="BUILD_TIME = "[0-9]*[<-<+][0-9]*";" />
      <substitution expression="BUILD_TIME = "${BUILD_TIME}";" />
    </replaceregexp>
  </target>

  <!-- d e b i a n - p r e p a r e ======================================= -->
  <target name="debian-prepare" depends="debian-clean,debian-build-number,build-war"
          description="Set up to create Debian package (3).">

    <!-- Create debian directory structure -->
    <echo message="Creating Debian package build directories..." />
    <mkdir dir="${debian.dir}/${ant.project.name}/DEBIAN/" />
    <mkdir dir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/keys/oauth/" />
    <mkdir dir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/keystore/" />
    <!--mkdir dir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/scripts/" /-->

    <echo message="    Copying control (version) file..." />
    <copy todir="${debian.dir}/${ant.project.name}/DEBIAN/">
      <fileset dir="${extras.dir}/debian/">
        <include name="**/control" />
      </fileset>
    </copy>

    <!-- Don't know why we need certs since they are on loadbalancers -->
    <!--echo message="    Copying SSL certificates to package area..." />
    <copy todir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/certs/" failonerror="false">
      <fileset dir="${staging.local.dir}/certs/">
        <include name="**/*.cer" />
      </fileset>
    </copy -->
    <echo message="    Copying key files to package area..." />
    <copy todir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/keys" failonerror="false">
      <fileset dir="${extras.dir}/private/keys">
        <include name="**/*.key" />
      </fileset>
    </copy>
    <copy todir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/keys/oauth" failonerror="false">
      <fileset dir="${extras.dir}/private/keys/oauth">
        <include name="**/*.key" />
      </fileset>
    </copy>
    <echo message="    Copying keystore files to package area..." />
    <copy todir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/keystore/" failonerror="false">
      <fileset dir="${extras.dir}/private/keystore/">
        <include name="**/*.keystore" />
      </fileset>
    </copy>

    <!-- None of these scripts actually exist
    <echo message="    Copying script files to package area..." />
    <copy todir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/scripts/" failonerror="false">
      <fileset dir="${extras.dir}/">
        <include name="reinit_db.sh" />
        <include name="crontab.sh" />
        <include name="update-installation.sh" />
        <include name="accountmgr_db.js" />
      </fileset>
    </copy>

    <echo message="    Fix up permissions on script files in package area..." />
    <chmod perm="a+x">
      <fileset dir="${debian.dir}/${ant.project.name}/opt/acme/apps/${ant.project.name}/scripts/">
        <include name="*.sh" />
      </fileset>
    </chmod>
    -->

    <echo message="    Copying WAR file..." />
    <copy file="${war.file}" todir="${debian.dir}/${ant.project.name}${debian.deploy.loc}" />
  </target>

  <!-- d e b i a n - b u i l d =========================================== -->
  <target name="debian-build-with-test" depends="test-run,debian-prepare" description="Run JUnit tests, build Debian package (4)." />

  <target name="debian-build" depends="debian-prepare" description="Build Debian package (4).">
    <echo message="Building the Debian package with build number ${build.number}..." />

    <echo message="    Copying packaging scripts..." />
    <copy todir="${debian.dir}">
      <fileset dir="${scripts.dir}">
        <include name="**/*.sh" />
      </fileset>
    </copy>

    <chmod dir="${debian.dir}" perm="+x" includes="**/*.sh" />
    <exec dir="${debian.dir}" executable="./build-package.sh" />

    <copy todir="${dist.dir}" file="${debian.dir}/${ant.project.name}.deb" />
  </target>

  <!-- p r e p a r e - t o - p u b l i s h =============================== -->
  <target name="prepare-to-publish" depends="debian-build"
          description="Prepare to publish Debian package (5). Best semi-final target to see if build's okay.">
    <!-- This is complicated: ant can't execute shell commands, but only scripts. We
         1) Make the package (accountmgr.deb).
         2) List its details so you can be sure of what you're submitting.

         This target does everything except actually publishing; it's the target to
         use to run a "dry-publish".
      -->
    <echo message="Preparing to publish the Debian package with build number ${build.number}..." />
    <exec dir="${debian.dir}" executable="./build-package.sh" />
    <exec dir="${debian.dir}" executable="./get-package-info.sh" />
  </target>

  <!-- p u b l i s h ===================================================== -->
  <target name="publish" depends="prepare-to-publish" description="Publish Debian package (6).">
    <!-- Use publish-package.sh to publish to the Acme/Aptitude repository, development
         status (unpromoted). To publish to staging or production, you must first publish
         to development (unstable), then use one of the other targets below, which do not
         build or publish anything, to promote an existing package.
      -->
    <property name="APT_CMD" value="acme-apt -d publish ${debian.dir}/${ant.project.name}.deb${line.separator}" />

    <echo file="${debian.dir}/scripts/publish-package.sh" message="#!/bin/sh${line.separator}${line.separator}" />
    <echo file="${debian.dir}/scripts/publish-package.sh"
          message="echo ${APT_CMD}"
          append="true" />
    <echo file="${debian.dir}/scripts/publish-package.sh"
          message="${APT_CMD}"
          append="true" />

    <chmod file="${debian.dir}/scripts/publish-package.sh" perm="+x" type="both" />
    <exec dir="${debian.dir}/scripts/" executable="./publish-package.sh" />
  </target>

  <!-- p r o m o t e - s t a g i n g ===================================== -->
  <target name="promote-staging"
          description="Executes script that promotes package to staging (testing) status (6).">
    <!-- Endow publish-package.sh with a statement to promote to staging.
         This target will not make use of anything built in this invocation, but
         only promote what's already in the Acme/Aptitude unstable repository
         to the testing or QA repository.
      -->
    <echo message="Promote package to staging (QA/testing) repository..." />
    <echo file="${debian.dir}/scripts/publish-package.sh" message="#!/bin/sh${line.separator}" />
    <echo file="${debian.dir}/scripts/publish-package.sh"
          message="acme-apt -d promote ${debian.dir}/accountmgr.deb --dist testing${line.separator}"
          append="true" />
    <chmod file="${debian.dir}/scripts/publish-package.sh" perm="+x" type="both" />
    <exec dir="${debian.dir}/scripts/" executable="./publish-package.sh" />
  </target>

  <!-- p r o m o t e - p r o d u c t i o n =============================== -->
  <target name="promote-production"
          description="Promote package to production (stable) status (6).">
    <!-- Endow publish-package.sh with a statement to promote to production.
         This target will not make use of anything built in this invocation, but
         only promote what's already in the Acme/Aptitude unstable or testing
         repository to the production repository.
      -->
    <echo message="Publish Debian package to product (stable) repository..." />
    <echo file="${debian.dir}/scripts/publish-package.sh" message="#!/bin/sh${line.separator}" />
    <echo file="${debian.dir}/scripts/publish-package.sh"
          message="acme-apt -d promote ${debian.dir}/accountmgr.deb --dist stable${line.separator}"
          append="true" />
    <chmod file="${debian.dir}/scripts/publish-package.sh" perm="+x" type="both" />
    <exec dir="${debian.dir}/scripts/" executable="./publish-package.sh" />
  </target>

  <!-- == j e n k i n s - b u i l d - a n d - d e p l o y ====================================== -->
  <target name="jenkins-build"
          depends="test-run,build-war"
          description="Build WAR for developer sandbox (Jenkins-only target).">
    <echo message="Jenkins building for deployment to developer sandbox..." />
  </target>

  <target name="jenkins-deploy"
          depends="jenkins-build"
          description="Deploy built WAR to developer sandbox (Jenkins-only target).">
    <echo message="Jenkins deploying built WAR to developer sandbox..." />
    <chmod perm="a+x" file="${deploy.to.sandbox.script}" />
    <exec executable="${deploy.to.sandbox.script}" />
  </target>

  <!-- r e f r e s h - d a t a b a s e - o n - l o c a l h o s t ========= -->
  <target name="refresh-database-on-localhost"
          depends="erase-database-on-localhost,load-database-on-localhost,load-test-database-on-localhost"
          description="Refresh MongoDB database on localhost.">
    <!-- This refreshes (bounces) the local MongoDB or the replica set on Black Pearl -->
    <echo message="Refreshing database with ${database.shell.script}..." />
    <echo message="    This consists of removing all the databases and collections to start over..." />
    <echo message="      ...from scratch and adding some cool stuff." />
  </target>

  <!-- e r a s e - d a t a b a s e - o n - l o c a l h o s t ========= -->
  <target name="erase-database-on-localhost" description="Erase MongoDB database on localhost.">
    <echo message="Erasing {database.shell.script}..." />
    <exec executable="/bin/bash">
      <arg value="${database.shell.script}" />
      <arg value="${erase.database.script}" />
    </exec>
  </target>

  <!-- l o a d - d a t a b a s e - o n - l o c a l h o s t ========= -->
  <target name="load-database-on-localhost" description="Reload data into MongoDB database on localhost.">
    <echo message="Reloading database with ${database.shell.script}..." />
    <exec executable="/bin/bash">
      <arg value="${database.shell.script}" />
      <arg value="${load.database.script}" />
    </exec>
  </target>

  <!-- l o a d - t e s t - d a t a b a s e - o n - l o c a l h o s t ========= -->
  <target name="load-test-database-on-localhost" description="Reload test data into MongoDB database on localhost.">
    <echo message="Reloading test data into database with ${database.shell.script}..." />
    <exec executable="/bin/bash">
      <arg value="${database.shell.script}" />
      <arg value="${load.test.database.script}" />
    </exec>
  </target>

  <!-- r e f r e s h - d b - w i n d o w s ========= -->
  <target name="refresh-db-windows">
    <echo message="Refreshing database with mongo, if found on path" />
    <echo message="Dropping dbs ..." />
    <exec executable="cmd">
      <arg value="/c"/>
      <arg value="mongo" />
      <arg value="localhost:27017/accountmgrdb" />
      <arg value="${erase.database.script}" />
    </exec>
    <echo />
    <echo message="Loading essential data..." />
    <exec executable="cmd">
      <arg value="/c"/>
      <arg value="mongo" />
      <arg value="localhost:27017/accountmgrdb" />
      <arg value="${load.database.script}" />
    </exec>
    <echo />
    <echo message="Loading test data..." />
    <exec executable="cmd">
      <arg value="/c"/>
      <arg value="mongo" />
      <arg value="localhost:27017/accountmgrdb" />
      <arg value="${load.test.database.script}" />
    </exec>
  </target>

  <!-- d o c ============================================================= -->
  <target name="doc" depends="init">
    <echo message="Generating Javadoc..." />
    <javadoc packagenames="*" sourcepath="${src.dir}" classpathref="build-classpath" destdir="${build.dir}/classes" />
  </target>


  <!-- == Building and running tests =========================================================== -->

  <!-- t e s t - c l e a n =============================================== -->
  <target name="test-clean" description="Clean and build tests (1).">
    <echo message="Cleaning up test classes ..." />
    <delete dir="${test-classes.dir}" />
  </target>

  <!-- t e s t - c o m p i l e =========================================== -->
  <target name="test-compile" depends="build-compile" description="Compile Java test code (2).">
    <echo message="Compiling tests..." />
    <mkdir dir="${test-classes.dir}" />
    <javac srcdir="${test.dir}" destdir="${test-classes.dir}"
           source="${src.level}" target="${target.level}" classpathref="test-classpath"
           debug="true" debuglevel="lines,vars,source" includeAntRuntime="no"
           verbose="${test.compile.verbose}" />
  </target>

  <!-- t e s t - r u n =================================================== -->
  <target name="test-run" depends="test-compile" description="Run JUnit tests (3).">
    <echo message="Running tests..." />
    <mkdir dir="${test-reports.dir}" />
    <junit printsummary="yes" haltonfailure="no" failureproperty="isUnitTestFailed" fork="yes" forkmode="once">
      <classpath refid="junit-classpath" />
      <formatter type="xml" />
      <batchtest fork="yes" todir="${test-reports.dir}">
        <fileset dir="${test.dir}" includes="**/*Test.java" />
      </batchtest>
    </junit>
  </target>

  <!-- t e s t - r e p o r t ============================================= -->
  <target name="test-report" depends="test-run" description="Report JUnit test results (4).">
    <echo message="Reporting tests..." />
    <junitreport todir="${build.dir}">
      <fileset dir="${test-reports.dir}">
        <include name="TEST-*.xml" />
      </fileset>
      <report format="frames" todir="${build.dir}" />
    </junitreport>
  </target>

  <!-- == Code coverage with Sonar (dependent on Jacoco) ===================================== -->

  <!-- s o n a r - r e m o t e ============================= -->
  <!-- == Code coverage with Sonar (dependent on Jacoco) ===================================== -->
  <target name="sonar-remote" depends="jacoco-clean,jacoco-report" description="Deploy build to remote Sonar.">
    <echo message="Deploying to remote Sonar..." />
    <property name="sonar.host.url"                 value="http://${sonar.mimosa.host}:${sonar.mimosa.port}${sonar.mimosa.context}" />
    <property name="sonar.jdbc.url"                 value="jdbc:mysql://${sonar.mimosa.host}:3306/sonar?useUnicode=true&characterEncoding=utf8" />
    <property name="sonar.jdbc.driverClassName"     value="com.mysql.jdbc.Driver" />
    <property name="sonar.jdbc.username"            value="sonar" />
    <property name="sonar.jdbc.password"            value="sonar" />

    <property name="sonar.projectName"              value="${sonarProjectName}" />
    <property name="sonar.projectKey"               value="${sonarProjectKey}" />
    <property name="sonar.projectVersion"           value="${sonarProjectVersion}" />

    <property name="sonar.language"                 value="java" />
    <property name="sonar.sources"                  value="${src.dir}" />
    <property name="sonar.binaries"                 value="${build-classes.dir}" />
    <property name="sonar.libraries"                value="${lib.dir}/common/*.jar" />
    <property name="sonar.tests"                    value="${test.dir}" />
    <property name="sonar.dynamicAnalysis"          value="reuseReports" />
    <property name="sonar.surefire.reportsPath"     value="${test-reports.dir}" />

    <!-- Tells Sonar which code coverage tool to use -->
    <property name="sonar.java.coveragePlugin"      value="jacoco" />
    <!-- Tells Sonar where the unit tests code coverage report is -->
    <property name="sonar.jacoco.reportPath"        value="${jacoco.exec.file}" />
    <!-- Tells Sonar where the integration tests code coverage report is -->
    <!--property name="sonar.jacoco.itReportPath"     value="${jacoco.exec.file}" /-->
    <sonar:sonar xmlns:sonar="antlib:org.sonar.ant" />
  </target>

  <!-- s o n a r - l o c a l =================== -->
  <target name="sonar-local" depends="jacoco-clean,jacoco-report" description="Deploy build to local Sonar.">
    <echo message="Deploying to local Sonar..." />
    <property name="sonar.host.url"                 value="http://${sonar.host}:${sonar.port}${sonar.context}" />

    <property name="sonar.projectName"              value="${sonarProjectName}" />
    <property name="sonar.projectKey"               value="${sonarProjectKey}" />
    <property name="sonar.projectVersion"           value="${sonarProjectVersion}" />

    <property name="sonar.language"                 value="java" />
    <property name="sonar.sources"                  value="${src.dir}" />
    <property name="sonar.binaries"                 value="${build-classes.dir}" />
    <property name="sonar.libraries"                value="${lib.dir}/common/*.jar" />
    <property name="sonar.tests"                    value="${test.dir}" />
    <property name="sonar.dynamicAnalysis"          value="reuseReports" />
    <property name="sonar.surefire.reportsPath"     value="${test-reports.dir}" />

    <!-- use 'forceAnalysis' is a sonar anaylsis was stopped prematurely and now think one is already running -->
    <!-- property name="sonar.forceAnalysis"            value="true" / -->

    <!-- Tells Sonar which code coverage tool to use -->
    <property name="sonar.java.coveragePlugin"      value="jacoco" />
    <!-- Tells Sonar where the unit tests code coverage report is -->
    <property name="sonar.jacoco.reportPath"        value="${jacoco.exec.file}" />
    <!-- Tells Sonar where the integration tests code coverage report is -->
    <!--property name="sonar.jacoco.itReportPath"     value="${jacoco.exec.file}" /-->
    <sonar:sonar xmlns:sonar="antlib:org.sonar.ant" />
  </target>

  <!-- == Code coverage with Jacoco ========================================================== -->
  <!-- Jacoco requires the jacocoant.jar to be added to the ANT_HOME/lib.
       This file can be downloaded from http://jacoco.org/jacoco/index.html.
    -->
  <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml" />

  <!-- j a c o c o - c l e a n =========================================== -->
  <target name="jacoco-clean" description="Clean Jacoco intermediate objects (1).">
    <echo message="Cleaning up Jacoco..." />
    <delete dir="${jacoco.dir}" />
  </target>

  <!-- j a c o c o - r e p o r t ========================================= -->
  <target name="jacoco-report" depends="jacoco-run" description="Generate Jacoco report (3).">
    <echo message="Generating Jacoco reports..." />
    <jacoco:report xmlns:jacoco="antlib:org.jacoco.ant">
      <executiondata>
        <file file="${jacoco.exec.file}"/>
      </executiondata>
      <structure name="JaCoCo Reports">
        <classfiles>
          <fileset dir="${build-classes.dir}"/>
        </classfiles>
        <sourcefiles encoding="UTF-8">
          <fileset dir="${src.dir}"/>
        </sourcefiles>
      </structure>
      <html destdir="${jacoco.report.dir}"/>
      <xml destfile="${jacoco.report.dir}/report.xml"/>
    </jacoco:report>
  </target>

  <!-- j a c o c o - r u n =============================================== -->
  <target name="jacoco-run" depends="test-compile" description="Run Jacoco (2).">
    <mkdir dir="${test-reports.dir}" />
    <jacoco:coverage xmlns:jacoco="antlib:org.jacoco.ant" destfile="${jacoco.exec.file}">
      <junit printsummary="yes" haltonfailure="no" failureproperty="isUnitTestFailed" fork="yes" forkmode="once">
        <classpath refid="junit-classpath" />
        <formatter type="xml" />
        <batchtest fork="yes" todir="${test-reports.dir}">
          <fileset dir="${test.dir}" includes="**/*Test.java" />
        </batchtest>
      </junit>
    </jacoco:coverage>
  </target>

</project>

Contents of build.number (after the 153rd build invocation):

#Build Number for ANT. Do not edit!
#Wed Sep 11 11:43:24 MDT 2013
build.number=153

Using build.xml with IDEs...

Unlike using Maven's pom.xml in an IDE (like IntelliJ IDEA or Eclipse), build.xml isn't adequately exploited for dependencies. In the libs subdirectory implied above, are JARs that neither IDE will bring in for its build. I'm not sure why this is, but it's a reality.

The solution for IDEA is the Project Structure → Modules → Dependencies tab. In Eclipse (this goes back the better part of a decade for me), there's Build Path and a way to add library JARs. The last time I used Eclipse, Build Path had disappeared and I didn't take the time to investigate. This simply means that there are two builds: one for command line/Jenkins/whatever and one for the IDE. A bit of nasty asymmetry.