Build Jar with Externals

Kimo Johnson

14 October 2006

In several different Java development projects, I've needed to create a single jar with my project classes and also bundle the external jars my project requires. A while ago, I wrote an Ant build script to do this and I figure this script could be useful for others.

If you’re unfamiliar with Ant, it’s similar to Make, but it’s more modern and the XML syntax makes it easier to read and write. There have been times in the past when I’ve understood and used many of Make’s cryptic commands, but when I return to my Makefile at a later date I have to decode what I wrote. My experience with Ant has been much better - I recently returned to this script, which I wrote over a year ago, and I remembered exactly what was going on just by reading it. Of course, it achieves increased clarity with verbosity, but that’s not a bad tradeoff in my opinion.

Here’s the script. I’ve put in lots of comments to describe what’s going on. Not all the steps are specifically necessary for this plugin (I modified the script from a past project), but they illustrate several of Ant’s features.

<?xml version="1.0"?>
<project name="My_ImageJ_Plugin" default="jar" basedir=".">
<!-- where the project source code is found -->
    <property name="src" location="src" />
    <property name="bin" location="bin" />
    <property name="extdir" location="externals" />
    <property name="jardir" location="jar" />
    <property name="jarfile" location="${jardir}/${ant.project.name}.jar" />        
    <property name="tempsrcdir" location="tempsrc"/>
    <property name="myname" value="${user.name}" />
    <property file="build.properties" />
       
    <!-- Delete old temporary dirs and creates new dirs -->
    <target name="init">
        <tstamp>
              <format property="compiletime" pattern="MM/dd/yyyy hh:mm aa"/>
        </tstamp>
        <echo message="creating build.properties"/>
        <delete dir="${jardir}" />
        <delete dir="${tempsrcdir}" />
        <delete dir="${bin}"/>
        <mkdir dir="${jardir}" />
        <mkdir dir="${tempsrcdir}" />
        <mkdir dir="${bin}"/>
    </target>

    <!-- Copy source files to a new directory.  I originally needed to do -->
    <!-- this because I did a substitution on the source code that put the-->
    <!-- name of the person compiling the code into the source.           -->
    <target name="copyfiles" depends="init">
        <copy todir="${tempsrcdir}" filtering="true">
            <fileset dir="${src}">
                <include name="**/*.java"/>
            </fileset>
        </copy>
        <echo message="copied source files"/>
    </target>
 
    <!-- Compile.  The classpath is set to include the external jars      -->
    <!-- mentioned in my previous blog about ImageJ plugin development.   -->
    <target name="compile" depends="init,copyfiles" description="compile classes">
        <javac destdir="${bin}" srcdir="${tempsrcdir}" classpath="${extdir}/imageio.jar:${extdir}/jama.jar:${basedir}/ij.jar">
        </javac>
    </target>
        
    <!-- Build the jar.  The important step here is the zipgroupfileset   -->
    <!-- which unpacks all my external jars and bundles them into the jar --> 
    <!-- we are building. The plugins.config file is for ImageJ plugins.  -->
    <!-- You would typically set the <manifest> here with something like  -->
    <!-- <attribute name="Main-Class" value="your.main.class"/>           -->
    <target name="jar" depends="init,copyfiles,compile" description="build jar">
           <jar destfile="${jarfile}">
              <fileset dir="${bin}">
                <include name="**/*.class"/>
              </fileset>
                <zipgroupfileset dir="${extdir}" includes="*.jar" excludes="*.config"/>        
            <fileset dir="${basedir}">
                <include name="plugins.config" />
            </fileset>
        </jar>
     </target>

     <!-- Clean up jar and other temp directories -->
    <target name="clean" description="delete all generated files">
        <delete dir="${jardir}" quiet="true"/>
        <delete dir="${tempsrcdir}" quiet="true"/>
        <delete dir="${bin}" quiet="true"/>
    </target>
 </project>

Here’s the file, save it as build.xml: build.xml