Tuesday, April 10, 2007

Building with Ant

In this short chapter we will replace the ugly build.bat file we created with
a nice little ant build file. You will need to have ant installed - get it from
the ant download
page
. How to install ant will not be covered here. Please refer to the ant
manual
. After you have installed ant, we can start to create the buildfile.
It will be called build.xml and placed directly in the development directory
and will replace the build.bat (which you can delete).


A basic build file


A basic build file looks like this:



<project name="hibernate-tutorial" default="compile">

<target name="compile">

</target>

</project>

The <project> tags are surround the whole buildfile. There are
two attributes, the name attribute which gives a name for the project being
built, and the default attribute which specifies the default target which will
be run if we launch ant without specifying a target.


Inside of the <project> tags, we have to give at least one <target>
block, where we can tell ant what to do - in this case, ant will do just nothing.
You can now run the build by running ant on the command line inside
the development directory.


You should get an output like this:



Buildfile: build.xml

compile:

BUILD SUCCESSFUL
Total time: 1 second

This tells us ant did run successfully and which build file was used. The default
target was run, which is why the compile: part of the output shows
us the compile target was executed. We can however give ant an explicit target
to run by calling ant compile from the command line, which will run
the compile target.


So now we want ant to actually compile our classes. So we insert the <javac>
task inside the <target> elements:



<project name="hibernate-tutorial" default="compile">

<target name="compile">
<javac srcdir="./src"
destdir="./bin"

debug="on"
/>
</target>

</project>

This will tell ant to launch the java compiler and compile everything it can
find under the the src directory and place the generated class files in the
bin directory. However if we now run ant, we will get a lot of compile errors,
because the compiler can not find the hibernate classes. So we have to tell
the compiler about the classpath to use, just as we did in the build.bat:



<project name="hibernate-tutorial" default="compile">

<target name="compile">
<javac srcdir="./src"
destdir="./bin"
debug="on"

>

<classpath>
<fileset dir="./lib">
<include name="*.jar"/>
</fileset>

</classpath>
</javac>
</target>

</project>

This will tell ant to find all files in the lib directory with .jar as file
ending and add them to the classpath used for compilation. If you now run ant,
you should get an output like this:



C:\hibernateTutorial\part2>ant
Buildfile: build.xml

compile:
[javac] Compiling 2 source files to C:\hibernateTutorial\part2\bin

BUILD SUCCESSFUL
Total time: 1 second


Dependent Targets


Great, so now we got ant to compile our two java files. This however still
leaves the log4j.properties and the mapping file, which are not copied to the
bin directory. We will take care of this now by adding an additional target:



<project name="hibernate-tutorial" default="compile">

<target name="compile">
<javac srcdir="./src"

destdir="./bin"
debug="on"
>

<classpath>
<fileset dir="./lib">

<include name="*.jar"/>
</fileset>
</classpath>
</javac>
</target>

<target name="copy-resources">

<copy todir="./bin">
<fileset dir="./src">
<exclude name="**/*.java"/>
</fileset>
</copy>

</target>

</project>

So this tells ant when it executes the copy-resources target to copy everything
it can find in the src directory or in any directories below to the bin directory
but exclude all java files anywhere under the src directory (this is what the
** in front of the / means).


So if you run ant now, you will see ... actually nothing. Ant will
execute the compile target and not touch our copy-resources target. So what
we need to do now is to tell ant it has to execute copy-resources before the
compile target - this is what the depends attribute of the <target>
element is for:



<project name="hibernate-tutorial" default="compile">

<target name="compile" depends="copy-resources">
<javac srcdir="./src"
destdir="./bin"
debug="on"

>

<classpath>
<fileset dir="./lib">
<include name="*.jar"/>
</fileset>

</classpath>
</javac>
</target>

<target name="copy-resources">
<copy todir="./bin">
<fileset dir="./src">

<exclude name="**/*.java"/>
</fileset>
</copy>
</target>

</project>


So if you now execute ant you should see output like this:



Buildfile: build.xml

copy-resources:
[copy] Copying 3 files to C:\hibernateTutorial\part2\bin

compile:

BUILD SUCCESSFUL
Total time: 0 seconds

So ant has now executed both targets and copied our resources over to the bin
directory. You will notice that ant does not print anything under the compile-target.
Ant notices that no source files have changed and does not compile them again.
The same will happen for copy-resources - ant will not copy our files again
unless we change them, or remove them from the bin directory.


Using Properties


So now we have a nice little build script in place. We could well go on from
here. You will notice however that our directory names are spread all over the
build file. Should we ever want to change them, we would have to change them
all over the build file. We will solve this problem by using ant property declarations:



<project name="hibernate-tutorial" default="compile">

<property name="sourcedir" value="${basedir}/src"/>
<property name="targetdir" value="${basedir}/bin"/>
<property name="librarydir" value="${basedir}/lib"/>

<target name="compile" depends="copy-resources">
<javac srcdir="${sourcedir}"
destdir="${targetdir}"
debug="on"

>

<classpath>
<fileset dir="${librarydir}">
<include name="*.jar"/>
</fileset>

</classpath>
</javac>
</target>

<target name="copy-resources">
<copy todir="${targetdir}">
<fileset dir="${sourcedir}">

<exclude name="**/*.java"/>
</fileset>
</copy>
</target>

</project>


So we can define our properties using the <property> tag, and
can insert them anywhere in the build file using the name we declared for the
property surrounded with ${}. Notice the ${basedir} property we use in the property
declarations - this is a property predefined by ant, which contains the path
of the directory where ant is executed.


In the next chapter, we will create associations between our classes using
java collections.


Code download


You can download the part two development directory here

No comments: