In order to tame the complexity of dealing with library dependencies within a project or across multiple projects, a scheme was developed to map them using Ant properties which refer to version number, directory, and JAR libraries within a common lib directory. Benefits include allowing easy switching of one library version to another on a per-project, per-build, or even a per-user basis.
The key to this scheme is Ant's property immutability. Once a property is set, its value may not be changed (with some exceptions, but generally speaking this is true). Property files are loaded by the main build file in this order (and order is important!):
<property file="${user.home}/.${ant.project.name}-build.properties"/>
<property file="${user.home}/.build.properties"/>
<property file="build.properties"/>
<!-- Load environment variables -->
<property environment="env"/>
<!-- Assign a friendly name for JBOSS_HOME -->
<property name="jboss.home" location="${env.JBOSS_HOME}"/>
<!-- Capture the computer name in a cross-platform manner -->
<property name="env.COMPUTERNAME" value="${env.HOSTNAME}"/>
<!-- Load generally common properties, such as javac.debug -->
<property file="common.properties"/>
The first three lines load user-project, user, and project specific settings,
with the user settings loaded from the ${user.home} directory. Then
environment variables are loaded and prefixed with "env.", allowing the earlier
property files a chance to override environment variable properties.
Next, a jboss.home property is set to make a friendlier appearance in
the remainder of the build file where it is needed.
The env.COMPUTERNAME property line is an interesting trick to get the
host name of the machine running the build in a cross-platform manner. Windows
machines use the COMPUTERNAME environment variable while 'nix platforms use
HOSTNAME (except Mac OS X, through version 10.2.3 at least, *arg*).
Finally, common.properties is loaded. It contains the general properties
that most build files have, such as build.dir.
Now back to the library properties. Just a few lines later in the
build file, the lib/lib.properties file is loaded:
<property name="lib.dir" location="lib"/>
<property file="${lib.dir}/lib.properties"/>
The lib.properties file contains properties for each of the dependent
libraries. The Struts library is a good example:
struts.version = 1.1-b2
struts.dir=${lib.dir}/jakarta-struts-${struts.version}-lib
struts.jar=${struts.dir}/struts.jar
Have a look at the directory structure under the lib directory to get
an idea of how this works, and match it up to other libraries listed in lib.properties.
Basically, the dependent libraries are extracted in-tact into the lib
directory, directory structure and all (sometimes the top-level directory
must be named manually, sometimes not). The mapping in lib.properties takes
this structure into account and maps the .dir and .jar properties appropriately.
All libraries referenced in the build file are done so through the .jar
properties (except XDoclet, but thats just out of laziness, but to be more
precise they should be also). For example, the classpath to compile
the web code in this project is:
<path id="web.compile.classpath">
<pathelement location="${dist.dir}/antbook-common.jar"/>
<pathelement location="${dist.dir}/antbook-ejb.jar"/>
<pathelement location="${struts.jar}"/>
<pathelement location="${oro.jar}"/>
<pathelement location="${commons-digester.jar}"/>
<pathelement location="${commons-fileupload.jar}"/>
<pathelement location="${commons-lang.jar}"/>
<pathelement location="${commons-resources.jar}"/>
<pathelement location="${commons-validator.jar}"/>
<pathelement location="${j2ee.jar}"/>
</path>
Now to the slick part of this scheme, making it all worthwhile.... to build with a newer version of a library, simply install it in the lib directory as the others are and build overriding the appropriate .version property. For example, to build with a new release (perhaps the 1.1 final release) of Struts:
ant -Dstruts.version=1.1
Or if you have a newly built version of Lucene that needs to be validated
against the test cases, simply point the .jar property to its location:
ant -Dlucene.jar=/path/to/new/lucene.jarOr modify (possibly create, if not already in existence) any of the three .properties files that are loaded and override the properties there for highly flexible control. The flexibility is not just for library properties, its for any of the other properties too.
<lib>
within the <war> task is a fileset reference too.<index> task is used:<index index="${index.dir}"
overwrite="false">
<fileset dir="${site.dir}"/>
</index>
A Lucene index is built/updated. The index (technically a directory
with many strangely named files in it) is left in place and the application
refers to it there. See the FAQ for more on the index
location.
<propertyfile> task.<propertyfile comment="Build Information"
file="${build.dir}/web/classes/build.properties">
<entry key="build.date"
type="date"
pattern="EEEE MMM dd, yyyy"
value="now"/>
<entry key="build.time"
type="date"
pattern="kk:mm:ss"
value="now"/>
<entry key="build.timestamp"
type="date"
pattern="yyyy-MM-dd'T'HH:mm:ss"
value="now"/>
<entry key="build.user.name" value="${user.name}"/>
<entry key="build.computer" value="${env.COMPUTERNAME}"/>
</propertyfile>
<copy todir="${build.dir}/${site}" overwrite="true">
<fileset dir="metadata/app" includes="application.xml"/>
<filterset>
<filter token="SITE" value="${site}"/>
</filterset>
</copy>
Within the templated application.xml, the string "@SITE@" ('@' is the default
delimiter) is replaced by the actual site name, which is in the value of
the${site} Ant property.